免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: g84ch

[原创]关于劫持系统调用隐藏进程的一些心得 [复制链接]

论坛徽章:
0
发表于 2009-09-23 22:20 |显示全部楼层

回复 #19 Godbach 的帖子

我没有在redhat上的试过,不过ubuntu上是试过的。。。

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
发表于 2009-09-24 19:23 |显示全部楼层
测试了albcamus版主给的劫持execve,open调用的例程。由于我的2.6.18的内核上的有个结构体和2.6.22(albcamus版主的例程)上的不一致,暂时屏蔽了劫持execve的代码,这里仅劫持open系统调用,修改后的代码如下:
  1. #include <linux/kernel.h>
  2. #include <linux/init.h>
  3. #include <linux/module.h>
  4. #include <linux/kprobes.h>
  5. #include <linux/kallsyms.h>
  6. #include <linux/sched.h>
  7. #include <linux/ptrace.h>
  8. #include <linux/mm.h>
  9. #include <linux/smp.h>
  10. #include <linux/user.h>
  11. #include <linux/errno.h>
  12. #include <linux/cpu.h>
  13. #include <asm/uaccess.h>
  14. #include <asm/fcntl.h>
  15. #include <asm/unistd.h>

  16. MODULE_DESCRIPTION("Intercept the system call table in Linux");
  17. MODULE_AUTHOR("alert7 ([email]alert7@xfocus.org[/email]) \n\t\talbcamus <[email]albcamus@gmail.com[/email]>");
  18. MODULE_LICENSE("GPL");


  19. /* comment the following line to shut me up */
  20. #define INTERCEPT_DEBUG

  21. #ifdef INTERCEPT_DEBUG
  22.     #define dbgprint(format,args...) \
  23.         printk("intercept: function:%s-L%d: "format, __FUNCTION__, __LINE__, ##args);
  24. #else
  25.     #define dbgprint(format,args...)  do {} while(0);
  26. #endif


  27. /**
  28. * the system call table
  29. */
  30. void **my_table;

  31. unsigned int orig_cr0;

  32. /**
  33. * the original syscall functions
  34. */
  35. asmlinkage long (*old_open) (char __user *filename, int flags, int mode);
  36. asmlinkage int  (*old_execve) (struct pt_regs regs);



  37. /** do_execve and do_fork */
  38. unsigned int can_exec_fork = 0;
  39. int    (*new_do_execve) (char * filename,
  40.             char __user *__user *argv,
  41.             char __user *__user *envp,
  42.             struct pt_regs * regs);


  43. struct idtr {
  44.     unsigned short limit;
  45.     unsigned int base;
  46. } __attribute__ ((packed));


  47. struct idt {
  48.     unsigned short off1;
  49.     unsigned short sel;
  50.     unsigned char none, flags;
  51.     unsigned short off2;
  52. } __attribute__ ((packed));


  53. #if 0
  54. /**
  55. *  check if we can intercept fork/vfork/clone/execve or not
  56. *
  57. *  return : 0 for no, 1 for yes
  58. */
  59. struct kprobe kp_exec;
  60. unsigned int can_intercept_fork_exec(void)
  61. {
  62.     int ret = 0;

  63. #ifndef CONFIG_KPROBES
  64.     return ret;
  65. #endif

  66.     kp_exec.symbol_name = "do_execve";

  67.     ret = register_kprobe(&kp_exec);
  68.     if (ret != 0 ) {
  69.         dbgprint("cannot find do_execve by kprobe.\n");
  70.         return 0;
  71.     }
  72.     new_do_execve = ( int (*)
  73.              (char *,
  74.               char __user * __user *,
  75.               char __user * __user *,
  76.               struct pt_regs *
  77.              )
  78.             ) kp_exec.addr;

  79.     dbgprint("do_execve at %p\n", (void *)kp_exec.addr);
  80.     unregister_kprobe(&kp_exec);


  81.     return 1;
  82. }

  83. #endif

  84. /**
  85. * clear WP bit of CR0, and return the original value
  86. */
  87. unsigned int clear_and_return_cr0(void)
  88. {
  89.     unsigned int cr0 = 0;
  90.     unsigned int ret;

  91.     asm volatile ("movl %%cr0, %%eax"
  92.               : "=a"(cr0)
  93.               );
  94.     ret = cr0;

  95.     /* clear the 20 bit of CR0, a.k.a WP bit */
  96.     cr0 &= 0xfffeffff;

  97.     asm volatile ("movl %%eax, %%cr0"
  98.               :
  99.               : "a"(cr0)
  100.               );
  101.     return ret;
  102. }

  103. /** set CR0 with new value
  104. *
  105. * @val : new value to set in cr0
  106. */
  107. void setback_cr0(unsigned int val)
  108. {
  109.     asm volatile ("movl %%eax, %%cr0"
  110.               :
  111.               : "a"(val)
  112.               );
  113. }


  114. /**
  115. * Return the first appearence of NEEDLE in HAYSTACK.  
  116. * */
  117. static void *memmem(const void *haystack, size_t haystack_len,
  118.             const void *needle, size_t needle_len)
  119. {/*{{{*/
  120.     const char *begin;
  121.     const char *const last_possible
  122.         = (const char *) haystack + haystack_len - needle_len;

  123.     if (needle_len == 0)
  124.         /* The first occurrence of the empty string is deemed to occur at
  125.            the beginning of the string.  */
  126.         return (void *) haystack;

  127.     /* Sanity check, otherwise the loop might search through the whole
  128.        memory.  */
  129.     if (__builtin_expect(haystack_len < needle_len, 0))
  130.         return NULL;

  131.     for (begin = (const char *) haystack; begin <= last_possible;
  132.          ++begin)
  133.         if (begin[0] == ((const char *) needle)[0]
  134.             && !memcmp((const void *) &begin[1],
  135.                    (const void *) ((const char *) needle + 1),
  136.                    needle_len - 1))
  137.             return (void *) begin;

  138.     return NULL;
  139. }/*}}}*/


  140. /**
  141. * Find the location of sys_call_table
  142. */
  143. static unsigned long get_sys_call_table(void)
  144. {/*{{{*/
  145. /* we'll read first 100 bytes of int $0x80 */
  146. #define OFFSET_SYSCALL 100        

  147.     struct idtr idtr;
  148.     struct idt idt;
  149.     unsigned sys_call_off;
  150.     unsigned retval;
  151.     char sc_asm[OFFSET_SYSCALL], *p;

  152.     /* well, let's read IDTR */
  153.     asm("sidt %0":"=m"(idtr)
  154.              :
  155.              :"memory" );

  156.     dbgprint("idtr base at 0x%X\n", (unsigned int)idtr.base);

  157.     /* Read in IDT for vector 0x80 (syscall) */
  158.     memcpy(&idt, (char *) idtr.base + 8 * 0x80, sizeof(idt));

  159.     sys_call_off = (idt.off2 << 16) | idt.off1;

  160.     dbgprint("idt80: flags=%X sel=%X off=%X\n",
  161.                  (unsigned) idt.flags, (unsigned) idt.sel, sys_call_off);

  162.     /* we have syscall routine address now, look for syscall table
  163.        dispatch (indirect call) */
  164.     memcpy(sc_asm, (void *)sys_call_off, OFFSET_SYSCALL);

  165.     /**
  166.      * Search opcode of `call sys_call_table(,eax,4)'
  167.      */
  168.     p = (char *) memmem(sc_asm, OFFSET_SYSCALL, "\xff\x14\x85", 3);
  169.     if (p == NULL)
  170.         return 0;

  171.     retval = *(unsigned *) (p + 3);
  172.     if (p) {
  173.         dbgprint("sys_call_table at 0x%x, call dispatch at 0x%x\n",
  174.              retval, (unsigned int) p);
  175.     }
  176.     return retval;
  177. #undef OFFSET_SYSCALL
  178. }/*}}}*/



  179. /**
  180. * new_open - replace the original sys_open when initilazing,
  181. *           as well as be got rid of when removed
  182. */
  183. asmlinkage long new_open(char *filename, int flags, int mode)
  184. {
  185.     dbgprint("hello\n");
  186.     return old_open (filename, flags, mode);
  187. }


  188. /**
  189. * new_execve - you should change this function whenever the kernel's sys_execve()
  190. * changes
  191. */
  192. asmlinkage int new_execve(struct pt_regs regs)
  193. {
  194.     int error;
  195.     char *filename;

  196.     dbgprint("Hello\n");

  197.     filename = getname( (char __user *) regs.ebx );
  198.     error = PTR_ERR(filename);
  199.     if ( IS_ERR(filename) )
  200.         goto out;
  201.     dbgprint("file to execve: %s\n", filename);
  202.     error = new_do_execve(filename,
  203.                   (char __user * __user *) regs.ecx,
  204.                   (char __user * __user *) regs.edx,
  205.                   &regs);
  206.     if (error == 0) {
  207.         task_lock(current);
  208.         current->ptrace &= ~PT_DTRACE;
  209.         task_unlock(current);
  210.         set_thread_flag(TIF_IRET);
  211.     }
  212.     putname (filename);
  213. out:
  214.     return error;
  215. }

  216. static int intercept_init(void)
  217. {
  218.     my_table = (void **)get_sys_call_table();
  219.     if (my_table == NULL)
  220.         return -1;

  221.     dbgprint("sys call table address %p\n", (void *) my_table);

  222. #define REPLACE(x) old_##x = my_table[__NR_##x];\
  223.     my_table[__NR_##x] = new_##x

  224.    
  225.     REPLACE(open);
  226. #if 0
  227.     can_exec_fork = can_intercept_fork_exec();
  228.     if(can_exec_fork == 1)
  229.         REPLACE(execve);
  230. #endif

  231. #undef REPLACE
  232.     return 0;
  233. }





  234. static int __init this_init(void)
  235. {
  236.     int ret;
  237.     printk("syscall intercept: Hi, poor linux!\n");

  238.     orig_cr0 = clear_and_return_cr0();   
  239.     ret = intercept_init();
  240.     setback_cr0(orig_cr0);

  241.     return ret;
  242. }

  243. static void __exit this_fini(void)
  244. {
  245.     printk("syscall intercept: bye, poor linux!\n");

  246. #define RESTORE(x) my_table[__NR_##x] = old_##x

  247.     orig_cr0 = clear_and_return_cr0();   
  248.     RESTORE(open);
  249. #if 0
  250.     if(can_exec_fork == 1)
  251.         RESTORE(execve);
  252. #endif
  253.     setback_cr0(orig_cr0);

  254. #undef RESTORE
  255. }

  256. module_init(this_init);
  257. module_exit(this_fini);
复制代码


然后查看一下日志,看到如下信息:
Sep 24 19:06:49 localhost kernel: intercept: function:get_sys_call_table-L220: sys_call_table at 0xc11f14e0, call dispatch at 0xcebeceaa
Sep 24 19:06:49 localhost kernel: intercept: function:intercept_init-L276: sys call table address c11f14e0
Sep 24 19:06:50 localhost kernel: intercept: function:new_open-L234: hello
Sep 24 19:07:00 localhost last message repeated 460 times

可见open系统调用被成功劫持,而且系统在执行的过程中,open的调用是很频繁的。

论坛徽章:
0
发表于 2009-09-25 11:24 |显示全部楼层

回复 #22 Godbach 的帖子

关于CR0的问题,当时困扰了我很久....CU上的帖子真是好资源啊~~~~

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
发表于 2009-09-25 11:27 |显示全部楼层
原帖由 g84ch 于 2009-9-25 11:24 发表
关于CR0的问题,当时困扰了我很久....CU上的帖子真是好资源啊~~~~


呵呵,众人拾柴火焰高啊

论坛徽章:
0
发表于 2009-10-12 15:17 |显示全部楼层

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
发表于 2009-10-13 15:45 |显示全部楼层
网上看到一个用户态获取syscall table地址的例程,计算出的idtr.base地址和我上面给出的内核模块计算出的不一样,用户态例程链接为http://www.phpweblog.net/GaRY/ar ... _table_address.html,源码如下:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #include <fcntl.h>
  7. #include <unistd.h>
  8. #include <errno.h>
  9. #include <sys/mman.h>
  10. #define CALLOFF 100 //读取100字节

  11. struct {

  12.     unsigned short limit;

  13.     unsigned int base;

  14. } __attribute__ ((packed)) idtr;    //这个结构表示IDTR寄存器,这个寄存器中保存中断描述符表 的地址


  15. struct {

  16.     unsigned short off1;

  17.     unsigned short sel;

  18.     unsigned char none,flags;

  19.     unsigned short off2;

  20. } __attribute__ ((packed)) idt;    //中断描述符表中的内容:中断门描述符


  21. unsigned int old_readkmem (int fd, void * buf,size_t off,unsigned int size) //用read方式读取kmem中一定长度内容

  22. {

  23.     if (lseek(fd, off,SEEK_SET)!=off)
  24.     {

  25.         //perror("fd lseek");
  26.         return 0;
  27.     }

  28.     if (read(fd, buf,size)!=size)
  29.     {

  30.         //perror("fd read");
  31.         return 0;
  32.     }

  33. }


  34. unsigned long  readkmem (int fd, void * buf, size_t off, unsigned int size)//用mmap方式从kmem中读取一定长度内容
  35. {
  36.     size_t    moff, roff;
  37.     size_t     sz = getpagesize();
  38.    
  39.     char * kmap;
  40.    
  41.     unsigned long  ret_old = old_readkmem(fd, buf, off, size); //先用老方法读取,不行再用mmap
  42.     if (ret_old != 0)
  43.         return ret_old;
  44.    
  45.     moff = ((size_t)(off/sz)) * sz;      
  46.     roff = off - moff;   
  47.    
  48.     kmap = mmap(0, size+sz, PROT_READ, MAP_PRIVATE, fd, moff);
  49.    
  50.     if (kmap == MAP_FAILED)
  51.     {
  52.         perror("readkmem: mmap");
  53.         return 0;
  54.     }
  55.    
  56.     memcpy (buf, &kmap[roff], size);
  57.    
  58.     if (munmap(kmap, size) != 0)
  59.     {
  60.         perror("readkmem: munmap");
  61.         return 0;
  62.     }
  63.    
  64.     return size;
  65. }

  66. int main (int argc, char **argv)

  67. {

  68.     unsigned sys_call_off;

  69.     int kmem_fd;    // /dev/kmem文件描述符

  70.     unsigned sct;

  71.     char sc_asm[CALLOFF],*p;

  72.    
  73.         /* 获得IDTR寄存器的值 */

  74.     asm ("sidt %0" : "=m" (idtr));

  75.     printf("idtr base at 0x%X\n",(int)idtr.base);

  76.    
  77.         /* 打开kmem */

  78.     kmem_fd = open ("/dev/kmem",O_RDONLY);

  79.     if (kmem_fd<0) return 1;

  80.    
  81.         /* 从IDT读出0x80向量 (syscall) */

  82.     readkmem (kmem_fd, &idt,idtr.base+8*0x80,sizeof(idt)); //idtr.base+8*0x80 表示80中断描述符的偏移

  83.     sys_call_off = (idt.off2 << 16) | idt.off1;        //idt.off2 表示地址的前16位,得到syscall地址

  84.     printf("idt80: flags=%X sel=%X off=%X\n", (unsigned)idt.flags,(unsigned)idt.sel,sys_call_off);

  85.    
  86.         /* 寻找sys_call_table的地址 */

  87.     readkmem (kmem_fd, sc_asm,sys_call_off,CALLOFF);      

  88.     p = (char*)memmem (sc_asm,CALLOFF,"\xff\x14\x85",3);  //只要找到邻近int $0x80入口点system_call的call sys_call_table(,eax,4)指令的机器指令就可以了,call something(,eax,4)指令的机器码是0xff 0x14 0x85,因此搜索这个字符串。

  89.     sct = *(unsigned*)(p+3); //sys_call_table地址就在0xff 0x14 0x85之后

  90.     if (p)
  91.     {

  92.         printf ("sys_call_table at 0x%x, call dispatch at 0x%x\n", sct, p);

  93.     }

  94.     close(kmem_fd);
  95.     return 0;
  96. }
复制代码

论坛徽章:
0
发表于 2009-10-20 10:06 |显示全部楼层

debian lenny 2.6.26-2-686测试结果

测试代码,都可以在
http://blog.chinaunix.net/u/12592/showart_1903466.html

测试结果,/dev/kmem和lkm方式获得的sys_call_table的地址是一样的
/dev/kmem测试:
idtr base at 0xC036C000
idt80: flags=EF sel=60 off=C010388C
sys_call_table at 0xc02bfaa0, call dispatch at 0xbf8debae
addr(__kmalloc): c0171d25
kmalloc: 0xc0171d25
Old sys_uname: 0xc012df46
off: 0xc02bfc88
write: 4
Now sys_uname: 0xc0171d25
off: 0xc02bfc88
write: 4
Kernel Space allocation: 0xf6d12440
Write Kill Opcode To Kernel Buf.
addr(u per_cpu__current_task): c03b4000
addr(sys_kill): c012ba45
off: 0xf6d12440
write: 85
Write Opcode Successed!
off: 0xc02bfb34
write: 4
hijack sys_kill from 0xc012ba45[-1072514491] to 0xf6d12440[-154065856]
###NOTES to recovery sys_kill.


lkm测试:
debian-wangyao:/home/wangyao/Documents/Reports/rootkit_report/0802/code/others/print_sys_call_table# tail /var/log/kern.log
Oct 20 09:59:35 debian-wangyao kernel: [   78.431383] CPU1 attaching NULL sched-domain.
Oct 20 09:59:35 debian-wangyao kernel: [   78.431454] CPU0 attaching sched-domain:
Oct 20 09:59:35 debian-wangyao kernel: [   78.431454]  domain 0: span 0-1
Oct 20 09:59:35 debian-wangyao kernel: [   78.431454]   groups: 0 1
Oct 20 09:59:35 debian-wangyao kernel: [   78.431454] CPU1 attaching sched-domain:
Oct 20 09:59:35 debian-wangyao kernel: [   78.431454]  domain 0: span 0-1
Oct 20 09:59:35 debian-wangyao kernel: [   78.431454]   groups: 1 0
Oct 20 10:02:27 debian-wangyao kernel: [  252.056927] system_call: 0xc010388c
Oct 20 10:02:27 debian-wangyao kernel: [  252.056927] Here Find sys_call_table: 0xc01038ca
Oct 20 10:02:27 debian-wangyao kernel: [  252.056927] sys_call_table: 0xc02bfaa0

论坛徽章:
0
发表于 2009-10-20 10:19 |显示全部楼层
2.6.x以后 kmem都没了, mem都不让写了, 别搞这些了, 有时间多搞搞lkm的吧

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
发表于 2009-10-20 10:19 |显示全部楼层

回复 #27 CUDev 的帖子

CUDev兄,你测试一下我在22楼的代码

论坛徽章:
0
发表于 2009-10-20 10:23 |显示全部楼层

回复 #26 Godbach 的帖子

不好意思,那个帖中的代码有些问题,已经更正了。
http://blog.chinaunix.net/u/12592/showart_1421096.html

[ 本帖最后由 CUDev 于 2009-10-20 10:26 编辑 ]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP