其他分享
首页 > 其他分享> > 使用LKM中的syscall表

使用LKM中的syscall表

作者:互联网

我正在从Linux(3.x)的syscall表中重写SYS_READ,但是在卸载模块本身时遇到了一些麻烦.
我首先加载我的模块,该模块找到syscall表,然后启用RW,使用我自己的SYS_READ函数覆盖SYS_READ(实际上,除了调用原始SYS_READ以外,它不执行其他任何操作),然后等待片刻,然后卸载模块.在模块的unload方法上,我将原始SYS_READ函数还原回syscall表中,并将syscall表重新设置为RO.

原始的SYS_READ函数已正确还原,但是在卸载模块时得到此信息:http://pastebin.com/JyYpqYgL

我想念什么?恢复真正的SYS_READ之后,我还应该做更多的事情吗?

编辑:GitHub链接到项目:https://github.com/alexandernst/procmon

编辑:

这是我获取系统调用表地址的方式:

void **sys_call_table;

struct idt_descriptor{
    unsigned short offset_low;
    unsigned short selector;
    unsigned char zero;
    unsigned char type_flags;
    unsigned short offset_high;
} __attribute__ ((packed));


struct idtr{
    unsigned short limit;
    void *base;
} __attribute__ ((packed));


void *get_sys_call_table(void){
    struct idtr idtr;
    struct idt_descriptor idtd;
    void *system_call;
    unsigned char *ptr;
    int i;

    asm volatile("sidt %0" : "=m" (idtr));
    memcpy(&idtd, idtr.base + 0x80 * sizeof(idtd), sizeof(idtd));
    system_call = (void*)((idtd.offset_high<<16) | idtd.offset_low);
    for(ptr=system_call, i=0; i<500; i++){
        if(ptr[0] == 0xff && ptr[1] == 0x14 && ptr[2] == 0x85)
            return *((void**)(ptr+3));
        ptr++;
    }

    return NULL;
}

sys_call_table = get_sys_call_table();

这就是我设置RW / RO的方式:

unsigned long set_rw_cr0(void){
    unsigned long cr0 = 0;
    unsigned long ret;
    asm volatile("movq %%cr0, %%rax" : "=a"(cr0));
    ret = cr0;
    cr0 &= 0xfffffffffffeffff;
    asm volatile("movq %%rax, %%cr0" : : "a"(cr0));
    return ret;
}

void set_ro_cr0(unsigned long val){
    asm volatile("movq %%rax, %%cr0" : : "a"(val));
}

最后,这是我定义系统调用并更改系统调用表的方式:

asmlinkage ssize_t (*real_sys_read)(unsigned int fd, char __user *buf, size_t count);
asmlinkage ssize_t hooked_sys_read(unsigned int fd, char __user *buf, size_t count);

//set my syscall
real_sys_read = (void *)sys_call_table[__NR_read];
sys_call_table[__NR_read] = (void *)hooked_sys_read;

//restore real syscall
sys_call_table[__NR_read] = (void *)real_sys_read;

解决方法:

如果您希望卸载拦截系统调用的模块,请注意某些情况仍在系统调用处理程序中并且您的代码(模块的文本段)离开内存时的情况.这导致页面错误,因为当进程从某个内核函数(休眠)返回到您的代码时,该代码不再存在.

因此,正确的模块卸载方案必须检查挂钩系统调用中可能休眠的进程.仅当syscall挂钩中没有任何进程处于休眠状态时,才可以卸载.

UPD

请查看证明我的理论的补丁.它添加了在hooked_sys_read调用时递增和递减的原子计数器.因此,正如我想的那样,模块卸载后,仍在read_sys_read中等待一个进程.该补丁显示了printk(read_counter)并为我打印了1,这意味着某人不会减少read_counter.

http://pastebin.com/1yLBuMDY

标签:module,linux-kernel,c-3,linux
来源: https://codeday.me/bug/20191122/2063415.html