Barebones library to launch a process with a DLL injected. Intended use-case is to be invoked from C# P/Invoke.

injector.cpp 3.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. #include "injector.h"
  2. #include <cassert>
  3. #include <windows.h>
  4. namespace {
  5. DWORD ExecuteFunctionInProcess(HANDLE process, void const* function, void const* argument, size_t argument_size)
  6. {
  7. void* argument_buffer{nullptr};
  8. if (argument != nullptr && argument_size > 0) {
  9. argument_buffer = VirtualAllocEx(process, nullptr, argument_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
  10. WriteProcessMemory(process, argument_buffer, argument, argument_size, nullptr);
  11. }
  12. auto thread = CreateRemoteThread(
  13. process,
  14. nullptr,
  15. 0,
  16. (LPTHREAD_START_ROUTINE)function,
  17. argument_buffer,
  18. 0,
  19. nullptr);
  20. DWORD result{0};
  21. WaitForSingleObject(thread, INFINITE);
  22. GetExitCodeThread(thread, &result);
  23. if (argument_buffer != nullptr) {
  24. VirtualFreeEx(process, argument_buffer, 0, MEM_RELEASE);
  25. argument_buffer = nullptr;
  26. }
  27. return result;
  28. }
  29. }
  30. uint32_t LaunchInjected(
  31. wchar_t const* command_line,
  32. wchar_t const* working_directory,
  33. wchar_t const* injected_dll,
  34. char const* initialize_function)
  35. {
  36. if (!command_line || !working_directory || !injected_dll || !initialize_function) {
  37. return 0;
  38. }
  39. PROCESS_INFORMATION process_info{};
  40. STARTUPINFO startup_info{};
  41. startup_info.cb = sizeof(startup_info);
  42. // or, you know, i could just const_cast and :pray:...
  43. auto command_length = wcslen(command_line);
  44. wchar_t* mutable_command_line = new wchar_t[command_length + 1];
  45. wcsncpy(mutable_command_line, command_line, command_length);
  46. mutable_command_line[command_length] = L'\0';
  47. auto process_launched = CreateProcess(
  48. nullptr,
  49. mutable_command_line,
  50. nullptr,
  51. nullptr,
  52. false,
  53. CREATE_SUSPENDED,
  54. nullptr,
  55. working_directory,
  56. &startup_info,
  57. &process_info);
  58. if (!process_launched) {
  59. return 0;
  60. }
  61. delete mutable_command_line;
  62. mutable_command_line = nullptr;
  63. // technically wrong, but oh-so-useful. the base address of kernel32.dll is
  64. // identical for all processes in any given session.
  65. void* load_library = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");
  66. assert(load_library);
  67. void* remote_base_address = (void*)ExecuteFunctionInProcess(
  68. process_info.hProcess,
  69. load_library,
  70. injected_dll,
  71. sizeof(wchar_t) * wcslen(injected_dll)
  72. );
  73. if (!remote_base_address) {
  74. TerminateProcess(process_info.hProcess, 0);
  75. return 0;
  76. }
  77. void* local_base_address = (void*)LoadLibrary(injected_dll);
  78. ptrdiff_t offset =
  79. (uintptr_t)GetProcAddress((HMODULE)local_base_address, initialize_function) - (uintptr_t)local_base_address;
  80. FreeLibrary((HMODULE)local_base_address);
  81. void* initialize_address = (void*)((uintptr_t)remote_base_address + offset);
  82. ExecuteFunctionInProcess(process_info.hProcess, initialize_address, nullptr, 0);
  83. ResumeThread(process_info.hThread);
  84. CloseHandle(process_info.hThread);
  85. CloseHandle(process_info.hProcess);
  86. return static_cast<uint32_t>(process_info.dwProcessId);
  87. }
  88. int WINAPI DllMain(HMODULE, DWORD, LPVOID)
  89. {
  90. return true;
  91. }