Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement attach for Windows #4626

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
build/
build2/
.vs/
10 changes: 6 additions & 4 deletions core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -320,9 +320,11 @@ endif (WIN32)
_DR_get_static_libc_list(static_libc_list)
if (WIN32)
if (DEBUG)
set(WIN32_C_LIB libcmtd)
set(WIN32_C_LIB libcmtd Psapi)
add_compile_options("-DPSAPI_VERSION=2")
else (DEBUG)
set(WIN32_C_LIB libcmt)
set(WIN32_C_LIB libcmt Psapi)
add_compile_options("-DPSAPI_VERSION=2")
endif (DEBUG)
set(NOLIBC_DLL_ENTRY /entry:DllMain)
endif ()
Expand Down Expand Up @@ -994,9 +996,9 @@ target_link_libraries(drdecode drlibc)
# It would be nice to be static on Windows to avoid copying the lib into bin dirs,
# but duplicate symbol problems make that non-trivial: we need i#1409.
if (WIN32)
set(inject_lib_type SHARED)
set(inject_lib_type SHARED)
else ()
set(inject_lib_type STATIC)
set(inject_lib_type STATIC)
endif ()
add_library(drinjectlib ${inject_lib_type} ${INJECTOR_SRCS})
add_gen_events_deps(drinjectlib)
Expand Down
4 changes: 2 additions & 2 deletions core/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@
* as earlier Windows versions give access denied on unknown flags!
*/
# define _WIN32_WINNT _WIN32_WINNT_NT4 /* ==0x0400; NTDDI_VERSION is set from this */

# undef _WIN32_WINNT
# define _WIN32_WINNT _WIN32_WINNT_WINXP
# define WIN32_LEAN_AND_MEAN
/* Exclude rarely-used stuff from Windows headers */

Expand All @@ -71,7 +72,6 @@
# pragma warning(disable : 4324) // structure was padded due to __declspec(align())
# pragma warning(disable : 4709) // comma operator within array index expression
# pragma warning(disable : 4214) // nonstd extension: bit field types other than int

/**************************************************/
/* warnings on compiling with VC 8.0, all on VC or PlatformSDK header files */

Expand Down
15 changes: 15 additions & 0 deletions core/lib/dr_inject.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,21 @@ DR_EXPORT
int
dr_inject_process_create(const char *app_name, const char **app_cmdline, void **data);

#ifdef WINDOWS
DR_EXPORT
/**
* Attach to a 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
67 changes: 63 additions & 4 deletions core/win32/injector.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,21 +56,24 @@
#define UNICODE
#define _UNICODE


#include "../globals.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <Psapi.h>
#include <commdlg.h>
#include <imagehlp.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <tchar.h>

#include "globals_shared.h"
#include "ntdll.h"
#include "inject_shared.h"
#include "os_private.h"
#include "dr_inject.h"
#include <io.h>
#pragma comment(lib, "psapi")

#define VERBOSE 0
#if VERBOSE
Expand Down Expand Up @@ -230,6 +233,7 @@ tchar_to_char(const TCHAR *wstr, OUT char *buf, size_t buflen /*# elements*/)
typedef struct _dr_inject_info_t {
PROCESS_INFORMATION pi;
bool using_debugger_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 @@ -749,6 +753,56 @@ append_app_arg_and_space(char *buf, size_t bufsz, size_t *sofar, const char *arg
}
}

DYNAMORIO_EXPORT
int
dr_inject_process_attach(process_id_t pid, void **data OUT)
{
dr_inject_info_t *info = HeapAlloc(GetProcessHeap(), 0, sizeof(*info));
if (!info) {
return ERROR_INVALID_PARAMETER;
}
memset(info, 0, sizeof(*info));
int errcode = ERROR_SUCCESS;
if (DebugActiveProcess((DWORD)pid))
{
info->using_debugger_injection = false;
info->attached = true;
DEBUG_EVENT dbgevt = { 0 };
while (true)
{
dbgevt.dwProcessId = (DWORD)pid;
WaitForDebugEvent(&dbgevt, INFINITE);
ContinueDebugEvent(dbgevt.dwProcessId, dbgevt.dwThreadId, DBG_CONTINUE);
if (dbgevt.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT)
break;
}
LPWSTR szExePath = malloc(600 * sizeof(wchar_t));
if (!szExePath)
return ERROR_INVALID_PARAMETER;
char *pExeName = NULL;
GetModuleFileNameExW(dbgevt.u.CreateProcessInfo.hProcess, NULL, szExePath, 600);
char buffer[600];
wcstombs(buffer, szExePath, 600);
pExeName = strrchr(buffer, '\\');
if (pExeName == NULL)
return ERROR_INVALID_PARAMETER;
strncpy(info->image_name, pExeName + 1, strlen(pExeName + 1));
char_to_tchar(info->image_name, info->wimage_name, BUFFER_SIZE_ELEMENTS(info->wimage_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 = GetLastError();
*data = info;
return errcode;
}

/* Returns 0 on success.
* On failure, returns a Windows API error code.
*/
Expand Down Expand Up @@ -932,9 +986,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) {
/* 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
34 changes: 22 additions & 12 deletions tools/drdeploy.c
Original file line number Diff line number Diff line change
Expand Up @@ -296,17 +296,16 @@ const char *options_list_str =
" -early Requests early injection (the default).\n"
" -late Requests late injection.\n"
# endif
" -attach <pid> Attach to the process with the given pid. Pass 0\n"
" for pid to launch and inject into a new process.\n"
" -logdir <dir> Logfiles will be stored in this directory.\n"
# endif
" -attach <pid> Attach to the process with the given pid. Pass 0\n"
" -use_dll <dll> Inject given dll instead of configured DR dll.\n"
" -force Inject regardless of configuration.\n"
" -exit0 Return a 0 exit code instead of the app's exit code.\n"
"\n"
" <app and args> Application command line to execute under DR.\n"
#endif /* !DRCONFIG */
;
;

static bool
does_file_exist(const char *path)
Expand Down Expand Up @@ -1107,6 +1106,7 @@ _tmain(int argc, TCHAR *targv[])
bool use_ptrace = false;
bool kill_group = false;
# endif
process_id_t attach_pid = 0;
char *app_name = NULL;
char full_app_name[MAXIMUM_PATH];
const char **app_argv;
Expand Down Expand Up @@ -1397,9 +1397,15 @@ _tmain(int argc, TCHAR *targv[])
}
#endif
#if defined(DRRUN) || defined(DRINJECT)
else if (strcmp(argv[i], "-pidfile") == 0) {
else if (strcmp(argv[i], "-pidfile") == 0)
{
pidfile = argv[++i];
} else if (strcmp(argv[i], "-use_dll") == 0) {
}
else if (strcmp(argv[i], "-attach") == 0)
{
attach_pid = atoi(argv[++i]);
}
else if (strcmp(argv[i], "-use_dll") == 0) {
DR_dll_not_needed = true;
/* Support relative path: very useful! */
get_absolute_path(argv[++i], custom_dll, BUFFER_SIZE_ELEMENTS(custom_dll));
Expand Down Expand Up @@ -1533,7 +1539,7 @@ _tmain(int argc, TCHAR *targv[])
/* Support no app if the tool has its own frontend, under the assumption
* it may have post-processing or other features.
*/
if (i < argc || native_tool[0] == '\0') {
if (i < argc || native_tool[0] == '\0' && attach_pid == 0) {
# endif
if (i >= argc)
usage(false, "%s", "no app specified");
Expand Down Expand Up @@ -1737,7 +1743,13 @@ _tmain(int argc, TCHAR *targv[])
info("will exec %s", app_name);
errcode = dr_inject_prepare_to_exec(app_name, app_argv, &inject_data);
} else
# endif /* UNIX */
# elif defined(WINDOWS)
if (attach_pid != 0)
{
//to delete
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 Expand Up @@ -1837,21 +1849,18 @@ _tmain(int argc, TCHAR *targv[])
# endif
goto error;
}

IF_WINDOWS(start_time = time(NULL);)

if (!dr_inject_process_run(inject_data)) {
error("unable to run");
goto error;
}

# ifdef WINDOWS
if (limit == 0 && dr_inject_using_debug_key(inject_data)) {
info("%s", "Using debugger key injection");
limit = -1; /* no wait */
}
# endif

if (limit >= 0) {
# ifdef WINDOWS
double wallclock;
Expand All @@ -1862,6 +1871,7 @@ _tmain(int argc, TCHAR *targv[])
# ifdef WINDOWS
end_time = time(NULL);
wallclock = difftime(end_time, start_time);

if (showstats || showmem)
dr_inject_print_stats(inject_data, (int)wallclock, showstats, showmem);
# endif
Expand All @@ -1872,7 +1882,6 @@ _tmain(int argc, TCHAR *targv[])
}

exitcode = dr_inject_process_exit(inject_data, !success /*kill process*/);

if (limit < 0)
exitcode = 0; /* Return success if we didn't wait. */

Expand All @@ -1884,8 +1893,9 @@ _tmain(int argc, TCHAR *targv[])
/* we created the process suspended so if we later had an error be sure
* to kill it instead of leaving it hanging
*/
if (inject_data != NULL)
if (inject_data != NULL) {
dr_inject_process_exit(inject_data, true /*kill process*/);
}
# ifdef DRRUN
if (tofree != NULL)
free(tofree);
Expand Down