306 lines
9.4 KiB
Diff
306 lines
9.4 KiB
Diff
From a63ea7e338cabc315b58b2356a004c8772ac0f6c Mon Sep 17 00:00:00 2001
|
|
From: Jeremy Drake <github@jdrake.com>
|
|
Date: Tue, 22 Jun 2021 22:06:26 -0700
|
|
Subject: [PATCH 44/N] getprocaddr: refactor/cleanup
|
|
|
|
Look in kernel32 and kernelbase for CtrlRoutine before breaking out the
|
|
big guns and trying to find it in the call stack. Fixes #51.
|
|
|
|
The change to look at the return code of the thread caused the program
|
|
to act as though ExitProcess failed to inject. Deal with this by
|
|
passing the thread exit code as an OUT param, and only looking at it
|
|
when calling the CtrlRoutine.
|
|
|
|
While I was there, I noticed a potential race between CreateEvent and
|
|
the ctrl_handler function that uses it, so cleaned that up. Also the
|
|
event doesn't need to be named, so don't name it to avoid potential
|
|
collisions with other processes.
|
|
|
|
Fixed a couple of printf type warnings. As this is mingw, it should be
|
|
safe to use %lu for DWORDs.
|
|
---
|
|
winsup/utils/getprocaddr.c | 202 +++++++++++++++++++++----------------
|
|
1 file changed, 116 insertions(+), 86 deletions(-)
|
|
|
|
diff --git a/winsup/utils/getprocaddr.c b/winsup/utils/getprocaddr.c
|
|
index d5ecd35..25814c7 100644
|
|
--- a/winsup/utils/getprocaddr.c
|
|
+++ b/winsup/utils/getprocaddr.c
|
|
@@ -27,13 +27,14 @@ static HANDLE CtrlEvent;
|
|
static int
|
|
inject_remote_thread_into_process (HANDLE process,
|
|
LPTHREAD_START_ROUTINE address,
|
|
- uintptr_t exit_code)
|
|
+ uintptr_t exit_code,
|
|
+ DWORD *thread_return)
|
|
{
|
|
int res = -1;
|
|
|
|
if (!address)
|
|
return res;
|
|
- DWORD thread_id, code;
|
|
+ DWORD thread_id;
|
|
HANDLE thread = CreateRemoteThread (process, NULL, 1024 * 1024, address,
|
|
(PVOID)exit_code, 0, &thread_id);
|
|
if (thread)
|
|
@@ -53,9 +54,8 @@ inject_remote_thread_into_process (HANDLE process,
|
|
CtrlEvent set by SetConsoleCtrlHandler is handled correctly, in all
|
|
other cases it returns something non-zero(not sure what it that).
|
|
*/
|
|
- GetExitCodeThread (thread, &code);
|
|
- if (code != 0)
|
|
- res = code;
|
|
+ if (thread_return != NULL)
|
|
+ GetExitCodeThread (thread, thread_return);
|
|
|
|
CloseHandle (thread);
|
|
}
|
|
@@ -80,6 +80,7 @@ ctrl_handler (DWORD ctrl_type)
|
|
HANDLE process;
|
|
PSYMBOL_INFOW info;
|
|
DWORD64 displacement;
|
|
+ DWORD thread_return = 0;
|
|
|
|
count = CaptureStackBackTrace (1l /* skip this function */,
|
|
1l /* return only one trace item */, &address,
|
|
@@ -134,15 +135,18 @@ ctrl_handler (DWORD ctrl_type)
|
|
return 1;
|
|
}
|
|
/* Inject the remote thread only when asked to */
|
|
- int t = inject_remote_thread_into_process (h, address, exit_code);
|
|
- if (t != 0)
|
|
+ if (inject_remote_thread_into_process (h, address, exit_code,
|
|
+ &thread_return) < 0)
|
|
{
|
|
fprintf (stderr,
|
|
- "Error while injecting remote thread %d for pid(%d)\n", t,
|
|
- pid);
|
|
+ "Error while injecting remote thread for pid(%lu)\n", pid);
|
|
exit (1); /*We should exit immediately or else there will a 10s hang
|
|
waiting for the event to happen.*/
|
|
}
|
|
+ if (thread_return)
|
|
+ fprintf (stderr,
|
|
+ "Injected remote thread for pid(%lu) returned %lu\n", pid,
|
|
+ thread_return);
|
|
}
|
|
SymCleanup (process);
|
|
if (!SetEvent (CtrlEvent))
|
|
@@ -150,77 +154,16 @@ ctrl_handler (DWORD ctrl_type)
|
|
fprintf (stderr, "SetEvent failed (%ld)\n", GetLastError ());
|
|
return 1;
|
|
}
|
|
- exit (0);
|
|
+ exit (thread_return != 0);
|
|
}
|
|
|
|
-int
|
|
-main (int argc, char **argv)
|
|
+/* The easy route for finding the address of CtrlRoutine
|
|
+ * would be use GetProcAddress() but this isn't viable
|
|
+ * here because that symbol isn't exported.
|
|
+ */
|
|
+static int
|
|
+find_ctrl_routine_the_hard_way ()
|
|
{
|
|
- char *end;
|
|
-
|
|
- if (argc == 4)
|
|
- {
|
|
- exit_code = atoi (argv[2]);
|
|
- pid = strtoul (argv[3], NULL, 0);
|
|
- }
|
|
- else if (argc == 2)
|
|
- {
|
|
- pid = 0;
|
|
- }
|
|
- else
|
|
- {
|
|
- fprintf (stderr, "Need a function name, exit code and pid\n"
|
|
- "Or needs a function name.\n");
|
|
- return 1;
|
|
- }
|
|
-
|
|
- /* The easy route for finding the address of CtrlRoutine
|
|
- * would be use GetProcAddress() but this isn't viable
|
|
- * because here because that symbol isn't exported.
|
|
- */
|
|
-
|
|
- if (strcmp (argv[1], "CtrlRoutine"))
|
|
- {
|
|
- HINSTANCE kernel32 = GetModuleHandle ("kernel32");
|
|
- if (!kernel32)
|
|
- return 1;
|
|
- void *address = (void *)GetProcAddress (kernel32, argv[1]);
|
|
-
|
|
- if (!address)
|
|
- {
|
|
- fprintf (stderr, "Could not find address in stack\n");
|
|
- return 1;
|
|
- }
|
|
-
|
|
- if (pid == 0)
|
|
- {
|
|
- printf ("%p\n", address);
|
|
- fflush (stdout);
|
|
- return 0;
|
|
- }
|
|
- HANDLE h = OpenProcess (PROCESS_CREATE_THREAD |
|
|
- PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION |
|
|
- PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, pid);
|
|
- if (h == NULL)
|
|
- {
|
|
- fprintf (stderr, "OpenProcess failed: %ld\n", GetLastError ());
|
|
- return 1;
|
|
- }
|
|
- /* Inject the remote thread */
|
|
- if (inject_remote_thread_into_process (h, (LPTHREAD_START_ROUTINE)address,
|
|
- exit_code) < 0)
|
|
- {
|
|
- fprintf (stderr, "Could not inject thread into process %d\n", pid);
|
|
- return 1;
|
|
- }
|
|
- return 0;
|
|
- }
|
|
- if (argc > 4)
|
|
- {
|
|
- fprintf (stderr, "Unhandled option: %s\n", argv[4]);
|
|
- return 1;
|
|
- }
|
|
-
|
|
/*
|
|
* Avoid terminating all processes attached to the current console;
|
|
* This would happen if we used the same console as the caller, though,
|
|
@@ -249,32 +192,119 @@ main (int argc, char **argv)
|
|
}
|
|
}
|
|
|
|
+ CtrlEvent = CreateEvent (NULL, // default security attributes
|
|
+ TRUE, // manual-reset event
|
|
+ FALSE, // initial state is nonsignaled
|
|
+ NULL // object name
|
|
+ );
|
|
+
|
|
+ if (CtrlEvent == NULL)
|
|
+ {
|
|
+ fprintf (stderr, "CreateEvent failed (%ld)\n", GetLastError ());
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+
|
|
if (!SetConsoleCtrlHandler (ctrl_handler, TRUE))
|
|
{
|
|
fprintf (stderr, "Could not register Ctrl handler\n");
|
|
return 1;
|
|
}
|
|
+
|
|
if (!GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, 0))
|
|
{
|
|
fprintf (stderr, "Could not simulate Ctrl+Break\n");
|
|
return 1;
|
|
}
|
|
|
|
- CtrlEvent = CreateEvent (NULL, // default security attributes
|
|
- TRUE, // manual-reset event
|
|
- FALSE, // initial state is nonsignaled
|
|
- TEXT ("CtrlEvent") // object name
|
|
- );
|
|
- if (CtrlEvent == NULL)
|
|
+ if (WaitForSingleObject (CtrlEvent, 10000 /* 10 seconds*/) != WAIT_OBJECT_0)
|
|
{
|
|
- fprintf (stderr, "CreateEvent failed (%ld)\n", GetLastError ());
|
|
+ fprintf (stderr, "WaitForSingleObject failed (%ld)\n", GetLastError ());
|
|
return 1;
|
|
}
|
|
+ return 0;
|
|
+}
|
|
|
|
- if (WaitForSingleObject (CtrlEvent, 10000 /* 10 seconds*/) != WAIT_OBJECT_0)
|
|
+static void *
|
|
+get_proc_addr (const char * module_name, const char * function_name)
|
|
+{
|
|
+ HMODULE module = GetModuleHandle (module_name);
|
|
+ if (!module)
|
|
+ return NULL;
|
|
+ return (void *)GetProcAddress (module, function_name);
|
|
+}
|
|
+
|
|
+int
|
|
+main (int argc, char **argv)
|
|
+{
|
|
+ char *end;
|
|
+ void *address;
|
|
+ BOOL is_ctrl_routine;
|
|
+ DWORD thread_return = 0;
|
|
+
|
|
+ if (argc == 4)
|
|
{
|
|
- fprintf (stderr, "WaitForSingleObject failed (%ld)\n", GetLastError ());
|
|
+ exit_code = atoi (argv[2]);
|
|
+ pid = strtoul (argv[3], NULL, 0);
|
|
+ }
|
|
+ else if (argc == 2)
|
|
+ {
|
|
+ pid = 0;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ fprintf (stderr, "Need a function name, exit code and pid\n"
|
|
+ "Or needs a function name.\n");
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ is_ctrl_routine = strcmp (argv[1], "CtrlRoutine") == 0;
|
|
+ address = get_proc_addr ("kernel32", argv[1]);
|
|
+ if (is_ctrl_routine && !address)
|
|
+ {
|
|
+ /* CtrlRoutine is undocumented, and has been seen in both
|
|
+ * kernel32 and kernelbase
|
|
+ */
|
|
+ address = get_proc_addr ("kernelbase", argv[1]);
|
|
+ if (!address)
|
|
+ return find_ctrl_routine_the_hard_way ();
|
|
+ }
|
|
+
|
|
+ if (!address)
|
|
+ {
|
|
+ fprintf (stderr, "Could not get proc address\n");
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ if (pid == 0)
|
|
+ {
|
|
+ printf ("%p\n", address);
|
|
+ fflush (stdout);
|
|
+ return 0;
|
|
+ }
|
|
+ HANDLE h = OpenProcess (PROCESS_CREATE_THREAD |
|
|
+ PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION |
|
|
+ PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, pid);
|
|
+ if (h == NULL)
|
|
+ {
|
|
+ fprintf (stderr, "OpenProcess failed: %ld\n", GetLastError ());
|
|
+ return 1;
|
|
+ }
|
|
+ /* Inject the remote thread */
|
|
+ if (inject_remote_thread_into_process (h, (LPTHREAD_START_ROUTINE)address,
|
|
+ exit_code, &thread_return) < 0)
|
|
+ {
|
|
+ fprintf (stderr, "Could not inject thread into process %lu\n", pid);
|
|
return 1;
|
|
}
|
|
+
|
|
+ if (is_ctrl_routine && thread_return)
|
|
+ {
|
|
+ fprintf (stderr,
|
|
+ "Injected remote thread for pid %lu returned %lu\n", pid,
|
|
+ thread_return);
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
return 0;
|
|
}
|
|
--
|
|
2.32.0.windows.1
|
|
|