截获或替换linux系统调用

来源:互联网 发布:财务金融知乎 编辑:程序博客网 时间:2024/04/30 22:46

     直接上代码吧:

     hello.c:

#include <linux/kernel.h> /*Needed by all modules*/
#include <linux/module.h> /*Needed for KERN_* */
#include <linux/init.h> /* Needed for the macros */
#include <linux/syscalls.h> //包含系统调用的相关定义和声明
//若要打印相关目录,则需要包含以下的头文件
#include <linux/fs.h>
#include <linux/fs_struct.h>
#include <linux/dcache.h>
#include <linux/path.h>

//内核开发者容不得闭源,所以要想利用内核编程,必须包含这个宏定义
MODULE_LICENSE("GPL");

//内核为了安全,虽然内核符号表导出了sys_call_table,但是不能直接使用
//否则,加载模块会报“未定义的符号”错误信息,所以,我们直接使用
//sys_call_table的内核地址,用cat /proc/kallsyms | grep sys_call_table
//命令可以找到他的内核地址:c165e140 R sys_call_table
//这里还有一个注意点,就是R表明其是只读的,所以我们要想替换系统调用(就必须
//写入我们自己的函数以替换内核系统调用,这里需要写的权限),后面我们将看到,
//我们在内核态改变了cr0寄存器的WP标志位,令其为0从而允许写操作。
#define SYS_TAB 0xc165e140

//指向系统调用数组
unsigned long* sys_call_table = (unsigned long*)SYS_TAB;
int (*orig_mkdir)(void);

unsigned int clear_and_return_cr0(void);
void setback_cr0(unsigned int val);
int orig_cr0;

asmlinkage int my_mkdir(const char *path)
{
    //获取当前进程的id
    int pid = current->pid;
    //获取当前进程的父进程id
    int ppid = current->real_parent->real_parent->pid;
    //获取当前进程的根目录
    const char *ppwd = (current->fs->root).dentry->d_name.name;
    struct path pwd;
    //获取当前目录
    get_fs_pwd(current->fs,&pwd);
    printk(KERN_WARNING "Warnning Someone(PID=%d,parent=%d) attempts to mkdir!\n",pid,ppid);
    printk(KERN_WARNING "ROOT:%s!\n",ppwd);
    printk(KERN_WARNING "PWD:%s!\n",pwd.dentry->d_name.name);
    return 0;
}

int hello_init(void)
{
    printk(KERN_WARNING "Hello kernel, it's %d!\n",2014);
   
    //清cr0的WP位
    orig_cr0 = clear_and_return_cr0();
    //保存原系统调用
    orig_mkdir=(int (*)(void))sys_call_table[__NR_mkdir];
    //等价sys_call_table[__NR_mkdir]=(unsigned long)&my_mkdir;
    //替换成我们自己的函数
    sys_call_table[__NR_mkdir]=(unsigned long)my_mkdir;
    //为了内核安全,恢复cr0的WP位
    setback_cr0(orig_cr0);
    return 0;
}


void hello_exit(void)
{
    printk("Bye, kernel!");
    orig_cr0 = clear_and_return_cr0();
    //恢复原系统调用
    sys_call_table[__NR_mkdir]=(unsigned long)orig_mkdir;
    setback_cr0(orig_cr0);
}

unsigned int clear_and_return_cr0(void)
{
    unsigned int cr0 = 0;
    unsigned int ret;
    asm volatile ("movl %%cr0, %%eax": "=a"(cr0));
    ret = cr0;
    /*clear the 20th bit of CR0,*/
    cr0 &= 0xfffeffff;
    asm volatile ("movl %%eax, %%cr0"::"a"(cr0));
    return ret;
}

void setback_cr0(unsigned int val)
{
    asm volatile ("movl %%eax, %%cr0": : "a"(val));
}
/* main module function*/
module_init(hello_init);
module_exit(hello_exit);

Makefile文件:

obj-m := hello.o

all:
    $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
    $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean


    把hello.c文件和Makefile文件放在同一个目录之下,执行make命令,即可得到hello.ko,以root权限执行insmod ./hello.ko。然后用lsmod | grep hello命令可看到此模块已经加载了。tail /var/log/kern.log命令可以查看代码中的printk输出。好了,我们执行mkdir adf命令想新建目录,但是相应的目录不会被创建,而且kern.log中会有相应的输出。

0 0
原创粉丝点击