(转)拦截系统调用 (Linux 3.2.0)
| 分类: 内核开发 |
转自: http://blog.csdn.net/u011923747/article/details/11650445
原理
因为分派表中的每个表项占4个字节,因此首先把系统调用号乘以4,再加上sys_call_table分配表的起始地址,然后从从这个地址单元获取指向服务例程的指针,内核就找到了要调用的服务例程。我们只要修改对应的分配表项,即可实现系统调用的拦截。
第二种方法,设置虚拟地址对应页表项的读写属性:
附:完整代码
今天在ubuntu中玩了下“拦截系统调用”,记录下自己对整个实现的理解。
原理
在linux kernel中,系统调用都放在一个叫做“sys_call_table”的分配表里面,在进入一个系统调用的最后一步,会调用与eax中包含的系统调用号对应的特定服务例程:
[cpp] view plaincopy
-
call
*sys_call_table(,�x,4)
因为分派表中的每个表项占4个字节,因此首先把系统调用号乘以4,再加上sys_call_table分配表的起始地址,然后从从这个地址单元获取指向服务例程的指针,内核就找到了要调用的服务例程。我们只要修改对应的分配表项,即可实现系统调用的拦截。
获取sys_call_table的地址
网上介绍了很多种方法得到sys_call_table的地址,我使用了相对简单的一种方法——从内核导出的符号表中获取。
图中,十六进制数c15b3000即为sys_call_table的地址。同时,我们也得到了一个重要的信息,该符号对应的内存区域是只读的!
清除写保护
因为sys_call_table分配表的内存属性为只读,因此,我们要先清除对应地址的写保护。暂时使用了两种方法实现该目的:
第一种方法,修改cr0读写保护位:
[cpp] view plaincopy
-
-
unsigned
int clear_and_return_cr0( void) -
{
-
unsigned int cr0 = 0; -
unsigned int ret; -
-
asm volatile ( "movl%%cr0, %�x" -
: "=a"(cr0) -
); -
ret = cr0; -
-
-
cr0 &= 0xfffeffff; -
-
asm volatile ( "movl%�x, %%cr0" -
: -
: "a"(cr0) -
); -
-
return ret; -
}
-
-
-
void
setback_cr0(unsigned intval) -
{
-
asm volatile ( "movl%�x, %%cr0" -
: -
: "a"(val) -
); -
}
第二种方法,设置虚拟地址对应页表项的读写属性:
[cpp] view plaincopy
-
-
int
make_rw(unsigned longaddress) -
{
-
unsigned int level; -
pte_t *pte = lookup_address(address, &level); -
if (pte->pte & ~_PAGE_RW) -
pte->pte |= _PAGE_RW; -
-
return 0; -
}
-
-
-
-
int
make_ro(unsigned longaddress) -
{
-
unsigned int level; -
pte_t *pte = lookup_address(address, &level); -
pte->pte &= ~_PAGE_RW; -
-
return 0; -
}
附:完整代码
[cpp] view plaincopy
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
-
-
-
-
unsigned
long **sys_call_table long= (unsigned **)0xc15b3000; -
unsigned
long *orig_mkdir = NULL; -
-
-
-
int
make_rw(unsigned longaddress) -
{
-
unsigned int level; -
pte_t *pte = lookup_address(address, &level); -
if (pte->pte & ~_PAGE_RW) -
pte->pte |= _PAGE_RW; -
-
return 0; -
}
-
-
-
-
int
make_ro(unsigned longaddress) -
{
-
unsigned int level; -
pte_t *pte = lookup_address(address, &level); -
pte->pte &= ~_PAGE_RW; -
-
return 0; -
}
-
-
-
-
asmlinkage
long hacked_mkdir( constchar __user int*pathname, mode) -
{
-
printk("mkdir pathname: ,%s\n" pathname); -
printk(KERN_ALERT "mkdir do );nothing!\n" -
-
return 0; -
}
-
-
-
static
int syscall_init_module( void) -
{
-
printk(KERN_ALERT "sys_call_table: 0x%lx\n" ,sys_call_table); -
orig_mkdir = (unsigned long *)(sys_call_table[__NR_mkdir]); -
printk(KERN_ALERT "orig_mkdir: 0x%lx\n" ,orig_mkdir); -
-
make_rw((unsigned long)sys_call_table); -
sys_call_table[__NR_mkdir] = (unsigned long *)hacked_mkdir; -
make_ro((unsigned long)sys_call_table); -
-
return 0; -
}
-
-
static
void syscall_cleanup_module( void) -
{
-
printk(KERN_ALERT "Module syscall );unloaded.\n" -
-
make_rw((unsigned long)sys_call_table); -
sys_call_table[__NR_mkdir] = (unsigned long *)orig_mkdir; -
make_ro((unsigned long)sys_call_table); -
}
-
-
-
module_init(syscall_init_module);
-
module_exit(syscall_cleanup_module);
-
-
MODULE_LICENSE("GPL");
-
MODULE_DESCRIPTION("hack
syscall" );
参考:
1、《深入理解linux内核(第三版)》

加载中…