linux hook open函数,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 :        push   %ebp

0xc042ef4d :        mov    %esp,%ebp

0xc042ef4f :        mov    0x8(%ebp),%eax

0xc042ef52 :        shl    $0x8,%eax

0xc042ef55 :        and    $0xffff,%eax

0xc042ef5a :       call   0xc042e79a

End of assembler dump.

(gdb) x/2 0xc042ef5a

0xc042ef5a :       0xfff83be8      0xc08555ff

(gdb) disass do_exit

Dump of assembler code for function do_exit:

0xc042e79a : push   %ebp

0xc042e79b : mov    %esp,%ebp

0xc042e79d : push   %edi

0xc042e79e : push   %esi

0xc042e79f : push   %ebx /*到此为止*, 可以看出do_exit不错,指令基本可以满足要求/

0xc042e7a0 : mov    %eax,%ebx

0xc042e7a2 : sub    $0x38,%esp

0xc042e7a5 :     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就崩溃了,原因我没有继续查找,留给您了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值