如何在C#中挂接一个API函数!
这里说的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的操作是有效的。