一 获取系统调用表地址
在Linux内核2.6之后,不能直接导出sys_call_table的地址后,我们要如何获得系统调用表的地址,从而实现系统调用的截获呢。
将生成的.ko 文件加载到内核中.
执行“dmesg”,查看系统日志,如图。
接下来就要解释解释原理了。
我们知道Linux系统中的系统调用是通过用户软件调用中断int0x80激发的,int0x80被执行后,内核获得CPU的控制权,并交由system_call程序处理。即sys_call_table是由system_call进行调用的。
而system_call是int0x80软中断,即int0x80中断对应的地址就是system_call函数的地址。而Linux系统中所有中断信息都保存在一张中断描述表IDT中,而这张表的地址又是保存在IDTR寄存器里面,所以整个截获过程可以用如下图表示。
即先在IDTR寄存器中获得IDT_TABLE的地址,再在IDT_TABLE中获得int0x80的地址,int0x80对应的是system_call函数的地址。最后通过system_call函数的地址获得sys_call_table的地址。
二. Hook系统调用举例(hook mkdir 系统调用)
“截获”的过程即是:修改系统调用表中调用函数的地址,将其执行我们自己实现的函数,再在我们自己的函数中完成我们想做的事情后,在返回到原来的系统调用执行流程中。
代码里面的这个函数asmlinkagelong my_sys_mkdir(const char __user, int mode),就是我们自己实现的调用函数,注意这里的形参是参考系统原有的open调用函数的原型来了,必须是这个样子。每种系统调用的原型是什么样子,可以自行百度。
在my_sys_mkdir()中,我们直接返回失败。
在模块初始化的过程中,执行start_hook()函数。在start_hook()函数中,先获得系统调用表的地址,将系统调用表中的原有地址保存下来,再将my_sys_mkdir()函数的地址赋到系统调用表中。
注意修改系统调用表时,由于内核中的很多东西,比如这里的系统调用表sys_call_table是只读的,我们需要修改一下权限才能修改。由于控制寄存器CR0的第 16位若置位,则表示禁止系统进程写那些只有只读权限的文件,所以我们在修改系统调用表sys_call_table之前先将CR0的第16位清零,在修改完后再恢复置位就好了。如代码里的close_cr()函数,即是将CR0第16位清零,open_cr()函数是将CR0第16位恢复。
最后在卸载模块的时候,将系统调用表的内容还原就OK了。