首页 > 代码库 > [fw]拦截系统调用

[fw]拦截系统调用

今天在ubuntu中玩了下“拦截系统调用”,记录下自己对整个实现的理解。

原理

在linux kernel中,系统调用都放在一个叫做“sys_call_table”的分配表里面,在进入一个系统调用的最后一步,会调用与eax中包含的系统调用号对应的特定服务例程:

 

[cpp] view plaincopy
 
  1. call *sys_call_table(,%eax,4)  


因为分派表中的每个表项占4个字节,因此首先把系统调用号乘以4,再加上sys_call_table分配表的起始地址,然后从从这个地址单元获取指向服务例程的指针,内核就找到了要调用的服务例程。我们只要修改对应的分配表项,即可实现系统调用的拦截。

 

获取sys_call_table的地址

网上介绍了很多种方法得到sys_call_table的地址,我使用了相对简单的一种方法——从内核导出的符号表中获取。

图中,十六进制数c15b3000即为sys_call_table的地址。同时,我们也得到了一个重要的信息,该符号对应的内存区域是只读的!

清除写保护

因为sys_call_table分配表的内存属性为只读,因此,我们要先清除对应地址的写保护。暂时使用了两种方法实现该目的:

第一种方法,修改cr0读写保护位:

 

[cpp] view plaincopy
 
  1. /* 清除写保护 */  
  2. unsigned int clear_and_return_cr0(void)  
  3. {  
  4.         unsigned int cr0 = 0;  
  5.         unsigned int ret;  
  6.   
  7.         asm volatile ("movl %%cr0, %%eax"  
  8.                         : "=a"(cr0)  
  9.                      );  
  10.         ret = cr0;  
  11.   
  12.         /* clear the 16 bit of CR0, a.k.a WP bit */  
  13.         cr0 &= 0xfffeffff;  
  14.   
  15.         asm volatile ("movl %%eax, %%cr0"  
  16.                         :  
  17.                         : "a"(cr0)  
  18.                      );  
  19.   
  20.         return ret;  
  21. }  
  22.   
  23. /* 设置cr0,--本程序用来恢复写保护 */  
  24. void setback_cr0(unsigned int val)  
  25. {  
  26.         asm volatile ("movl %%eax, %%cr0"  
  27.                         :  
  28.                         : "a"(val)  
  29.                      );  
  30. }  


第二种方法,设置虚拟地址对应页表项的读写属性:

 

 

[cpp] view plaincopy
 
  1. /* make the page writable */  
  2. int make_rw(unsigned long address)  
  3. {  
  4.         unsigned int level;  
  5.         pte_t *pte = lookup_address(address, &level);  
  6.         if (pte->pte & ~_PAGE_RW)  
  7.                 pte->pte |=  _PAGE_RW;  
  8.           
  9.         return 0;  
  10. }  
  11.   
  12.   
  13. /* make the page write protected */  
  14. int make_ro(unsigned long address)  
  15. {  
  16.         unsigned int level;  
  17.         pte_t *pte = lookup_address(address, &level);  
  18.         pte->pte &= ~_PAGE_RW;  
  19.   
  20.         return 0;  
  21. }  


附:完整代码

 

 

[cpp] view plaincopy
 
  1. #include <linux/module.h>  
  2. #include <linux/kernel.h>  
  3. #include <linux/init.h>  
  4. #include <linux/sched.h>  
  5. #include <linux/init.h>  
  6. #include <linux/fs.h>  
  7. #include <linux/file.h>  
  8. #include <linux/fs_struct.h>  
  9. #include <linux/fdtable.h>  
  10. #include <linux/string.h>  
  11. #include <linux/mm.h>  
  12. #include <linux/syscalls.h>  
  13. #include <linux/list.h>  
  14. #include <linux/jiffies.h>  
  15. #include <linux/cdev.h>  
  16. #include <asm/unistd.h>  
  17. #include <asm/uaccess.h>  
  18. #include <linux/path.h>  
  19. #include <linux/time.h>  
  20. #include <linux/stat.h>  
  21. #include <net/sock.h>  
  22. #include <net/inet_sock.h>  
  23. #include <asm/cpufeature.h>  
  24.   
  25.   
  26.   
  27. /* grep sys_call_table  /boot/System.map-`uname -r` */  
  28. unsigned long **sys_call_table = (unsigned long **)0xc15b3000;  
  29. unsigned long *orig_mkdir = NULL;  
  30.   
  31.   
  32. /* make the page writable */  
  33. int make_rw(unsigned long address)  
  34. {  
  35.         unsigned int level;  
  36.         pte_t *pte = lookup_address(address, &level);  
  37.         if (pte->pte & ~_PAGE_RW)  
  38.                 pte->pte |=  _PAGE_RW;  
  39.           
  40.         return 0;  
  41. }  
  42.   
  43.   
  44. /* make the page write protected */  
  45. int make_ro(unsigned long address)  
  46. {  
  47.         unsigned int level;  
  48.         pte_t *pte = lookup_address(address, &level);  
  49.         pte->pte &= ~_PAGE_RW;  
  50.   
  51.         return 0;  
  52. }  
  53.   
  54.   
  55.   
  56. asmlinkage long hacked_mkdir(const char __user *pathname, int mode)  
  57. {  
  58.         printk("mkdir pathname: %s\n", pathname);  
  59.         printk(KERN_ALERT "mkdir do nothing!\n");  
  60.   
  61.         return 0; /*everything is ok, but he new systemcall does nothing*/  
  62. }  
  63.   
  64.   
  65. static int syscall_init_module(void)  
  66. {  
  67.         printk(KERN_ALERT "sys_call_table: 0x%lx\n", sys_call_table);  
  68.         orig_mkdir = (unsigned long *)(sys_call_table[__NR_mkdir]);  
  69.         printk(KERN_ALERT "orig_mkdir: 0x%lx\n", orig_mkdir);  
  70.   
  71.         make_rw((unsigned long)sys_call_table);  
  72.         sys_call_table[__NR_mkdir] = (unsigned long *)hacked_mkdir;  
  73.         make_ro((unsigned long)sys_call_table);  
  74.   
  75.         return 0;  
  76. }  
  77.   
  78. static void syscall_cleanup_module(void)  
  79. {  
  80.         printk(KERN_ALERT "Module syscall unloaded.\n");  
  81.   
  82.         make_rw((unsigned long)sys_call_table);  
  83.         sys_call_table[__NR_mkdir] = (unsigned long *)orig_mkdir; /*set mkdir syscall to the origal one*/  
  84.         make_ro((unsigned long)sys_call_table);  
  85. }  
  86.   
  87.   
  88. module_init(syscall_init_module);  
  89. module_exit(syscall_cleanup_module);  
  90.   
  91. MODULE_LICENSE("GPL");  
  92. MODULE_DESCRIPTION("hack syscall");  



 

参考:

1、《深入理解linux内核(第三版)》

2、Hijack Linux System Calls: Part III. System Call Table