Linux内核hook 演示


我以前利用0x80中断程序找到system_call然后找到 sys_call_table的方法,现在试下hook系统调用sys_mkdir来阻止创建目录小试牛刀。

#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/unistd.h>
#include <linux/sched.h>
#include <linux/syscalls.h>
MODULE_LICENSE("Dual BSD/GPL");

#define _DEBUG
#ifdef _DEBUG
#define kprintk(fmt,args...) printk(KERN_DEBUG fmt,##args)
#define kprintf(fmt,args...) printf(fmt,##args)
#define kperror(str) perror(str)
#else
#define kprintk
#define kprintf
#define kperror
#endif


long g_old_sys_mkdir=0;
long * g_sys_call_table=NULL;
long g_oldcr0=0;

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;

    /*clear the 20th bit of 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)
            );
}

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));
    printk("addr of idtr: 0x%x\n", (unsigned int)&idtr);
    idt=(struct _idt_descriptor *)(idtr.base+8*0x80);
    sys_call_off=((unsigned int )(idt->offset_high<<16)|(unsigned int )idt->offset_low);
    printk("addr 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("addr of sys_call_table: 0x%x\n", sys_call_table);
            return (long*)sys_call_table;
        }
    }
    return 0;
}


//asmlinkage long my_sys_getdents64(unsigned int fd, struct linux_dirent64 __user * dirent, unsigned int count)

asmlinkage long my_sys_mkdir(const char __user *pathname, int mode)
{

    kprintk("can't' mkidr ^ ^\n");
    return -1;
}


void start_hook(void)
{
    g_sys_call_table=get_sys_call_table();

    if(!g_sys_call_table)
    {
        kprintk("get sys_call_table error");
        return;
    }
    //检测获取到的地址是不是正确的
    if (g_sys_call_table[__NR_close] != (unsigned long)sys_close)
    {  
        kprintk("Incorrect sys_call_table address.\n");
        return ;
    }
    g_old_sys_mkdir=g_sys_call_table[__NR_mkdir];
    //hoot it
    g_oldcr0=close_cr();
    g_sys_call_table[__NR_mkdir]=my_sys_mkdir;
    open_cr(g_oldcr0);
    kprintk("new %08x %08x\n",(unsigned int)my_sys_mkdir,(unsigned int)g_sys_call_table[__NR_mkdir]);

}

int raider_init(void)
{
    kprintk("raider init\n");
    start_hook();
    return  0;
}

void raider_exit(void)
{
    kprintk("raider exit");
    if(g_sys_call_table && g_old_sys_mkdir)
    {
        g_oldcr0=close_cr();
        g_sys_call_table[__NR_mkdir]=g_old_sys_mkdir;
        kprintk("restore %08x\n",(unsigned int)g_sys_call_table[__NR_mkdir]);
        open_cr(g_oldcr0);
    }
}


module_init(raider_init);
module_exit(raider_exit);

代码在2.6.28下测试通过。

可见,只要得到sys_call_table后,就能轻易的做很多手段,其实和windows下修改ssdt没有本质区别只是网上关于linux的实现大部分内核版本比较老,有的都不用改cr0的,所以我很奇怪,也许以前的内核版本可行吧.

其实通往内核的接口还有一个sysenter_entry,通过它也可以得到sys_call_table的,只是这个接口同样没有导出,要得到也得用特殊办法了.

相关内容