转自http://blog.chinaunix.net/uid-20196318-id-28808.html
方法1:修改系统调用表(适用于linux-2.4内核)
内核使用sys_call_table数组来存储系统调用表,将系统调用号与系统调用处理函数对应起来,通过修改sys_call_table数组的某一个元素,即可实现截获系统调用的功能,在2.4内核中,sys_call_table符号是被导出的,外部模块可以使用,故能简单的实现截获系统调用,在加载模块时,修改sys_call_table的处理函数,并保存其原来的值,在卸载模块时,还原其处理函数。
在2.6内核中,由于sys_call_table符号不可见,网上有介绍在内存中查找sys_call_table地址的方法,不过比较复杂,尚未弄清其原理。
方法2:通过修改VFS操作表
该方法只能截获vfs相关的系统调用,通过修改file_operations,inode_operations的成员操作函数来实现,代码如下所示:
#include<linux/sched.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/file.h> MODULE_LICENSE("GPL"); char *root_fs="/"; typedef int(*readdir_t)(structfile *,void*,filldir_t); readdir_t orig_root_readdir=NULL; int myreaddir(structfile *fp,void*buf,filldir_t filldir) { int r; printk("<1>You got me partner!\n"); r=orig_root_readdir(fp,buf,filldir); return r; } int patch_vfs(constchar *p,readdir_t*orig_readdir,readdir_t new_readdir) { struct file*filep; filep=filp_open(p,O_RDONLY,0); if(IS_ERR(filep)) return -1; if(orig_readdir) *orig_readdir=filep->f_op->readdir; //filep->f_op->readdir=new_readdir; struct file_operations *fop = filep->f_op; fop->readdir = new_readdir; filep->f_op = fop; filp_close(filep,0); return 0; } int unpatch_vfs(constchar *p,readdir_t orig_readdir) { struct file*filep; filep=filp_open(p,O_RDONLY,0); if(IS_ERR(filep)) return -1; //filep->f_op->readdir=orig_readdir; struct file_operations *fop = filep->f_op; fop->readdir = orig_readdir; filep->f_op = fop; filp_close(filep,0); return 0; } static int patch_init(void) { patch_vfs(root_fs,&orig_root_readdir,myreaddir); printk("<1>VFS is patched!\n"); return 0; } static void patch_cleanup(void) { unpatch_vfs(root_fs,orig_root_readdir); printk("<1>VFS is unpatched!\n"); } module_init(patch_init); module_exit(patch_cleanup); |
以上模块在linux-2.6.19内核上编译不能通过,提示错误消息为:readdir成员为只读的,不能对其赋值(红色部分)。于是,我引入一个中间变量,来达到修改操作表的目的(将红色部分修改为蓝色部分)。