如何从WOW64进程注入x86 DLL到x64进程
Posted
技术标签:
【中文标题】如何从WOW64进程注入x86 DLL到x64进程【英文标题】:How to inject x86 DLL from the WOW64 process to the x64 process 【发布时间】:2015-07-27 15:17:27 【问题描述】:有没有办法将x86 DLL从WOW64进程注入x64进程?我知道根据 MSDN,这通常是不可能的:
在 64 位 Windows 上,64 位进程无法加载 32 位动态链接 库(DLL)。此外,32 位进程无法加载 64 位 DLL
但我在 Metasploit 的存储库中找到了以下 code 并想知道如何在我的项目中使用它:
/*
* Attempt to gain code execution in a native x64 process from a wow64 process by transitioning out of the wow64 (x86)
* enviroment into a native x64 enviroment and accessing the native win64 API's.
* Note: On Windows 2003 the injection will work but in the target x64 process issues occur with new
* threads (kernel32!CreateThread will return ERROR_NOT_ENOUGH_MEMORY). Because of this we filter out
* Windows 2003 from this method of injection, however the APC injection method will work on 2003.
*/
DWORD inject_via_remotethread_wow64( HANDLE hProcess, LPVOID lpStartAddress, LPVOID lpParameter, HANDLE * pThread )
DWORD dwResult = ERROR_SUCCESS;
EXECUTEX64 pExecuteX64 = NULL;
X64FUNCTION pX64function = NULL;
WOW64CONTEXT * ctx = NULL;
OSVERSIONINFO os = 0;
do
os.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
if( !GetVersionEx( &os ) )
BREAK_ON_ERROR( "[INJECT] inject_via_remotethread_wow64: GetVersionEx failed" )
// filter out Windows 2003
if ( os.dwMajorVersion == 5 && os.dwMinorVersion == 2 )
SetLastError( ERROR_ACCESS_DENIED );
BREAK_ON_ERROR( "[INJECT] inject_via_remotethread_wow64: Windows 2003 not supported." )
// alloc a RWX buffer in this process for the EXECUTEX64 function
pExecuteX64 = (EXECUTEX64)VirtualAlloc( NULL, sizeof(migrate_executex64), MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
if( !pExecuteX64 )
BREAK_ON_ERROR( "[INJECT] inject_via_remotethread_wow64: VirtualAlloc pExecuteX64 failed" )
// alloc a RWX buffer in this process for the X64FUNCTION function (and its context)
pX64function = (X64FUNCTION)VirtualAlloc( NULL, sizeof(migrate_wownativex)+sizeof(WOW64CONTEXT), MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
if( !pX64function )
BREAK_ON_ERROR( "[INJECT] inject_via_remotethread_wow64: VirtualAlloc pX64function failed" )
// copy over the wow64->x64 stub
memcpy( pExecuteX64, &migrate_executex64, sizeof(migrate_executex64) );
// copy over the native x64 function
memcpy( pX64function, &migrate_wownativex, sizeof(migrate_wownativex) );
// set the context
ctx = (WOW64CONTEXT *)( (BYTE *)pX64function + sizeof(migrate_wownativex) );
ctx->h.hProcess = hProcess;
ctx->s.lpStartAddress = lpStartAddress;
ctx->p.lpParameter = lpParameter;
ctx->t.hThread = NULL;
dprintf( "[INJECT] inject_via_remotethread_wow64: pExecuteX64=0x%08X, pX64function=0x%08X, ctx=0x%08X", pExecuteX64, pX64function, ctx );
// Transition this wow64 process into native x64 and call pX64function( ctx )
// The native function will use the native Win64 API's to create a remote thread in the target process.
if( !pExecuteX64( pX64function, (DWORD)ctx ) )
SetLastError( ERROR_ACCESS_DENIED );
BREAK_ON_ERROR( "[INJECT] inject_via_remotethread_wow64: pExecuteX64( pX64function, ctx ) failed" )
if( !ctx->t.hThread )
SetLastError( ERROR_INVALID_HANDLE );
BREAK_ON_ERROR( "[INJECT] inject_via_remotethread_wow64: ctx->t.hThread is NULL" )
// Success! grab the new thread handle from of the context
*pThread = ctx->t.hThread;
dprintf( "[INJECT] inject_via_remotethread_wow64: Success, hThread=0x%08X", ctx->t.hThread );
while( 0 );
if( pExecuteX64 )
VirtualFree( pExecuteX64, 0, MEM_DECOMMIT );
if( pX64function )
VirtualFree( pX64function, 0, MEM_DECOMMIT );
return dwResult;
我尝试使用以下代码:
#include <boost/scope_exit.hpp>
#include <Windows.h>
#include <cstdlib>
#include <iostream>
typedef struct _WOW64CONTEXT
union
HANDLE hProcess;
BYTE bPadding2[8];
h;
union
LPVOID lpStartAddress;
BYTE bPadding1[8];
s;
union
LPVOID lpParameter;
BYTE bPadding2[8];
p;
union
HANDLE hThread;
BYTE bPadding2[8];
t;
WOW64CONTEXT, *LPWOW64CONTEXT;
typedef BOOL(WINAPI * X64FUNCTION)(DWORD dwParameter);
typedef DWORD(WINAPI * EXECUTEX64)(X64FUNCTION pFunction, DWORD dwParameter);
BYTE migrate_executex64[] = "\x55\x89\xE5\x56\x57\x8B\x75\x08\x8B\x4D\x0C\xE8\x00\x00\x00\x00"
"\x58\x83\xC0\x25\x83\xEC\x08\x89\xE2\xC7\x42\x04\x33\x00\x00\x00"
"\x89\x02\xE8\x09\x00\x00\x00\x83\xC4\x14\x5F\x5E\x5D\xC2\x08\x00"
"\x8B\x3C\x24\xFF\x2A\x48\x31\xC0\x57\xFF\xD6\x5F\x50\xC7\x44\x24"
"\x04\x23\x00\x00\x00\x89\x3C\x24\xFF\x2C\x24";
BYTE migrate_wownativex[] = "\xFC\x48\x89\xCE\x48\x89\xE7\x48\x83\xE4\xF0\xE8\xC8\x00\x00\x00"
"\x41\x51\x41\x50\x52\x51\x56\x48\x31\xD2\x65\x48\x8B\x52\x60\x48"
"\x8B\x52\x18\x48\x8B\x52\x20\x48\x8B\x72\x50\x48\x0F\xB7\x4A\x4A"
"\x4D\x31\xC9\x48\x31\xC0\xAC\x3C\x61\x7C\x02\x2C\x20\x41\xC1\xC9"
"\x0D\x41\x01\xC1\xE2\xED\x52\x41\x51\x48\x8B\x52\x20\x8B\x42\x3C"
"\x48\x01\xD0\x66\x81\x78\x18\x0B\x02\x75\x72\x8B\x80\x88\x00\x00"
"\x00\x48\x85\xC0\x74\x67\x48\x01\xD0\x50\x8B\x48\x18\x44\x8B\x40"
"\x20\x49\x01\xD0\xE3\x56\x48\xFF\xC9\x41\x8B\x34\x88\x48\x01\xD6"
"\x4D\x31\xC9\x48\x31\xC0\xAC\x41\xC1\xC9\x0D\x41\x01\xC1\x38\xE0"
"\x75\xF1\x4C\x03\x4C\x24\x08\x45\x39\xD1\x75\xD8\x58\x44\x8B\x40"
"\x24\x49\x01\xD0\x66\x41\x8B\x0C\x48\x44\x8B\x40\x1C\x49\x01\xD0"
"\x41\x8B\x04\x88\x48\x01\xD0\x41\x58\x41\x58\x5E\x59\x5A\x41\x58"
"\x41\x59\x41\x5A\x48\x83\xEC\x20\x41\x52\xFF\xE0\x58\x41\x59\x5A"
"\x48\x8B\x12\xE9\x4F\xFF\xFF\xFF\x5D\x4D\x31\xC9\x41\x51\x48\x8D"
"\x46\x18\x50\xFF\x76\x10\xFF\x76\x08\x41\x51\x41\x51\x49\xB8\x01"
"\x00\x00\x00\x00\x00\x00\x00\x48\x31\xD2\x48\x8B\x0E\x41\xBA\xC8"
"\x38\xA4\x40\xFF\xD5\x48\x85\xC0\x74\x0C\x48\xB8\x00\x00\x00\x00"
"\x00\x00\x00\x00\xEB\x0A\x48\xB8\x01\x00\x00\x00\x00\x00\x00\x00"
"\x48\x83\xC4\x50\x48\x89\xFC\xC3";
int main(int argc, char* argv[])
if (argc != 2)
std::cout << "Usage: " << argv[0] << " [pid]" << std::endl;
return EXIT_SUCCESS;
int pid = std::atoi(argv[1]);
if (pid == 0)
std::cerr << "Invalid pid" << std::endl;
return EXIT_FAILURE;
HANDLE process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (process_handle == NULL)
std::cerr << "An error occurred while using function OpenProcess. Error code: " << GetLastError() << std::endl;
return EXIT_FAILURE;
BOOST_SCOPE_EXIT_ALL(process_handle)
CloseHandle(process_handle);
;
LPVOID load_library_addr = (LPVOID)GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
if (load_library_addr == NULL)
std::cerr << "An error occurred while using function GetProcAddress. Error code: " << GetLastError() << std::endl;
return EXIT_FAILURE;
const char* dll_path = "D:\\helper.dll";
LPVOID dll_path_memory = VirtualAllocEx(process_handle, NULL, strlen(dll_path) + 1, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (dll_path_memory == NULL)
std::cerr << "An error occurred while using function VirtualAllocEx. Error code: " << GetLastError() << std::endl;
return EXIT_FAILURE;
BOOL res = WriteProcessMemory(process_handle, dll_path_memory, dll_path, strlen(dll_path) + 1, NULL);
if (res == 0)
std::cerr << "An error occurred while using function WriteProcessMemory. Error code: " << GetLastError() << std::endl;
return EXIT_FAILURE;
HANDLE thread_handle = CreateRemoteThread(process_handle, NULL, 0, (LPTHREAD_START_ROUTINE)load_library_addr, dll_path_memory, 0, NULL);
if (thread_handle == NULL)
std::cerr << "An error occurred while using function CreateRemoteThread. Error code: " << GetLastError() << std::endl;
EXECUTEX64 pExecuteX64 = (EXECUTEX64)VirtualAlloc(NULL, sizeof(migrate_executex64), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (pExecuteX64 == NULL)
std::cerr << "An error occurred while using function VirtualAlloc. Error code: " << GetLastError() << std::endl;
return EXIT_FAILURE;
X64FUNCTION pX64function = (X64FUNCTION)VirtualAlloc(NULL, sizeof(migrate_wownativex) + sizeof(WOW64CONTEXT), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (pX64function == NULL)
std::cerr << "An error occurred while using function VirtualAlloc. Error code: " << GetLastError() << std::endl;
return EXIT_FAILURE;
std::memcpy(pExecuteX64, &migrate_executex64, sizeof(migrate_executex64));
std::memcpy(pX64function, &migrate_wownativex, sizeof(migrate_wownativex));
WOW64CONTEXT* ctx = (WOW64CONTEXT *)((BYTE *)pX64function + sizeof(migrate_wownativex));
ctx->h.hProcess = process_handle;
ctx->s.lpStartAddress = load_library_addr;
ctx->p.lpParameter = dll_path_memory;
ctx->t.hThread = NULL;
if (!pExecuteX64(pX64function, (DWORD)ctx))
std::cerr << "Error" << std::endl;
return EXIT_FAILURE;
if (!ctx->t.hThread)
std::cerr << "ctx->t.hThread is NULL" << std::endl;
return EXIT_FAILURE;
HANDLE hThread = ctx->t.hThread;
if (ResumeThread(hThread) == (DWORD)-1)
std::cerr << "An error occurred while using function ResumeThread. Error code: " << GetLastError() << std::endl;
return EXIT_FAILURE;
WaitForSingleObject(thread_handle, INFINITE);
std::cout << "Done" << std::endl;
在将 x86 DLL 注入到 x86 进程的情况下,它可以完美运行,但在将 x86 DLL 注入到 x64 进程的情况下则无法正常工作(注入的进程会崩溃)。
我做错了什么?我该如何解决?
提前致谢。
【问题讨论】:
虽然它做的事情并不完全相同(将 x86 代码注入 x64 进程),您是否考虑过使用 COM 包装器?我已经使用 COM 解决了这个问题,双向(x86 到 x64,或 x64 到 x86)。不要尝试在进程中加载 dll,使用 windows 代理服务器。 @Steven Hansen 你能给我更多的信息吗?另外我没有这两个应用程序的源代码——我正在尝试修复一个软件中的错误 在您的 COM 包装器中,您可以创建自己的自定义接口并正常加载/调用 Dll。 Dll 的源代码不是必需的。只需研究 COM 服务器。有时这些是用 ATL 编写的(可能对您的搜索很有帮助),并且 Visual Studio 支持相当多的帮助程序来编写它们。 根据您发布的 Metasploit 代码中的 cmets,它确实不将 32 位代码注入目标 64 位进程。它将 64 位代码注入到目标 64 位进程中,来自一个 32 位进程。 【参考方案1】:不是真的。
64 位 Windows 操作系统可以原生运行 64 位代码,也可以使用 WOW64 运行 32 位代码。但是,每个过程可能只是一个或另一个。
进入内部时,您可以看到每个进程都被标记为 32 位或 64 位。当操作系统将指示 CPU 运行与该进程关联的线程时,它将作为 32 位或 64 位代码跳转。
因此,如果您以某种方式将 32 位字节码(无论它从何处编码,DLL 或其他东西)加载到 64 位进程中,当操作系统运行它时,该进程将崩溃,因为该进程将处于 64 位模式,并将操作码解释为 64 位操作码。
在 32 位代码上运行 64 位代码也是如此。
编辑:
事实上,x32 WOW64 进程可以执行 x64 代码。据此article。但是,我找不到有关运行 x86 代码的 x64 进程的资源。
【讨论】:
他们使用的 WinAPI 函数比我发布的要多——查看 Metasploit 存储库中的源代码。事实上,他们也是这样做的——将 DLL 从 WOW64 进程注入 x64 进程,所以我想知道我的情况出了什么问题 您检查过 Metasploit 的代码是否成功地将 x86 DLL 注入到 x64 进程中? 实际上有人声称您可以在一个进程中在 32 位和 64 位之间切换。我正在检查这个 - blog.rewolf.pl/blog/?p=102 检查一下:spth.virii.lu/v1/articles/HEAVEN.TXT - 进程可以使用适当的程序集来切换模式(操作系统为什么不禁止它?)。但是,没有用于此的操作系统 API,通常不受支持。以上是关于如何从WOW64进程注入x86 DLL到x64进程的主要内容,如果未能解决你的问题,请参考以下文章
使用来自 x64 注入器的 x86 dll 注入 x86 目标