cyliu
论坛版主
论坛版主
  • 注册日期2003-06-13
  • 最后登录2014-04-11
  • 粉丝5
  • 关注0
  • 积分1238分
  • 威望2531点
  • 贡献值0点
  • 好评度577点
  • 原创分14分
  • 专家分10分
阅读:2161回复:0

linux 中 inline hook 简单分析(do_exit)

楼主#
更多 发布于:2011-12-15 13:15
author: jonathan
本文档的CopyRight归jonathan所有,可自由转载,转载时请保持文档的完整性。
/*----------------------------------------------------------------------------------------------------------------------------*/
此文源:http://hi.baidu.com/jonathan2004/blog/item/9c5ca037b2092a0191ef3941.html
本文中重点描述一下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(); // Note: 3.x.x版本已经去掉了这个函数
        CLEAR_CR0;
 
        memcpy((unsigned char*)g_do_exit_address, g_stub_do_exit, 5);
 
        SET_CR0;
        unlock_kernel(); // Note: 3.x.x版本已经去掉了这个函数
 
        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就崩溃了,原因我没有继续查找,留给您了。
走走看看开源好 Solaris vs Linux
游客

返回顶部