【发布时间】:2014-12-06 14:07:22
【问题描述】:
我最近在做 DLL 注入工作,所以我做了一些研究 在谷歌上。现在我知道使用 CreateRemoteThread 是一个好方法。
ASLR(地址空间布局随机化,从 Windows Vista 开始)使 kernel32.dll的地址是随机的,但这并不影响整体,因为 在会话中,所有进程中 kernel32.dll 的基地址只是 相同 - 直到操作系统重置。
所以这段代码在正常情况下可能是安全的:
void launchAndInject(const char* app, const char* dll)
{
STARTUPINFOA si = {0};
si.cb = sizeof(si);
PROCESS_INFORMATION pi = {0};
if (CreateProcessA(app, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi))
{
LPVOID loadLibrary = GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
if (loadLibrary == NULL) {
return;
}
SIZE_T len = ::strlen(dll) + 1;
LPVOID addr = VirtualAllocEx(pi.hProcess, NULL, len, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
if (addr == NULL) {
return;
}
if (!WriteProcessMemory(pi.hProcess, addr, dll, len, NULL)) {
return;
}
HANDLE th = CreateRemoteThread(pi.hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)loadLibrary, addr, 0, NULL);
WaitForSingleObject(th, INFINITE);
DWORD ret = 0;
GetExitCodeThread(th, &ret);
CloseHandle(th);
ResumeThread(pi.hThread);
}
}
注入线程的退出代码就是返回值 LoadLibrary,所以 ret 只是加载的 DLL 的 HMODULE(在 当然),它就像魔法一样,到目前为止一切都很好。
我读过很多关于 DLL 注入的项目,他们使用 DLLMain 来做很多 作业——比如创建线程或钩子 API 等等。他们一定很 仔细去做这些事情,参考文档《Best Practices for 微软的“创建 DLL”,创建线程等行为可能会导致 死锁,“理想的 DllMain 将只是一个空存根”,所以我不认为 这是一个很好的方法。
因此,获取已加载 DLL 的 HMODULE 很重要。有了这个手柄,你可以 使用 CreateRemoteThread 调用注入的 DLL 的导出函数,做任何事情 你想要的,不用担心加载器锁定的事情。
不幸的是,上面的代码只适用于 32 位进程,这是因为 线程退出代码的类型是 DWORD - 一个 32 位无符号整数,但是 HMODULE 是一个指针,它可以是 64 位的。所以在 64 位进程中,你可能会得到一个 DWORD 值 0xeb390000 来自 GetExitCodeThread,但实际上是 HMODULE LoadLibrary 返回的是 0x7feeb390000。 0xeb390000 只是截断的 64 位 指针。
我们如何解决这个问题?
【问题讨论】:
标签: dll-injection