赞
踩
Hook技术被广泛应用于安全的多个领域,比如杀毒软件的主动防御功能,涉及到对一些敏感API的监控,就需要对这些API进行Hook;窃取密码的木马病毒,为了接收键盘的输入,需要Hook键盘消息;甚至是Windows系统及一些应用程序,在打补丁时也需要用到Hook技术。接下来,我们就来学习Hook技术的原理。
分类
Windows下的应用程序大部分都是基于消息机制的,它们都会有一个消息过程函数,根据不同的消息完成不同的功能。Windows操作系统提供的钩子机制的作用就是用来截获和监视这些系统中的消息。
应用程序可以通过设置Hook对某个进程或窗口进行监视。键盘、鼠标、日志、窗口。是一个队列,CallNextHookEx
该API在简单高效的同时也有一个弊端,就是它只能监视较少的消息,如:击键消息、鼠标移动消息、窗口消息。想要对系统更全面的进行Hook就要使用以下介绍的两种Hook方法。
该Hook方法的原理跟调试器的工作机制相似,核心思想都是让进程发生异常,然后自己捕获到该异常,对处于被调试状态下的级才能进行恶意操作。
调试Hook的核心思路就是将API的第一个字节修改为0xCC(INT 3),当API被调用时,由于触发了异常,控制权就被转交给调试器。
Hook的核心思想就是修改API的代码,但是,比如我A进程要Hook一个B进程的CreateProcess函数,A是没有权限修改B内存中的代码的,怎么办?这时候使用DLL注入技术就可以解决这问题,我们将Hook的代码写入一个DLL(或直接一个shellcode),将此DLL注入到B进程中,此时因为DLL在B进程的内存中,所以就有权限直接修改B内存中的代码了。
IAT Hook顾名思义就是通过修改IAT里的函数地址对API进行Hook。 IAT 是函数的导入表,在动态库加载过程中,可以找到函数导入表,修改导入表中的数据。
内联Hook相比于IAT Hook,显得更简单粗暴,它直接修改内存中任意函数的代码,将其劫持至Hook API。同时,它比IAT Hook的适用范围更广,因为只要是内存中有的函数它都能Hook,而后者只能Hook IAT表里存在的函数(有些程序会动态加载函数)。
属于硬编码,也就改变了函数的执行逻辑,将函数前5个字节进行覆盖,在使用完成后还需要恢复前5个字节
我们会发现Code Hook存在一个效率的问题,因为每次Code Hook都要进行“挂钩+脱钩”的操作,也就是要对API的前5字节修改两次,这样,当我们要进行全局Hook的时候,系统运行效率会受影响。而且,当一个线程尝试运行某段代码时,若另一个线程正在对该段代码进行“写”操作,这时就会程序冲突,最终引发一些错误。
以上累出的API起始代码有如下两个明显的相似点:
[1]API代码以“MOV EDI,EDI”指令开始。
[2]API代码上方有5个NOP指令。
MOV EDI,EDI用于将EDI的值再次复制给EDI,这没有什么实际意义。也就是说,API起始代码的MOV指令(2个字节)与其上方的5个NOP指令(5个字节)合起来共7个字节的指令没有任何意义。所以我们就可以通过修改这7个字节来实现Hook操作。这种方法因为可以在进程处于运行状态时临时更改进程内存中的库文件,所以微软也常用这种方法来打“热补丁”。如下图:
第一个 jmp FUNCTION 占用5个字节,0xE9是跳转指令,后边跟4字节的偏移量
第二个 jmp label 只占用2个字节呢,因为本身LABEL 很近,可以翻译成近跳指令(0x0xF9EB ), 0xF9就是 近跳指令,0xEB 就 -4 的补码。
下面是一段obs 中function_hook 的实现,可以通过代码加深下印象
#define X86_NOP 0x90
#define X86_JMP_NEG_5 0xF9EB
static inline void hook_reverse_new(struct func_hook *hook, uint8_t *p)
{
hook->call_addr = (void *)(hook->func_addr + 2);
hook->type = HOOKTYPE_REVERSE_CHAIN;
hook->hooked = true;
p[0] = 0xE9;
*((uint32_t *)&p[1]) = (uint32_t)(hook->hook_addr - hook->func_addr);
*((uint16_t *)&p[5]) = X86_JMP_NEG_5;
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。