linux 中 inline hook 简单分析(do_exit)



author: jonathan

本文档的CopyRight归jonathan所有,可自由转载,转载时请保持文档的完整性。
/*----------------------------------------------------------------------------------------------------------------------------*/

Hook多年不搞了,总认为是上不了台面的技术。但是由于产品的需要没有办法,还是要弄一弄。

本文中重点描述一下Linux的函数Hook中注意的关键点。

1 概念

Hook两个字描述:劫持

对于目标是对象,就是对象HOOK;对于目标是函数,就叫Inline Hook;对于目标是IAT,就叫IAT Hook.

2 方法

对于函数HOOK,要能够跳转到劫持函数中,再跳转原函数。如何跳转,使用什么指令?其实方法很多,最多就是使用jmp,因为简单。如果使用call也可以,还需要自己维护call产生的栈问题。

当然,插入HOOK点理论上可以在函数任何位置,但是要保障插入HOOK点前后指令的完整性。现在disassembler库也很多,都不是难题。

2.1 jmp方法

    jmp dst_address_offset

    此命令占5个地址; dst_address_offset = 目的地址 - HOOK点开始位置 - jmp指令占用地址(5)

2.2 call方法
   
    call dst_address_offset

    此处注意call的分解:push 返回地址;jmp 目标地址。因此在HOOK函数返回时,注意平衡堆栈,特别时使用jmp方式返回

    投机的方式:找到一个以有的call处,修改call处的跳转地址即可。

3 注意事项

3.1 内存要可写,可检查CR0寄存器中的WP位

3.2 处理好堆栈平衡

3.3 原子操作,特别是多cpu情况

4 Windows平台 HOOK

文章数不胜数,略。

5 Linux下函数Inline Hook

这里以do_exit为例。do_exit函数是导出函数,所以可以直接获取函数地址;对于非导出函数,则需要相关函数查找地址。现在假设do_exit未导出。

为了找到非导出函数地址,需要在内存中找特征码。但是对于要搜寻的函数空间也有两点要求:

    函数地址可知,否则陷入鸡生蛋问题;

    该函数要调用寻找的未导出函数地址。

对于do_exit,我们首先应该想到是sys_exit函数。

5.1 查找do_exit函数地址

(gdb) disass sys_exit
Dump of assembler code for function sys_exit:
0xc042ef4c <sys_exit+0>:        push   %ebp
0xc042ef4d <sys_exit+1>:        mov    %esp,%ebp
0xc042ef4f <sys_exit+3>:        mov    0x8(%ebp),%eax
0xc042ef52 <sys_exit+6>:        shl    $0x8,%eax
0xc042ef55 <sys_exit+9>:        and    $0xffff,%eax
0xc042ef5a <sys_exit+14>:       call   0xc042e79a <do_exit>
End of assembler dump.
(gdb) x/2 0xc042ef5a
0xc042ef5a <sys_exit+14>:       0xfff83be8      0xc08555ff
(gdb) disass do_exit
Dump of assembler code for function do_exit:
0xc042e79a <do_exit+0>: push   %ebp
0xc042e79b <do_exit+1>: mov    %esp,%ebp
0xc042e79d <do_exit+3>: push   %edi
0xc042e79e <do_exit+4>: push   %esi
0xc042e79f <do_exit+5>: push   %ebx /*到此为止*, 可以看出do_exit不错,指令基本可以满足要求/
0xc042e7a0 <do_exit+6>: mov    %eax,%ebx
0xc042e7a2 <do_exit+8>: sub    $0x38,%esp
0xc042e7a5 <get_current+0>:     mov    %fs:0xc0858000,%edi
...

具体就不用多说了,看看2中原理,对照一下上面红色字体部分就明白了。

5.2 替换do_exit

static unsigned char g_original_do_exit[5] = { 0 };
static unsigned char g_stub_do_exit[5] = { 0xe9, 0, 0, 0, 0};
static unsigned long g_do_exit_address = 0;

static int hook_do_exit(unsigned char* do_exit_address)
{
        int ret = -1;
        unsigned long offset = 0;

//      g_do_exit_address = (unsigned long)(do_exit_address + 3);
        g_do_exit_address = (unsigned long)(do_exit_address );


        memcpy(g_original_do_exit, (unsigned char *)g_do_exit_address, 5);

        offset = (unsigned long)my_do_exit - g_do_exit_address - 5;
        *((unsigned long *)(g_stub_do_exit + 1)) = offset;

        lock_kernel();
        CLEAR_CR0;

        memcpy((unsigned char*)g_do_exit_address, g_stub_do_exit, 5);

        SET_CR0;
        unlock_kernel();

        return ret;
}

static void unhook_do_exit(void)
{
        lock_kernel();
        CLEAR_CR0;

        memcpy((unsigned char*)g_do_exit_address, g_original_do_exit, 5);

        SET_CR0;
        unlock_kernel();
}

5.3 my_do_exit处理

static long my_do_exit(int error_code)
{
#if 1 /* 从do_exit头开始hook */
         asm("pushl %%ebp\n\t"
                "movl %%esp,%%ebp\n\t"
                "pushl %%edi\n\t"
                "pushl %%esi\n\t"
                "pushl %%ebx\n\t"            /* 为何先pushl?其后面语句也是push ebx?你自己来思考了,这里有小弯 */
                "movl %0, %%ebx\n\t"      /* 为何选择 ebx而不是eax ,需要你自己来思考 */
                "addl $5, %%ebx\n\t"
                "jmp *%%ebx\n\t"
                ::"m"(g_do_exit_address)
                );
#else /* 从do_exit + 3的位置hook */
        asm("pushl %%edi\n\t"
                "pushl %%esi\n\t"
                "pushl %%ebx\n\t"
                "movl %%eax, %%ebx\n\t"
                "movl %0, %%eax\n\t"   /* 为什么选择是eax , 您要思考一下 */
                "addl $5, %%eax\n\t"
                "jmp *%%eax\n\t"::"m"(g_do_exit_address)
                );

#endif

        return 0;
}

5.4 注意事项

      应用层程序退出一般从sys_exit不到信息的,但是一定能够从do_exit获取到信息。

      卸载do_exit的hook就崩溃了,原因我没有继续查找,留给您了。

你可能感兴趣的:(linux 中 inline hook 简单分析(do_exit))