Winapi x64中HMODULE的结构是什么?
我正试图实现一个64位进程。我的主机进程调用Winapi x64中HMODULE的结构是什么?,winapi,Winapi,我正试图实现一个64位进程。我的主机进程调用CreateRemoteThread,线程子例程指向LoadLibrary。DLL随后通过调用FreeLibraryAndExitThread“从内部”卸载自己 我的目标是知道注入的LoadLibrary调用是否成功。不幸的是,我不能在我的(主机)进程中使用GetExitCodeThread,因为返回的64位HMODULE句柄被远程线程截断为DWORD。我不想使用,因为他们会引入比赛条件 因此,我想知道HMODULE在64位进程中由LoadLibrar
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位地址,高部分为非零。因此,您不能在callCreateRemoteThread
中使用它。只有一种方法-您需要首先从进程进入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;
}