Winapi x64中HMODULE的结构是什么?

Winapi x64中HMODULE的结构是什么?,winapi,Winapi,我正试图实现一个64位进程。我的主机进程调用CreateRemoteThread,线程子例程指向LoadLibrary。DLL随后通过调用FreeLibraryAndExitThread“从内部”卸载自己 我的目标是知道注入的LoadLibrary调用是否成功。不幸的是,我不能在我的(主机)进程中使用GetExitCodeThread,因为返回的64位HMODULE句柄被远程线程截断为DWORD。我不想使用,因为他们会引入比赛条件 因此,我想知道HMODULE在64位进程中由LoadLibrar

我正试图实现一个64位进程。我的主机进程调用
CreateRemoteThread
,线程子例程指向
LoadLibrary
。DLL随后通过调用
FreeLibraryAndExitThread
“从内部”卸载自己

我的目标是知道注入的
LoadLibrary
调用是否成功。不幸的是,我不能在我的(主机)进程中使用
GetExitCodeThread
,因为返回的64位
HMODULE
句柄被远程线程截断为
DWORD
。我不想使用,因为他们会引入比赛条件

因此,我想知道
HMODULE
在64位进程中由
LoadLibrary
返回的
HMODULE
的低32位——我能可靠地假设它的低32位对于有效句柄不是0吗

另外,我不需要
HMODULE
自身处理,我只需要知道
LoadLibrary
是否成功

编辑。来自我的主机进程的调用就是这样完成的(在一个非常简洁的伪代码中——没有错误检查):

我能可靠地假设它的低32位对于有效句柄不是0吗

不,你不能。
HMODULE
在64位中与在32位中完全相同。它是加载模块的基址。因此,没有理由认为有效的
HMODULE
必须具有非零低位

你很容易就能确认这一点。创建一个64位DLL,将
IMAGEBASE
设置为,例如
0x0000000100000000
。加载该DLL,并检查返回的
HMODULE
的值

我能可靠地假设它的低32位对于有效句柄不是0吗

不,你不能。
HMODULE
在64位中与在32位中完全相同。它是加载模块的基址。因此,没有理由认为有效的
HMODULE
必须具有非零低位


你很容易就能确认这一点。创建一个64位DLL,将
IMAGEBASE
设置为,例如
0x0000000100000000
。加载该DLL,并检查返回的
HMODULE

的值,而不是
CreateRemoteThread
,使用指向
LoadLibraryW
的线程子例程,我们可以向远程进程注入微小的shell代码,该进程首先调用
LoadLibraryW
,如果失败,
GetLastError
-结果是远程线程返回错误代码(如果没有错误,则返回0)-您将确切知道-是否
LoadLibrary
正常,如果没有,则返回错误代码。64 asm代码可以是:

CONST segment

SHELLDATA struct
    LoadLibrary DQ ?
    GetLastError DQ ?
SHELLDATA ends

public RemoteThreadProc_begin
public RemoteThreadProc_end

RemoteThreadProc_begin:
RemoteThreadProc proc
    nop
    nop
    nop
    call @@0
    ___ SHELLDATA <>
@@0:
    xchg [rsp],rbp
    sub rsp,20h
    call SHELLDATA.LoadLibrary[rbp]
    test rax,rax
    jz @@1
    xor eax,eax
@@2:
    add rsp,20h
    pop rbp
    ret
@@1:
    call SHELLDATA.GetLastError[rbp]
    jmp @@2
RemoteThreadProc endp
RemoteThreadProc_end:

CONST ends

相反,
CreateRemoteThread
使用一个指向
LoadLibraryW
的线程子例程,我们可以将微小的shell代码注入远程进程,该进程首先调用
LoadLibraryW
,如果失败,
GetLastError
,结果远程线程返回错误代码(如果没有错误,则返回0)-您将确切地知道-是否
LoadLibrary
正常,如果没有,则有错误代码。64 asm代码可以是:

CONST segment

SHELLDATA struct
    LoadLibrary DQ ?
    GetLastError DQ ?
SHELLDATA ends

public RemoteThreadProc_begin
public RemoteThreadProc_end

RemoteThreadProc_begin:
RemoteThreadProc proc
    nop
    nop
    nop
    call @@0
    ___ SHELLDATA <>
@@0:
    xchg [rsp],rbp
    sub rsp,20h
    call SHELLDATA.LoadLibrary[rbp]
    test rax,rax
    jz @@1
    xor eax,eax
@@2:
    add rsp,20h
    pop rbp
    ret
@@1:
    call SHELLDATA.GetLastError[rbp]
    jmp @@2
RemoteThreadProc endp
RemoteThreadProc_end:

CONST ends


CreateRemoteThread
,线程子例程指向
LoadLibrary
”-很好。现在你有两个问题。如果您选择实现一个真正的解决方案(即使用一个真正的线程过程),您就不必解决一个没有解决方案的问题。在16位的日子里,它曾经是一个真正的句柄。不再是了,它现在只是内存中加载模块的基址。在ASLR存在的情况下,您无法对其价值做出硬假设。LoadLibrary()返回BOOL,永远不要忽略它。@IInspectable:我不能使用“real”线程过程,因为我无法解析代码中的WinAPI调用和静态字符串偏移量来将其注入远程进程。@HansPassant:如果它是
BOOL
,则很容易检查:)在从wow注入64位进程之前,您需要先在自己的进程中进入64位模式。仅从这里“
CreateRemoteThread
,线程子例程指向
LoadLibrary
”-很好。现在你有两个问题。如果您选择实现一个真正的解决方案(即使用一个真正的线程过程),您就不必解决一个没有解决方案的问题。在16位的日子里,它曾经是一个真正的句柄。不再是了,它现在只是内存中加载模块的基址。在ASLR存在的情况下,您无法对其价值做出硬假设。LoadLibrary()返回BOOL,永远不要忽略它。@IInspectable:我不能使用“real”线程过程,因为我无法解析代码中的WinAPI调用和静态字符串偏移量来将其注入远程进程。@HansPassant:如果它是
BOOL
,则很容易检查:)在从wow注入64位进程之前,您需要先在自己的进程中进入64位模式。从这里开始是的,你是对的。对不起,我说错了。(我会纠正我的问题。)当然,主机进程是64位的。但问题仍然存在。如何知道注入的LoadLibrary成功了?这还不完全是空谈。即使在使用64位主机进程时,线程的返回值也是32位宽,因此无法使用它返回指针大小的值。@MikeF-是否使用x64 kernel32.dll中的
LoadLibrary
地址?该地址可以是(并且始终是例如在win10上)64位地址,高部分为非零。因此,您不能在call
CreateRemoteThread
中使用它。只有一种方法-您需要首先从进程进入64位模式,然后在此调用64位ntdllapi@RbMm:正确。不涉及任何
WOW64
。我在原来的问题上说错了。(现已更正)所有进程和DLL都是64位的。是的,我从64位进程加载
LoadLibrary
地址,因此我假设它从x64内核获取地址。dll@MikeF-在哪个api调用中使用加载库的64位地址?您是否将64位kernel32.dll加载到进程中?是的,您是正确的。对不起,我说错了。(我会纠正我的问题。)当然是h
extern "C"
{
    extern UCHAR RemoteThreadProc_begin[], RemoteThreadProc_end[];
}

enum INJECT_PHASE {
    fOpenProcess, fVirtualAlloc, fWriteProcessMemory, fCreateRemoteThread, fMax
};

ULONG injectDll(ULONG dwprocessId, PCWSTR dllFilePath, INJECT_PHASE& phase)
{
    ULONG err = 0;

    struct SHELLDATA 
    {
        __int64 code;
        PVOID LoadLibrary, GetLastError;
    };

    if (HANDLE hProcess = OpenProcess(PROCESS_CREATE_THREAD|PROCESS_VM_OPERATION|PROCESS_VM_WRITE, FALSE, dwprocessId))
    {
        SIZE_T cbStr = (wcslen(dllFilePath) + 1) * sizeof(WCHAR);
        SIZE_T cbCode = ((RemoteThreadProc_end - RemoteThreadProc_begin) + sizeof(WCHAR) - 1) & ~(sizeof(WCHAR) - 1);

        union {
            PVOID RemoteAddress;
            PBYTE pbRemote;
            PTHREAD_START_ROUTINE lpStartAddress;
        };

        if (RemoteAddress = VirtualAllocEx(hProcess, 0, cbStr + cbCode, MEM_COMMIT, PAGE_EXECUTE_READWRITE))
        {
            union {
                PVOID pv;
                PBYTE pb;
                SHELLDATA* ps;
            };

            pv = alloca(cbStr + cbCode);

            memcpy(pv, RemoteThreadProc_begin, cbCode);
            memcpy(pb + cbCode, dllFilePath, cbStr);

            HMODULE hmod = GetModuleHandle(L"kernel32");
            ps->GetLastError = GetProcAddress(hmod, "GetLastError");
            ps->LoadLibrary = GetProcAddress(hmod, "LoadLibraryW");

            if (WriteProcessMemory(hProcess, RemoteAddress, pv, cbStr + cbCode, 0))
            {
                if (HANDLE hThread = CreateRemoteThread(hProcess, 0, 0, lpStartAddress, pbRemote + cbCode, 0, 0))
                {
                    phase = fMax;
                    WaitForSingleObject(hThread, INFINITE);
                    GetExitCodeThread(hThread, &err);

                    CloseHandle(hThread);
                }
                else
                {
                    phase = fCreateRemoteThread;
                    err = GetLastError();
                }
            }
            else
            {
                phase = fWriteProcessMemory;
                err = GetLastError();
            }

            VirtualFreeEx(hProcess, RemoteAddress, 0, MEM_RELEASE);
        }
        else
        {
            phase = fVirtualAlloc;
            err = GetLastError();
        }

        CloseHandle(hProcess);
    }
    else
    {
        phase = fOpenProcess;
        err = GetLastError();
    }

    return err;
}