From 3b51eddd7fb73b72aaa6647c02898e043c488cb5 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 30 Jul 2024 01:49:12 +0100 Subject: [PATCH] library: inject plugin lib and luajit from stub --- src/library/CMakeLists.txt | 36 ++- src/library/dll/dll_inject.h | 9 + src/library/dll/dll_inject_generator.cxx | 224 ++++++++++++++++++ src/library/dll/main.c | 7 +- src/library/dll/stub.c | 31 ++- .../dll/{stub_inject.hxx => stub_inject.h} | 0 src/library/dll/stub_inject_generator.cxx | 2 +- 7 files changed, 279 insertions(+), 30 deletions(-) create mode 100644 src/library/dll/dll_inject.h create mode 100644 src/library/dll/dll_inject_generator.cxx rename src/library/dll/{stub_inject.hxx => stub_inject.h} (100%) diff --git a/src/library/CMakeLists.txt b/src/library/CMakeLists.txt index d1d7d11..41ac3fc 100644 --- a/src/library/CMakeLists.txt +++ b/src/library/CMakeLists.txt @@ -14,31 +14,41 @@ if (WIN32) set(BOLT_STUB_ENTRYNAME entry) set(BOLT_STUB_ENTRYORDINAL 1) file(GENERATE OUTPUT stub.def CONTENT "LIBRARY STUB\nEXPORTS\n${BOLT_STUB_ENTRYNAME} @${BOLT_STUB_ENTRYORDINAL}\n") - add_library(stub SHARED dll/stub.c "${CMAKE_CURRENT_BINARY_DIR}/stub.def") + file(GENERATE OUTPUT plugin.def CONTENT "LIBRARY BOLT-PLUGIN\nEXPORTS\n${BOLT_STUB_ENTRYNAME} @${BOLT_STUB_ENTRYORDINAL}\n") + + add_library(${BOLT_PLUGIN_LIB_NAME} SHARED dll/main.c plugin/plugin.c gl.c + rwlock/rwlock_win32.c ipc_win32.c plugin/plugin_win32.c ../../modules/hashmap/hashmap.c + ../miniz/miniz.c ../../modules/spng/spng/spng.c "${CMAKE_CURRENT_BINARY_DIR}/plugin.def") + target_compile_definitions(${BOLT_PLUGIN_LIB_NAME} PUBLIC BOLT_STUB_ENTRYNAME=${BOLT_STUB_ENTRYNAME}) + target_link_libraries(${BOLT_PLUGIN_LIB_NAME} PUBLIC "${BOLT_LUAJIT_DIR}/lua51.lib") + target_include_directories(${BOLT_PLUGIN_LIB_NAME} PUBLIC "${BOLT_LUAJIT_DIR}" "${BOLT_ZLIB_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/../miniz") + + add_executable(dll_inject_generator dll/dll_inject_generator.cxx) + target_compile_definitions(dll_inject_generator PUBLIC BOLT_STUB_ENTRYORDINAL=${BOLT_STUB_ENTRYORDINAL}) + set_target_properties(dll_inject_generator PROPERTIES CXX_STANDARD 20 CXX_EXTENSIONS OFF) + add_custom_command( + OUTPUT dll_inject_cmake_gen.c + DEPENDS ${BOLT_PLUGIN_LIB_NAME} dll_inject_generator + COMMAND dll_inject_generator $ "${BOLT_LUAJIT_DIR}/lua51.dll" "${CMAKE_CURRENT_SOURCE_DIR}/dll/dll_inject.h" ">dll_inject_cmake_gen.c" + ) + + add_library(stub SHARED dll/stub.c dll_inject_cmake_gen.c "${CMAKE_CURRENT_BINARY_DIR}/stub.def") target_compile_definitions(stub PUBLIC BOLT_STUB_ENTRYNAME=${BOLT_STUB_ENTRYNAME}) add_executable(stub_inject_generator dll/stub_inject_generator.cxx) target_compile_definitions(stub_inject_generator PUBLIC BOLT_STUB_ENTRYORDINAL=${BOLT_STUB_ENTRYORDINAL}) - set_target_properties(stub_inject_generator PROPERTIES CXX_STANDARD 20 CXX_EXTENSIONS OFF) add_custom_command( OUTPUT stub_inject_cmake_gen.cxx DEPENDS stub stub_inject_generator - COMMAND stub_inject_generator $ "${CMAKE_CURRENT_SOURCE_DIR}/dll/stub_inject.hxx" ">stub_inject_cmake_gen.cxx" + COMMAND stub_inject_generator $ "${CMAKE_CURRENT_SOURCE_DIR}/dll/stub_inject.h" ">stub_inject_cmake_gen.cxx" ) add_custom_target(BOLT_STUB_INJECT_DEPENDENCY DEPENDS stub_inject_cmake_gen.cxx) set(BOLT_STUB_INJECT_CXX "${CMAKE_CURRENT_BINARY_DIR}/stub_inject_cmake_gen.cxx" PARENT_SCOPE) - add_library(${BOLT_PLUGIN_LIB_NAME} SHARED dll/main.c plugin/plugin.c gl.c - rwlock/rwlock_win32.c ipc_win32.c plugin/plugin_win32.c ../../modules/hashmap/hashmap.c - ../miniz/miniz.c ../../modules/spng/spng/spng.c) - target_link_libraries(${BOLT_PLUGIN_LIB_NAME} PUBLIC "${BOLT_LUAJIT_DIR}/lua51.lib") - target_include_directories(${BOLT_PLUGIN_LIB_NAME} PUBLIC "${BOLT_LUAJIT_DIR}" "${BOLT_ZLIB_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/../miniz") - install(TARGETS ${BOLT_PLUGIN_LIB_NAME} DESTINATION "${BOLT_CEF_INSTALLDIR}/bolt-launcher") - install(FILES "${BOLT_LUAJIT_DIR}/lua51.dll" DESTINATION "${BOLT_CEF_INSTALLDIR}/bolt-launcher") - if(MSVC) - set_property(TARGET stub PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") - set_property(TARGET stub_inject_generator PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") set_property(TARGET ${BOLT_PLUGIN_LIB_NAME} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") + set_property(TARGET stub PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") + set_property(TARGET dll_inject_generator PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") + set_property(TARGET stub_inject_generator PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") else() target_compile_options(stub_inject_generator PUBLIC ${BOLT_WINDOWS_NONMSVC_OPTIONS}) target_link_options(stub_inject_generator PUBLIC ${BOLT_WINDOWS_NONMSVC_LINKOPTIONS}) diff --git a/src/library/dll/dll_inject.h b/src/library/dll/dll_inject.h new file mode 100644 index 0000000..af6b0d6 --- /dev/null +++ b/src/library/dll/dll_inject.h @@ -0,0 +1,9 @@ +#include + +struct PluginInjectParams { + HMODULE kernel32; + HMODULE(__stdcall* pGetModuleHandleW)(LPCWSTR); + FARPROC(__stdcall* pGetProcAddress)(HMODULE, LPCSTR); +}; + +void InjectPluginDll(HANDLE process, HMODULE kernel32, HMODULE(__stdcall* pGetModuleHandleW)(LPCWSTR), FARPROC(__stdcall* pGetProcAddress)(HMODULE, LPCSTR)); diff --git a/src/library/dll/dll_inject_generator.cxx b/src/library/dll/dll_inject_generator.cxx new file mode 100644 index 0000000..987a4a3 --- /dev/null +++ b/src/library/dll/dll_inject_generator.cxx @@ -0,0 +1,224 @@ +#include "stub_inject.h" +#include +#include + +DWORD perms_for_characteristics(DWORD characteristics) { + const bool readp = (characteristics & IMAGE_SCN_MEM_READ); + const bool writep = (characteristics & IMAGE_SCN_MEM_WRITE); + const bool execp = (characteristics & IMAGE_SCN_MEM_EXECUTE); + return + readp ? + writep ? + execp ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE + : + execp ? PAGE_EXECUTE_READ : PAGE_READONLY + : + writep ? + execp ? PAGE_EXECUTE_WRITECOPY : PAGE_WRITECOPY + : + execp ? PAGE_EXECUTE : PAGE_NOACCESS; +} + +/// This program is run by the build system on Windows. It manually maps the plugin DLL, then outputs some C code which +/// writes that DLL into a target process, invokes the payload on a remote thread, and blocks until that thread returns. +/// The function generated by this code will be called by the stub DLL during a CreateProcessW hook. +int wmain(int argc, const wchar_t **argv) { + if (argc != 4) { + std::cerr << "dll_inject_generator must be called with exactly three arguments: the absolute path to the " + "plugin dll, the absolute path to lua51.dll, and the absolute path to dll_inject.h." << std::endl; + return 1; + } + + HANDLE plugin_file = CreateFileW(argv[1], GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr); + if (plugin_file == INVALID_HANDLE_VALUE) { + std::cerr << "dll_inject_generator could not open the dll file: "; + std::wcerr << argv[1]; + std::cerr << std::endl; + return 1; + } + DWORD plugin_file_size = GetFileSize(plugin_file, NULL); + PVOID plugin_dll = VirtualAlloc(NULL, plugin_file_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if (!ReadFile(plugin_file, plugin_dll, plugin_file_size, nullptr, nullptr)) { + std::cerr << "dll_inject_generator could not read the dll file: " << GetLastError() << std::endl; + return 1; + } + + HANDLE luajit_file = CreateFileW(argv[2], GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr); + if (luajit_file == INVALID_HANDLE_VALUE) { + std::cerr << "dll_inject_generator could not open the lua dll file: "; + std::wcerr << argv[2]; + std::cerr << std::endl; + return 1; + } + DWORD luajit_file_size = GetFileSize(luajit_file, NULL); + PVOID luajit_dll = VirtualAlloc(NULL, luajit_file_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if (!ReadFile(luajit_file, luajit_dll, luajit_file_size, nullptr, nullptr)) { + std::cerr << "dll_inject_generator could not read the lua dll file: " << GetLastError() << std::endl; + return 1; + } + + PIMAGE_DOS_HEADER plugin_dos_header = (PIMAGE_DOS_HEADER)plugin_dll; + PIMAGE_NT_HEADERS plugin_nt_headers = (PIMAGE_NT_HEADERS)((LPBYTE)plugin_dll + plugin_dos_header->e_lfanew); + PIMAGE_SECTION_HEADER plugin_section_header = (PIMAGE_SECTION_HEADER)(plugin_nt_headers + 1); + PIMAGE_DOS_HEADER luajit_dos_header = (PIMAGE_DOS_HEADER)luajit_dll; + PIMAGE_NT_HEADERS luajit_nt_headers = (PIMAGE_NT_HEADERS)((LPBYTE)luajit_dll + luajit_dos_header->e_lfanew); + PIMAGE_SECTION_HEADER luajit_section_header = (PIMAGE_SECTION_HEADER)(luajit_nt_headers + 1); + + // find a function in the export table with ordinal value 1, so we can hard-code its RVA. + // this assumes the whole export table of plugin.dll is in one section. + DWORD entry_point_rva = 0; + IMAGE_DATA_DIRECTORY plugin_export_virtual_details = plugin_nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; + for (size_t i = 0; i < plugin_nt_headers->FileHeader.NumberOfSections; i += 1) { + if (plugin_section_header[i].VirtualAddress > plugin_export_virtual_details.VirtualAddress) continue; + if (plugin_section_header[i].VirtualAddress + plugin_section_header[i].Misc.VirtualSize <= plugin_export_virtual_details.VirtualAddress) continue; + const DWORD export_section_offset = plugin_section_header[i].PointerToRawData - plugin_section_header[i].VirtualAddress; + PIMAGE_EXPORT_DIRECTORY plugin_exports = (PIMAGE_EXPORT_DIRECTORY)((LPBYTE)plugin_dll + (plugin_export_virtual_details.VirtualAddress + export_section_offset)); + PDWORD export_address_list = (PDWORD)((LPBYTE)plugin_dll + (plugin_exports->AddressOfFunctions + export_section_offset)); + entry_point_rva = export_address_list[BOLT_STUB_ENTRYORDINAL - plugin_exports->Base]; + break; + } + if (!entry_point_rva) { + std::cerr << "dll_inject_generator could not locate entry point" << std::endl; + return 1; + } + + // output file header and #include lines + std::cout << "// This file was created programmatically by Bolt's build system. Don't change this file - change the code that generated it." << std::endl; + std::cout << "#include " << std::endl << "#include " << std::endl << "#include \""; + std::wcout << argv[3]; + std::cout << "\"" << std::endl; + // embed dll headers + std::cout << "static const uint8_t plugin_dll_headers[] = {"; + for (size_t i = 0; i < plugin_nt_headers->OptionalHeader.SizeOfHeaders; i += 1) { + if (i != 0) std::cout << ","; + std::cout << (WORD)((LPBYTE)plugin_dll)[i]; + } + std::cout << "};" << std::endl; + std::cout << "static const uint8_t luajit_dll_headers[] = {"; + for (size_t i = 0; i < luajit_nt_headers->OptionalHeader.SizeOfHeaders; i += 1) { + if (i != 0) std::cout << ","; + std::cout << (WORD)((LPBYTE)luajit_dll)[i]; + } + std::cout << "};" << std::endl; + // embed dll sections + for (size_t i = 0; i < plugin_nt_headers->FileHeader.NumberOfSections; i += 1) { + std::cout << "static const uint8_t psect" << i << "[] = {"; + LPBYTE section_start = (LPBYTE)plugin_dll + plugin_section_header[i].PointerToRawData; + for (size_t j = 0; j < plugin_section_header[i].SizeOfRawData; j += 1) { + if (j != 0) std::cout << ","; + std::cout << (WORD)section_start[j]; + } + std::cout << "};" << std::endl; + } + for (size_t i = 0; i < luajit_nt_headers->FileHeader.NumberOfSections; i += 1) { + std::cout << "static const uint8_t luasect" << i << "[] = {"; + LPBYTE section_start = (LPBYTE)luajit_dll + luajit_section_header[i].PointerToRawData; + for (size_t j = 0; j < luajit_section_header[i].SizeOfRawData; j += 1) { + if (j != 0) std::cout << ","; + std::cout << (WORD)section_start[j]; + } + std::cout << "};" << std::endl; + } + // embed the amount of zeroes we'll need (WriteProcessMemory needs actual memory to copy from) + std::cout << "static const uint8_t zeroes[" + << max(plugin_nt_headers->OptionalHeader.SizeOfHeaders, luajit_nt_headers->OptionalHeader.SizeOfHeaders) + << "] = {0};" << std::endl; + + std::cout << "void InjectPluginDll(HANDLE process, HMODULE kernel32, HMODULE(__stdcall* pGetModuleHandleW)(LPCWSTR), FARPROC(__stdcall* pGetProcAddress)(HMODULE, LPCSTR)) {" << std::endl; + // we can't call imported functions at this point, so lookup the ones we need from kernel32 +#define DEFLOOKUP(RET, FN, ARGS) #RET "(__stdcall* p" #FN ")" ARGS "=(" #RET "(__stdcall*)" ARGS ")pGetProcAddress(kernel32, \"" #FN "\");" + std::cout << + DEFLOOKUP(LPVOID, VirtualAllocEx, "(HANDLE, LPVOID, SIZE_T, DWORD, DWORD)") + DEFLOOKUP(BOOL, VirtualFreeEx, "(HANDLE, LPVOID, SIZE_T, DWORD)") + DEFLOOKUP(BOOL, WriteProcessMemory, "(HANDLE, LPVOID, LPCVOID, SIZE_T, SIZE_T*)") + DEFLOOKUP(BOOL, ReadProcessMemory, "(HANDLE, LPCVOID, LPVOID, SIZE_T, SIZE_T*)") + DEFLOOKUP(BOOL, VirtualProtectEx, "(HANDLE, LPVOID, SIZE_T, DWORD, PDWORD)") + DEFLOOKUP(HANDLE, CreateRemoteThread, "(HANDLE, LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD)") + DEFLOOKUP(DWORD, WaitForSingleObject, "(HANDLE, DWORD)") + << std::endl; +#undef DEFLOOKUP + // allocate memory in the remote process + std::cout << "LPBYTE dll_plugin = (LPBYTE)pVirtualAllocEx(process, NULL, " << plugin_nt_headers->OptionalHeader.SizeOfImage << ", MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);" << std::endl; + std::cout << "LPBYTE dll_lua = (LPBYTE)pVirtualAllocEx(process, NULL, " << luajit_nt_headers->OptionalHeader.SizeOfImage << ", MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);" << std::endl; + // copy DLL headers to the target process + std::cout << "pWriteProcessMemory(process, dll_plugin, (PVOID)plugin_dll_headers, " << plugin_nt_headers->OptionalHeader.SizeOfHeaders << ", NULL);" << std::endl; + std::cout << "pWriteProcessMemory(process, dll_lua, (PVOID)luajit_dll_headers, " << luajit_nt_headers->OptionalHeader.SizeOfHeaders << ", NULL);" << std::endl; + // copy each section to the target process + for (size_t i = 0; i < plugin_nt_headers->FileHeader.NumberOfSections; i += 1) { + std::cout << "pWriteProcessMemory(process, (PVOID)(dll_plugin + " << plugin_section_header[i].VirtualAddress << "), (PVOID)psect" << i << ", " << plugin_section_header[i].SizeOfRawData << ", NULL);" << std::endl; + } + for (size_t i = 0; i < luajit_nt_headers->FileHeader.NumberOfSections; i += 1) { + std::cout << "pWriteProcessMemory(process, (PVOID)(dll_lua + " << luajit_section_header[i].VirtualAddress << "), (PVOID)luasect" << i << ", " << luajit_section_header[i].SizeOfRawData << ", NULL);" << std::endl; + } + // fix relocations + std::cout << + "ptrdiff_t reloc_delta = (ptrdiff_t)(dll_plugin - " << plugin_nt_headers->OptionalHeader.ImageBase << ");" + "PIMAGE_BASE_RELOCATION remote_reloc_address = (PIMAGE_BASE_RELOCATION)(dll_plugin + " << plugin_nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress << ");" + "WORD offset;" + "DWORD ptr;" + "while (1) {" + "IMAGE_BASE_RELOCATION remote_reloc;" + "pReadProcessMemory(process, (LPCVOID)remote_reloc_address, (LPVOID)&remote_reloc, sizeof(remote_reloc), NULL);" + "if (!remote_reloc.VirtualAddress) break;" + "if (remote_reloc.SizeOfBlock > sizeof(remote_reloc)) {" + "PWORD list = (PWORD)(remote_reloc_address + 1);" + "for (size_t i = 0; i < (remote_reloc.SizeOfBlock - sizeof(remote_reloc)) / sizeof(WORD); i += 1) {" + "pReadProcessMemory(process, (LPCVOID)(list + i), (LPVOID)&offset, sizeof(offset), NULL);" + "if (!offset) continue;" + "LPVOID remote_ptr_address = (LPVOID)(dll_plugin + remote_reloc.VirtualAddress + (offset & 0xFFF));" + "pReadProcessMemory(process, (LPCVOID)remote_ptr_address, (LPVOID)&ptr, sizeof(ptr), NULL);" + "ptr += reloc_delta;" + "pWriteProcessMemory(process, remote_ptr_address, (LPCVOID)&ptr, sizeof(ptr), NULL);" + "}" + "}" + "remote_reloc_address = (PIMAGE_BASE_RELOCATION)((LPBYTE)remote_reloc_address + remote_reloc.SizeOfBlock);" + "}" + "reloc_delta = (ptrdiff_t)(dll_lua - " << luajit_nt_headers->OptionalHeader.ImageBase << ");" + "remote_reloc_address = (PIMAGE_BASE_RELOCATION)(dll_lua + " << luajit_nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress << ");" + "while (1) {" + "IMAGE_BASE_RELOCATION remote_reloc;" + "pReadProcessMemory(process, (LPCVOID)remote_reloc_address, (LPVOID)&remote_reloc, sizeof(remote_reloc), NULL);" + "if (!remote_reloc.VirtualAddress) break;" + "if (remote_reloc.SizeOfBlock > sizeof(remote_reloc)) {" + "PWORD list = (PWORD)(remote_reloc_address + 1);" + "for (size_t i = 0; i < (remote_reloc.SizeOfBlock - sizeof(remote_reloc)) / sizeof(WORD); i += 1) {" + "pReadProcessMemory(process, (LPCVOID)(list + i), (LPVOID)&offset, sizeof(offset), NULL);" + "if (!offset) continue;" + "LPVOID remote_ptr_address = (LPVOID)(dll_lua + remote_reloc.VirtualAddress + (offset & 0xFFF));" + "pReadProcessMemory(process, (LPCVOID)remote_ptr_address, (LPVOID)&ptr, sizeof(ptr), NULL);" + "ptr += reloc_delta;" + "pWriteProcessMemory(process, remote_ptr_address, (LPCVOID)&ptr, sizeof(ptr), NULL);" + "}" + "}" + "remote_reloc_address = (PIMAGE_BASE_RELOCATION)((LPBYTE)remote_reloc_address + remote_reloc.SizeOfBlock);" + "}" << std::endl; + // set rwx permissions for each section + std::cout << "DWORD oldp;" << std::endl; + for (size_t i = 0; i < plugin_nt_headers->FileHeader.NumberOfSections; i += 1) { + DWORD permission = perms_for_characteristics(plugin_section_header[i].Characteristics); + std::cout << "pVirtualProtectEx(process, (LPVOID)(dll_plugin + " << plugin_section_header[i].VirtualAddress << "), " << plugin_section_header[i].Misc.VirtualSize << ", " << permission << ", &oldp);" << std::endl; + } + for (size_t i = 0; i < luajit_nt_headers->FileHeader.NumberOfSections; i += 1) { + DWORD permission = perms_for_characteristics(luajit_section_header[i].Characteristics); + std::cout << "pVirtualProtectEx(process, (LPVOID)(dll_lua + " << luajit_section_header[i].VirtualAddress << "), " << luajit_section_header[i].Misc.VirtualSize << ", " << permission << ", &oldp);" << std::endl; + } + // invoke entrypoint + std::cout << "struct PluginInjectParams params = {.kernel32=kernel32, .pGetModuleHandleW=pGetModuleHandleW, .pGetProcAddress=pGetProcAddress};" << std::endl; + std::cout << "struct PluginInjectParams* remote_params = (struct PluginInjectParams*)pVirtualAllocEx(process, NULL, sizeof(params), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);" << std::endl; + std::cout << "pWriteProcessMemory(process, remote_params, ¶ms, sizeof(params), NULL);" << std::endl; + std::cout << "HANDLE remote_thread = pCreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)(dll_plugin + " << entry_point_rva << "), remote_params, 0, NULL);" << std::endl; + std::cout << "pWaitForSingleObject(remote_thread, INFINITE);" << std::endl; + std::cout << "pVirtualFreeEx(process, remote_params, 0, MEM_RELEASE);" << std::endl; + + // wipe dll headers from remote process + std::cout << "pWriteProcessMemory(process, dll_plugin, (PVOID)zeroes, " << plugin_nt_headers->OptionalHeader.SizeOfHeaders << ", NULL);" << std::endl; + std::cout << "pWriteProcessMemory(process, dll_lua, (PVOID)zeroes, " << luajit_nt_headers->OptionalHeader.SizeOfHeaders << ", NULL);" << std::endl; + + std::cout << "}" << std::endl; + + CloseHandle(luajit_file); + CloseHandle(plugin_file); + VirtualFree(luajit_dll, luajit_file_size, MEM_RELEASE); + VirtualFree(plugin_dll, plugin_file_size, MEM_RELEASE); + return 0; +} diff --git a/src/library/dll/main.c b/src/library/dll/main.c index 508a432..3699d92 100644 --- a/src/library/dll/main.c +++ b/src/library/dll/main.c @@ -1,5 +1,6 @@ -#include - -BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { +#include +#include "dll_inject.h" +DWORD __stdcall BOLT_STUB_ENTRYNAME(struct PluginInjectParams* data) { + return 0; } diff --git a/src/library/dll/stub.c b/src/library/dll/stub.c index 5d50a40..d66b2e3 100644 --- a/src/library/dll/stub.c +++ b/src/library/dll/stub.c @@ -1,18 +1,17 @@ -#include "stub_inject.hxx" +#include "stub_inject.h" +#include "dll_inject.h" -//typedef HANDLE(__stdcall* CREATEFILEW)(LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE); -//typedef FARPROC(__stdcall* CLOSEHANDLE)(HANDLE); -//typedef BOOL(__stdcall* WRITEFILE)(HANDLE, LPCVOID, DWORD, LPDWORD, LPOVERLAPPED); typedef BOOL(__stdcall* CREATEPROCESSW)(LPCWSTR, LPWSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCWSTR, LPSTARTUPINFOW, LPPROCESS_INFORMATION); typedef BOOL(__stdcall* VIRTUALPROTECT)(LPVOID, SIZE_T, DWORD, PDWORD); +typedef DWORD(__stdcall* RESUMETHREAD)(HANDLE); -//static CREATEFILEW real_CreateFileW; -//static CLOSEHANDLE real_CloseHandle; -//static WRITEFILE real_WriteFile; +static RESUMETHREAD real_ResumeThread; static CREATEPROCESSW real_CreateProcessW; BOOL hook_CreateProcessW(LPCWSTR, LPWSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCWSTR, LPSTARTUPINFOW, LPPROCESS_INFORMATION); +struct StubInjectParams params; + // true if match, false if not, for case-insensitive mode a2 must be all lowercase BOOL bolt_cmp(const char* a1, const char* a2, BOOL case_sensitive) { while (1) { @@ -33,10 +32,9 @@ BOOL bolt_cmp(const char* a1, const char* a2, BOOL case_sensitive) { // process passes us its function pointers for GetModuleHandleW and GetProcAddress. using those, we // can find the game's imported modules and use them for ourselves. DWORD __stdcall BOLT_STUB_ENTRYNAME(struct StubInjectParams* data) { + params = *data; VIRTUALPROTECT pVirtualProtect = (VIRTUALPROTECT)data->pGetProcAddress(data->kernel32, "VirtualProtect"); - //real_CreateFileW = (CREATEFILEW)data->pGetProcAddress(data->kernel32, "CreateFileW"); - //real_CloseHandle = (CLOSEHANDLE)data->pGetProcAddress(data->kernel32, "CloseHandle"); - //real_WriteFile = (WRITEFILE)data->pGetProcAddress(data->kernel32, "WriteFile"); + real_ResumeThread = (RESUMETHREAD)data->pGetProcAddress(data->kernel32, "ResumeThread"); HMODULE main_module = data->pGetModuleHandleW(NULL); PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)main_module; @@ -78,17 +76,24 @@ BOOL hook_CreateProcessW( LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ) { - // CreateProcessW hook, check if we're starting rs2client and if so, inject bolt-plugin.dll - return real_CreateProcessW( + // CreateProcessW hook - the game launcher downloads a new version of the game client, if any, + // then runs it using CreateProcessW. it's the only time it uses this function, so we don't need + // to check what the child process is. just inject the plugin loader into it. + const BOOL is_already_suspended = dwCreationFlags & CREATE_SUSPENDED ? TRUE : FALSE; + BOOL ret = real_CreateProcessW( lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, - dwCreationFlags, + dwCreationFlags | CREATE_SUSPENDED, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation ); + if (!ret) return 0; + InjectPluginDll(lpProcessInformation->hProcess, params.kernel32, params.pGetModuleHandleW, params.pGetProcAddress); + if (!is_already_suspended) real_ResumeThread(lpProcessInformation->hThread); + return ret; } diff --git a/src/library/dll/stub_inject.hxx b/src/library/dll/stub_inject.h similarity index 100% rename from src/library/dll/stub_inject.hxx rename to src/library/dll/stub_inject.h diff --git a/src/library/dll/stub_inject_generator.cxx b/src/library/dll/stub_inject_generator.cxx index a3ee085..ee0c60b 100644 --- a/src/library/dll/stub_inject_generator.cxx +++ b/src/library/dll/stub_inject_generator.cxx @@ -1,4 +1,4 @@ -#include "stub_inject.hxx" +#include "stub_inject.h" #include /// This program is run by the build system on Windows. It manually maps the stub DLL, then outputs some C++ code which