DLL劫持技术
CO6` F8a N3M)R0在逆向工程中,经常需要改变程序原有的执行流程,使其增加或者减少一些功能代码,这就需要对原文件进行补丁。补丁分文件补丁和内存补丁两种。文件补丁就是修改文件本身某个数据,达到一劳永逸的效果。顾名思义,内存补丁是在内存中打补丁、修改,确切地说,是对正在运行的程序的数据进行修改,以达到某种效果。IXPUB技术博客NyQw"L H+g
%n2i/jUTp0……略……
`0Ouu)s4Dz0y}QO*V9CB018.2.4 DLL劫持技术
J \o.d cA |X0IXPUB技术博客sr!Ih d;^;N#q#P当一个可执行文件运行时,Windows加载器将可执行模块映射到进程的地址空间中,加载器分析可执行模块的输入表,并设法找出任何需要的DLL,并将它们映射到进程的地址空间中。IXPUB技术博客6d Z;?0S'C
IXPUB技术博客+`2ve7e.J6p*{由于输入表中只包含DLL名而没有它的路径名,因此加载程序必须在磁盘上搜索DLL文件。首先会尝试从当前程序所在的目录加载DLL,如果没找到,则在Windows系统目录中查找,最后是在环境变量中列出的各个目录下查找。利用这个特点,先伪造一个系统同名的DLL,提供同样的输出表,每个输出函数转向真正的系统DLL。程序调用系统DLL时会先调用当前目录下伪造的DLL,完成相关功能后,再跳到系统DLL同名函数里执行,如图18.3所示。这个过程用个形象的词来描述就是系统DLL被劫持(hijack)了。
L$aOd8`"c`!v'dg\0 |
| 图18.3 DLL劫持技术演示 |
%Ys"Q8E}3Wgz0利用这种方法取得控制权后,可以对主程序进行补丁。此种方法只对除kernel32.dll、ntdll.dll等核心系统库以外的DLL有效,如网络应用程序的ws2_32.dll、游戏程序中的d3d8.dll,还有大部分应用程序都调用的lpk.dll,这些DLL都可被劫持。
~4WX'[t n5_D0N f*w#?5vX T-v0利用第5章5.6.2节提供的CrackMeNet.exe来演示一下如何利用劫持技术制作补丁,目标文件用Themida v1.9.2.0加壳保护。
,Y@/m9YG)ML7X*S3n0IXPUB技术博客^&fc1Jfb}1p pX,e1.补丁地址IXPUB技术博客S.f&i#?9f3vh/g.K,Bc
IXPUB技术博客q2H!R,r|q Q去除这个CrackMe网络验证方法参考第5章5.6.2节,将相关补丁代码存放到函数PatchProcess()里。例如将401496h改成:
]C'H%} q&LN gY0O000401496 EB 29 jmp short 004014C1 |
&czJ:M+R0
unsigned char p401496[2] = {0xEB, 0x29};IXPUB技术博客6S!Q*[3u)f
lU |
oA6]'nWG0p401496这个数组的数据格式,可以用OllyDbg插件获得,或十六进制工具转换。例如Hex Workshop打开文件,执行菜单“Edit/Copy As/Source”即可得到相应的代码格式。IXPUB技术博客W],S!K8H
IXPUB技术博客ZF$Z O.a9\P,a&Q2.构建输出函数
E\'~/pvHGD)F0IXPUB技术博客*?7on$\h查看实例CrackMeNet.exe输入表,会发现名称为“ws2_32.dll”的DLL,因此构造一个同名的DLL来完成补丁任务。伪造的ws2_32.dll有着真实ws2_32.dll一样的输出函数,完整源码见光盘映像文件。实现时,可以利用DLL模块中的函数转发器来实现这个目标,其会将对一个函数的调用转至另一个DLL中的另一个函数。可以这样使用一个pragma指令:
[qO,uF\#J9C0#pragma comment(linker, "/EXPORT:SomeFunc=DllWork.someOtherFunc") |
这个pragma告诉链接程序,被编译的DLL应该输出一个名叫SomeFunc的函数。但是SomeFunc函数的实现实际上位于另一个名叫SomeOtherFunc的函数中,该函数包含在称为DllWork. dll的模块中。
o+Ighq{ c8V!W0IXPUB技术博客+?1EM[3G4Y7RV M8N如果要达到劫持DLL的目的,生成的DLL输出函数必须与目标DLL输出函数名一样。本例可以这样构造pragma指令:
:i2{#?N
W,Zv}Q#Xc0IXPUB技术博客%~'y0Q&|v9x
e&]
#pragma comment(linker, "/EXPORT:WSAStartup=_MemCode_WSAStartup,@115") |
hJ1l:dc0编译后的DLL,会有与ws2_32.dll同名的一个输出函数WSAStartup,实际操作时,必须为想要转发的每个函数创建一个单独的pragma代码行,读者可以用工具AheadLib或用其他办法,将ws2_32.dll输出函数转换成相应的pragma指令。
6sa!O^ UpZo W0K2|&?:v jx0当应用程序调用伪装ws2_32.dll的输出函数时,必须将其转到系统ws2_32.dll中,这部分的代码自己实现。例如,WSAStartup输出函数构造如下:
|M{(_*`uRV0ALCDECL MemCode_WSAStartup(void)IXPUB技术博客8h:d8j.rV } |
// MemCode 命名空间IXPUB技术博客{\ Rc!Y4DQ // 释放原始模块IXPUB技术博客`r*`F0W*o r |