Created
November 15, 2014 06:39
-
-
Save mook/33abbeb13b6bb511fc21 to your computer and use it in GitHub Desktop.
Win32 children attaching, for http://thread.gmane.org/gmane.comp.gnu.mingw.w64.general/10634
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <windows.h> | |
#include <stdio.h> | |
#include <map> | |
#define print_error(msg) printf(msg ": %08lx\n", GetLastError()) | |
#if defined(PARENT) | |
struct DebugProcessData { | |
HANDLE hProcess; | |
HANDLE hThread; | |
LPVOID lpStartAddress; | |
BYTE originalByte; | |
}; | |
int main() { | |
STARTUPINFO si = { | |
}; | |
PROCESS_INFORMATION pi = {0}; | |
BOOL success; | |
success = CreateProcess(NULL, | |
(LPSTR)"child.exe", | |
NULL, NULL, | |
FALSE, | |
DEBUG_PROCESS, | |
NULL, | |
NULL, | |
&si, | |
&pi); | |
if (!success) { | |
print_error("Failed to start process"); | |
return 1; | |
} | |
printf("Parent started successfully\n"); | |
DEBUG_EVENT debugEvent = {0}; | |
std::map<DWORD, DebugProcessData> debugProcessData; | |
BOOL done = FALSE; | |
while (!done) { | |
if (!WaitForDebugEvent(&debugEvent, INFINITE)) { | |
print_error("Failed to wait for debug event"); | |
break; | |
} | |
BOOL handled = FALSE; | |
DebugProcessData& processData = debugProcessData[debugEvent.dwProcessId]; | |
switch (debugEvent.dwDebugEventCode) { | |
case CREATE_PROCESS_DEBUG_EVENT: { | |
printf("Child %ld started\n", debugEvent.dwProcessId); | |
success = DuplicateHandle(GetCurrentProcess(), | |
debugEvent.u.CreateProcessInfo.hProcess, | |
GetCurrentProcess(), | |
&processData.hProcess, | |
0, | |
FALSE, | |
DUPLICATE_SAME_ACCESS); | |
if (!success) { | |
print_error("Failed to duplicate process handle"); | |
return 1; | |
} | |
success = DuplicateHandle(GetCurrentProcess(), | |
debugEvent.u.CreateProcessInfo.hThread, | |
GetCurrentProcess(), | |
&processData.hThread, | |
0, | |
FALSE, | |
DUPLICATE_SAME_ACCESS); | |
if (!success) { | |
print_error("Failed to duplicate thread handle"); | |
return 1; | |
} | |
processData.lpStartAddress = (LPVOID)debugEvent.u.CreateProcessInfo.lpStartAddress; | |
printf("Child process %p start address: %p\n", | |
processData.hProcess, | |
processData.lpStartAddress); | |
success = ReadProcessMemory(processData.hProcess, | |
processData.lpStartAddress, | |
&processData.originalByte, | |
sizeof(processData.originalByte), | |
NULL); | |
if (!success) { | |
print_error("Failed to read original byte"); | |
return 1; | |
} | |
printf("Got original byte: %02x\n", processData.originalByte); | |
BYTE newData = 0xCC; /* int 3 */ | |
success = WriteProcessMemory(processData.hProcess, | |
processData.lpStartAddress, | |
&newData, sizeof(newData), NULL); | |
if (!success) { | |
print_error("Failed to write int 3"); | |
return 1; | |
} | |
break; | |
} | |
case EXCEPTION_DEBUG_EVENT: { | |
switch (debugEvent.u.Exception.ExceptionRecord.ExceptionCode) { | |
case EXCEPTION_BREAKPOINT: { | |
if (debugEvent.u.Exception.ExceptionRecord.ExceptionAddress == processData.lpStartAddress) { | |
printf("Start breakpoint hit\n"); | |
success = WriteProcessMemory(processData.hProcess, | |
processData.lpStartAddress, | |
&processData.originalByte, | |
sizeof(processData.originalByte), NULL); | |
if (!success) { | |
print_error("Failed to restore code"); | |
return 1; | |
} | |
CONTEXT context = {0}; | |
context.ContextFlags = CONTEXT_CONTROL; | |
success = GetThreadContext(processData.hThread, &context); | |
if (!success) { | |
print_error("Failed to get thread context"); | |
return 1; | |
} | |
#if defined(_M_X64) | |
context.Rip = (DWORD64)processData.lpStartAddress; | |
#elif defined(_M_IX86) | |
context.Eip = (DWORD)processData.lpStartAddress; | |
#else | |
#error "Don't know how to handle thread context" | |
#endif | |
success = SetThreadContext(processData.hThread, &context); | |
if (!success) { | |
print_error("Failed to set thread context"); | |
return 1; | |
} | |
break; | |
} else { | |
printf("Unknown breakpoint hit at %p\n", | |
debugEvent.u.Exception.ExceptionRecord.ExceptionAddress); | |
} | |
break; | |
} | |
default: { | |
printf("Debug exception %08lx\n", | |
debugEvent.u.Exception.ExceptionRecord.ExceptionCode); | |
break; | |
} | |
} | |
break; | |
} | |
case EXIT_PROCESS_DEBUG_EVENT: { | |
printf("Process %ld exited\n", debugEvent.dwProcessId); | |
debugProcessData.erase(debugEvent.dwProcessId); | |
if (debugProcessData.empty()) { | |
printf("All decendents have exited\n"); | |
done = TRUE; | |
} | |
break; | |
} | |
case LOAD_DLL_DEBUG_EVENT: { | |
printf("Loading dll\n"); | |
break; | |
} | |
default: { | |
printf("Unhandled debug event: %08lx\n", debugEvent.dwDebugEventCode); | |
} | |
} | |
if (!handled) { | |
success = ContinueDebugEvent(debugEvent.dwProcessId, | |
debugEvent.dwThreadId, | |
DBG_CONTINUE); | |
if (!success) { | |
print_error("Failed to continue debuggee"); | |
return 1; | |
} | |
} | |
} | |
return 0; | |
} | |
#elif defined(CHILD) | |
int main(int argc, char **) { | |
printf("hello from child (%d)\n", argc); | |
if (argc == 1) { | |
STARTUPINFO si = {0}; | |
PROCESS_INFORMATION pi = {0}; | |
BOOL success; | |
success = CreateProcess(NULL, | |
(LPSTR)"child.exe 1", | |
NULL, NULL, | |
FALSE, | |
0, | |
NULL, | |
NULL, | |
&si, | |
&pi); | |
if (!success) { | |
print_error("Failed to start process"); | |
return 1; | |
} | |
} | |
return 0; | |
} | |
#else | |
#error "Neither parent nor child" | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Sample output: