a wonderful way to implement HOOK

一、Hook定义

      改变函数执行流程,将原函数的执行流引导入自定义的代码中,再处理参数或结果后在返回原函数继续执行的处理方式。常见的Hook方式绝大多数是在函数的开头,无论通过改变一些函数指针,如导入表,SSDT,甚至C++类的虚函数表,还是inline hook中在函数开头写入跳转指令,还是采用其他一些奇技淫巧,比如在函数头设断点,在异常处理中改变EIP实现(用硬件执行断点来玩的话,会令hook比较难以检测,但是很可惜 只能玩4个。用内存访问断点来实现的话更淫荡,完全没有数量限制,但是会有强烈的性能劣势) 。这些HOOK的特点是在函数还没有执行任何指令时获取控制权的,比较好处理。也有在函数开头执行过几条无关痛痒的指令后跳转的,这种情况稍微复杂点,要个现场保护。还有在函数执行完返回前hook的,这个显然只能处理结果,而且比较麻烦,因为稍微复杂的函数都不会只一条返回路径。当然,还有一些直接修改指令的patch操作,比如把第一条指令改成 ret; 这里由于没有将流程引入我们的函数进行处理,只能叫patch。

 

二、一般实现

      Hook的目的一般两种:过滤参数和过滤函数执行结果。而且大多数是需要调用原函数的。修改各种函数表的hook实现着比较简单,只需保存原函数指针,然后调用即可。

      Inline Hook 稍微复杂一点点,因为改了原函数的指令(暂不描述奇技淫巧的HOOK),所以需要跳来跳去的。首先是在函数头的一个跳转,跳到我们的函数中。我们在执行原函数时,必须先执行那条(那几条)被我们写的跳转指令破坏掉的指令,然后再跳转到之后的指令。如果只是这样跳,那很可惜,只能改参数了,还得纠结栈平衡。为了能稍微方便点,我们可以这样认为。将函数执行流程引入一个包装函数中,函数的参数,调用个数完全一致。在包装函数中,我们调用原函数。这样可以之前处理参数,之后处理结果。啊,多么惬意。这样一来,我们就需要实现个原函数,好在我们只破坏了开头,我们帮忙实现一个开头,然后跳转即可。示例代码如下:

__declspec(naked) 是告诉编译器不要动我函数的栈。DetourMessageBoxA的作用就是执行两条MessageBoxA原来的两条指令

mov edi,edi;   push ebp; (当然,第一条可以不要)。因为这两条指令正好是5字节(jmp 指令也是5字节),然后后面3条指令计算出随后的指令,完成跳转。到包装函数那,就简单多了,各种改。在实现hook时,只需要将MessageBoxA的头5字节改了即可:

*(BYTE*)(MessageBoxA) = 0xE9;
*(DWORD*)((BYTE*)MessageBoxA+1) = (DWORD)MessageBoxA - (DWORD)MyMessageBoxA - 5;

第一条指令的0xE9是跳转指令的机器码,第二条指令则是计算便宜的。0xE9的跳转是将EIP的值加上后面的偏移得出目的地址的,因为EIP总指向下一条指令,故执行该跳转时,EIP为该跳转指令所在地址加5。 从这里我们也大致感受到了类型强制的烦人地方,上面两句还不如用汇编用着简单那。

__asm
{
 mov eax,MessageBoxA;
 mov byte ptr[eax],0xe9;
 mov ebx,eax;
 lea ecx,MyMessageBoxA;
 sub ebx,ecx;
 sub ebx,5;
 mov dword ptr[eax+1],ebx;
}

好吧,一样简单。

这里看起来比较简化了,但其实,当你hook的函数多到时候,就开始纠结了。每个函数都得定义俩函数。每个Detour还得查原来代码是啥。太不爽了。就算用的HOOK的各种函数表,在用调原函数时,还是得没个函数用个typedef DWORD (__stdcall* FUNC1)(int,int);

之类的定义,然后调用时也得玩个((FUNC1)0x77777777)(1,2); 写多了也不爽。

幸好C有个可变参数可以玩,不如弄个这吧:

至少在调用时,不用一直typedef了,比如可以CALLFUNC(0x77777777,(1,2,3));当然,可变参数必须是__cdecl的,API都是 __stdcall的,所以需要修正一下堆栈在上面的调用后,应该加个AdjStack(3)。注意这样玩是要关闭什么基本运行时检查了,缓冲区安全检查了什么的,不然编译器在每个函数结束都要先荡一把。

 

      啊,其实这时候已经比较简化了。后来发现个叫LDE的神奇东西,可以检测指令长度,这时,已经可以不用再为每个函数写一个Detour了。比如我们可以定义一个这样的数据结构:

BYTE instruction[11]; 用来储存原函数的头几条指令,考虑到最差情况下第一条指令4字节,第二条指令离奇的7字节,所以需11字节。如果一般情况,只需要全部设置成0x90,即nop指令即可
BYTE jmpcode;  设置为0xE9
int Offset;   计算出便宜填入即可。

这样一来,我们基本实现了一堆函数表,实现HOOK时,基本只需:

参数PVOID OriFunc,PVOID NewFunc ,DWORD detourNum中,前两不用说,detourNum为FUNCTRUNK数组下标。

这样实现后,只需写包装函数即可:

 

啊,比较简单了!!

啊,又3点半多了。昨晚给个小朋友发短信,早上她说恰好3月3号3点33分33秒。一下子7个3,应该是好运气的标志吧,希望考试的成绩能不错吧!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值