| /* ********************************************************** |
| * Copyright (c) 2011-2013 Google, Inc. All rights reserved. |
| * Copyright (c) 2003-2010 VMware, Inc. All rights reserved. |
| * **********************************************************/ |
| |
| /* |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * * Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * * Neither the name of VMware, Inc. nor the names of its contributors may be |
| * used to endorse or promote products derived from this software without |
| * specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
| * DAMAGE. |
| */ |
| |
| /* note - include order matters */ |
| #include <tchar.h> |
| #include "share.h" |
| #include "win32/ntdll.h" /* just for typedefs and defines */ |
| #include "processes.h" |
| #include "win32/drmarker.h" |
| #include <AccCtrl.h> |
| #include <Aclapi.h> |
| #include <stdio.h> |
| |
| /* detach.c(74) : warning C4055: 'type cast' : from data pointer |
| * 'void *' to function pointer 'unsigned long (__stdcall *)(void *)' |
| * (cf. drmarker.h) |
| */ |
| #pragma warning(disable : 4055) |
| |
| #define PAGE_SIZE 0x1000 |
| #define MIN_ALLOCATION_SIZE 0x10000 |
| |
| typedef NTSTATUS (NTAPI *NtCreateThreadType) (OUT PHANDLE ThreadHandle, |
| IN ACCESS_MASK DesiredAccess, |
| IN POBJECT_ATTRIBUTES ObjectAttributes, |
| IN HANDLE ProcessHandle, |
| OUT PCLIENT_ID ClientId, |
| IN PCONTEXT ThreadContext, |
| IN PUSER_STACK UserStack, |
| IN BOOLEAN CreateSuspended); |
| NtCreateThreadType NtCreateThread = NULL; |
| |
| #ifdef X64 |
| typedef unsigned __int64 ptr_uint_t; |
| # define CXT_XIP Rip |
| # define CXT_XAX Rax |
| # define CXT_XCX Rcx |
| # define CXT_XDX Rdx |
| # define CXT_XBX Rbx |
| # define CXT_XSP Rsp |
| # define CXT_XBP Rbp |
| # define CXT_XSI Rsi |
| # define CXT_XDI Rdi |
| /* It looks like both CONTEXT.Xmm0 and CONTEXT.FltSave.XmmRegisters[0] are filled in. |
| * We use the latter so that we don't have to hardcode the index. |
| */ |
| # define CXT_XMM(cxt, idx) ((dr_xmm_t*)&((cxt)->FltSave.XmmRegisters[idx])) |
| /* they kept the 32-bit EFlags field; sure, the upper 32 bits of Rflags |
| * are undefined right now, but doesn't seem very forward-thinking. */ |
| # define CXT_XFLAGS EFlags |
| #else |
| typedef uint ptr_uint_t; |
| # define CXT_XIP Eip |
| # define CXT_XAX Eax |
| # define CXT_XCX Ecx |
| # define CXT_XDX Edx |
| # define CXT_XBX Ebx |
| # define CXT_XSP Esp |
| # define CXT_XBP Ebp |
| # define CXT_XSI Esi |
| # define CXT_XDI Edi |
| # define CXT_XFLAGS EFlags |
| /* This is not documented, but CONTEXT.ExtendedRegisters looks like fxsave layout. |
| * Presumably there are no processors that have SSE but not FXSR |
| * (we ASSERT on that in proc_init()). |
| */ |
| # define FXSAVE_XMM0_OFFSET 160 |
| # define CXT_XMM(cxt, idx) \ |
| ((dr_xmm_t*)&((cxt)->ExtendedRegisters[FXSAVE_XMM0_OFFSET + (idx)*16])) |
| #endif |
| |
| #define THREAD_START_ADDR IF_X64_ELSE(CXT_XCX, CXT_XAX) |
| #define THREAD_START_ARG IF_X64_ELSE(CXT_XDX, CXT_XBX) |
| |
| PVOID WINAPI |
| dummy_func(LPVOID dummy_arg) |
| { |
| return dummy_arg; |
| } |
| |
| /* Gets kernel32!BaseThreadStartThunk which, unfortunately, isn't exported. */ |
| /* Returns NULL on error. NOTE - on Vista there is no kernel32!BaseThreadStartThunk, |
| * rather there is the moral equivalent ntdll!RtlUserThreadStart which is exported |
| * (so doesn't require this convoluted lookup). */ |
| static void* |
| get_kernel_thread_start_thunk() |
| { |
| static void* start_address = NULL; |
| DWORD platform = 0; |
| |
| get_platform(&platform); |
| DO_ASSERT(platform != 0 && platform != PLATFORM_VISTA); |
| if (platform >= PLATFORM_VISTA) |
| return NULL; |
| |
| if (start_address == NULL) { |
| HANDLE hThread = CreateThread(NULL, 0, dummy_func, NULL, |
| CREATE_SUSPENDED, NULL); |
| if (hThread != NULL) { |
| CONTEXT cxt; |
| cxt.ContextFlags = CONTEXT_FULL; |
| if (GetThreadContext(hThread, &cxt)) { |
| /* FIXME - would be a little more elegant to have a lock |
| * protecting start_address, but is not necessary for |
| * correctness as we'll always be writing the same value and |
| * the actual write itself should be atomic. There are places |
| * in utils.c that really do need synchronization so if we |
| * ever add some synchronization mechanisms to share module |
| * might as well use here as well. */ |
| start_address = (void *)cxt.CXT_XIP; |
| #ifdef DEBUG |
| { |
| TCHAR buf[MAX_PATH]; |
| MEMORY_BASIC_INFORMATION mbi; |
| int i = 0; |
| |
| memset(buf, 0, sizeof(buf)); |
| VirtualQuery(start_address, &mbi, sizeof(mbi)); |
| /* a module handle is just the allocation base of the |
| * module */ |
| GetModuleFileName((HMODULE)mbi.AllocationBase, |
| buf, BUFFER_SIZE_ELEMENTS(buf)); |
| NULL_TERMINATE_BUFFER(buf); |
| /* can't believe there is no stristr */ |
| while(buf[i] != _T('\0')) { |
| buf[i] = _totupper(buf[i]); |
| i++; |
| } |
| DO_ASSERT(_tcsstr(buf, _T("KERNEL32.DLL")) != NULL); |
| } |
| #endif |
| } |
| /* don't terminate, otherwise won't be cleaned up on 2k */ |
| /* thread routine is empty so just resume */ |
| ResumeThread(hThread); |
| CloseHandle(hThread); |
| } |
| } |
| DO_ASSERT(start_address != NULL); |
| return start_address; |
| } |
| |
| /* returns NULL on error */ |
| /* FIXME - is similar to core create_thread, but uses API routines where |
| * possible, could try to share. */ |
| /* NOTE - stack_reserve and stack commit must be multiples of PAGE_SIZE and reserve |
| * should be at least 5 pages larger then commit */ |
| /* NOTE - if !target_api, target thread routine can't exit by by returning, instead it |
| * must terminate itself. |
| * NOTE - caller or target thread routine is responsible for informing csrss (if |
| * necessary) and freeing the the thread stack (caller is informed of the stack |
| * via OUT remote_stack arg). |
| * If arg_buf is non-null then the arg_buf_size bytes of arg_buf are copied onto the |
| * new thread's stack and pointer to them is passed as the argument to the start |
| * routine instead of arg. |
| * FIXME - on Vista we could be using NtCreateThreadEx (like all the api routines do), |
| * but this still works fine and that would complicate argument handling. |
| */ |
| static HANDLE |
| nt_create_thread(HANDLE hProcess, PTHREAD_START_ROUTINE start_addr, |
| void *arg, const void *arg_buf, SIZE_T arg_buf_size, |
| SIZE_T stack_reserve, SIZE_T stack_commit, bool suspended, |
| ptr_uint_t *tid, bool target_api, bool target_64bit, |
| void **remote_stack /* OUT */) |
| { |
| HANDLE hThread = NULL; |
| USER_STACK stack = {0}; |
| OBJECT_ATTRIBUTES oa; |
| CLIENT_ID cid; |
| CONTEXT context = {0}; |
| SIZE_T num_commit_bytes, code; |
| unsigned long old_prot; |
| void *p; |
| SECURITY_DESCRIPTOR *sd = NULL; |
| DWORD platform = 0; |
| void *thread_arg = arg; |
| BOOL wow64 = is_wow64(hProcess); |
| |
| #ifndef X64 |
| DO_ASSERT(!target_64bit); /* Not supported. */ |
| #endif |
| |
| if (NtCreateThread == NULL) { |
| /* FIXME - on Vista we could instead use NtCreateThreadEx which is what all |
| * the api routines use and requires less setup. But it also uses structs that |
| * we haven't fully reversed or found good documentation for. */ |
| /* Don't need a lock, writing the pointer (4 [8 on 64-bit] byte write) is atomic. */ |
| NtCreateThread = (NtCreateThreadType) |
| GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtCreateThread"); |
| if (NtCreateThread == NULL) |
| goto error; |
| } |
| |
| /* both stack size and stack reserve must be multiples of PAGE_SIZE */ |
| DO_ASSERT((stack_reserve & (PAGE_SIZE - 1)) == 0); |
| DO_ASSERT((stack_commit & (PAGE_SIZE - 1)) == 0); |
| /* We stick a non-committed page on each end just to be safe and windows |
| * needs three pages at the end to properly handle end of expandable stack |
| * case (wants to pass exception back to the app on overflow, so needs some |
| * stack for that). Plus we need an extra page for wow64 (PR 252008). */ |
| DO_ASSERT(stack_reserve >= stack_commit + (5 + (wow64 ? 1 : 0) * PAGE_SIZE)); |
| |
| /* Use the security descriptor from the target process for creating the |
| * thread so that once created the thread will be able to open a full |
| * access handle to itself (xref case 2096). */ |
| /* NOTES - tried many ways to impersonate based on target process token |
| * so we could just use the default and was unable to get anywhere with |
| * that. Easiest thing to do here is just create a new security descriptor |
| * with a NULL (not empty) DACL [just InitializeSecurityDescriptor(); |
| * SetSecurityDescriptorDacl()], but that's a privilege escalation |
| * problem (allows anybody full access to the thread)]. If we instead get |
| * the full security descriptor from the target process and try to use that |
| * the kernel complains that its a bad choice of owner. What we do instead |
| * is get just the DACL and leave the rest empty (will be filled in with |
| * defaults during create thread). Thus the security descriptor for the |
| * thread will end up having the owner, group, and SACL from this |
| * process and the DACL from the target. Upshot is the the thread pseudo |
| * handle will have full permissions (from the DACL), but the owner will be |
| * us and, even though the handle we get back from CreateThread will be |
| * fully permissioned as we request, any subsequent attempts by us to |
| * OpenThread will fail since we aren't on the DACL. We could always add |
| * ourselves to the DACL later or we can use the SE_DEBUG_PRIVILEGE to |
| * allow us to open it anyways. Note if for some reason we want to view the |
| * SACL we need to enable the ACCESS_SYSTEM_SECURITY privilege when opening |
| * the handle. |
| * FIXME - we could instead build our own DACL combining the two, we could |
| * also try setting the owner/group after the thread is created if we |
| * really wanted to look like the target process thread, and could also |
| * start with a NULL sd and set the DACL later if want to match |
| * CreateThread as closely as possible. If we do anything post system |
| * call should be sure to always create the thread suspended. |
| */ |
| code = GetSecurityInfo(hProcess, SE_KERNEL_OBJECT, |
| DACL_SECURITY_INFORMATION, |
| NULL, NULL, NULL, NULL, &sd); |
| DO_ASSERT(code == ERROR_SUCCESS); |
| |
| InitializeObjectAttributes(&oa, NULL, OBJ_CASE_INSENSITIVE, NULL, sd); |
| |
| stack.ExpandableStackBottom = |
| VirtualAllocEx(hProcess, NULL, |
| /* we leave the top page MEM_FREE: we could reserve it instead |
| * and then adjust our get_stack_bounds() core assert */ |
| stack_reserve - PAGE_SIZE, |
| MEM_RESERVE, PAGE_READWRITE); |
| if (remote_stack != NULL) |
| *remote_stack = stack.ExpandableStackBottom; |
| if (stack.ExpandableStackBottom == NULL) |
| goto error; |
| |
| /* We provide non-committed boundary page on each side of the stack just to |
| * be safe (note we will get a stack overflow exception if stack grows to |
| * 3rd to last page of this region (xpsp2)). */ |
| stack.ExpandableStackBottom = |
| ((byte *)stack.ExpandableStackBottom) + PAGE_SIZE; |
| stack.ExpandableStackBase = |
| ((byte *)stack.ExpandableStackBottom) + stack_reserve - (2*PAGE_SIZE); |
| /* PR 252008: WOW64's initial APC uses the stack base, ignoring CONTEXT.Esp, |
| * so we put an extra page in place for the nudge arg. It should be |
| * freed w/ no problems since the Bottom's region is freed. |
| * An alternative is a separate allocation and setting NUDGE_FREE_ARG, |
| * but the caller is the one who knows the structure of the arg. |
| */ |
| if (wow64) |
| stack.ExpandableStackBase = ((byte*)stack.ExpandableStackBase) - PAGE_SIZE; |
| |
| stack.ExpandableStackLimit = |
| ((byte *)stack.ExpandableStackBase) - stack_commit; |
| num_commit_bytes = stack_commit + PAGE_SIZE; |
| p = ((byte *)stack.ExpandableStackBase) - num_commit_bytes; |
| if (wow64) |
| num_commit_bytes += PAGE_SIZE; |
| p = VirtualAllocEx(hProcess, p, num_commit_bytes, MEM_COMMIT, PAGE_READWRITE); |
| if (p == NULL) |
| goto error; |
| if (!VirtualProtectEx(hProcess, p, PAGE_SIZE, PAGE_READWRITE|PAGE_GUARD, &old_prot)) |
| goto error; |
| |
| get_platform(&platform); |
| DO_ASSERT(platform != 0); |
| |
| /* set the context: initialize with our own */ |
| context.ContextFlags = CONTEXT_FULL; |
| GetThreadContext(GetCurrentThread(), &context); |
| |
| /* set esp */ |
| context.CXT_XSP = (ptr_uint_t)stack.ExpandableStackBase; |
| /* On vista, the kernel sets esp a random number of bytes in from the base of |
| * the stack as part of the stack ASLR. RtlUserThreadStart assumes that |
| * esp will be set at least 8 bytes into the region by writing to esp+4 and |
| * esp+8. So we need to move up by at least 8 bytes. The smallest offset |
| * I've seen is 56 bytes so we'll go with that as there could be another |
| * place assuming there's room above esp. We do it irregardless of whether we |
| * target api or not in case it's relied on elsewhere. */ |
| #define VISTA_THREAD_STACK_PAD 56 |
| if (platform >= PLATFORM_VISTA) { |
| context.CXT_XSP -= VISTA_THREAD_STACK_PAD; |
| } |
| /* Anticipating x64 we align to 16 bytes everywhere */ |
| #define STACK_ALIGNMENT 16 |
| /* write the argument buffer to the stack */ |
| if (arg_buf != NULL) { |
| SIZE_T written; |
| if (wow64) { /* PR 252008: see above */ |
| thread_arg = (void *) |
| (((byte *)stack.ExpandableStackBase) + PAGE_SIZE - arg_buf_size); |
| } else { |
| context.CXT_XSP -= ALIGN_FORWARD(arg_buf_size, STACK_ALIGNMENT); |
| thread_arg = (void *)context.CXT_XSP; |
| } |
| if (!WriteProcessMemory(hProcess, thread_arg, arg_buf, arg_buf_size, &written) || |
| written != arg_buf_size) { |
| goto error; |
| } |
| if (platform >= PLATFORM_VISTA) { |
| /* pad after our argument so RtlUserThreadStart won't clobber it */ |
| context.CXT_XSP -= VISTA_THREAD_STACK_PAD; |
| } |
| } |
| |
| /* set eip and argument */ |
| if (target_api) { |
| if (platform >= PLATFORM_VISTA) { |
| context.CXT_XIP = (ptr_uint_t)GetProcAddress(GetModuleHandle(L"ntdll.dll"), |
| "RtlUserThreadStart"); |
| } else { |
| context.CXT_XIP = (ptr_uint_t)get_kernel_thread_start_thunk(); |
| } |
| /* For kernel32!BaseThreadStartThunk and ntdll!RltUserThreadStartThunk |
| * Eax contains the address of the thread routine and Ebx the arg */ |
| context.THREAD_START_ADDR = (ptr_uint_t)start_addr; |
| context.THREAD_START_ARG = (ptr_uint_t)thread_arg; |
| } else { |
| void *buf[2]; |
| BOOL res; |
| SIZE_T written; |
| /* directly targeting the start_address */ |
| context.CXT_XIP = (ptr_uint_t)start_addr; |
| context.CXT_XSP -= sizeof(buf); |
| /* set up arg on stack, give NULL return address */ |
| buf[1] = thread_arg; |
| buf[0] = NULL; |
| res = WriteProcessMemory(hProcess, (void *)context.CXT_XSP, &buf, |
| sizeof(buf), &written); |
| if (!res || written != sizeof(buf)) { |
| goto error; |
| } |
| } |
| if (context.CXT_XIP == 0) { |
| DO_ASSERT(false); |
| goto error; |
| } |
| |
| /* NOTE - CreateThread passes NULL for object attributes so despite Nebbet |
| * must be optional (checked NTsp6a, XPsp2). We don't pass NULL so we can |
| * specify the security descriptor. */ |
| if (!NT_SUCCESS(NtCreateThread(&hThread, THREAD_ALL_ACCESS, &oa, |
| hProcess, &cid, &context, &stack, |
| (byte)(suspended ? TRUE : FALSE)))) { |
| goto error; |
| } |
| |
| if (tid != NULL) |
| *tid = (ptr_uint_t)cid.UniqueThread; |
| |
| exit: |
| if (sd != NULL) { |
| /* Free the security descriptor. */ |
| LocalFree(sd); |
| } |
| |
| return hThread; |
| |
| error: |
| if (stack.ExpandableStackBottom != NULL) { |
| /* Free remote stack on error. */ |
| VirtualFreeEx(hProcess, stack.ExpandableStackBottom, 0, MEM_RELEASE); |
| if (remote_stack != NULL) |
| *remote_stack = NULL; |
| } |
| DO_ASSERT(hThread == NULL); |
| hThread = NULL; /* just to be safe */ |
| goto exit; |
| } |
| |
| /* flag defines for create_remote_thread */ |
| /* Use ntdll!NtCreateThread instead of kernel32!CreateThread to allow creating |
| * threads in a different session (case 872). */ |
| #define CREATE_REMOTE_THREAD_USE_NT 0x01 |
| |
| /* If ...USE_NT then target the natively created thread to the same entry point as |
| * the api routines would (kernel32!BaseThreadStartThunk for pre-Vista and |
| * ntdll!RtlUserThreadStart for Vista). This allows the target thread to exit by |
| * returning from its thread function. NOTE - be very careful about who frees the |
| * new thread's stack. On NT and 2k returning from the thread function will free the |
| * stack while on >= XP it won't. */ |
| #define CREATE_REMOTE_THREAD_TARGET_API 0x02 |
| |
| /* 64kb, same as allocation granularity so is as small as we can get */ |
| #define STACK_RESERVE 0x10000 |
| /* 12kb, matches current core stack size, note can expand to |
| * STACK_RESERVE - (5 * PAGE_SIZE), i.e. 44kb */ |
| #define STACK_COMMIT 0x3000 |
| |
| /* returns NULL on failure */ |
| static HANDLE |
| create_remote_thread(HANDLE hProc, PTHREAD_START_ROUTINE pfnThreadRtn, |
| void *arg, const void *arg_buf, SIZE_T arg_buf_size, |
| uint flags, void **remote_stack /* OUT */) |
| { |
| HANDLE hThread = NULL; |
| if (remote_stack != NULL) |
| *remote_stack = NULL; |
| if (TEST(CREATE_REMOTE_THREAD_USE_NT, flags)) { |
| /* FIXME - should we allow the caller to specify the stack sizes? |
| * we already just use defaults if we're using CreateRemoteThread */ |
| hThread = nt_create_thread(hProc, pfnThreadRtn, arg, arg_buf, arg_buf_size, |
| STACK_RESERVE, STACK_COMMIT, false, NULL, |
| TEST(CREATE_REMOTE_THREAD_TARGET_API, flags), |
| false /* !64-bit */, |
| remote_stack); |
| } else { |
| DO_ASSERT(false && "not tested (not currently used)"); |
| DO_ASSERT(false && "if used for nudge need update flags for stack/arg freeing"); |
| DO_ASSERT(arg_buf == NULL && "no support for buffer arguments to " |
| "create_remote_thread when not using NT"); |
| |
| hThread = CreateRemoteThread(hProc, NULL, 0, pfnThreadRtn, arg, 0, NULL); |
| } |
| return hThread; |
| } |
| |
| /* define this here so that clients don't need to know about dr_marker_t */ |
| DWORD |
| get_dr_marker(process_id_t ProcessID, dr_marker_t *marker, |
| hotp_policy_status_table_t **hotp_status, int *found); |
| |
| DWORD |
| nudge_dr(process_id_t pid, BOOL allow_upgraded_perms, DWORD timeout_ms, nudge_arg_t *nudge_arg) |
| { |
| DWORD res = ERROR_SUCCESS; |
| int found; |
| dr_marker_t marker; |
| PTHREAD_START_ROUTINE pfnThreadRtn; |
| /* FIXME: case 7038 */ |
| /* It is not safe to convert our routines to a PTHREAD_START_ROUTINE? |
| * That one is expected to be a |
| * DWORD (WINAPI *PTHREAD_START_ROUTINE)(LPVOID lpThreadParameter); |
| * and if we are using the wrong calling convention the application may fail |
| * MSDN notes that this is common but crashes only on 64-bit |
| */ |
| |
| HANDLE hThread = NULL, hProcess = NULL; |
| void *remote_stack = NULL; |
| |
| DWORD platform = 0; |
| get_platform(&platform); |
| DO_ASSERT(platform != 0); |
| |
| /* Open the process handle before getting the drmarker, is slight perf. |
| * hit if process turns out not to be running under DR, but we avoid the |
| * race of the process exiting and it's id getting recycled, the os won't |
| * recycle the id till we free our handle */ |
| hProcess = OpenProcess(PROCESS_QUERY_INFORMATION + |
| PROCESS_VM_WRITE + |
| PROCESS_VM_READ + |
| PROCESS_VM_OPERATION + |
| PROCESS_CREATE_THREAD + |
| READ_CONTROL + |
| SYNCHRONIZE, |
| FALSE, (DWORD)pid); |
| res = GetLastError(); |
| if (hProcess == NULL) { |
| if (allow_upgraded_perms) { |
| acquire_privileges(); |
| hProcess = OpenProcess(PROCESS_QUERY_INFORMATION + |
| PROCESS_VM_WRITE + |
| PROCESS_VM_READ + |
| PROCESS_VM_OPERATION + |
| PROCESS_CREATE_THREAD + |
| READ_CONTROL + |
| SYNCHRONIZE, |
| FALSE, (DWORD)pid); |
| res = GetLastError(); |
| release_privileges(); |
| if (hProcess == NULL) { |
| if (res != ERROR_SUCCESS) |
| res = ERROR_DETACH_ERROR; |
| goto exit; |
| } |
| } else { |
| if (res != ERROR_SUCCESS) |
| res = ERROR_DETACH_ERROR; |
| goto exit; |
| } |
| } |
| |
| res = get_dr_marker(pid, &marker, NULL, &found); |
| /* first make sure the PID is running under dr */ |
| if (res != ERROR_SUCCESS || found != DR_MARKER_FOUND) { |
| res = ERROR_MOD_NOT_FOUND; |
| goto exit; |
| } |
| |
| pfnThreadRtn = (PTHREAD_START_ROUTINE) marker.dr_generic_nudge_target; |
| |
| if (pfnThreadRtn != NULL) { /* Fix for case 5464. */ |
| /* We use the native api to create the thread (CREATE_REMOTE_THREAD_USE_NT) |
| * to avoid session id issues. FIXME - we don't really need to TARGET_API |
| * anymore since the nudge routine never returns now (which could simplify |
| * the core nudge thread detection a little). Note - if we stop using USE_NT |
| * we need to update the stack freeing code below and change the nudge flags. */ |
| hThread = create_remote_thread(hProcess, pfnThreadRtn, |
| NULL, (void *)nudge_arg, sizeof(nudge_arg_t), |
| CREATE_REMOTE_THREAD_USE_NT | |
| CREATE_REMOTE_THREAD_TARGET_API, |
| &remote_stack); |
| if (hThread == NULL) { |
| res = GetLastError(); |
| goto exit; |
| } |
| } else { |
| hThread = NULL; |
| res = ERROR_INVALID_FUNCTION; |
| goto exit; |
| } |
| |
| res = WaitForSingleObject(hThread, timeout_ms); |
| |
| if (res == WAIT_FAILED) { |
| res = GetLastError(); |
| } else if (res == WAIT_TIMEOUT) { |
| res = ERROR_TIMEOUT; |
| } else { |
| res = ERROR_SUCCESS; |
| /* We have a general problem on how to free the application stack for nudges. |
| * Currently the app/os will never free a nudge thread's app stack: |
| * On NT and 2k ExitThread would normally free the app stack, but we always |
| * terminate nudge threads instead of allowing them to return and exit normally. |
| * On XP and 2k3 none of our nudge creation routines inform csrss of the new |
| * thread(which is who typically frees the stacks) assuming we don't use switch |
| * to using CreateRemoteThread() here. |
| * On Vista we don't use NtCreateThreadEx to create the nudge threads so the |
| * kernel doesn't free the stack. |
| * As such we are left with two options: free the app stack here (nudgee free) or |
| * have the nudge thread creator free the app stack (nudger free). We use |
| * nudge flags to specify which we are using. */ |
| if (TEST(NUDGE_NUDGER_FREE_STACK, nudge_arg->flags) && remote_stack != NULL) { |
| VirtualFreeEx(hProcess, remote_stack, 0, MEM_RELEASE); |
| } |
| } |
| |
| exit: |
| if (hThread != NULL) |
| CloseHandle(hThread); |
| if (hProcess != NULL) |
| CloseHandle(hProcess); |
| return res; |
| } |
| |
| /* Detaches from the specified process by creating a thread in the target |
| * process that directly targets the detach routine. |
| */ |
| DWORD |
| detach(process_id_t pid, BOOL allow_upgraded_perms, DWORD timeout_ms) |
| { |
| return generic_nudge(pid, allow_upgraded_perms, NUDGE_GENERIC(detach), 0, |
| NULL, timeout_ms); |
| } |
| |
| /* generic nudge DR, action mask determines which actions will be executed */ |
| DWORD |
| generic_nudge(process_id_t pid, BOOL allow_upgraded_perms, DWORD action_mask, |
| client_id_t client_id /* optional */, uint64 client_arg /* optional */, |
| DWORD timeout_ms) |
| { |
| nudge_arg_t arg = {0}; |
| arg.version = NUDGE_ARG_CURRENT_VERSION; |
| arg.nudge_action_mask = action_mask; |
| /* default flags */ |
| arg.client_id = client_id; |
| arg.client_arg = client_arg; |
| return nudge_dr(pid, allow_upgraded_perms, timeout_ms, &arg); |
| } |
| |
| /* Loads the specified dll in process pid and waits on the loading thread, does |
| * not free the dll once the loading thread exits. Usual usage is for the |
| * loaded dll to do something in its DllMain. If you do not want the dll to |
| * stay loaded its DllMain should return false. To unload a dll from a process |
| * later, inject another dll whose dll main unloads that dll and then returns |
| * false. If loading_thread != NULL returns a handle to the loading thread (dll |
| * could call FreeLibraryAndExitThread on itself in its dll main to return a |
| * value out via the exit code). inject_dll provides no way to pass arguments |
| * in to the dll. */ |
| DWORD |
| inject_dll(process_id_t pid, const WCHAR *dll_name, BOOL allow_upgraded_perms, |
| DWORD timeout_ms, PHANDLE loading_thread) |
| { |
| DWORD res; |
| PTHREAD_START_ROUTINE pfnThreadRtn= NULL; |
| HANDLE hThread = NULL, hProcess = NULL, file_tmp = NULL; |
| void *remote_stack = NULL; |
| DWORD platform = 0; |
| get_platform(&platform); |
| DO_ASSERT(platform != 0); |
| |
| if (loading_thread != NULL) |
| *loading_thread = NULL; |
| |
| file_tmp = CreateFile(dll_name, 0, 0, NULL, OPEN_EXISTING, |
| FILE_ATTRIBUTE_NORMAL, NULL); |
| if (file_tmp == INVALID_HANDLE_VALUE) |
| return ERROR_FILE_NOT_FOUND; |
| CloseHandle(file_tmp); |
| |
| hProcess = OpenProcess(PROCESS_QUERY_INFORMATION + |
| PROCESS_VM_WRITE + |
| PROCESS_VM_READ + |
| PROCESS_VM_OPERATION + |
| PROCESS_CREATE_THREAD + |
| READ_CONTROL + |
| SYNCHRONIZE, |
| FALSE, (DWORD)pid); |
| if (hProcess == NULL) { |
| if (allow_upgraded_perms) { |
| acquire_privileges(); |
| hProcess = OpenProcess(PROCESS_QUERY_INFORMATION + |
| PROCESS_VM_WRITE + |
| PROCESS_VM_READ + |
| PROCESS_VM_OPERATION + |
| PROCESS_CREATE_THREAD + |
| READ_CONTROL + |
| SYNCHRONIZE, |
| FALSE, (DWORD)pid); |
| release_privileges(); |
| if (hProcess == NULL) { |
| return GetLastError(); |
| } |
| } else { |
| return GetLastError(); |
| } |
| } |
| |
| pfnThreadRtn = (PTHREAD_START_ROUTINE) GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW"); |
| if (pfnThreadRtn == NULL) { |
| res = GetLastError(); |
| goto exit; |
| } |
| /* USE_NT to avoid session id issues, TARGET_API so LoadLibrary can return, if |
| * stop using NT need to update stack freeing case below and allocate space to |
| * hold the remote library name ourselves like we used to. */ |
| hThread = create_remote_thread(hProcess, pfnThreadRtn, |
| NULL, dll_name, sizeof(WCHAR)*(1 + wcslen(dll_name)), |
| CREATE_REMOTE_THREAD_USE_NT | |
| CREATE_REMOTE_THREAD_TARGET_API , &remote_stack); |
| if (hThread == NULL) { |
| res = GetLastError(); |
| goto exit; |
| } |
| |
| res = WaitForSingleObject(hThread, timeout_ms); |
| if (res == WAIT_FAILED) { |
| res = GetLastError(); |
| goto exit; |
| } else if (res == WAIT_TIMEOUT) { |
| res = ERROR_TIMEOUT; |
| goto exit; |
| } |
| |
| /* thread has finished */ |
| /* On NT and 2k the remote stack will be freed by the remote thread exiting |
| * (returning from LoadLibrary or calling ExitThread), though not if it terminates |
| * itself (which it shouldn't do, lock issues etc.). On XP and higher the stack |
| * won't be freed since we didn't inform csrss of this thread so we free here. */ |
| if (remote_stack != NULL && |
| platform != PLATFORM_WIN_NT_4 && platform != PLATFORM_WIN_2000) { |
| VirtualFreeEx(hProcess, remote_stack, 0, MEM_RELEASE); |
| } |
| res = ERROR_SUCCESS; |
| |
| exit: |
| if (hThread != NULL) { |
| if (loading_thread != NULL) { |
| *loading_thread = hThread; |
| } else { |
| CloseHandle(hThread); |
| } |
| } |
| if (hProcess != NULL) |
| CloseHandle(hProcess); |
| |
| return res; |
| } |