在LoadLibrary上创建CreateRemoteThread并返回HMODULE

时间:2014-12-06 14:07:22

标签: dll-injection

我最近在做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等。他们一定非常 仔细做这些事情,参考文件"最佳实践 创建DLL"对于Microsoft,创建线程等行为可能会导致 死锁,#34;理想的DllMain只是一个空的存根"所以我不认为 这是一个非常好的方式。

因此,获取加载的DLL的HMODULE很重要。有了这个手柄,你可以 使用CreateRemoteThread来调用注入DLL的导出函数,做任何事情 你想要的,不用担心加载锁定的东西。

不幸的是,上面的代码只适用于32位进程,这是因为 线程的退出代码类型是DWORD - 一个32位无符号整数,但是 HMODULE是一个指针,它可以是64位。所以在64位进程中,你可能会得到一个 来自GetExitCodeThread的DWORD值为0xeb390000,但实际上是HMODULE LoadLibrary返回的是0x7feeb390000。 0xeb390000只是一个截断的64位 指针。

我们如何解决这个问题?

4 个答案:

答案 0 :(得分:2)

您可以假设显示的代码可能正常工作,并且截断的HMODULE在大多数情况下可能实际上很好,因为模块通常在进程地址空间中加载得足够低而不是# 39;无所谓。为了确保代码始终有效,尽管您可以遵循“已破坏”的代码。调用EnumProcessModules()函数的示例代码。如果返回的HMODULE出现在目标流程的流程模块列表中,那么您就可以了。如果没有,则需要迭代返回的HMODULE并调用GetModuleBaseName()GetModuleFileNameEx(),直到找到注入的DLL。

或者,如果您已经作为自定义调试器运行(无论如何我觉得它很有用),那么您可以匹配在注入相应的{时加载的模块{1}}将由WaitForDebugEvent()报告。这将为您提供LOAD_DLL_DEBUG_EVENT(图像的基础)和图像文件名,并且在您注入DLL后会立即发生事件。

就个人而言,我采取后一种方法,但实际上我还没有看到从破碎的代码中返回的截断的HMODULE是正确的,但我希望我能做到这一点。我很幸运,依靠加载程序在进程的地址空间中加载DLL。

答案 1 :(得分:1)

对于64位进程,我可以使用IPC(http://msdn.microsoft.com/en-us/library/windows/desktop/aa365574%28v=vs.85%29.aspx)来恢复HMODULE。

我应该注意,例如,并非每个IPC机制都可以在DLLMain中工作 管道将导致死锁,请参阅文档"创建的最佳实践 的DLL" (http://download.microsoft.com/download/a/f/7/af7777e5-7dcd-4800-8a0a-b18336565f5b/DLL_bestprac.doc)的Microsoft,调用kernel32.dll中的函数(除了一些指定的 功能)会好的。

我测试了共享内存(在Windows XP上使用SP3和Windows 7 64位PRO), 它有效。

答案 2 :(得分:0)

您可以按照https://docs.microsoft.com/en-us/windows/desktop/WinProg64/interprocess-communication安全地使用返回的32位句柄,但64位窗口仍将使用32位句柄。

  

64位版本的Windows使用32位句柄以实现互操作性。   在32位和64位应用程序之间共享句柄时,仅   低32位有效,因此可以安全地截断句柄   (将其从64位传递到32位时)或对句柄进行符号扩展   (将其从32位传递到64位时)。可以共享的句柄   包括对用户对象(例如Windows(HWND))的句柄,对GDI的句柄   诸如笔和刷子(HBRUSH和HPEN)之类的物体,以及   命名对象,例如互斥体,信号量和文件句柄。

答案 3 :(得分:0)

有点高级,但是与其直接进行CreateRemoteThread()调用LoadLibrary(),还可以使用HMODULE在远程进程中分配VirtualAllocEx(),然后分配一个可执行内存块(也与VirtualAllocEx()一起),并在其中放入一些汇编代码以调用LoadLibrary()并将返回值保存到分配的HMODULE中,然后您可以拥有{{1} }运行分配的“功能”,等待线程退出,然后使用CreateRemoteThread()读取HMODULE

Implementing Remote LoadLibrary and Remote GetProcAddress Using PowerShell and Assembly(在PowerShell中,但可以根据需要将其转换为C / C ++)进行了演示。