【问题标题】:CreateRemoteThread on LoadLibrary and get the HMODULE back在 LoadLibrary 上 CreateRemoteThread 并取回 HMODULE
【发布时间】: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


    【解决方案1】:

    您可以假设所示代码可能有效,并且截断的HMODULE 在大多数情况下实际上可能没问题,因为模块通常在进程地址空间中加载得足够低,这无关紧要.为了确保代码始终有效,尽管您可以通过调用 EnumProcessModules() 函数来遵循您的“损坏”示例代码。如果返回的HMODULE 出现在目标进程的进程模块列表中,那么您就可以开始了。如果没有,则需要迭代返回的HMODULEs 并调用GetModuleBaseName()GetModuleFileNameEx(),直到找到注入的DLL。

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

    就我个人而言,我会采用后一种方法,但实际上我还没有看到从损坏的代码返回的截断的HMODULE 不正确,但我希望我只是幸运并且依赖加载程序将 DLL 加载到进程地址空间的低位。

    【讨论】:

    • 我的另一个问题:stackoverflow.com/questions/27331014/… 我不知道为什么 EnumProcessModules 不起作用。对于调试事件,我担心性能。我最终选择了IPC方案。
    • 关于 EnumProcessModules 为什么不起作用的答案:stackoverflow.com/a/27317947/996540
    • 知道这一点很有用。
    • 在 Windows 10 上,我还没有看到任何加载的模块的基地址都小到足以放入 DWORD 的过程,因此必须使用两种方法之一。
    【解决方案2】:

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

    我应该注意并不是每个 IPC 机制都可以在 DLLMain 中工作,例如 Pipe会导致死锁,参考文档《Best Practices for Creating Microsoft 的 DLLs" (http://download.microsoft.com/download/a/f/7/af7777e5-7dcd-4800-8a0a-b18336565f5b/DLL_bestprac.doc),调用 kernel32.dll 中的函数(除了一些指定的 功能)会好的。

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

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • 时间久了,脑子里已经没有上下文了。当我跳回复杂的注射事情时,我会考虑你的答案。谢谢你的回答,顺便我会说中文。
      • 恐怕这不是真的(对于这种特殊情况)。如您所见,复制的 MS 文档摘录没有将 HMODULE 列为“可共享”句柄类型之一。 LoadLibrary(A|W) 的返回值是模块的基地址(它可以在这样的机器上使用所有 64 位),我刚刚在我的测试 DLL 注入程序中看到截断(0x7FEF3AA0000 != 0xF3AA0000)搞砸了“句柄”值!...
      【解决方案4】:

      更高级一点,但不是让CreateRemoteThread() 直接调用LoadLibrary(),而是可以使用VirtualAllocEx() 在远程进程中分配一个HMODULE,然后分配一块可执行内存(也可以使用@ 987654326@) 并在其中放入一些汇编代码调用LoadLibrary()并将返回值保存到分配的HMODULE,然后您可以让CreateRemoteThread()运行分配的“函数”,等待线程退出,然后然后使用ReadProcessMemory()阅读HMODULE

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-10-26
        • 1970-01-01
        • 2012-08-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多