// coop_inject_x86 -- a 32-bit injector helper the (x64) host spawns to load the // x86 hook DLL into a 32-bit (WOW64) game. A 64-bit process can't cleanly // CreateRemoteThread(LoadLibraryW) into a 32-bit target (its LoadLibraryW lives // in the 32-bit kernel32 at an address the 64-bit host doesn't have), so the // host shells out to this same-bitness helper instead. // // coop_inject_x86 // // Exit code 0 = injected, 1 = failure, 2 = bad arguments. #include #include #include namespace { int inject(unsigned long pid, const std::wstring& dll_path) { if (GetFileAttributesW(dll_path.c_str()) == INVALID_FILE_ATTRIBUTES) { std::fprintf(stderr, "coop_inject_x86: dll not found: %ls\n", dll_path.c_str()); return 1; } const DWORD access = PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ; HANDLE process = OpenProcess(access, FALSE, pid); if (process == nullptr) { std::fprintf(stderr, "coop_inject_x86: OpenProcess(%lu) failed (%lu)\n", pid, GetLastError()); return 1; } int result = 1; const SIZE_T bytes = (dll_path.size() + 1) * sizeof(wchar_t); void* remote = VirtualAllocEx(process, nullptr, bytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (remote != nullptr && WriteProcessMemory(process, remote, dll_path.c_str(), bytes, nullptr)) { // In a 32-bit process kernel32 is mapped at the same base as in this 32-bit // helper, so LoadLibraryW's address here is valid as the remote start routine. auto load_library = reinterpret_cast( GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "LoadLibraryW")); HANDLE thread = CreateRemoteThread(process, nullptr, 0, load_library, remote, 0, nullptr); if (thread != nullptr) { WaitForSingleObject(thread, INFINITE); DWORD exit_code = 0; GetExitCodeThread(thread, &exit_code); CloseHandle(thread); result = (exit_code != 0) ? 0 : 1; // LoadLibraryW returns the module handle } else { std::fprintf(stderr, "coop_inject_x86: CreateRemoteThread failed (%lu)\n", GetLastError()); } } if (remote != nullptr) { VirtualFreeEx(process, remote, 0, MEM_RELEASE); } CloseHandle(process); return result; } } // namespace int wmain(int argc, wchar_t** argv) { if (argc < 3) { std::printf("usage: coop_inject_x86 \n"); return 2; } const unsigned long pid = std::wcstoul(argv[1], nullptr, 10); if (pid == 0) { std::fprintf(stderr, "coop_inject_x86: invalid pid\n"); return 2; } return inject(pid, argv[2]); }