DLL劫持技术
当一个可执行文件运行时,Windows加载器将可执行模块映射到进程的地址空间中,加载器分析可执行模块的输入表,并设法找出任何需要的DLL,并将它们映射到进程的地址空间中。
由于输入表中只包含DLL名而没有它的路径名,因此加载程序必须在磁盘上搜索DLL文件。首先会尝试从当前程序所在的目录加载DLL,如果没找到,则在Windows系统目录中查找,最后是在环境变量中列出的各个目录下查找。利用这个特点,先伪造一个系统同名的DLL,提供同样的输出表,每个输出函数转向真正的系统DLL。程序调用系统DLL时会先调用当前目录下伪造的DLL,完成相关功能后,再跳到系统DLL同名函数里执行,如图18.3所示。这个过程用个形象的词来描述就是系统DLL被劫持(hijack)了。
我热爱生活 图18.3 DLL劫持技术演示
利用这种方法取得控制权后,可以对主程序进行补丁。此种方法只对除kernel32.dll、ntdll.dll等核心系统库以外的DLL有效,如网络应用程序的ws2_32.dll、游戏程序中的d3d8.dll,还有大部分应用程序都调用的lpk.dll,这些DLL都可被劫持。
合力公式利用第5章5.6.2节提供的来演示一下如何利用劫持技术制作补丁,目标文件用Themida v1.9.2.0加壳保护。
1.补丁地址
去除这个CrackMe网络验证方法参考第5章5.6.2节,将相关补丁代码存放到函数PatchProcess()里。例如将401496h改成:
00401496 EB 29 jmp short 004014C1
补丁编程实现就是:
unsigned char p401496[2] = {0xEB, 0x29};
WriteProcessMemory(hProcess,(LPVOID)0x401496, p401496, 2, NULL);
p401496这个数组的数据格式,可以用OllyDbg插件获得,或十六进制工具转换。例如Hex Workshop担的组词打开文件,执行菜单“Edit/Copy As/Source”即可得到相应的代码格式。
2.构建输出函数
查看实例输入表,会发现名称为“ws2_32.dll”的DLL,因此构造一个同名的DLL来完成补丁任务。伪造的ws2_32.dll有着真实ws2_32.dll一样的输出函数,完整源码见光盘映像文件。实现时,可以利用DLL模块中的函数转发器来实现这个目标,其会将对一个函数的调用转至另一个DLL中的另一个函数。可以这样使用一个pragma指令:
#pragma comment(linker, "/EXPORT:SomeFunc=DllWork.someOtherFunc")
这个pragma告诉链接程序,被编译的DLL应该输出一个名叫SomeFunc的函数。但是SomeFunc函数的实现实际上位于另一个名叫孕期可以喝茶吗SomeOtherFunc的函数中,该函数包含在称为DllWork. dll的模块中。
如果要达到劫持DLL的目的,生成的DLL输出函数必须与目标DLL输出函数名一样。本例可以这样构造pragma指令:
#pragma comment(linker, "/EXPORT:WSAStartup=_MemCode_WSAStartup,@115")
编译后的DLL,会有与ws2_32.dll同名的一个输出函数WSAStartup,实际操作时,必须为想要转发的每个函数创建一个单独的pragma代码行,读者可以用工具AheadLib专科分数或用其他办法,将ws2_32.dll输出函数转换成相应的pragma指令。
当应用程序调用伪装ws2_32.dll的输出函数时,必须将其转到系统ws2_32.dll中,这部分的代码自己实现。例如,WSAStartup输出函数构造如下:
ALCDECL MemCode_WSAStartup(void)
{
GetAddress("WSAStartup");
__asm JMP EAX;//转到系统ws2_32.dll的WSAStartup输出函数
}
其中,GetAddress()函数的代码如下:
// MemCode 命名空间
namespace MemCode
{
HMODULE m_hModule = NULL; //原始模块句柄
DWORD m_dwReturn[500] = {0}; //原始函数返回地址
// 加载原始模块
inline BOOL WINAPI Load()
{
TCHAR tzPath[MAX_PATH]={0};
TCHAR tzTemp[MAX_PATH]={0};
GetSystemDirectory(tzPath, sizeof(tzPath));
strcat(tzPath,"\\ws2_32.dll");
m_hModule = LoadLibrary(tzPath);//加载系统系统目录下ws2_32.dll
if (m_hModule == NULL)
{
wsprintf(tzTemp, TEXT("无法加载 %s,程序无法正常运行。"), tzPath);
MessageBox(NULL, tzTemp, TEXT("MemCode"), MB_ICONSTOP);
}
return (m_hModule != NULL);
}
// 释放原始模块
inline VOID WINAPI Free()点挂
{
if (m_hModule)
FreeLibrary(m_hModule);
}
// 获取原始函数地址
FARPROC WINAPI GetAddress(PCSTR pszProcName)
围绕近义词{
FARPROC fpAddress;婴儿隔尿垫
TCHAR szProcName[16]={0};
TCHAR tzTemp[MAX_PATH]={0};
if (m_hModule == NULL)
{
if (Load() == FALSE)
ExitProcess(-1);
}
fpAddress = GetProcAddress(m_hModule, pszProcName);
if (fpAddress == NULL)
{
if (HIWORD(pszProcName) == 0)
{
wsprintf(szProcName, "%d", pszProcName);
pszProcName = szProcName;
}
wsprintf(tzTemp, TEXT("无法找到函数 %hs,程序无法正常运行。"), pszProcName);
MessageBox(NULL, tzTemp, TEXT("MemCode"), MB_ICONSTOP);
ExitProcess(-2);
}
return fpAddress;
}
}
using namespace MemCode;