21 const DWORD error_code = GetLastError();
22 if (error_code == ERROR_SUCCESS) {
29 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
30 | FORMAT_MESSAGE_IGNORE_INSERTS,
33 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
40 "%s failed (Win32 API Error 0x%lX: %s)",
43 message ? message :
"unknown"
58 SIZE_T bytes_read = 0;
60 if (!ReadProcessMemory(process, (LPCVOID)address, out, out_size, &bytes_read)
61 || bytes_read != out_size) {
64 "Could not read %s from child process (address=0x%08X, expected=%u, "
79 const CONTEXT *thread_context,
80 uintptr_t *out_image_base
82 const uintptr_t peb_address = (uintptr_t)thread_context->Ebx;
83 uintptr_t image_base = 0;
85 DTTR_LOG_DEBUG(
"Reading image base from PEB at 0x%08X", (
unsigned)peb_address);
98 *out_image_base = image_base;
104 uintptr_t image_base,
105 uintptr_t *out_entry_point_rva
107 IMAGE_DOS_HEADER dos = {0};
108 if (!
read_remote_bytes(process, image_base, &dos,
sizeof(dos),
"remote DOS header")) {
112 if (dos.e_magic != IMAGE_DOS_SIGNATURE) {
117 if (dos.e_lfanew <= 0) {
118 DTTR_LOG_ERROR(
"Invalid NT header offset in child process image");
122 const uintptr_t nt_headers_address = image_base + (uintptr_t)dos.e_lfanew;
124 "DOS header valid; remote NT headers at 0x%08X",
125 (
unsigned)nt_headers_address
128 IMAGE_NT_HEADERS32 nt = {0};
139 if (nt.Signature != IMAGE_NT_SIGNATURE) {
144 if (nt.OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
145 DTTR_LOG_ERROR(
"Unsupported PE optional header in child process image");
149 const uintptr_t rva = (uintptr_t)nt.OptionalHeader.AddressOfEntryPoint;
151 *out_entry_point_rva = rva;
158 sdsfree(sidecar_path);
169 const char *dll_path,
170 uintptr_t original_entry
172 static const WCHAR KERNEL32_NAME[] = L
"kernel32.dll";
174 memset(out_payload, 0,
sizeof(*out_payload));
176 const size_t dll_path_len = strlen(dll_path);
177 if (dll_path_len >=
sizeof(out_payload->
dll_path)) {
178 DTTR_LOG_ERROR(
"Sidecar DLL path is too long for shellcode buffer: %s", dll_path);
182 memcpy(out_payload->
dll_path, dll_path, dll_path_len + 1);
205 uint8_t **out_buffer,
206 uint32_t *out_buffer_size
208 const size_t out_size = (size_t)dttr_sidecar_shellcode_len +
sizeof(*payload);
209 if (out_size > UINT32_MAX) {
214 uint8_t *
const buffer = malloc(out_size);
220 *out_buffer = buffer;
221 *out_buffer_size = (uint32_t)out_size;
222 memcpy(buffer, dttr_sidecar_shellcode, dttr_sidecar_shellcode_len);
223 memcpy(buffer + dttr_sidecar_shellcode_len, payload,
sizeof(*payload));
226 "Shellcode payload built (bytes=%u, shellcode=%u, payload=%u)",
228 dttr_sidecar_shellcode_len,
229 (
unsigned)
sizeof(*payload)
239 LPVOID *out_remote_buffer
241 DTTR_LOG_DEBUG(
"Allocating %u bytes in remote process", (
unsigned)buffer_size);
243 LPVOID remote_buffer = VirtualAllocEx(
247 MEM_COMMIT | MEM_RESERVE,
250 if (!remote_buffer) {
255 DTTR_LOG_DEBUG(
"Remote allocation at 0x%08X", (
unsigned)(uintptr_t)remote_buffer);
257 SIZE_T bytes_written = 0;
258 if (!WriteProcessMemory(process, remote_buffer, buffer, buffer_size, &bytes_written)
259 || bytes_written != buffer_size) {
262 "Could not write shellcode to child process (expected=%u, wrote=%u)",
263 (
unsigned)buffer_size,
264 (
unsigned)bytes_written
266 VirtualFreeEx(process, remote_buffer, 0, MEM_RELEASE);
272 DWORD old_protect = 0;
273 if (!VirtualProtectEx(
277 PAGE_EXECUTE_READWRITE,
281 VirtualFreeEx(process, remote_buffer, 0, MEM_RELEASE);
285 DTTR_LOG_DEBUG(
"Remote memory protection set to PAGE_EXECUTE_READWRITE");
287 *out_remote_buffer = remote_buffer;
292 CONTEXT child_thread_context = {0};
293 child_thread_context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
294 if (!GetThreadContext(child_info->hThread, &child_thread_context)) {
299 uintptr_t image_base = 0;
301 child_info->hProcess,
302 &child_thread_context,
308 uintptr_t entry_point_rva = 0;
310 child_info->hProcess,
317 const uintptr_t original_entry = image_base + entry_point_rva;
320 "Resolved original entry point: 0x%08X (base=0x%08X + RVA)",
321 (
unsigned)original_entry,
325 char sidecar_dll_path[MAX_PATH];
337 uint8_t *shellcode_buffer =
NULL;
338 uint32_t shellcode_buffer_len = 0;
343 LPVOID payload_buffer =
NULL;
345 child_info->hProcess,
347 shellcode_buffer_len,
350 free(shellcode_buffer);
351 if (!wrote_payload) {
355 child_thread_context.Eip = (
DWORD)(uintptr_t)payload_buffer;
356 if (!SetThreadContext(child_info->hThread, &child_thread_context)) {
358 VirtualFreeEx(child_info->hProcess, payload_buffer, 0, MEM_RELEASE);
363 "Thread context updated: EIP=0x%08X",
364 (
unsigned)(uintptr_t)payload_buffer
367 if (ResumeThread(child_info->hThread) == (
DWORD)-1) {
369 VirtualFreeEx(child_info->hProcess, payload_buffer, 0, MEM_RELEASE);
DTTR_Graphics_COM_DirectDrawSurface7 DWORD flags void NULL
#define DTTR_LOG_DEBUG(...)
#define DTTR_LOG_ERROR(...)
sds DTTR_Path_ModuleSibling(void *module, const char *relative_path)
bool DTTR_Path_CopySds(char *out, size_t out_size, sds value)
static bool resolve_sidecar_dll_path(char *out_path, size_t out_path_size)
static bool read_remote_image_base_from_thread_context(HANDLE process, const CONTEXT *thread_context, uintptr_t *out_image_base)
static const char *const SIDECAR_DLL_RELATIVE_PATH
bool DTTR_Loader_InjectSidecar(const PROCESS_INFORMATION *child_info)
static bool initialize_shellcode_payload(DTTR_LoaderShellcodePayload *out_payload, const char *dll_path, uintptr_t original_entry)
static const char EXIT_THREAD_NAME[]
static const char GET_LAST_ERROR_NAME[]
static const uintptr_t PEB_IMAGE_BASE_OFFSET
static bool read_remote_bytes(HANDLE process, uintptr_t address, void *out, SIZE_T out_size, const char *name)
static bool build_sidecar_shellcode(const DTTR_LoaderShellcodePayload *payload, uint8_t **out_buffer, uint32_t *out_buffer_size)
static bool write_remote_payload(HANDLE process, const void *buffer, SIZE_T buffer_size, LPVOID *out_remote_buffer)
static void log_win32_failure(const char *operation)
static bool read_entry_point_rva_from_remote_image(HANDLE process, uintptr_t image_base, uintptr_t *out_entry_point_rva)
static const char LOAD_LIBRARY_EX_NAME[]
WCHAR kernel32_name[sizeof(L"kernel32.dll")/sizeof(WCHAR)]
char getlasterror_name[sizeof("GetLastError")]
char loadlibraryex_name[sizeof("LoadLibraryExA")]
char exitthread_name[sizeof("ExitThread")]