Skip to content

Commit

Permalink
i#725: add attach for windows
Browse files Browse the repository at this point in the history
  • Loading branch information
orbp committed Sep 4, 2021
1 parent 0546c39 commit 208fb7b
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 11 deletions.
7 changes: 4 additions & 3 deletions core/dynamo.c
Original file line number Diff line number Diff line change
Expand Up @@ -2880,8 +2880,6 @@ dynamo_thread_not_under_dynamo(dcontext_t *dcontext)
#endif
}

#define MAX_TAKE_OVER_ATTEMPTS 8

/* Mark this thread as under DR, and take over other threads in the current process.
*/
void
Expand All @@ -2892,6 +2890,7 @@ dynamorio_take_over_threads(dcontext_t *dcontext)
*/
bool found_threads;
uint attempts = 0;
uint max_takeover_attempts = DYNAMO_OPTION(takeover_attempts);

os_process_under_dynamorio_initiate(dcontext);
/* We can start this thread now that we've set up process-wide actions such
Expand All @@ -2912,7 +2911,9 @@ dynamorio_take_over_threads(dcontext_t *dcontext)
attempts++;
if (found_threads && !bb_lock_start)
bb_lock_start = true;
} while (found_threads && attempts < MAX_TAKE_OVER_ATTEMPTS);
if (DYNAMO_OPTION(sleep_between_takeovers))
dr_sleep(1);
} while (found_threads && attempts < max_takeover_attempts);
os_process_under_dynamorio_complete(dcontext);
/* End the barrier to new threads. */
signal_event(dr_attach_finished);
Expand Down
16 changes: 16 additions & 0 deletions core/lib/dr_inject.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,22 @@ DR_EXPORT
int
dr_inject_process_create(const char *app_name, const char **app_cmdline, void **data);

#ifdef WINDOWS
DR_EXPORT
/**
* Attach to an existing process.
*
* \param[in] pid PID for process to attach.
*
* \param[out] data An opaque pointer that should be passed to
* subsequent dr_inject_* routines to refer to
* this process.
* \return Returns 0 on success. On failure, returns a system error code.`
*/
int
dr_inject_process_attach(process_id_t pid, void **data);
#endif

#ifdef UNIX

DR_EXPORT
Expand Down
10 changes: 10 additions & 0 deletions core/optionsx.h
Original file line number Diff line number Diff line change
Expand Up @@ -2633,6 +2633,16 @@ OPTION_COMMAND(bool, native_exec_opt, false, "native_exec_opt",
"optimize control flow transition between native and non-native modules",
STATIC, OP_PCACHE_GLOBAL)

#ifdef WINDOWS
OPTION_DEFAULT(bool, skip_terminating_threads, false,
"do not takeover threads that are terminating")
#endif

OPTION_DEFAULT(bool, sleep_between_takeovers, false,
"sleep between takeover attempts to allow threads to exit syscalls")

OPTION_DEFAULT(uint, takeover_attempts, 8, "number of takeover attempts")

/* vestiges from our previous life as a dynamic optimizer */
OPTION_DEFAULT_INTERNAL(bool, inline_calls, true, "inline calls in traces")

Expand Down
88 changes: 85 additions & 3 deletions core/win32/injector.c
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ typedef struct _dr_inject_info_t {
PROCESS_INFORMATION pi;
bool using_debugger_injection;
bool using_thread_injection;
bool attached;
TCHAR wimage_name[MAXIMUM_PATH];
/* We need something to point at for dr_inject_get_image_name so we just
* keep a utf8 buffer as well.
Expand Down Expand Up @@ -843,6 +844,82 @@ dr_inject_process_create(const char *app_name, const char **argv, void **data OU
return errcode;
}

DYNAMORIO_EXPORT
int
dr_inject_process_attach(process_id_t pid, void **data OUT)
{
dr_inject_info_t *info = HeapAlloc(GetProcessHeap(), 0, sizeof(*info));
memset(info, 0, sizeof(*info));
int errcode = ERROR_SUCCESS;
bool received_initial_debug_event = false;

if (DebugActiveProcess((DWORD)pid)) {
DebugSetProcessKillOnExit(false);

info->using_debugger_injection = false;
info->attached = true;

DEBUG_EVENT dbgevt = { 0 };
do {
dbgevt.dwProcessId = (DWORD)pid;
WaitForDebugEvent(&dbgevt, INFINITE);
ContinueDebugEvent(dbgevt.dwProcessId, dbgevt.dwThreadId, DBG_CONTINUE);

if (dbgevt.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT) {
received_initial_debug_event = true;
}
} while (received_initial_debug_event == false);

wchar_t exe_path[MAX_PATH];
DWORD exe_path_size = MAX_PATH;

HANDLE process_handle = OpenProcess(PROCESS_ALL_ACCESS, false, (DWORD)pid);
if (process_handle != NULL) {
BOOL(*query_full_process_image_name_w)
(HANDLE, DWORD, LPWSTR, PDWORD) =
(BOOL(*)(HANDLE, DWORD, LPWSTR, PDWORD))(GetProcAddress(
GetModuleHandle(TEXT("Kernel32")), "QueryFullProcessImageNameW"));

if (query_full_process_image_name_w(process_handle, 0, exe_path,
&exe_path_size) != 0) {
wchar_t *exe_name = wcsrchr(exe_path, '\\');
if (exe_name != NULL) {
wcsncpy(info->wimage_name, exe_name + 1,
BUFFER_SIZE_ELEMENTS(info->wimage_name));
NULL_TERMINATE_BUFFER(info->wimage_name);

tchar_to_char(info->wimage_name, info->image_name,
BUFFER_SIZE_ELEMENTS(info->image_name));

info->pi.dwProcessId = dbgevt.dwProcessId;
info->pi.dwThreadId = dbgevt.dwThreadId;

DuplicateHandle(GetCurrentProcess(),
dbgevt.u.CreateProcessInfo.hProcess,
GetCurrentProcess(), &info->pi.hProcess, 0, FALSE,
DUPLICATE_SAME_ACCESS);

DuplicateHandle(GetCurrentProcess(),
dbgevt.u.CreateProcessInfo.hThread,
GetCurrentProcess(), &info->pi.hThread, 0, FALSE,
DUPLICATE_SAME_ACCESS);
} else {
errcode = ERROR_INVALID_PARAMETER;
}
} else {
errcode = GetLastError();
}
CloseHandle(process_handle);
} else {
errcode = GetLastError();
}
} else {
errcode = GetLastError();
}
*data = info;
return errcode;
}

DYNAMORIO_EXPORT
bool
dr_inject_use_late_injection(void *data)
Expand Down Expand Up @@ -954,9 +1031,14 @@ bool
dr_inject_process_run(void *data)
{
dr_inject_info_t *info = (dr_inject_info_t *)data;
/* resume the suspended app process so its main thread can run */
ResumeThread(info->pi.hThread);
close_handle(info->pi.hThread);
if (info->attached == true) {
/* detach the debugger */
DebugActiveProcessStop(info->pi.dwProcessId);
} else {
/* resume the suspended app process so its main thread can run */
ResumeThread(info->pi.hThread);
close_handle(info->pi.hThread);
}

return true;
}
Expand Down
15 changes: 15 additions & 0 deletions core/win32/ntdll.c
Original file line number Diff line number Diff line change
Expand Up @@ -2383,6 +2383,21 @@ nt_set_context(HANDLE hthread, CONTEXT *cxt)
return NT_SYSCALL(SetContextThread, hthread, cxt);
}

bool
nt_is_thread_terminating(HANDLE hthread)
{
ULONG previous_suspend_count;
NTSTATUS res;
GET_RAW_SYSCALL(SuspendThread, IN HANDLE ThreadHandle,
OUT PULONG PreviousSuspendCount OPTIONAL);
res = NT_SYSCALL(SuspendThread, hthread, &previous_suspend_count);
if (NT_SUCCESS(res)) {
nt_thread_resume(hthread, (int *)&previous_suspend_count);
}

return res == STATUS_THREAD_IS_TERMINATING;
}

bool
nt_thread_suspend(HANDLE hthread, int *previous_suspend_count)
{
Expand Down
3 changes: 3 additions & 0 deletions core/win32/ntdll.h
Original file line number Diff line number Diff line change
Expand Up @@ -1436,6 +1436,9 @@ nt_get_context(HANDLE hthread, CONTEXT *cxt);
NTSTATUS
nt_set_context(HANDLE hthread, CONTEXT *cxt);

bool
nt_is_thread_terminating(HANDLE hthread);

bool
nt_thread_suspend(HANDLE hthread, int *previous_suspend_count);

Expand Down
9 changes: 9 additions & 0 deletions core/win32/os.c
Original file line number Diff line number Diff line change
Expand Up @@ -2497,6 +2497,15 @@ static bool
os_take_over_thread(dcontext_t *dcontext, HANDLE hthread, thread_id_t tid, bool suspended)
{
bool success = true;

if (DYNAMO_OPTION(skip_terminating_threads)) {
if (nt_is_thread_terminating(hthread)) {
// takeover fails when attaching and trying to takeover terminating threads
// luckily, we don't really need to take over them
return success;
}
}

DWORD cxt_flags = CONTEXT_DR_STATE;
size_t bufsz = nt_get_context_size(cxt_flags);
char *buf = (char *)heap_alloc(dcontext, bufsz HEAPACCT(ACCT_THREAD_MGT));
Expand Down
35 changes: 30 additions & 5 deletions tools/drdeploy.c
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,19 @@ const char *options_list_str =
" Attaching to a process will force blocking system calls\n"
" to fail with EINTR.\n"
# endif
# endif
# ifdef WINDOWS
" -attach <pid> (Experimental)\n"
" Must be used with -late.\n"
" Attach to the process with the given pid.\n"
" If attach to a process which is in middle of blocking\n"
" system call, attach could fail.\n"
" try takeover_sleep and larger takeovers to increase\n"
" the chances of success:\n"
" -takeover_sleep Sleep 1 millisecond between takeover attempts.\n"
" -takeovers <num> Number of takeover attempts. Defaults to 8.\n"
" The larger, the more likely attach will succeed,\n"
" however, the attach process will take longer.\n"
# endif
" -use_dll <dll> Inject given dll instead of configured DR dll.\n"
" -force Inject regardless of configuration.\n"
Expand Down Expand Up @@ -1265,7 +1278,6 @@ _tmain(int argc, TCHAR *targv[])
}
# endif
else if (strcmp(argv[i], "-attach") == 0) {
# ifdef UNIX
const char *pid_str = argv[++i];
process_id_t pid = strtoul(pid_str, NULL, 10);
if (pid == ULONG_MAX)
Expand All @@ -1274,14 +1286,23 @@ _tmain(int argc, TCHAR *targv[])
usage(false, "attach to invalid pid");
}
attach_pid = pid;
# endif
# ifdef UNIX
use_ptrace = true;
# endif
# ifdef WINDOWS
usage(false, "attach in Windows not yet implemented");
add_extra_option(extra_ops, BUFFER_SIZE_ELEMENTS(extra_ops), &extra_ops_sofar,
"-skip_terminating_threads");
# endif
continue;
} else if (strcmp(argv[i], "-takeovers") == 0) {
const char *num_attemps = argv[++i];
add_extra_option(extra_ops, BUFFER_SIZE_ELEMENTS(extra_ops), &extra_ops_sofar,
"-takeover_attempts %s", num_attemps);
continue;
} else if (strcmp(argv[i], "-takeover_sleep") == 0) {
add_extra_option(extra_ops, BUFFER_SIZE_ELEMENTS(extra_ops), &extra_ops_sofar,
"-sleep_between_takeovers");
continue;
}
# ifdef UNIX
# ifdef X86
Expand Down Expand Up @@ -1568,7 +1589,6 @@ _tmain(int argc, TCHAR *targv[])
_snprintf(exe_str, BUFFER_SIZE_ELEMENTS(exe_str), "/proc/%d/exe", attach_pid);
NULL_TERMINATE_BUFFER(exe_str);
size = readlink(exe_str, exe, BUFFER_SIZE_ELEMENTS(exe));
# endif /* UNIX */
if (size > 0) {
if (size < BUFFER_SIZE_ELEMENTS(exe))
exe[size] = '\0';
Expand All @@ -1577,6 +1597,7 @@ _tmain(int argc, TCHAR *targv[])
} else {
usage(false, "attach to invalid pid");
}
# endif /* UNIX */
app_name = exe;
}
/* Support no app if the tool has its own frontend, under the assumption
Expand Down Expand Up @@ -1789,7 +1810,11 @@ _tmain(int argc, TCHAR *targv[])
errcode =
dr_inject_prepare_to_attach(attach_pid, app_name, wait_syscall, &inject_data);
} else
# endif /* UNIX */
# elif defined(WINDOWS)
if (attach_pid != 0) {
errcode = dr_inject_process_attach(attach_pid, &inject_data);
} else
# endif /* WINDOWS */
{
errcode = dr_inject_process_create(app_name, app_argv, &inject_data);
info("created child with pid " PIDFMT " for %s",
Expand Down

0 comments on commit 208fb7b

Please sign in to comment.