如何在C#中挂接一个API函数!

HOOK API是一个永恒的话题。如果没有HOOK,很多技术将很难实现,或者可能根本无法实现。

这里说的API是广义上的API,包括DOS中的中断,WINDOWS中的API,中断服务,IFS和。

NDIS滤波等。比如大家熟悉的即时翻译软件,依靠的就是HOOK TextOut()或者ExtTextOut()。

函数,在操作系统使用这两个函数输出文本之前,它用中文替换相应的英文来实现。

时间翻译;对于IFS和NDIS滤波也是如此。在读写磁盘和收发数据之前,系统会调用第三方。

回调函数来判断操作是否可以释放,这和普通的钩子不同,是操作系统允许的,是由操作系统决定的。

提供安装回调函数的接口。

即使没有钩子也没有病毒,因为不管是DOS下的病毒还是WINDOWS下的病毒,

都是通过HOOK系统服务实现功能的:DOS中的病毒通过HOOK INT 21感染文件(文件病毒),通过HOOK INT 13感染引导扇区(引导病毒);WINDOWS下的病毒通过挂接系统API(包括RING0层和RING3层)或安装IFS(CIH病毒使用的方法)感染文件。因此,可以说“没有HOOK,就没有今天多姿多彩的软件世界”。

因为涉及到专利和知识产权,或者商业机密,微软一直不提倡将其系统API挂钩。

为了满足杀毒软件和防火墙的需要,还开放了其他过滤接口,如IFS和NDIS。所以在

很多时候HOOK API都要自己做。

HOOK API有一个原则,就是不能以任何方式影响被挂接API的原始功能。正如

医生拯救生命。如果患者体内的病毒被杀死,患者死亡,那么这个“救命”就没有意义了。

如果你挂钩了API,你的目的达到了,但是API原来的功能失效了,所以不是挂钩,而是替换,操作系统的正常功能会受到影响甚至崩溃。

HOOK API技术,说起来并不复杂,就是改变程序流程的技术。CPU里有几条指令。

指令可以改变程序的进程:JMP、CALL、INT、RET、RETF、IRET等指令。理论上,只需更改API

任何入口和出口的机器码都可以被HOOK,但实际实现要复杂得多,因为要处理好以下几个问题:

1,CPU指令长度问题,在32位系统中,一条JMP/CALL指令的长度是5个字节,所以你要替换API。

长度超过5个字节的机器码(或者用5个字节的总长度代替几个指令),否则会影响修正。

机器码后面的几条指令改成5字节以下,甚至程序流程会被打乱,造成不可预知的后果;

2、参数问题,为了访问原API的参数,你不得不通过EBP或者ESP来引用参数,所以这个时候你要非常清楚你的钩子代码中EBP/ESP的值;

3、计时,有些钩子必须在API的开头,有些必须在API的结尾,比如HOOK CreateFilaA()。

如果在API的末尾挂接了API,那么此时就不能写文件,甚至不能访问文件;钩子RECV(),

如果你在API头钩子中,此时还没有收到数据,可以检查RECV()的接收缓冲区,但是里面肯定没有数据。

你想要的数据,你必须等待RECV()正常执行,并在RECV()的末尾钩子,然后检查RECV()的缓冲区。

里面只有想要的数据;

4、上下文的问题,有些钩子代码不能执行某些操作,否则会破坏原API的上下文,原API失效;

5、同步问题,钩子代码中尽量不要使用全局变量,而要使用局部变量,这也是模块化程序的需要;

6.最后需要注意的是,被替换的CPU指令的原函数必须在钩子代码的某个地方模拟。

以ws2_32.dll中的send()为例,说明如何挂接这个函数:

出口fn():发送- Ord:0013h

地址机器代码汇编代码

:71a 21af 455 push ebp//要挂钩的机器码(方法1)

:71A21F58BEC MOVEBP,ESP//要挂钩的机器码(方法2)。

:71a 21af 7 83ec 10 sub esp,00000010

:71A21AFA 56推送esi

:71A21AFB 57推送edi

:71A21AFC 33FF xor edi,edi

:71a 21afe 813d 1931a 271 CMP DWORD PTR[71a 3201C],71A26558。

:71a 21b 08 0f 84853d 0000 je 71a 25893

:71a 21B0E 8d 45 F8 lea eax,dword ptr [ebp-08]

:71a 21b 11 50推送eax

:71a 21b 12 e 869 f 7 ffff电话71A21280

:71a 21b 17 3bc 7 CMP eax,edi

:71a 21b 19 8945 fc mov dword ptr[ebp-04],eax

:71a 21b 1C 0f 85c 4940000 jne 71a 2 AFE 6

:71A21B22 FF7508推送[ebp+08]

:71A21B25 E826F7FFFF电话71A21250

:71A21B2A 8BF0 mov esi,eax

:71A21B2C 3BF7 cmp esi,edi

:71a 21B2E 0f 84 ab 940000 je 71a 2 afdf

:71a 21b 34 8b 4510 mov eax,dword ptr [ebp+10]

:71A21B37 53推送ebx

:71a 21b 38 8d 4 DFC lea ecx,dword ptr [ebp-04]

:71A21B3B 51推送ecx

:71A21B3C FF75F8推送[ebp-08]

:71a 21B3F 8d 4d 08 lea ecx,dword ptr [ebp+08]

:71A21B42 57推送edi

:71A21B43 57推送edi

:71a 21b 44 ff 7514推送[ebp+14]

:71a 21b 47 8945 f 0 mov dword ptr[ebp-10],eax

:71a 21B4A 8b 450 c mov eax,dword ptr [ebp+0C]

:71A21B4D 51推送ecx

:71A21B4E 6A01推送00000001

:71a 21b 50 8d 4 df 0 lea ecx,dword ptr [ebp-10]

:71A21B53 51推送ecx

:71A21B54 FF7508推送[ebp+08]

:71a 21b 57 8945 F4 mov dword ptr[ebp-0C],eax

:71a 21B5A 8b 460 c mov eax,dword ptr [esi+0C]

:71a 21B5D ff 5064 call[eax+64]

:71A21B60 8BCE mov ecx,esi

:71A21B62 8BD8 mov ebx,eax

:71a 21b 64 E8 c 7 6 fff call 71a 21230//要挂钩的机器码(方法3)

:71A21B69 3BDF cmp ebx,edi

:71A21B6B 5B pop ebx

:71a 21B6C 0f 855 f 940000 jne 71a 2 AFD 1

:71a 21b 72 8b 4508 mov eax,dword ptr [ebp+08]

:71A21B75 5F pop edi

:71A21B76 5E pop esi

:71A21B77 C9离开

:71a 21b 78 c 21000 ret 0010

以下是挂钩该API的四种方法:

1,用INT 3(机器代码0xcc)将API条目中的第一条指令替换为推送EBP指令(机器代码0x55)。

然后使用WINDOWS提供的调试功能执行自己的代码,这是软冰等DEBUGER广泛使用的。

就是通过BPX在相应的地方设置一个INT 3指令来破点。但是不推荐使用这种方法,因为它

会和WINDOWS或者调试工具冲突,汇编代码基本调试完毕;

2.将第二条mov ebp,esp指令(机器码8BEC,2字节)替换为INT F0指令(机器码CDF0)。

然后在IDT设置一个中断门指向我们的代码。我在这里给出一个钩子代码:

Lea ebp,[esp+12] //模拟原指令mov ebp,esp的功能。

Pushfd //保存站点

Pushad //保存场景

//在这里做你想做的。

Popad //还原场景

Popfd //还原现场

Iretd //返回原指令的下一条指令继续执行原函数(地址为71A21AF7)。

这种方法很好,但缺点是在IDT中设置了一个中断门,即进入RING0。

3.更改调用指令的相对地址(调用分别是71A21B12,71A21B25,71A21B64,但是前两次调用前有一个条件。

跳转指令可能无法执行,所以我们需要HOOK 71A21B64处的CALL指令)。为什么要找通话指令?

因为都是5字节指令,而且都是调用指令,只要操作码0xE8不变,就可以通过改变后面的相对地址来转移。

转到我们的钩子代码去执行,然后转到我们的钩子代码后的目标地址去执行。

假设我们的钩子代码在71A20400,那么我们把71A21B64的调用指令改为调用71A20400(原来的指令是这样的:调用71A21230)。

而71A20400处的钩子代码是这样的:

71A20400:

普沙德

//在这里做你想做的。

波普德

Jmp 71A21230 //跳转到原调用指令的目标地址,如下:调用71A21230。

这种方法隐蔽性很好,但是很难找到这个5字节的调用指令,计算相对地址也很复杂。

4.替换地址为71A265438的CMP DWORD PTR[71a 3201C]和71A21C93指令(机器代码:813D 10A3765438+)。

致电71A20400

not otherwise provided 除非另有规定

not otherwise provided 除非另有规定

not otherwise provided 除非另有规定

not otherwise provided 除非另有规定

not otherwise provided 除非另有规定

(机器代码:E8XX XX XX 90 90 90 90 90 10字节)

71A20400中的挂钩代码是:

普沙德

Mov edx,71A3201Ch //模拟原指令CMP DWORD PTR[71a 3201c 93],71A21C93。

Cmpword ptr [EDX],71a21c93h//模拟原指令cmpd word ptr[71a 3201c],71a 21c 93。

pushfd

//在这里做你想做的。

popfd

波普德

浸水使柔软

这种方法是最好的隐蔽,但并不是每个API都有这样的说明,需要根据具体情况来操作。

以上方法都是常用的。值得一提的是,很多人更改API的前五个字节,但是现在很多杀毒软件都是用这种方法。

检查API是否在你之后被HOOK或者其他病毒木马改了,这样会互相覆盖,最后一个HOOK API的操作是有效的。