Linux内核--hook系统调用

Linux系统调用劫持:

其实就是修改内核符号表,来达到一个劫持的作用。因为系统调用实际上是触发了一个0x80的软中断,然后转到了系统调用处理程序的入口system_call()system_call()会检查系统调用号来得出到底是调用哪种服务,然后会根据内核符号表跳转到所需要调用的内核函数的入口地址,所以,如果我们这个时候修改了内核符号表,使其跳转到我们自己的函数上,就可以完成劫持。

不过2.6之后,内核符号表便不再导出了,所以需要我们在内存中找到它的位置。具体查找的方式为:查找到中断向量表中的0x80中断服务函数的位置,然后在这个中断服务函数代码中查找根据内核符号表跳转的指令,从指令中可以分析出内核符号表的位置。

还是先贴代码吧。

 

#include<linux/init.h>

#include<linux/module.h>

#include<linux/moduleparam.h>

#include<linux/unistd.h>

#include<linux/sched.h>

#include<linux/syscalls.h>

#include<linux/string.h>

#include<linux/fs.h>

#include<linux/fdtable.h>

#include<linux/uaccess.h>

 

#include<linux/rtc.h>

 

MODULE_LICENSE("Dual BSD/GPL");

 

#define _DEBUG

#ifdef _DEBUG

#define kprintk(fmt,args...) printk(KERN_ALERT fmt,##args)

#define kprintf(fmt,args...) printf(fmt,##args)

#define kperror(str) perror(str)

#else

#define kprintk

#define kprintf

#define kperror

#endif

 

/*Function declaration*/

long * get_sys_call_table(void);

unsigned int close_cr(void);

void open_cr(unsigned int oldval);

void start_hook(void);

asmlinkage long (*orig_open)(char __user *filename, int flags, int mode);

 

long * g_sys_call_table = NULL; //save address of sys_call_table

long g_old_sys_open = 0; //save old address of sys_open

long g_oldcr0 = 0; //save address of cr0

 

 

struct _idtr{

    unsigned short limit;

    unsigned int base;

}__attribute__((packed));

 

struct _idt_descriptor{

    unsigned short offset_low;

    unsigned short sel;

    unsigned char none,flags;

    unsigned short offset_high;

}__attribute__((packed));

 

unsigned int close_cr(void){

    unsigned int cr0 = 0;

    unsigned int ret;

    asm volatile("movl%%cr0,%%eax":"=a"(cr0));

    ret = cr0;

    cr0 &= 0xfffeffff;

    asm volatile("movl%%eax,%%cr0"::"a"(cr0));

    return ret;

}

 

void open_cr(unsigned int oldval){

    asm volatile("movl%%eax,%%cr0"::"a"(oldval));

}

 

/*Get the address of sys_call_table*/

long * get_sys_call_table(void){

   

 

    struct _idt_descriptor * idt;

    struct _idtr idtr;

    unsigned int sys_call_off;

    int sys_call_table=0;

    unsigned char* p;

    int i;

    asm("sidt%0":"=m"(idtr));

    kprintk("   address of idtr: 0x%x\n",(unsignedint)&idtr);

    idt=(struct _idt_descriptor*)(idtr.base+8*0x80);

    sys_call_off=((unsignedint)(idt->offset_high<<16)|(unsigned int)idt->offset_low);

    kprintk("   address of idt 0x80:0x%x\n",sys_call_off);

    p=(unsigned char *)sys_call_off;

    for(i=0;i<100;i++){

       if(p[i]==0xff&&p[i+1]==0x14&&p[i+2]==0x85){

           sys_call_table=*(int*)((int)p+i+3);

            kprintk("   address of sys_call_table:0x%x\n",sys_call_table);

 

            return(long*)sys_call_table;

        }

    }

   

    return 0;

}

 

//My own sys_open

asmlinkage long my_sys_open(char * filename, int flags, int mode){

    kprintk("The process is\"%s\"(pid is %i)\n",current->comm,current->pid);

    kprintk("The file is beingaccessed is \"%s\"\n",filename);

    return orig_open(filename,flags,mode);

}

 

void start_hook(void){

    g_sys_call_table =get_sys_call_table();

    if(!g_sys_call_table){

        kprintk("Getsys_call_table error!\n");

        return;

    }

    if(g_sys_call_table[__NR_close]!= (unsigned long)sys_close){

        kprintk("Incorrect sys_call_tableaddress!\n");

        return;

    }

   

    g_old_sys_open =g_sys_call_table[__NR_open];

    orig_open = (long(*)(char *,int, int))g_sys_call_table[__NR_open];

   

    g_oldcr0=close_cr();

    g_sys_call_table[__NR_open] =my_sys_open;

    open_cr(g_oldcr0);

}

 

int monitor_init(void){

    kprintk("Monitorinit\n");

    start_hook();

    return 0;

}

 

void monitor_exit(void){

    if(g_sys_call_table &&g_old_sys_open){

        g_oldcr0 = close_cr();

        g_sys_call_table[__NR_open]= g_old_sys_open;

        open_cr(g_oldcr0);

    }

    kprintk("Monitorexit\n");

}

 

module_init(monitor_init);

module_exit(monitor_exit);

modu.c编译,然后加载到内核中。

加载成功后,执行“dmesg”,看到的系统日志如图。

如果稍微晚一点执行“dmesg”,系统日志就可能被截获open调用打印的信息刷屏了,因为open系统调用实在是被调用的太多了。

可以执行“lsmod”,查看当前系统中含有的模块,可以找到我们刚刚加载的modu


接下来解释一下原理:

 “截获”的过程即是:修改系统调用表中调用函数的地址,将其执行我们自己实现的函数,再在我们自己的函数中完成我们想做的事情后,在返回到原来的系统调用执行流程中。

代码里面的这个函数asmlinkage long my_sys_open(char *filename, int flags, int mode),就是我们自己实现的调用函数,注意这里的形参是参考系统原有的open调用函数的原型来了,必须是这个样子。每种系统调用的原型是什么样子,可以自行百度。

my_sys_open()中,我们打印了当前是哪个进程在访问(进程名和进程号的信息),访问的是哪个文件(文件的绝对路径),打印完后跳转到原来的系统调用函数。

在模块初始化的过程中,执行start_hook()函数。在start_hook()函数中,先获得系统调用表的地址,将系统调用表中的原有地址保存下来,再将my_sys_open()函数的地址赋到系统调用表中。

注意修改系统调用表时,由于内核中的很多东西,比如这里的系统调用表sys_call_table是只读的,我们需要修改一下权限才能修改。由于控制寄存器CR0的第16位若置位,则表示禁止系统进程写那些只有只读权限的文件,所以我们在修改系统调用表sys_call_table之前先将CR0的第16位清零,在修改完后再恢复置位就好了。如代码里的close_cr()函数,即是将CR016位清零,open_cr()函数是将CR016位恢复。

最后在卸载modu模块的时候,将系统调用表的内容还原就OK了。

 



  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值