From 48f4449d87a20af2c6e0f9830b5b83550d56ea2a Mon Sep 17 00:00:00 2001 From: parikhakshat <68412398+parikhakshat@users.noreply.github.com> Date: Fri, 19 Aug 2022 13:16:21 -0700 Subject: [PATCH 1/8] Add files via upload --- common.cpp | 25 +++++++++++++++++++++++++ common.h | 1 + 2 files changed, 26 insertions(+) diff --git a/common.cpp b/common.cpp index 02a3131..6ce23b9 100755 --- a/common.cpp +++ b/common.cpp @@ -20,6 +20,12 @@ limitations under the License. #include #include "common.h" +#include +#include +#include +#include +#include +#include uint64_t GetCurTime(void) { auto duration = std::chrono::system_clock::now().time_since_epoch(); @@ -96,6 +102,25 @@ int GetIntOption(const char *name, int argc, char** argv, int default_value) { return (int)strtol(option, NULL, 0); } +DWORD FindProcessId(char * process_name) +{ + PROCESSENTRY32 entry; + entry.dwSize = sizeof(PROCESSENTRY32); + + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); + + if (Process32First(snapshot, &entry) == TRUE) + { + while (Process32Next(snapshot, &entry) == TRUE) + { + if (stricmp(entry.szExeFile, process_name) == 0) + { + CloseHandle(snapshot); + return entry.th32ProcessID; + } + } + } +} //quoting on Windows is weird size_t ArgvEscapeWindows(char *in, char *out) { diff --git a/common.h b/common.h index 03ef16e..5720e6d 100755 --- a/common.h +++ b/common.h @@ -75,6 +75,7 @@ uint64_t GetCurTime(void); char *GetOption(const char *name, int argc, char** argv); void GetOptionAll(const char *name, int argc, char** argv, std::list *results); bool GetBinaryOption(const char *name, int argc, char** argv, bool default_value); +DWORD FindProcessId(char * process_name); int GetIntOption(const char *name, int argc, char** argv, int default_value); char *ArgvToCmd(int argc, char** argv); From 8417ab35b99512bb19973384f13a56c0a7939054 Mon Sep 17 00:00:00 2001 From: parikhakshat <68412398+parikhakshat@users.noreply.github.com> Date: Fri, 19 Aug 2022 13:17:52 -0700 Subject: [PATCH 2/8] Add files via upload --- Windows/debugger.cpp | 31 +++++++++++++++++++++---------- Windows/debugger.h | 4 ++++ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/Windows/debugger.cpp b/Windows/debugger.cpp index 1c72773..ca6b23b 100644 --- a/Windows/debugger.cpp +++ b/Windows/debugger.cpp @@ -1471,7 +1471,7 @@ DebuggerStatus Debugger::DebugLoop(uint32_t timeout, bool killing) uint64_t time_elapsed = end_time - begin_time; timeout = ((uint64_t)timeout >= time_elapsed) ? timeout - (uint32_t)time_elapsed : 0; - // printf("timeout: %u\n", timeout); + //printf("timeout: %u\n", timeout); // printf("time: %lld\n", get_cur_time_us()); if (wait_ret) { @@ -1480,7 +1480,13 @@ DebuggerStatus Debugger::DebugLoop(uint32_t timeout, bool killing) dbg_continue_needed = false; } - if (timeout == 0) return DEBUGGER_HANGED; + if (timeout == 0) { + if(attach_mode) { + return DEBUGGER_PROCESS_EXIT; + } else { + return DEBUGGER_HANGED; + } + } if (!wait_ret) { //printf("WaitForDebugEvent returned 0\n"); @@ -1491,7 +1497,7 @@ DebuggerStatus Debugger::DebugLoop(uint32_t timeout, bool killing) thread_id = DebugEv->dwThreadId; - // printf("eventCode: %x\n", DebugEv->dwDebugEventCode); + //printf("eventCode: %x\n", DebugEv->dwDebugEventCode); switch (DebugEv->dwDebugEventCode) { @@ -1739,11 +1745,10 @@ DebuggerStatus Debugger::Kill() { // attaches to an active process DebuggerStatus Debugger::Attach(unsigned int pid, uint32_t timeout) { attach_mode = true; + processID = pid; if (!DebugActiveProcess(pid)) { DWORD error_code = GetLastError(); - - if(error_code == 5) { HANDLE hToken = NULL; LUID luid; @@ -1810,19 +1815,25 @@ DebuggerStatus Debugger::Continue(uint32_t timeout) { dbg_last_status = DEBUGGER_TARGET_START; return dbg_last_status; } + if (script != NULL) { + HANDLE thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)system, script, 0, NULL); + CloseHandle(thread_handle); + } dbg_last_status = DebugLoop(timeout); if (dbg_last_status == DEBUGGER_PROCESS_EXIT) { - CloseHandle(child_handle); - CloseHandle(child_thread_handle); - child_handle = NULL; - child_thread_handle = NULL; + if (!attach_mode) { + CloseHandle(child_handle); + CloseHandle(child_thread_handle); + child_handle = NULL; + child_thread_handle = NULL; + } } return dbg_last_status; } - + // initializes options from command line void Debugger::Init(int argc, char **argv) { have_thread_context = false; diff --git a/Windows/debugger.h b/Windows/debugger.h index c0cc4c2..bc57cbd 100644 --- a/Windows/debugger.h +++ b/Windows/debugger.h @@ -77,8 +77,12 @@ class Debugger { return last_exception; } + char * script; + protected: + DWORD processID; + enum MemoryProtection { READONLY, READWRITE, From 1a406fe5fdcc1d1e8c787b1cb858b6cc0d1d079b Mon Sep 17 00:00:00 2001 From: Akshat Parikh <68412398+parikhakshat@users.noreply.github.com> Date: Mon, 29 Aug 2022 21:26:12 -0400 Subject: [PATCH 3/8] Remove unused variable and implement windows specific checks --- common.cpp | 40 +- common.h | 4 +- debugger.cpp | 1917 ++++++++++++++++++++++++++++++++++++++++++++++++++ debugger.h | 237 +++++++ 4 files changed, 2178 insertions(+), 20 deletions(-) create mode 100644 debugger.cpp create mode 100644 debugger.h diff --git a/common.cpp b/common.cpp index 6ce23b9..db1d7bd 100755 --- a/common.cpp +++ b/common.cpp @@ -102,25 +102,27 @@ int GetIntOption(const char *name, int argc, char** argv, int default_value) { return (int)strtol(option, NULL, 0); } -DWORD FindProcessId(char * process_name) -{ - PROCESSENTRY32 entry; - entry.dwSize = sizeof(PROCESSENTRY32); - - HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); - - if (Process32First(snapshot, &entry) == TRUE) - { - while (Process32Next(snapshot, &entry) == TRUE) - { - if (stricmp(entry.szExeFile, process_name) == 0) - { - CloseHandle(snapshot); - return entry.th32ProcessID; - } - } - } -} +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) + DWORD FindProcessId(char * process_name) + { + PROCESSENTRY32 entry; + entry.dwSize = sizeof(PROCESSENTRY32); + + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); + + if (Process32First(snapshot, &entry) == TRUE) + { + while (Process32Next(snapshot, &entry) == TRUE) + { + if (stricmp(entry.szExeFile, process_name) == 0) + { + CloseHandle(snapshot); + return entry.th32ProcessID; + } + } + } + } +#endif //quoting on Windows is weird size_t ArgvEscapeWindows(char *in, char *out) { diff --git a/common.h b/common.h index 5720e6d..847e866 100755 --- a/common.h +++ b/common.h @@ -75,7 +75,9 @@ uint64_t GetCurTime(void); char *GetOption(const char *name, int argc, char** argv); void GetOptionAll(const char *name, int argc, char** argv, std::list *results); bool GetBinaryOption(const char *name, int argc, char** argv, bool default_value); -DWORD FindProcessId(char * process_name); +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) + DWORD FindProcessId(char * process_name); +#endif int GetIntOption(const char *name, int argc, char** argv, int default_value); char *ArgvToCmd(int argc, char** argv); diff --git a/debugger.cpp b/debugger.cpp new file mode 100644 index 0000000..04610f2 --- /dev/null +++ b/debugger.cpp @@ -0,0 +1,1917 @@ +/* +Copyright 2020 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +https ://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include + +#include "windows.h" +#include "psapi.h" +#include "dbghelp.h" + +#include "../common.h" +#include "debugger.h" + + +#define CALLCONV_MICROSOFT_X64 0 +#define CALLCONV_THISCALL 1 +#define CALLCONV_FASTCALL 2 +#define CALLCONV_CDECL 3 +#define CALLCONV_DEFAULT 4 + +#define BREAKPOINT_UNKNOWN 0 +#define BREAKPOINT_ENTRYPOINT 1 +#define BREAKPOINT_TARGET 2 + +#define PERSIST_END_EXCEPTION 0x0F22 + +// cleans up all breakpoint structures +// does not actually remove breakpoints in target process +void Debugger::DeleteBreakpoints() { + for (auto iter = breakpoints.begin(); iter != breakpoints.end(); iter++) { + delete *iter; + } + breakpoints.clear(); +} + +void Debugger::CreateException(EXCEPTION_RECORD *win_exception_record, + Exception *exception) +{ + switch (win_exception_record->ExceptionCode) { + case EXCEPTION_BREAKPOINT: + case 0x4000001f: + exception->type = BREAKPOINT; + break; + case EXCEPTION_ACCESS_VIOLATION: + exception->type = ACCESS_VIOLATION; + break; + case EXCEPTION_ILLEGAL_INSTRUCTION: + exception->type = ILLEGAL_INSTRUCTION; + break; + case EXCEPTION_STACK_OVERFLOW: + exception->type = STACK_OVERFLOW; + break; + default: + exception->type = OTHER; + break; + } + + exception->ip = win_exception_record->ExceptionAddress; + + exception->maybe_execute_violation = false; + exception->maybe_write_violation = false; + exception->access_address = 0; + if (win_exception_record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { + if (win_exception_record->ExceptionInformation[0] == 8) { + exception->maybe_execute_violation = true; + } + if (win_exception_record->ExceptionInformation[0] == 1) { + exception->maybe_write_violation = true; + } + + exception->access_address = (void *)(win_exception_record->ExceptionInformation[1]); + } +} + +void Debugger::RetrieveThreadContext() { + if (have_thread_context) return; // already done + lcContext.ContextFlags = CONTEXT_ALL; + HANDLE thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, thread_id); + GetThreadContext(thread_handle, &lcContext); + CloseHandle(thread_handle); + have_thread_context = true; +} + +void Debugger::SaveRegisters(SavedRegisters* registers) { + RetrieveThreadContext(); + memcpy(®isters->saved_context, &lcContext, sizeof(registers->saved_context)); +} + +void Debugger::RestoreRegisters(SavedRegisters* registers) { + have_thread_context = false; + HANDLE thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, thread_id); + if (!SetThreadContext(thread_handle, &lcContext)) { + FATAL("Error restoring registers"); + } + CloseHandle(thread_handle); +} + +size_t Debugger::GetRegister(Register r) { + RetrieveThreadContext(); + +#ifdef _WIN64 + + switch (r) { + case RAX: + return lcContext.Rax; + case RCX: + return lcContext.Rcx; + case RDX: + return lcContext.Rdx; + case RBX: + return lcContext.Rbx; + case RSP: + return lcContext.Rsp; + case RBP: + return lcContext.Rbp; + case RSI: + return lcContext.Rsi; + case RDI: + return lcContext.Rdi; + case R8: + return lcContext.R8; + case R9: + return lcContext.R9; + case R10: + return lcContext.R10; + case R11: + return lcContext.R11; + case R12: + return lcContext.R12; + case R13: + return lcContext.R13; + case R14: + return lcContext.R14; + case R15: + return lcContext.R15; + case RIP: + return lcContext.Rip; + default: + FATAL("Unimplemented"); + } + +#else + + switch (r) { + case RAX: + return lcContext.Eax; + case RCX: + return lcContext.Ecx; + case RDX: + return lcContext.Edx; + case RBX: + return lcContext.Ebx; + case RSP: + return lcContext.Esp; + case RBP: + return lcContext.Ebp; + case RSI: + return lcContext.Esi; + case RDI: + return lcContext.Edi; + case RIP: + return lcContext.Eip; + default: + FATAL("Unimplemented"); +} + +#endif + +} + +void Debugger::SetRegister(Register r, size_t value) { + RetrieveThreadContext(); + +#ifdef _WIN64 + + switch (r) { + case RAX: + lcContext.Rax = value; + break; + case RCX: + lcContext.Rcx = value; + break; + case RDX: + lcContext.Rdx = value; + break; + case RBX: + lcContext.Rbx = value; + break; + case RSP: + lcContext.Rsp = value; + break; + case RBP: + lcContext.Rbp = value; + break; + case RSI: + lcContext.Rsi = value; + break; + case RDI: + lcContext.Rdi = value; + break; + case R8: + lcContext.R8 = value; + break; + case R9: + lcContext.R9 = value; + break; + case R10: + lcContext.R10 = value; + break; + case R11: + lcContext.R11 = value; + break; + case R12: + lcContext.R12 = value; + break; + case R13: + lcContext.R13 = value; + break; + case R14: + lcContext.R14 = value; + break; + case R15: + lcContext.R15 = value; + break; + case RIP: + lcContext.Rip = value; + break; + default: + FATAL("Unimplemented"); + } + +#else + + switch (r) { + case RAX: + lcContext.Eax = value; + break; + case RCX: + lcContext.Ecx = value; + break; + case RDX: + lcContext.Edx = value; + break; + case RBX: + lcContext.Ebx = value; + break; + case RSP: + lcContext.Esp = value; + break; + case RBP: + lcContext.Ebp = value; + break; + case RSI: + lcContext.Esi = value; + break; + case RDI: + lcContext.Edi = value; + break; + case RIP: + lcContext.Eip = value; + break; + default: + FATAL("Unimplemented"); + } + +#endif + + HANDLE thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, thread_id); + SetThreadContext(thread_handle, &lcContext); + CloseHandle(thread_handle); +} + + +// converts between MemoryProtection and Windows protection flags +DWORD Debugger::WindowsProtectionFlags(MemoryProtection protection) { + switch (protection) { + case READONLY: + return PAGE_READONLY; + case READWRITE: + return PAGE_READWRITE; + case READEXECUTE: + return PAGE_EXECUTE_READ; + case READWRITEEXECUTE: + return PAGE_EXECUTE_READWRITE; + default: + FATAL("Unumplemented memory protection"); + } +} + +// allocates memory within 2GB of memory region +// between region_min and region_max +void *Debugger::RemoteAllocateNear(uint64_t region_min, + uint64_t region_max, + size_t size, + MemoryProtection protection, + bool use_shared_memory) +{ + void *ret = NULL; + + // try before first + uint64_t min_address = region_max; + if (min_address < 0x80000000) min_address = 0; + else min_address -= 0x80000000; + uint64_t max_address = region_min; + if (max_address < size) max_address = 0; + else max_address -= size; + + ret = RemoteAllocateBefore(min_address, + max_address, + size, + protection); + + if (ret) return ret; + + min_address = region_max; + uint64_t address_range_max = 0xFFFFFFFFFFFFFFFFULL; + if (child_ptr_size == 4) { + address_range_max = 0xFFFFFFFFULL; + } + if ((address_range_max - 0x80000000) < region_min) { + max_address = address_range_max - size; + } else { + max_address = region_min + 0x80000000 - size; + } + + ret = RemoteAllocateAfter(min_address, + max_address, + size, + protection); + + return ret; +} + + +// allocates memory in target process as close as possible +// to max_address, but at address larger than min_address +void *Debugger::RemoteAllocateBefore(uint64_t min_address, + uint64_t max_address, + size_t size, + MemoryProtection protection) +{ + DWORD protection_flags = WindowsProtectionFlags(protection); + + MEMORY_BASIC_INFORMATION meminfobuf; + void *ret_address = NULL; + + uint64_t cur_code = max_address; + while (cur_code > min_address) { + // Don't attempt allocating on the null page + if (cur_code < 0x1000) break; + + size_t step = size; + + size_t query_ret = VirtualQueryEx(child_handle, + (LPCVOID)cur_code, + &meminfobuf, + sizeof(MEMORY_BASIC_INFORMATION)); + if (!query_ret) break; + + if (meminfobuf.State == MEM_FREE) { + if (meminfobuf.RegionSize >= size) { + size_t address = (size_t)meminfobuf.BaseAddress + + (meminfobuf.RegionSize - size); + ret_address = VirtualAllocEx(child_handle, + (LPVOID)address, + size, + MEM_COMMIT | MEM_RESERVE, + protection_flags); + if (ret_address) { + if (((size_t)ret_address >= min_address) && + ((size_t)ret_address <= max_address)) { + return ret_address; + } else { + return NULL; + } + } + } else { + step = size - meminfobuf.RegionSize; + } + } + + cur_code = (size_t)meminfobuf.BaseAddress; + if (cur_code < step) break; + else cur_code -= step; + } + + return ret_address; +} + +// allocates memory in target process as close as possible +// to min_address, but not higher than max_address +void *Debugger::RemoteAllocateAfter(uint64_t min_address, + uint64_t max_address, + size_t size, + MemoryProtection protection) +{ + DWORD protection_flags = WindowsProtectionFlags(protection); + + MEMORY_BASIC_INFORMATION meminfobuf; + void *ret_address = NULL; + + uint64_t cur_code = min_address; + while (cur_code < max_address) { + size_t query_ret = VirtualQueryEx(child_handle, + (LPCVOID)cur_code, + &meminfobuf, + sizeof(MEMORY_BASIC_INFORMATION)); + if (!query_ret) break; + + if (meminfobuf.State == MEM_FREE) { + size_t region_address = (size_t)meminfobuf.BaseAddress; + size_t region_size = meminfobuf.RegionSize; + // make sure we are allocating on an address that + // is aligned according to allocation_granularity + size_t alignment = region_address & (allocation_granularity - 1); + if (alignment) { + size_t offset = (allocation_granularity - alignment); + region_address += offset; + if (region_size > offset) { + region_size -= offset; + } else { + region_size = 0; + } + } + if (region_size >= size) { + ret_address = VirtualAllocEx(child_handle, + (LPVOID)region_address, + size, + MEM_COMMIT | MEM_RESERVE, + protection_flags); + if (ret_address) { + if (((size_t)ret_address >= min_address) && + ((size_t)ret_address <= max_address)) { + return ret_address; + } else { + return NULL; + } + } + } + } + + cur_code = (size_t)meminfobuf.BaseAddress + meminfobuf.RegionSize; + } + + return ret_address; +} + +void Debugger::RemoteFree(void *address, size_t size) { + if (!child_handle) return; + VirtualFreeEx(child_handle, address, 0, MEM_RELEASE); +} + +void Debugger::RemoteWrite(void *address, void *buffer, size_t size) { + SIZE_T size_written; + if (WriteProcessMemory( + child_handle, + address, + buffer, + size, + &size_written)) + { + return; + } + + // we need to (a) read page permissions + // (b) make it writable, and (c) restore permissions + DWORD oldProtect; + if (!VirtualProtectEx(child_handle, + address, + size, + PAGE_READWRITE, + &oldProtect)) + { + FATAL("Error in VirtualProtectEx"); + } + + if (!WriteProcessMemory( + child_handle, + address, + buffer, + size, + &size_written)) + { + FATAL("Error writing target memory\n"); + } + + DWORD ignore; + if (!VirtualProtectEx(child_handle, + address, + size, + oldProtect, + &ignore)) + { + FATAL("Error in VirtualProtectEx"); + } +} + +void Debugger::RemoteRead(void *address, void *buffer, size_t size) { + SIZE_T size_read; + if (!ReadProcessMemory( + child_handle, + address, + buffer, + size, + &size_read)) + { + FATAL("Error reading target memory\n"); + } +} + +void Debugger::RemoteProtect(void *address, size_t size, MemoryProtection protect) { + DWORD protection_flags = WindowsProtectionFlags(protect); + DWORD old_protect; + + if (!VirtualProtectEx(child_handle, + address, + size, + protection_flags, + &old_protect)) + { + FATAL("Could not apply memory protection"); + } +} + + +// detects executable memory regions in the module +// makes them non-executable +// and copies code out into this process +void Debugger::ExtractCodeRanges(void *module_base, + size_t min_address, + size_t max_address, + std::list *executable_ranges, + size_t *code_size) +{ + LPCVOID end_address = (char *)max_address; + LPCVOID cur_address = module_base; + MEMORY_BASIC_INFORMATION meminfobuf; + + AddressRange newRange; + + for (auto iter = executable_ranges->begin(); + iter != executable_ranges->end(); iter++) + { + free(iter->data); + } + executable_ranges->clear(); + *code_size = 0; + + while (cur_address < end_address) { + size_t ret = VirtualQueryEx(child_handle, + cur_address, + &meminfobuf, + sizeof(MEMORY_BASIC_INFORMATION)); + if (!ret) break; + + if (meminfobuf.Protect & 0xF0) { + // printf("%p, %llx, %lx\n", meminfobuf.BaseAddress, meminfobuf.RegionSize, meminfobuf.Protect); + + SIZE_T size_read; + newRange.data = (char *)malloc(meminfobuf.RegionSize); + if (!ReadProcessMemory(child_handle, + meminfobuf.BaseAddress, + newRange.data, + meminfobuf.RegionSize, + &size_read)) + { + FATAL("Error in ReadProcessMemory"); + } + if (size_read != meminfobuf.RegionSize) { + FATAL("Error in ReadProcessMemory"); + } + + uint8_t low = meminfobuf.Protect & 0xFF; + low = low >> 4; + DWORD newProtect = (meminfobuf.Protect & 0xFFFFFF00) + low; + DWORD oldProtect; + if (!VirtualProtectEx(child_handle, + meminfobuf.BaseAddress, + meminfobuf.RegionSize, + newProtect, + &oldProtect)) + { + FATAL("Error in VirtualProtectEx"); + } + + newRange.from = (size_t)meminfobuf.BaseAddress; + newRange.to = (size_t)meminfobuf.BaseAddress + meminfobuf.RegionSize; + executable_ranges->push_back(newRange); + + *code_size += newRange.to - newRange.from; + } + + cur_address = (char *)meminfobuf.BaseAddress + meminfobuf.RegionSize; + } +} + +// sets all pages containing (previously detected) +// code to non-executable +void Debugger::ProtectCodeRanges(std::list *executable_ranges) { + MEMORY_BASIC_INFORMATION meminfobuf; + + for (auto iter = executable_ranges->begin(); + iter != executable_ranges->end(); iter++) + { + size_t ret = VirtualQueryEx(child_handle, + (void *)iter->from, + &meminfobuf, + sizeof(MEMORY_BASIC_INFORMATION)); + + // if the module was already instrumented, everything must be the same as before + if (!ret) { + FATAL("Error in ProtectCodeRanges." + "Target incompatible with persist_instrumentation_data"); + } + if (iter->from != (size_t)meminfobuf.BaseAddress) { + FATAL("Error in ProtectCodeRanges." + "Target incompatible with persist_instrumentation_data"); + } + if (iter->to != (size_t)meminfobuf.BaseAddress + meminfobuf.RegionSize) { + FATAL("Error in ProtectCodeRanges." + "Target incompatible with persist_instrumentation_data"); + } + if (!(meminfobuf.Protect & 0xF0)) { + FATAL("Error in ProtectCodeRanges." + "Target incompatible with persist_instrumentation_data"); + } + + uint8_t low = meminfobuf.Protect & 0xFF; + low = low >> 4; + DWORD newProtect = (meminfobuf.Protect & 0xFFFFFF00) + low; + DWORD oldProtect; + if (!VirtualProtectEx(child_handle, + meminfobuf.BaseAddress, + meminfobuf.RegionSize, + newProtect, + &oldProtect)) + { + FATAL("Error in VirtualProtectEx"); + } + } +} + +void Debugger::PatchPointersRemote(size_t min_address, size_t max_address, std::unordered_map& search_replace) { + if (child_ptr_size == 4) { + PatchPointersRemoteT(min_address, max_address, search_replace); + } else { + PatchPointersRemoteT(min_address, max_address, search_replace); + } +} + +template +void Debugger::PatchPointersRemoteT(size_t min_address, size_t max_address, std::unordered_map& search_replace) { + size_t module_size = max_address - min_address; + char* buf = (char *)malloc(module_size); + RemoteRead((void *)min_address, buf, module_size); + + size_t remote_address = min_address; + for (size_t i = 0; i < (module_size - child_ptr_size + 1); i++) { + T ptr = *(T *)(buf + i); + auto iter = search_replace.find(ptr); + if (iter != search_replace.end()) { + // printf("patching entry %zx at address %zx\n", (size_t)ptr, remote_address); + T fixed_ptr = (T)iter->second; + RemoteWrite((void *)remote_address, &fixed_ptr, child_ptr_size); + } + remote_address += 1; + } + + free(buf); +} + +// returns an array of handles for all modules loaded in the target process +DWORD Debugger::GetLoadedModules(HMODULE **modules) { + DWORD module_handle_storage_size = 1024 * sizeof(HMODULE); + HMODULE *module_handles = (HMODULE *)malloc(module_handle_storage_size); + DWORD hmodules_size; + while (true) { + if (!EnumProcessModulesEx(child_handle, + module_handles, + module_handle_storage_size, + &hmodules_size, + LIST_MODULES_ALL)) + { + FATAL("EnumProcessModules failed, %x\n", GetLastError()); + } + if (hmodules_size <= module_handle_storage_size) break; + module_handle_storage_size *= 2; + module_handles = (HMODULE *)realloc(module_handles, module_handle_storage_size); + } + *modules = module_handles; + return hmodules_size / sizeof(HMODULE); +} + +// parses PE headers and gets the module entypoint +void *Debugger::GetModuleEntrypoint(void *base_address) { + unsigned char headers[4096]; + SIZE_T num_read = 0; + if (!ReadProcessMemory(child_handle, base_address, headers, 4096, &num_read) || + (num_read != 4096)) + { + FATAL("Error reading target memory\n"); + } + DWORD pe_offset; + pe_offset = *((DWORD *)(headers + 0x3C)); + unsigned char *pe = headers + pe_offset; + DWORD signature = *((DWORD *)pe); + if (signature != 0x00004550) { + FATAL("PE signature error\n"); + } + pe = pe + 0x18; + WORD magic = *((WORD *)pe); + if ((magic != 0x10b) && (magic != 0x20b)) { + FATAL("Unknown PE magic value\n"); + } + DWORD entrypoint_offset = *((DWORD *)(pe + 16)); + if (entrypoint_offset == 0) return NULL; + return (char *)base_address + entrypoint_offset; +} + +// parses PE headers and gets the image size +DWORD Debugger::GetImageSize(void *base_address) { + unsigned char headers[4096]; + SIZE_T num_read = 0; + if (!ReadProcessMemory(child_handle, base_address, headers, 4096, &num_read) || + (num_read != 4096)) + { + FATAL("Error reading target memory\n"); + } + DWORD pe_offset; + pe_offset = *((DWORD *)(headers + 0x3C)); + unsigned char *pe = headers + pe_offset; + DWORD signature = *((DWORD *)pe); + if (signature != 0x00004550) { + FATAL("PE signature error\n"); + } + pe = pe + 0x18; + WORD magic = *((WORD *)pe); + if ((magic != 0x10b) && (magic != 0x20b)) { + FATAL("Unknown PE magic value\n"); + } + DWORD SizeOfImage = *((DWORD *)(pe + 56)); + return SizeOfImage; +} + + +// parses PE headers and gets the image size +void Debugger::GetImageSize(void *base_address, size_t *min_address, size_t *max_address) { + *min_address = (size_t)base_address; + DWORD SizeOfImage = GetImageSize(base_address); + *max_address = *min_address + SizeOfImage; +} + +// adds a one-time breakpoint at a specified address +// type, is an arbitrary int +// that can be accessed later when the breakpoint gets hit +void Debugger::AddBreakpoint(void *address, int type) { + Breakpoint *new_breakpoint = new Breakpoint; + SIZE_T rwsize = 0; + if (!ReadProcessMemory(child_handle, address, &(new_breakpoint->original_opcode), 1, &rwsize) || + (rwsize != 1)) { + FATAL("Error reading target memory\n"); + } + rwsize = 0; + unsigned char cc = 0xCC; + if (!WriteProcessMemory(child_handle, address, &cc, 1, &rwsize) || (rwsize != 1)) { + FATAL("Error writing target memory\n"); + } + FlushInstructionCache(child_handle, address, 1); + new_breakpoint->address = address; + new_breakpoint->type = type; + breakpoints.push_back(new_breakpoint); +} + +// damn it Windows, why don't you have a GetProcAddress +// that works on another process +DWORD Debugger::GetProcOffset(HMODULE module, const char *name) { + char* base_of_dll = (char*)module; + DWORD size_of_image = GetImageSize(base_of_dll); + + // try the exported symbols next + char* modulebuf = (char*)malloc(size_of_image); + SIZE_T num_read; + if (!ReadProcessMemory(child_handle, base_of_dll, modulebuf, size_of_image, &num_read) || + (num_read != size_of_image)) + { + FATAL("Error reading target memory\n"); + } + + DWORD pe_offset; + pe_offset = *((DWORD *)(modulebuf + 0x3C)); + char *pe = modulebuf + pe_offset; + DWORD signature = *((DWORD *)pe); + if (signature != 0x00004550) { + free(modulebuf); + return 0; + } + pe = pe + 0x18; + WORD magic = *((WORD *)pe); + DWORD exporttableoffset; + if (magic == 0x10b) { + exporttableoffset = *(DWORD *)(pe + 96); + } else if (magic == 0x20b) { + exporttableoffset = *(DWORD *)(pe + 112); + } else { + free(modulebuf); + return 0; + } + + if (!exporttableoffset) { + free(modulebuf); + return 0; + } + + char *exporttable = modulebuf + exporttableoffset; + + DWORD numentries = *(DWORD *)(exporttable + 24); + DWORD addresstableoffset = *(DWORD *)(exporttable + 28); + DWORD nameptrtableoffset = *(DWORD *)(exporttable + 32); + DWORD ordinaltableoffset = *(DWORD *)(exporttable + 36); + DWORD *nameptrtable = (DWORD *)(modulebuf + nameptrtableoffset); + WORD *ordinaltable = (WORD *)(modulebuf + ordinaltableoffset); + DWORD *addresstable = (DWORD *)(modulebuf + addresstableoffset); + + DWORD i; + for (i = 0; i < numentries; i++) { + char *nameptr = modulebuf + nameptrtable[i]; + if (strcmp(name, nameptr) == 0) break; + } + + if (i == numentries) { + free(modulebuf); + return 0; + } + + WORD oridnal = ordinaltable[i]; + DWORD offset = addresstable[oridnal]; + + free(modulebuf); + return offset; +} + +// Gets the registered safe exception handlers for the module +void Debugger::GetExceptionHandlers(size_t module_haeder, std::unordered_set & handlers) { + // only present on x86 + if (child_ptr_size != 4) return; + + DWORD size_of_image = GetImageSize((void *)module_haeder); + + char* modulebuf = (char*)malloc(size_of_image); + SIZE_T num_read; + if (!ReadProcessMemory(child_handle, (void *)module_haeder, modulebuf, size_of_image, &num_read) || + (num_read != size_of_image)) + { + FATAL("Error reading target memory\n"); + } + + DWORD pe_offset; + pe_offset = *((DWORD*)(modulebuf + 0x3C)); + char* pe = modulebuf + pe_offset; + DWORD signature = *((DWORD*)pe); + if (signature != 0x00004550) { + free(modulebuf); + return; + } + pe = pe + 0x18; + WORD magic = *((WORD*)pe); + DWORD lc_offset; + DWORD lc_size; + if (magic == 0x10b) { + lc_offset = *(DWORD*)(pe + 176); + lc_size = *(DWORD*)(pe + 180); + } else if (magic == 0x20b) { + lc_offset = *(DWORD*)(pe + 192); + lc_size = *(DWORD*)(pe + 196); + } else { + free(modulebuf); + return; + } + + if (!lc_offset || (lc_size != 64)) { + free(modulebuf); + return; + } + + char* lc = modulebuf + lc_offset; + + size_t seh_table_address; + DWORD seh_count; + if (magic == 0x10b) { + seh_table_address = *(DWORD*)(lc + 64); + seh_count = *(DWORD*)(lc + 68); + } else if (magic == 0x20b) { + seh_table_address = *(uint64_t*)(lc + 96); + seh_count = *(DWORD*)(lc + 104); + } else { + free(modulebuf); + return; + } + + size_t seh_table_offset = seh_table_address - module_haeder; + + DWORD* seh_table = (DWORD *)(modulebuf + seh_table_offset); + for (DWORD i = 0; i < seh_count; i++) { + handlers.insert(module_haeder + seh_table[i]); + } + + free(modulebuf); +} + +// attempt to obtain the address of target function +// in various ways +char *Debugger::GetTargetAddress(HMODULE module) { + char* base_of_dll = (char *)module; + + // if persist_offset is defined, use that + if (target_offset) { + return base_of_dll + target_offset; + } + + DWORD offset = GetProcOffset(module, target_method.c_str()); + if (offset) { + return (char *)module + offset; + } + + // finally, try the debug symbols + char *method_address = NULL; + char base_name[MAX_PATH]; + GetModuleBaseNameA(child_handle, + module, + (LPSTR)(&base_name), + sizeof(base_name)); + + char module_path[MAX_PATH]; + if (!GetModuleFileNameExA(child_handle, + module, + module_path, + sizeof(module_path))) + return NULL; + + ULONG64 buffer[(sizeof(SYMBOL_INFO) + + MAX_SYM_NAME * sizeof(TCHAR) + + sizeof(ULONG64) - 1) / + sizeof(ULONG64)]; + PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; + pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); + pSymbol->MaxNameLen = MAX_SYM_NAME; + SymInitialize(child_handle, NULL, false); + DWORD64 sym_base_address = SymLoadModuleEx(child_handle, + NULL, + module_path, + NULL, + 0, + 0, + NULL, + 0); + if (SymFromName(child_handle, target_method.c_str(), pSymbol)) { + target_offset = (unsigned long)(pSymbol->Address - sym_base_address); + method_address = base_of_dll + target_offset; + } + SymCleanup(child_handle); + + return method_address; +} + +// called when a module gets loaded +void Debugger::OnModuleLoaded(void *module, char *module_name) { + // printf("In on_module_loaded, name: %s, base: %p\n", module_name, module_info.lpBaseOfDll); + + if (target_function_defined && _stricmp(module_name, target_module.c_str()) == 0) { + target_address = GetTargetAddress((HMODULE)module); + if (!target_address) { + FATAL("Error determining target method address\n"); + } + + AddBreakpoint(target_address, BREAKPOINT_TARGET); + } +} + +// called when a module gets unloaded +void Debugger::OnModuleUnloaded(void *module) { } + +// reads numitems entries from stack in remote process +// from stack_addr +// into buffer +void Debugger::ReadStack(void *stack_addr, void **buffer, size_t numitems) { + SIZE_T numrw = 0; +#ifdef _WIN64 + if (wow64_target) { + uint32_t *buf32 = (uint32_t *)malloc(numitems * child_ptr_size); + ReadProcessMemory(child_handle, stack_addr, buf32, numitems * child_ptr_size, &numrw); + for (size_t i = 0; i < numitems; i++) { + buffer[i] = (void *)((size_t)buf32[i]); + } + free(buf32); + return; + } +#endif + ReadProcessMemory(child_handle, stack_addr, buffer, numitems * child_ptr_size, &numrw); +} + +// writes numitems entries to stack in remote process +// from buffer +// into stack_addr +void Debugger::WriteStack(void *stack_addr, void **buffer, size_t numitems) { + SIZE_T numrw = 0; +#ifdef _WIN64 + if (wow64_target) { + uint32_t *buf32 = (uint32_t *)malloc(numitems * child_ptr_size); + for (size_t i = 0; i < numitems; i++) { + buf32[i] = (uint32_t)((size_t)buffer[i]); + } + WriteProcessMemory(child_handle, stack_addr, buf32, numitems * child_ptr_size, &numrw); + free(buf32); + return; + } +#endif + WriteProcessMemory(child_handle, stack_addr, buffer, numitems * child_ptr_size, &numrw); +} + +// called when the target method is reached +void Debugger::HandleTargetReachedInternal() { + // printf("in OnTargetMethod\n"); + + SIZE_T numrw = 0; + + CONTEXT lcContext; + lcContext.ContextFlags = CONTEXT_ALL; + HANDLE thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, thread_id); + GetThreadContext(thread_handle, &lcContext); + + // read out and save the params +#ifdef _WIN64 + saved_sp = (void *)lcContext.Rsp; +#else + saved_sp = (void *)lcContext.Esp; +#endif + + saved_return_address = 0; + ReadProcessMemory(child_handle, saved_sp, &saved_return_address, child_ptr_size, &numrw); + + if (loop_mode) { + switch (calling_convention) { +#ifdef _WIN64 + case CALLCONV_DEFAULT: + case CALLCONV_MICROSOFT_X64: + if (target_num_args > 0) saved_args[0] = (void *)lcContext.Rcx; + if (target_num_args > 1) saved_args[1] = (void *)lcContext.Rdx; + if (target_num_args > 2) saved_args[2] = (void *)lcContext.R8; + if (target_num_args > 3) saved_args[3] = (void *)lcContext.R9; + if (target_num_args > 4) { + ReadStack((void *)(lcContext.Rsp + 5 * child_ptr_size), saved_args + 4, target_num_args - 4); + } + break; + case CALLCONV_CDECL: + if (target_num_args > 0) { + ReadStack((void *)(lcContext.Rsp + child_ptr_size), saved_args, target_num_args); + } + break; + case CALLCONV_FASTCALL: + if (target_num_args > 0) saved_args[0] = (void *)lcContext.Rcx; + if (target_num_args > 1) saved_args[1] = (void *)lcContext.Rdx; + if (target_num_args > 3) { + ReadStack((void *)(lcContext.Rsp + child_ptr_size), saved_args + 2, target_num_args - 2); + } + break; + case CALLCONV_THISCALL: + if (target_num_args > 0) saved_args[0] = (void *)lcContext.Rcx; + if (target_num_args > 3) { + ReadStack((void *)(lcContext.Rsp + child_ptr_size), saved_args + 1, target_num_args - 1); + } + break; +#else + case CALLCONV_MICROSOFT_X64: + FATAL("X64 callong convention not supported for 32-bit targets"); + break; + case CALLCONV_DEFAULT: + case CALLCONV_CDECL: + if (target_num_args > 0) { + ReadStack((void *)(lcContext.Esp + child_ptr_size), saved_args, target_num_args); + } + break; + case CALLCONV_FASTCALL: + if (target_num_args > 0) saved_args[0] = (void *)lcContext.Ecx; + if (target_num_args > 1) saved_args[1] = (void *)lcContext.Edx; + if (target_num_args > 3) { + ReadStack((void *)(lcContext.Esp + child_ptr_size), saved_args + 2, target_num_args - 2); + } + break; + case CALLCONV_THISCALL: + if (target_num_args > 0) saved_args[0] = (void *)lcContext.Ecx; + if (target_num_args > 3) { + ReadStack((void *)(lcContext.Esp + child_ptr_size), saved_args + 1, target_num_args - 1); + } + break; +#endif + default: + break; + } + + // todo store any target-specific additional context here + } + + // modify the return address on the stack so that an exception is triggered + // when the target function finishes executing + // another option would be to allocate a block of executable memory + // and point return address over there, but this is quicker + size_t return_address = PERSIST_END_EXCEPTION; + WriteProcessMemory(child_handle, saved_sp, &return_address, child_ptr_size, &numrw); + + CloseHandle(thread_handle); + + if (!target_reached) { + target_reached = true; + OnTargetMethodReached(); + } +} + +// called every time the target method returns +void Debugger::HandleTargetEnded() { + // printf("in OnTargetMethodEnded\n"); + + CONTEXT lcContext; + lcContext.ContextFlags = CONTEXT_ALL; + HANDLE thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, thread_id); + GetThreadContext(thread_handle, &lcContext); + +#ifdef _WIN64 + target_return_value = (uint64_t)lcContext.Rax; +#else + target_return_value = (uint64_t)lcContext.Eax; +#endif + + if (loop_mode) { + // restore params +#ifdef _WIN64 + lcContext.Rip = (size_t)target_address; + lcContext.Rsp = (size_t)saved_sp; +#else + lcContext.Eip = (size_t)target_address; + lcContext.Esp = (size_t)saved_sp; +#endif + + // restore return address as it might have been overwritten by instrumentation + SIZE_T numrw = 0; + size_t return_address = PERSIST_END_EXCEPTION; + WriteProcessMemory(child_handle, saved_sp, &return_address, child_ptr_size, &numrw); + + switch (calling_convention) { +#ifdef _WIN64 + case CALLCONV_DEFAULT: + case CALLCONV_MICROSOFT_X64: + if (target_num_args > 0) lcContext.Rcx = (size_t)saved_args[0]; + if (target_num_args > 1) lcContext.Rdx = (size_t)saved_args[1]; + if (target_num_args > 2) lcContext.R8 = (size_t)saved_args[2]; + if (target_num_args > 3) lcContext.R9 = (size_t)saved_args[3]; + if (target_num_args > 4) { + WriteStack((void *)(lcContext.Rsp + 5 * child_ptr_size), saved_args + 4, target_num_args - 4); + } + break; + case CALLCONV_CDECL: + if (target_num_args > 0) { + WriteStack((void *)(lcContext.Rsp + child_ptr_size), saved_args, target_num_args); + } + break; + case CALLCONV_FASTCALL: + if (target_num_args > 0) lcContext.Rcx = (size_t)saved_args[0]; + if (target_num_args > 1) lcContext.Rdx = (size_t)saved_args[1]; + if (target_num_args > 3) { + WriteStack((void *)(lcContext.Rsp + child_ptr_size), saved_args + 2, target_num_args - 2); + } + break; + case CALLCONV_THISCALL: + if (target_num_args > 0) lcContext.Rcx = (size_t)saved_args[0]; + if (target_num_args > 3) { + WriteStack((void *)(lcContext.Rsp + child_ptr_size), saved_args + 1, target_num_args - 1); + } + break; +#else + case CALLCONV_MICROSOFT_X64: + FATAL("X64 callong convention not supported for 32-bit targets"); + break; + case CALLCONV_DEFAULT: + case CALLCONV_CDECL: + if (target_num_args > 0) { + WriteStack((void *)(lcContext.Esp + child_ptr_size), saved_args, target_num_args); + } + break; + case CALLCONV_FASTCALL: + if (target_num_args > 0) lcContext.Ecx = (size_t)saved_args[0]; + if (target_num_args > 1) lcContext.Edx = (size_t)saved_args[1]; + if (target_num_args > 3) { + WriteStack((void *)(lcContext.Esp + child_ptr_size), saved_args + 2, target_num_args - 2); + } + break; + case CALLCONV_THISCALL: + if (target_num_args > 0) lcContext.Ecx = (size_t)saved_args[0]; + if (target_num_args > 3) { + WriteStack((void *)(lcContext.Esp + child_ptr_size), saved_args + 1, target_num_args - 1); + } + break; +#endif + default: + break; + } + + // todo restore any target-specific additional context here + + } else { /* loop_mode == false */ + +#ifdef _WIN64 + lcContext.Rip = (size_t)saved_return_address; +#else + lcContext.Eip = (size_t)saved_return_address; +#endif + + // restore target entry breakpoint + // note that this time, the breakpoint address might be + // in instrumented code + // so we need to use translated address + AddBreakpoint((void *)GetTranslatedAddress((size_t)target_address), + BREAKPOINT_TARGET); + } + + SetThreadContext(thread_handle, &lcContext); + CloseHandle(thread_handle); +} + +// called when process entrypoint gets reached +void Debugger::OnEntrypoint() { + // printf("Entrypoint\n"); + + HMODULE *module_handles = NULL; + DWORD num_modules = GetLoadedModules(&module_handles); + for (DWORD i = 0; i < num_modules; i++) { + char base_name[MAX_PATH]; + GetModuleBaseNameA(child_handle, module_handles[i], (LPSTR)(&base_name), sizeof(base_name)); + if(trace_debug_events) + printf("Debugger: Loaded module %s at %p\n", base_name, (void *)module_handles[i]); + OnModuleLoaded((void *)module_handles[i], base_name); + } + if (module_handles) free(module_handles); + + child_entrypoint_reached = true; + + if (trace_debug_events) printf("Debugger: Process entrypoint reached\n"); +} + +// called when the debugger hits a breakpoint +int Debugger::HandleDebuggerBreakpoint(void *address) { + int ret = BREAKPOINT_UNKNOWN; + SIZE_T rwsize = 0; + + Breakpoint *breakpoint = NULL, *tmp_breakpoint; + for (auto iter = breakpoints.begin(); iter != breakpoints.end(); iter++) { + tmp_breakpoint = *iter; + if (tmp_breakpoint->address == address) { + breakpoint = tmp_breakpoint; + breakpoints.erase(iter); + break; + } + } + + if (!breakpoint) return ret; + + // restore address + if (!WriteProcessMemory(child_handle, address, &breakpoint->original_opcode, 1, &rwsize) || + (rwsize != 1)) + { + FATAL("Error writing child memory\n"); + } + FlushInstructionCache(child_handle, address, 1); + // restore context + CONTEXT lcContext; + lcContext.ContextFlags = CONTEXT_ALL; + HANDLE thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, thread_id); + GetThreadContext(thread_handle, &lcContext); +#ifdef _WIN64 + lcContext.Rip--; +#else + lcContext.Eip--; +#endif + SetThreadContext(thread_handle, &lcContext); + CloseHandle(thread_handle); + // handle breakpoint + switch (breakpoint->type) { + case BREAKPOINT_ENTRYPOINT: + OnEntrypoint(); + break; + case BREAKPOINT_TARGET: + if (trace_debug_events) printf("Target method reached\n"); + HandleTargetReachedInternal(); + break; + default: + break; + } + + // return the brekpoint type + ret = breakpoint->type; + + // delete the breakpoint object + free(breakpoint); + + return ret; +} + +// called when a dll gets loaded +void Debugger::HandleDllLoadInternal(LOAD_DLL_DEBUG_INFO *LoadDll) { + // Don't do anything until the processentrypoint is reached. + // Before that time we can't do much anyway, a lot of calls are going to fail + // Modules loaded before entrypoint is reached are going to be enumerated at that time + if (child_entrypoint_reached) { + char filename[MAX_PATH]; + GetFinalPathNameByHandleA(LoadDll->hFile, (LPSTR)(&filename), sizeof(filename), 0); + char *base_name = strrchr(filename, '\\'); + if (base_name) base_name += 1; + else base_name = filename; + if (trace_debug_events) + printf("Debugger: Loaded module %s at %p\n", + base_name, + (void *)LoadDll->lpBaseOfDll); + OnModuleLoaded(LoadDll->lpBaseOfDll, base_name); + } +} + +// called when a process gets created +// or attached to +void Debugger::OnProcessCreated() { + CREATE_PROCESS_DEBUG_INFO *info = &dbg_debug_event.u.CreateProcessInfo; + + if (attach_mode) { + // assume entrypoint has been reached already + child_handle = info->hProcess; + child_thread_handle = info->hThread; + child_entrypoint_reached = true; + GetProcessPlatform(); + + // In case of attaching to an existing process + // the dll load event for the main module + // will *not* be generated. + // Handle the main module load below + char filename[MAX_PATH]; + GetFinalPathNameByHandleA(info->hFile, (LPSTR)(&filename), sizeof(filename), 0); + char* base_name = strrchr(filename, '\\'); + if (base_name) base_name += 1; + else base_name = filename; + if (trace_debug_events) + printf("Debugger: Loaded module %s at %p\n", + base_name, + (void*)info->lpBaseOfImage); + OnModuleLoaded(info->lpBaseOfImage, base_name); + + } else { + // add a brekpoint to the process entrypoint + void *entrypoint = GetModuleEntrypoint(info->lpBaseOfImage); + AddBreakpoint(entrypoint, BREAKPOINT_ENTRYPOINT); + } +} + +// called when an exception in the target occurs +DebuggerStatus Debugger::HandleExceptionInternal(EXCEPTION_RECORD *exception_record) +{ + CreateException(exception_record, &last_exception); + + // note: instrumentation could have placed breakpoints + // on the same addresses as debugger + // handle one-time debugger breakpoints first + if ((exception_record->ExceptionCode == EXCEPTION_BREAKPOINT) || + (exception_record->ExceptionCode == 0x4000001f)) + { + void *address = exception_record->ExceptionAddress; + // printf("Breakpoint at address %p\n", address); + int breakpoint_type = HandleDebuggerBreakpoint(address); + if (breakpoint_type == BREAKPOINT_TARGET) { + return DEBUGGER_TARGET_START; + } else if (breakpoint_type != BREAKPOINT_UNKNOWN) { + return DEBUGGER_CONTINUE; + } + } + + // check if cleient can handle it + if (OnException(&last_exception)) { + return DEBUGGER_CONTINUE; + } + + // don't print exceptions handled by clients + if (trace_debug_events) + printf("Debugger: Exception %x at address %p\n", + exception_record->ExceptionCode, + exception_record->ExceptionAddress); + + switch (exception_record->ExceptionCode) + { + case EXCEPTION_BREAKPOINT: + case 0x4000001f: //STATUS_WX86_BREAKPOINT + // not handled above + dbg_continue_status = DBG_EXCEPTION_NOT_HANDLED; + return DEBUGGER_CONTINUE; + + case EXCEPTION_ACCESS_VIOLATION: { + if (target_function_defined && + ((size_t)exception_record->ExceptionAddress == PERSIST_END_EXCEPTION)) + { + if (trace_debug_events) printf("Debugger: Persistence method ended\n"); + HandleTargetEnded(); + return DEBUGGER_TARGET_END; + } else { + // Debug(&DebugEv->u.Exception.ExceptionRecord); + dbg_continue_status = DBG_EXCEPTION_NOT_HANDLED; + return DEBUGGER_CRASHED; + } + break; + } + + case EXCEPTION_ILLEGAL_INSTRUCTION: + case EXCEPTION_PRIV_INSTRUCTION: + case EXCEPTION_INT_DIVIDE_BY_ZERO: + case EXCEPTION_STACK_OVERFLOW: + case STATUS_HEAP_CORRUPTION: + case STATUS_STACK_BUFFER_OVERRUN: + case STATUS_FATAL_APP_EXIT: + dbg_continue_status = DBG_EXCEPTION_NOT_HANDLED; + return DEBUGGER_CRASHED; + break; + + default: + if (trace_debug_events) + printf("Unhandled exception %x\n", exception_record->ExceptionCode); + dbg_continue_status = DBG_EXCEPTION_NOT_HANDLED; + return DEBUGGER_CONTINUE; + } +} + +// standard debugger loop that listens to events in the target process +DebuggerStatus Debugger::DebugLoop(uint32_t timeout, bool killing) +{ + DebuggerStatus ret; + bool alive = true; + + if (dbg_continue_needed) { + ContinueDebugEvent(dbg_debug_event.dwProcessId, + dbg_debug_event.dwThreadId, + dbg_continue_status); + } + + LPDEBUG_EVENT DebugEv = &dbg_debug_event; + + while (alive) + { + have_thread_context = false; + + uint64_t begin_time = GetCurTime(); + BOOL wait_ret = WaitForDebugEvent(DebugEv, 100); + uint64_t end_time = GetCurTime(); + + uint64_t time_elapsed = end_time - begin_time; + timeout = ((uint64_t)timeout >= time_elapsed) ? timeout - (uint32_t)time_elapsed : 0; + + //printf("timeout: %u\n", timeout); + // printf("time: %lld\n", get_cur_time_us()); + + if (wait_ret) { + dbg_continue_needed = true; + } else { + dbg_continue_needed = false; + } + + if (timeout == 0) { + if(attach_mode) { + return DEBUGGER_PROCESS_EXIT; + } else { + return DEBUGGER_HANGED; + } + } + + if (!wait_ret) { + //printf("WaitForDebugEvent returned 0\n"); + continue; + } + + dbg_continue_status = DBG_CONTINUE; + + thread_id = DebugEv->dwThreadId; + + //printf("eventCode: %x\n", DebugEv->dwDebugEventCode); + + switch (DebugEv->dwDebugEventCode) + { + case EXCEPTION_DEBUG_EVENT: + if (!killing) { + ret = HandleExceptionInternal(&DebugEv->u.Exception.ExceptionRecord); + if (ret == DEBUGGER_CRASHED) OnCrashed(&last_exception); + if (ret != DEBUGGER_CONTINUE) return ret; + } else { + dbg_continue_status = DBG_EXCEPTION_NOT_HANDLED; + } + break; + + case CREATE_THREAD_DEBUG_EVENT: + break; + + case CREATE_PROCESS_DEBUG_EVENT: { + if (trace_debug_events) printf("Debugger: Process created or attached\n"); + OnProcessCreated(); + CloseHandle(DebugEv->u.CreateProcessInfo.hFile); + break; + } + + case EXIT_THREAD_DEBUG_EVENT: + break; + + case EXIT_PROCESS_DEBUG_EVENT: + if (trace_debug_events) printf("Debugger: Process exit\n"); + OnProcessExit(); + alive = false; + break; + + case LOAD_DLL_DEBUG_EVENT: { + if(!killing) HandleDllLoadInternal(&DebugEv->u.LoadDll); + CloseHandle(DebugEv->u.LoadDll.hFile); + break; + } + + case UNLOAD_DLL_DEBUG_EVENT: + if (trace_debug_events) + printf("Debugger: Unloaded module from %p\n", DebugEv->u.UnloadDll.lpBaseOfDll); + OnModuleUnloaded(DebugEv->u.UnloadDll.lpBaseOfDll); + break; + + default: + break; + } + + ContinueDebugEvent(DebugEv->dwProcessId, + DebugEv->dwThreadId, + dbg_continue_status); + } + + return DEBUGGER_PROCESS_EXIT; +} + +// starts the target process +void Debugger::StartProcess(char *cmd) { + dbg_continue_needed = false; + + STARTUPINFOA si; + STARTUPINFOEXA si_ex; + LPSTARTUPINFOA si_ptr; + LPSTARTUPINFOA si_basic_ptr; + LPPROC_THREAD_ATTRIBUTE_LIST attr_list_buf = NULL; + + DWORD creation_flags = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS; + + PROCESS_INFORMATION pi; + HANDLE hJob = NULL; + JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_limit; + + DeleteBreakpoints(); + + if (sinkhole_stds && devnul_handle == INVALID_HANDLE_VALUE) { + devnul_handle = CreateFile( + "nul", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, + NULL); + + if (devnul_handle == INVALID_HANDLE_VALUE) { + FATAL("Unable to open the nul device."); + } + } + BOOL inherit_handles = TRUE; + + if (force_dep) { + ZeroMemory(&si_ex, sizeof(si_ex)); + si_ex.StartupInfo.cb = sizeof(si_ex); + + creation_flags |= EXTENDED_STARTUPINFO_PRESENT; + + SIZE_T attr_size = 0; + InitializeProcThreadAttributeList(NULL, 1, 0, &attr_size); + if (attr_size == 0) { + FATAL("Error getting attribute list size"); + } + + attr_list_buf = (LPPROC_THREAD_ATTRIBUTE_LIST)malloc(attr_size); + if (!InitializeProcThreadAttributeList(attr_list_buf, 1, 0, &attr_size)) { + FATAL("Error in InitializeProcThreadAttributeList"); + } + + DWORD flags = PROCESS_CREATION_MITIGATION_POLICY_DEP_ENABLE; + size_t flags_size = sizeof(flags); + + if (!UpdateProcThreadAttribute(attr_list_buf, + 0, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, + &flags, flags_size, NULL, NULL)) + { + FATAL("Error in UpdateProcThreadAttribute"); + } + + si_ex.lpAttributeList = attr_list_buf; + + si_ptr = (LPSTARTUPINFOA)&si_ex; + si_basic_ptr = &si_ex.StartupInfo; + } else { + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + si_ptr = &si; + si_basic_ptr = &si; + } + + if (sinkhole_stds) { + si_basic_ptr->hStdOutput = si_basic_ptr->hStdError = devnul_handle; + si_basic_ptr->dwFlags |= STARTF_USESTDHANDLES; + } else { + inherit_handles = FALSE; + } + + ZeroMemory(&pi, sizeof(pi)); + + if (mem_limit || cpu_aff) { + hJob = CreateJobObject(NULL, NULL); + if (hJob == NULL) { + FATAL("CreateJobObject failed, GLE=%d.\n", GetLastError()); + } + + ZeroMemory(&job_limit, sizeof(job_limit)); + if (mem_limit) { + job_limit.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_PROCESS_MEMORY; + job_limit.ProcessMemoryLimit = (size_t)(mem_limit * 1024 * 1024); + } + + if (cpu_aff) { + job_limit.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_AFFINITY; + job_limit.BasicLimitInformation.Affinity = (DWORD_PTR)cpu_aff; + } + + if (!SetInformationJobObject( + hJob, + JobObjectExtendedLimitInformation, + &job_limit, + sizeof(job_limit) + )) { + FATAL("SetInformationJobObject failed, GLE=%d.\n", GetLastError()); + } + } + + if (!CreateProcessA(NULL, + cmd, + NULL, + NULL, + inherit_handles, + creation_flags, + NULL, + NULL, + si_ptr, + &pi)) + { + FATAL("CreateProcess failed, GLE=%d.\n", GetLastError()); + } + + if (attr_list_buf) { + DeleteProcThreadAttributeList(attr_list_buf); + free(attr_list_buf); + } + + child_handle = pi.hProcess; + child_thread_handle = pi.hThread; + child_entrypoint_reached = false; + target_reached = false; + have_thread_context = false; + + if (mem_limit || cpu_aff) { + if (!AssignProcessToJobObject(hJob, child_handle)) { + FATAL("AssignProcessToJobObject failed, GLE=%d.\n", GetLastError()); + } + } + + GetProcessPlatform(); +} + +void Debugger::GetProcessPlatform() { + BOOL wow64current, wow64remote; + if (!IsWow64Process(child_handle, &wow64remote)) { + FATAL("IsWow64Process failed"); + } + if (wow64remote) { + wow64_target = 1; + child_ptr_size = 4; + if (calling_convention == CALLCONV_DEFAULT) { + calling_convention = CALLCONV_CDECL; + } + } + if (!IsWow64Process(GetCurrentProcess(), &wow64current)) { + FATAL("IsWow64Process failed"); + } + // Will probably fail before we reach this, but oh well + if (sizeof(void*) < child_ptr_size) { + FATAL("64-bit build is needed to run 64-bit targets\n"); + } +} + +// kills the target process +// (if not dead already) +DebuggerStatus Debugger::Kill() { + if (!child_handle) return DEBUGGER_PROCESS_EXIT; + + TerminateProcess(child_handle, 0); + + dbg_last_status = DebugLoop(0xFFFFFFFFUL, true); + if (dbg_last_status != DEBUGGER_PROCESS_EXIT) { + FATAL("Error killing target process\n"); + } + + CloseHandle(child_handle); + CloseHandle(child_thread_handle); + + child_handle = NULL; + child_thread_handle = NULL; + have_thread_context = false; + + // delete any breakpoints that weren't hit + DeleteBreakpoints(); + + return dbg_last_status; +} + +// attaches to an active process +DebuggerStatus Debugger::Attach(unsigned int pid, uint32_t timeout) { + attach_mode = true; + + if (!DebugActiveProcess(pid)) { + DWORD error_code = GetLastError(); + if(error_code == 5) { + HANDLE hToken = NULL; + LUID luid; + TOKEN_PRIVILEGES tp; + + if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { + FATAL("OpenProcessToken() failed, error code = %d\n", GetLastError()); + } + + if(!LookupPrivilegeValueA(NULL, "SeDebugPrivilege", &luid)) { + FATAL("LookupPrivilegeValueA() failed, error code = %d\n", GetLastError()); + } + + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + tp.Privileges[0].Luid = luid; + tp.PrivilegeCount = 1; + + if(!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) { + FATAL("AdjustTokenPrivileges() failed, error code = %d\n", GetLastError()); + } + + if(!DebugActiveProcess(pid)) { + FATAL("Could not attach to the process.\n" + "Make sure the process exists and you have permissions to debug it.\n"); + } + + } else { + FATAL("DebugActiveProcess() failed, error code = %d\n", error_code); + } + } + + dbg_last_status = DEBUGGER_ATTACHED; + + return Continue(timeout); +} + +// starts the process and waits for the next event +DebuggerStatus Debugger::Run(char *cmd, uint32_t timeout) { + attach_mode = false; + + StartProcess(cmd); + + return Continue(timeout); +} + +DebuggerStatus Debugger::Run(int argc, char **argv, uint32_t timeout) { + char* cmd = NULL; + cmd = ArgvToCmd(argc, argv); + + DebuggerStatus ret_dbg_status = Run(cmd, timeout); + free(cmd); + + return ret_dbg_status; +} + +// continues after Run() or previous Continue() +// return with a non-terminal status +DebuggerStatus Debugger::Continue(uint32_t timeout) { + if (!child_handle && (dbg_last_status != DEBUGGER_ATTACHED)) + return DEBUGGER_PROCESS_EXIT; + + if (loop_mode && (dbg_last_status == DEBUGGER_TARGET_END)) { + // saves us a breakpoint + dbg_last_status = DEBUGGER_TARGET_START; + return dbg_last_status; + } + if (script != NULL) { + HANDLE thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)system, script, 0, NULL); + CloseHandle(thread_handle); + } + + dbg_last_status = DebugLoop(timeout); + + if (dbg_last_status == DEBUGGER_PROCESS_EXIT) { + if (!attach_mode) { + CloseHandle(child_handle); + CloseHandle(child_thread_handle); + child_handle = NULL; + child_thread_handle = NULL; + } + } + + return dbg_last_status; +} + +// initializes options from command line +void Debugger::Init(int argc, char **argv) { + have_thread_context = false; + sinkhole_stds = false; + mem_limit = 0; + cpu_aff = 0; + + attach_mode = false; + trace_debug_events = false; + loop_mode = false; + target_function_defined = false; + + target_return_value = 0; + + child_handle = NULL; + child_thread_handle = NULL; + + target_module[0] = 0; + target_method[0] = 0; + target_offset = 0; + saved_args = NULL; + target_num_args = 0; + calling_convention = CALLCONV_DEFAULT; + target_address = NULL; + + char *option; + + trace_debug_events = GetBinaryOption("-trace_debug_events", + argc, argv, + trace_debug_events); + + option = GetOption("-target_module", argc, argv); + if (option) target_module = option; + + option = GetOption("-target_method", argc, argv); + if (option) target_method = option; + + loop_mode = GetBinaryOption("-loop", argc, argv, loop_mode); + + option = GetOption("-nargs", argc, argv); + if (option) target_num_args = atoi(option); + + option = GetOption("-target_offset", argc, argv); + if (option) target_offset = strtoul(option, NULL, 0); + + option = GetOption("-callconv", argc, argv); + if (option) { + if (strcmp(option, "stdcall") == 0) + calling_convention = CALLCONV_CDECL; + else if (strcmp(option, "fastcall") == 0) + calling_convention = CALLCONV_FASTCALL; + else if (strcmp(option, "thiscall") == 0) + calling_convention = CALLCONV_THISCALL; + else if (strcmp(option, "ms64") == 0) + calling_convention = CALLCONV_MICROSOFT_X64; + else + FATAL("Unknown calling convention"); + } + + force_dep = GetBinaryOption("-force_dep", argc, argv, false); + + // check if we are running in persistence mode + if (target_module[0] || target_offset || target_method[0]) { + target_function_defined = true; + if ((target_module[0] == 0) || ((target_offset == 0) && (target_method[0] == 0))) { + FATAL("target_module and either target_offset or target_method must be specified together\n"); + } + } + + if (loop_mode && !target_function_defined) { + FATAL("Target function needs to be defined to use the loop mode\n"); + } + + if (target_num_args) { + saved_args = (void **)malloc(target_num_args * sizeof(void *)); + } + + // get allocation granularity + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + allocation_granularity = system_info.dwAllocationGranularity; +} diff --git a/debugger.h b/debugger.h new file mode 100644 index 0000000..9b19b0c --- /dev/null +++ b/debugger.h @@ -0,0 +1,237 @@ +/* +Copyright 2020 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +https ://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#ifndef DEBUGGER_H +#define DEBUGGER_H + +#include +#include +#include +#include + +#include "common.h" +#include "windows.h" +#include "arch/x86/reg.h" + + +enum DebuggerStatus { + DEBUGGER_NONE, + DEBUGGER_CONTINUE, + DEBUGGER_PROCESS_EXIT, + DEBUGGER_TARGET_START, + DEBUGGER_TARGET_END, + DEBUGGER_CRASHED, + DEBUGGER_HANGED, + DEBUGGER_ATTACHED +}; + +struct SavedRegisters { + CONTEXT saved_context; +}; + +class Debugger { +public: + + virtual void Init(int argc, char **argv); + DebuggerStatus Run(char *cmd, uint32_t timeout); + DebuggerStatus Run(int argc, char **argv, uint32_t timeout); + DebuggerStatus Kill(); + DebuggerStatus Continue(uint32_t timeout); + DebuggerStatus Attach(unsigned int pid, uint32_t timeout); + + bool IsTargetAlive() { return (child_handle != NULL); }; + bool IsTargetFunctionDefined() { return target_function_defined; } + + uint64_t GetTargetReturnValue() { return target_return_value; } + + enum ExceptionType { + BREAKPOINT, + ACCESS_VIOLATION, + ILLEGAL_INSTRUCTION, + STACK_OVERFLOW, + OTHER + }; + + struct Exception { + ExceptionType type; + void *ip; + bool maybe_write_violation; + bool maybe_execute_violation; + void *access_address; + }; + + Exception GetLastException() { + return last_exception; + } + + char * script; + +protected: + + enum MemoryProtection { + READONLY, + READWRITE, + READEXECUTE, + READWRITEEXECUTE + }; + + virtual void OnModuleLoaded(void *module, char *module_name); + virtual void OnModuleUnloaded(void *module); + virtual void OnTargetMethodReached() {} + virtual void OnProcessCreated(); + virtual void OnProcessExit() {}; + virtual void OnEntrypoint(); + + // should return true if the exception has been handled + virtual bool OnException(Exception *exception_record) { + return false; + } + + virtual void OnCrashed(Exception *exception_record) { } + + void *GetModuleEntrypoint(void *base_address); + void ReadStack(void *stack_addr, void **buffer, size_t numitems); + void WriteStack(void *stack_addr, void **buffer, size_t numitems); + void GetImageSize(void *base_address, size_t *min_address, size_t *max_address); + + // helper functions + void *RemoteAllocateNear(uint64_t region_min, + uint64_t region_max, + size_t size, + MemoryProtection protection, + bool use_shared_memory = false); + + void ExtractCodeRanges(void *module_base, + size_t min_address, + size_t max_address, + std::list *executable_ranges, + size_t *code_size); + + void ProtectCodeRanges(std::list *executable_ranges); + + // returns address in (potentially) instrumented code + virtual size_t GetTranslatedAddress(size_t address) { return address; } + + void RemoteFree(void *address, size_t size); + void RemoteWrite(void *address, void *buffer, size_t size); + void RemoteRead(void *address, void *buffer, size_t size); + void RemoteProtect(void *address, size_t size, MemoryProtection protect); + + size_t GetRegister(Register r); + void SetRegister(Register r, size_t value); + + void *GetTargetMethodAddress() { return target_address; } + + DWORD GetProcOffset(HMODULE module, const char* name); + + void SaveRegisters(SavedRegisters* registers); + void RestoreRegisters(SavedRegisters* registers); + + void GetExceptionHandlers(size_t module_haeder, std::unordered_set & handlers); + + void PatchPointersRemote(size_t min_address, size_t max_address, std::unordered_map& search_replace); + template + void PatchPointersRemoteT(size_t min_address, size_t max_address, std::unordered_map& search_replace); + +private: + struct Breakpoint { + void *address; + int type; + unsigned char original_opcode; + }; + std::list breakpoints; + + void StartProcess(char *cmd); + void GetProcessPlatform(); + DebuggerStatus DebugLoop(uint32_t timeout, bool killing=false); + int HandleDebuggerBreakpoint(void *address); + void HandleDllLoadInternal(LOAD_DLL_DEBUG_INFO *LoadDll); + DebuggerStatus HandleExceptionInternal(EXCEPTION_RECORD *exception_record); + void HandleTargetReachedInternal(); + void HandleTargetEnded(); + char *GetTargetAddress(HMODULE module); + void AddBreakpoint(void *address, int type); + DWORD GetLoadedModules(HMODULE **modules); + void DeleteBreakpoints(); + DWORD WindowsProtectionFlags(MemoryProtection protection); + DWORD GetImageSize(void *base_address); + void *RemoteAllocateBefore(uint64_t min_address, + uint64_t max_address, + size_t size, + MemoryProtection protection); + void *RemoteAllocateAfter(uint64_t min_address, + uint64_t max_address, + size_t size, + MemoryProtection protection); + +protected: + + bool child_entrypoint_reached; + bool target_reached; + + int32_t child_ptr_size = sizeof(void *); + +private: + HANDLE child_handle, child_thread_handle; + + HANDLE devnul_handle = INVALID_HANDLE_VALUE; + + DEBUG_EVENT dbg_debug_event; + DWORD dbg_continue_status; + bool dbg_continue_needed; + DebuggerStatus dbg_last_status; + + int wow64_target = 0; + +protected: + bool target_function_defined; + bool loop_mode; + bool attach_mode; + bool trace_debug_events; + + bool sinkhole_stds; + uint64_t mem_limit; + uint64_t cpu_aff; + +private: + // persistence related + int target_num_args; + uint64_t target_offset; + std::string target_module; + std::string target_method; + int calling_convention; + void *target_address; + void *saved_sp; + void *saved_return_address; + void **saved_args; + + uint64_t target_return_value; + + void RetrieveThreadContext(); + void CreateException(EXCEPTION_RECORD *win_exception_record, + Exception *exception); + + Exception last_exception; + // thread id of the last event + DWORD thread_id; + CONTEXT lcContext; + bool have_thread_context; + size_t allocation_granularity; + + bool force_dep; +}; + +#endif // DEBUGGER_H From 7489c6de9ac36882760c4059c59bb2b8b40f6678 Mon Sep 17 00:00:00 2001 From: Akshat Parikh <68412398+parikhakshat@users.noreply.github.com> Date: Tue, 30 Aug 2022 20:55:00 -0400 Subject: [PATCH 4/8] Revert "Remove unused variable and implement windows specific checks" This reverts commit 1a406fe5fdcc1d1e8c787b1cb858b6cc0d1d079b. --- common.cpp | 40 +- common.h | 4 +- debugger.cpp | 1917 -------------------------------------------------- debugger.h | 237 ------- 4 files changed, 20 insertions(+), 2178 deletions(-) delete mode 100644 debugger.cpp delete mode 100644 debugger.h diff --git a/common.cpp b/common.cpp index db1d7bd..6ce23b9 100755 --- a/common.cpp +++ b/common.cpp @@ -102,27 +102,25 @@ int GetIntOption(const char *name, int argc, char** argv, int default_value) { return (int)strtol(option, NULL, 0); } -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) - DWORD FindProcessId(char * process_name) - { - PROCESSENTRY32 entry; - entry.dwSize = sizeof(PROCESSENTRY32); - - HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); - - if (Process32First(snapshot, &entry) == TRUE) - { - while (Process32Next(snapshot, &entry) == TRUE) - { - if (stricmp(entry.szExeFile, process_name) == 0) - { - CloseHandle(snapshot); - return entry.th32ProcessID; - } - } - } - } -#endif +DWORD FindProcessId(char * process_name) +{ + PROCESSENTRY32 entry; + entry.dwSize = sizeof(PROCESSENTRY32); + + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); + + if (Process32First(snapshot, &entry) == TRUE) + { + while (Process32Next(snapshot, &entry) == TRUE) + { + if (stricmp(entry.szExeFile, process_name) == 0) + { + CloseHandle(snapshot); + return entry.th32ProcessID; + } + } + } +} //quoting on Windows is weird size_t ArgvEscapeWindows(char *in, char *out) { diff --git a/common.h b/common.h index 847e866..5720e6d 100755 --- a/common.h +++ b/common.h @@ -75,9 +75,7 @@ uint64_t GetCurTime(void); char *GetOption(const char *name, int argc, char** argv); void GetOptionAll(const char *name, int argc, char** argv, std::list *results); bool GetBinaryOption(const char *name, int argc, char** argv, bool default_value); -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) - DWORD FindProcessId(char * process_name); -#endif +DWORD FindProcessId(char * process_name); int GetIntOption(const char *name, int argc, char** argv, int default_value); char *ArgvToCmd(int argc, char** argv); diff --git a/debugger.cpp b/debugger.cpp deleted file mode 100644 index 04610f2..0000000 --- a/debugger.cpp +++ /dev/null @@ -1,1917 +0,0 @@ -/* -Copyright 2020 Google LLC - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -https ://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -#define _CRT_SECURE_NO_WARNINGS - -#include -#include -#include - -#include "windows.h" -#include "psapi.h" -#include "dbghelp.h" - -#include "../common.h" -#include "debugger.h" - - -#define CALLCONV_MICROSOFT_X64 0 -#define CALLCONV_THISCALL 1 -#define CALLCONV_FASTCALL 2 -#define CALLCONV_CDECL 3 -#define CALLCONV_DEFAULT 4 - -#define BREAKPOINT_UNKNOWN 0 -#define BREAKPOINT_ENTRYPOINT 1 -#define BREAKPOINT_TARGET 2 - -#define PERSIST_END_EXCEPTION 0x0F22 - -// cleans up all breakpoint structures -// does not actually remove breakpoints in target process -void Debugger::DeleteBreakpoints() { - for (auto iter = breakpoints.begin(); iter != breakpoints.end(); iter++) { - delete *iter; - } - breakpoints.clear(); -} - -void Debugger::CreateException(EXCEPTION_RECORD *win_exception_record, - Exception *exception) -{ - switch (win_exception_record->ExceptionCode) { - case EXCEPTION_BREAKPOINT: - case 0x4000001f: - exception->type = BREAKPOINT; - break; - case EXCEPTION_ACCESS_VIOLATION: - exception->type = ACCESS_VIOLATION; - break; - case EXCEPTION_ILLEGAL_INSTRUCTION: - exception->type = ILLEGAL_INSTRUCTION; - break; - case EXCEPTION_STACK_OVERFLOW: - exception->type = STACK_OVERFLOW; - break; - default: - exception->type = OTHER; - break; - } - - exception->ip = win_exception_record->ExceptionAddress; - - exception->maybe_execute_violation = false; - exception->maybe_write_violation = false; - exception->access_address = 0; - if (win_exception_record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { - if (win_exception_record->ExceptionInformation[0] == 8) { - exception->maybe_execute_violation = true; - } - if (win_exception_record->ExceptionInformation[0] == 1) { - exception->maybe_write_violation = true; - } - - exception->access_address = (void *)(win_exception_record->ExceptionInformation[1]); - } -} - -void Debugger::RetrieveThreadContext() { - if (have_thread_context) return; // already done - lcContext.ContextFlags = CONTEXT_ALL; - HANDLE thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, thread_id); - GetThreadContext(thread_handle, &lcContext); - CloseHandle(thread_handle); - have_thread_context = true; -} - -void Debugger::SaveRegisters(SavedRegisters* registers) { - RetrieveThreadContext(); - memcpy(®isters->saved_context, &lcContext, sizeof(registers->saved_context)); -} - -void Debugger::RestoreRegisters(SavedRegisters* registers) { - have_thread_context = false; - HANDLE thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, thread_id); - if (!SetThreadContext(thread_handle, &lcContext)) { - FATAL("Error restoring registers"); - } - CloseHandle(thread_handle); -} - -size_t Debugger::GetRegister(Register r) { - RetrieveThreadContext(); - -#ifdef _WIN64 - - switch (r) { - case RAX: - return lcContext.Rax; - case RCX: - return lcContext.Rcx; - case RDX: - return lcContext.Rdx; - case RBX: - return lcContext.Rbx; - case RSP: - return lcContext.Rsp; - case RBP: - return lcContext.Rbp; - case RSI: - return lcContext.Rsi; - case RDI: - return lcContext.Rdi; - case R8: - return lcContext.R8; - case R9: - return lcContext.R9; - case R10: - return lcContext.R10; - case R11: - return lcContext.R11; - case R12: - return lcContext.R12; - case R13: - return lcContext.R13; - case R14: - return lcContext.R14; - case R15: - return lcContext.R15; - case RIP: - return lcContext.Rip; - default: - FATAL("Unimplemented"); - } - -#else - - switch (r) { - case RAX: - return lcContext.Eax; - case RCX: - return lcContext.Ecx; - case RDX: - return lcContext.Edx; - case RBX: - return lcContext.Ebx; - case RSP: - return lcContext.Esp; - case RBP: - return lcContext.Ebp; - case RSI: - return lcContext.Esi; - case RDI: - return lcContext.Edi; - case RIP: - return lcContext.Eip; - default: - FATAL("Unimplemented"); -} - -#endif - -} - -void Debugger::SetRegister(Register r, size_t value) { - RetrieveThreadContext(); - -#ifdef _WIN64 - - switch (r) { - case RAX: - lcContext.Rax = value; - break; - case RCX: - lcContext.Rcx = value; - break; - case RDX: - lcContext.Rdx = value; - break; - case RBX: - lcContext.Rbx = value; - break; - case RSP: - lcContext.Rsp = value; - break; - case RBP: - lcContext.Rbp = value; - break; - case RSI: - lcContext.Rsi = value; - break; - case RDI: - lcContext.Rdi = value; - break; - case R8: - lcContext.R8 = value; - break; - case R9: - lcContext.R9 = value; - break; - case R10: - lcContext.R10 = value; - break; - case R11: - lcContext.R11 = value; - break; - case R12: - lcContext.R12 = value; - break; - case R13: - lcContext.R13 = value; - break; - case R14: - lcContext.R14 = value; - break; - case R15: - lcContext.R15 = value; - break; - case RIP: - lcContext.Rip = value; - break; - default: - FATAL("Unimplemented"); - } - -#else - - switch (r) { - case RAX: - lcContext.Eax = value; - break; - case RCX: - lcContext.Ecx = value; - break; - case RDX: - lcContext.Edx = value; - break; - case RBX: - lcContext.Ebx = value; - break; - case RSP: - lcContext.Esp = value; - break; - case RBP: - lcContext.Ebp = value; - break; - case RSI: - lcContext.Esi = value; - break; - case RDI: - lcContext.Edi = value; - break; - case RIP: - lcContext.Eip = value; - break; - default: - FATAL("Unimplemented"); - } - -#endif - - HANDLE thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, thread_id); - SetThreadContext(thread_handle, &lcContext); - CloseHandle(thread_handle); -} - - -// converts between MemoryProtection and Windows protection flags -DWORD Debugger::WindowsProtectionFlags(MemoryProtection protection) { - switch (protection) { - case READONLY: - return PAGE_READONLY; - case READWRITE: - return PAGE_READWRITE; - case READEXECUTE: - return PAGE_EXECUTE_READ; - case READWRITEEXECUTE: - return PAGE_EXECUTE_READWRITE; - default: - FATAL("Unumplemented memory protection"); - } -} - -// allocates memory within 2GB of memory region -// between region_min and region_max -void *Debugger::RemoteAllocateNear(uint64_t region_min, - uint64_t region_max, - size_t size, - MemoryProtection protection, - bool use_shared_memory) -{ - void *ret = NULL; - - // try before first - uint64_t min_address = region_max; - if (min_address < 0x80000000) min_address = 0; - else min_address -= 0x80000000; - uint64_t max_address = region_min; - if (max_address < size) max_address = 0; - else max_address -= size; - - ret = RemoteAllocateBefore(min_address, - max_address, - size, - protection); - - if (ret) return ret; - - min_address = region_max; - uint64_t address_range_max = 0xFFFFFFFFFFFFFFFFULL; - if (child_ptr_size == 4) { - address_range_max = 0xFFFFFFFFULL; - } - if ((address_range_max - 0x80000000) < region_min) { - max_address = address_range_max - size; - } else { - max_address = region_min + 0x80000000 - size; - } - - ret = RemoteAllocateAfter(min_address, - max_address, - size, - protection); - - return ret; -} - - -// allocates memory in target process as close as possible -// to max_address, but at address larger than min_address -void *Debugger::RemoteAllocateBefore(uint64_t min_address, - uint64_t max_address, - size_t size, - MemoryProtection protection) -{ - DWORD protection_flags = WindowsProtectionFlags(protection); - - MEMORY_BASIC_INFORMATION meminfobuf; - void *ret_address = NULL; - - uint64_t cur_code = max_address; - while (cur_code > min_address) { - // Don't attempt allocating on the null page - if (cur_code < 0x1000) break; - - size_t step = size; - - size_t query_ret = VirtualQueryEx(child_handle, - (LPCVOID)cur_code, - &meminfobuf, - sizeof(MEMORY_BASIC_INFORMATION)); - if (!query_ret) break; - - if (meminfobuf.State == MEM_FREE) { - if (meminfobuf.RegionSize >= size) { - size_t address = (size_t)meminfobuf.BaseAddress + - (meminfobuf.RegionSize - size); - ret_address = VirtualAllocEx(child_handle, - (LPVOID)address, - size, - MEM_COMMIT | MEM_RESERVE, - protection_flags); - if (ret_address) { - if (((size_t)ret_address >= min_address) && - ((size_t)ret_address <= max_address)) { - return ret_address; - } else { - return NULL; - } - } - } else { - step = size - meminfobuf.RegionSize; - } - } - - cur_code = (size_t)meminfobuf.BaseAddress; - if (cur_code < step) break; - else cur_code -= step; - } - - return ret_address; -} - -// allocates memory in target process as close as possible -// to min_address, but not higher than max_address -void *Debugger::RemoteAllocateAfter(uint64_t min_address, - uint64_t max_address, - size_t size, - MemoryProtection protection) -{ - DWORD protection_flags = WindowsProtectionFlags(protection); - - MEMORY_BASIC_INFORMATION meminfobuf; - void *ret_address = NULL; - - uint64_t cur_code = min_address; - while (cur_code < max_address) { - size_t query_ret = VirtualQueryEx(child_handle, - (LPCVOID)cur_code, - &meminfobuf, - sizeof(MEMORY_BASIC_INFORMATION)); - if (!query_ret) break; - - if (meminfobuf.State == MEM_FREE) { - size_t region_address = (size_t)meminfobuf.BaseAddress; - size_t region_size = meminfobuf.RegionSize; - // make sure we are allocating on an address that - // is aligned according to allocation_granularity - size_t alignment = region_address & (allocation_granularity - 1); - if (alignment) { - size_t offset = (allocation_granularity - alignment); - region_address += offset; - if (region_size > offset) { - region_size -= offset; - } else { - region_size = 0; - } - } - if (region_size >= size) { - ret_address = VirtualAllocEx(child_handle, - (LPVOID)region_address, - size, - MEM_COMMIT | MEM_RESERVE, - protection_flags); - if (ret_address) { - if (((size_t)ret_address >= min_address) && - ((size_t)ret_address <= max_address)) { - return ret_address; - } else { - return NULL; - } - } - } - } - - cur_code = (size_t)meminfobuf.BaseAddress + meminfobuf.RegionSize; - } - - return ret_address; -} - -void Debugger::RemoteFree(void *address, size_t size) { - if (!child_handle) return; - VirtualFreeEx(child_handle, address, 0, MEM_RELEASE); -} - -void Debugger::RemoteWrite(void *address, void *buffer, size_t size) { - SIZE_T size_written; - if (WriteProcessMemory( - child_handle, - address, - buffer, - size, - &size_written)) - { - return; - } - - // we need to (a) read page permissions - // (b) make it writable, and (c) restore permissions - DWORD oldProtect; - if (!VirtualProtectEx(child_handle, - address, - size, - PAGE_READWRITE, - &oldProtect)) - { - FATAL("Error in VirtualProtectEx"); - } - - if (!WriteProcessMemory( - child_handle, - address, - buffer, - size, - &size_written)) - { - FATAL("Error writing target memory\n"); - } - - DWORD ignore; - if (!VirtualProtectEx(child_handle, - address, - size, - oldProtect, - &ignore)) - { - FATAL("Error in VirtualProtectEx"); - } -} - -void Debugger::RemoteRead(void *address, void *buffer, size_t size) { - SIZE_T size_read; - if (!ReadProcessMemory( - child_handle, - address, - buffer, - size, - &size_read)) - { - FATAL("Error reading target memory\n"); - } -} - -void Debugger::RemoteProtect(void *address, size_t size, MemoryProtection protect) { - DWORD protection_flags = WindowsProtectionFlags(protect); - DWORD old_protect; - - if (!VirtualProtectEx(child_handle, - address, - size, - protection_flags, - &old_protect)) - { - FATAL("Could not apply memory protection"); - } -} - - -// detects executable memory regions in the module -// makes them non-executable -// and copies code out into this process -void Debugger::ExtractCodeRanges(void *module_base, - size_t min_address, - size_t max_address, - std::list *executable_ranges, - size_t *code_size) -{ - LPCVOID end_address = (char *)max_address; - LPCVOID cur_address = module_base; - MEMORY_BASIC_INFORMATION meminfobuf; - - AddressRange newRange; - - for (auto iter = executable_ranges->begin(); - iter != executable_ranges->end(); iter++) - { - free(iter->data); - } - executable_ranges->clear(); - *code_size = 0; - - while (cur_address < end_address) { - size_t ret = VirtualQueryEx(child_handle, - cur_address, - &meminfobuf, - sizeof(MEMORY_BASIC_INFORMATION)); - if (!ret) break; - - if (meminfobuf.Protect & 0xF0) { - // printf("%p, %llx, %lx\n", meminfobuf.BaseAddress, meminfobuf.RegionSize, meminfobuf.Protect); - - SIZE_T size_read; - newRange.data = (char *)malloc(meminfobuf.RegionSize); - if (!ReadProcessMemory(child_handle, - meminfobuf.BaseAddress, - newRange.data, - meminfobuf.RegionSize, - &size_read)) - { - FATAL("Error in ReadProcessMemory"); - } - if (size_read != meminfobuf.RegionSize) { - FATAL("Error in ReadProcessMemory"); - } - - uint8_t low = meminfobuf.Protect & 0xFF; - low = low >> 4; - DWORD newProtect = (meminfobuf.Protect & 0xFFFFFF00) + low; - DWORD oldProtect; - if (!VirtualProtectEx(child_handle, - meminfobuf.BaseAddress, - meminfobuf.RegionSize, - newProtect, - &oldProtect)) - { - FATAL("Error in VirtualProtectEx"); - } - - newRange.from = (size_t)meminfobuf.BaseAddress; - newRange.to = (size_t)meminfobuf.BaseAddress + meminfobuf.RegionSize; - executable_ranges->push_back(newRange); - - *code_size += newRange.to - newRange.from; - } - - cur_address = (char *)meminfobuf.BaseAddress + meminfobuf.RegionSize; - } -} - -// sets all pages containing (previously detected) -// code to non-executable -void Debugger::ProtectCodeRanges(std::list *executable_ranges) { - MEMORY_BASIC_INFORMATION meminfobuf; - - for (auto iter = executable_ranges->begin(); - iter != executable_ranges->end(); iter++) - { - size_t ret = VirtualQueryEx(child_handle, - (void *)iter->from, - &meminfobuf, - sizeof(MEMORY_BASIC_INFORMATION)); - - // if the module was already instrumented, everything must be the same as before - if (!ret) { - FATAL("Error in ProtectCodeRanges." - "Target incompatible with persist_instrumentation_data"); - } - if (iter->from != (size_t)meminfobuf.BaseAddress) { - FATAL("Error in ProtectCodeRanges." - "Target incompatible with persist_instrumentation_data"); - } - if (iter->to != (size_t)meminfobuf.BaseAddress + meminfobuf.RegionSize) { - FATAL("Error in ProtectCodeRanges." - "Target incompatible with persist_instrumentation_data"); - } - if (!(meminfobuf.Protect & 0xF0)) { - FATAL("Error in ProtectCodeRanges." - "Target incompatible with persist_instrumentation_data"); - } - - uint8_t low = meminfobuf.Protect & 0xFF; - low = low >> 4; - DWORD newProtect = (meminfobuf.Protect & 0xFFFFFF00) + low; - DWORD oldProtect; - if (!VirtualProtectEx(child_handle, - meminfobuf.BaseAddress, - meminfobuf.RegionSize, - newProtect, - &oldProtect)) - { - FATAL("Error in VirtualProtectEx"); - } - } -} - -void Debugger::PatchPointersRemote(size_t min_address, size_t max_address, std::unordered_map& search_replace) { - if (child_ptr_size == 4) { - PatchPointersRemoteT(min_address, max_address, search_replace); - } else { - PatchPointersRemoteT(min_address, max_address, search_replace); - } -} - -template -void Debugger::PatchPointersRemoteT(size_t min_address, size_t max_address, std::unordered_map& search_replace) { - size_t module_size = max_address - min_address; - char* buf = (char *)malloc(module_size); - RemoteRead((void *)min_address, buf, module_size); - - size_t remote_address = min_address; - for (size_t i = 0; i < (module_size - child_ptr_size + 1); i++) { - T ptr = *(T *)(buf + i); - auto iter = search_replace.find(ptr); - if (iter != search_replace.end()) { - // printf("patching entry %zx at address %zx\n", (size_t)ptr, remote_address); - T fixed_ptr = (T)iter->second; - RemoteWrite((void *)remote_address, &fixed_ptr, child_ptr_size); - } - remote_address += 1; - } - - free(buf); -} - -// returns an array of handles for all modules loaded in the target process -DWORD Debugger::GetLoadedModules(HMODULE **modules) { - DWORD module_handle_storage_size = 1024 * sizeof(HMODULE); - HMODULE *module_handles = (HMODULE *)malloc(module_handle_storage_size); - DWORD hmodules_size; - while (true) { - if (!EnumProcessModulesEx(child_handle, - module_handles, - module_handle_storage_size, - &hmodules_size, - LIST_MODULES_ALL)) - { - FATAL("EnumProcessModules failed, %x\n", GetLastError()); - } - if (hmodules_size <= module_handle_storage_size) break; - module_handle_storage_size *= 2; - module_handles = (HMODULE *)realloc(module_handles, module_handle_storage_size); - } - *modules = module_handles; - return hmodules_size / sizeof(HMODULE); -} - -// parses PE headers and gets the module entypoint -void *Debugger::GetModuleEntrypoint(void *base_address) { - unsigned char headers[4096]; - SIZE_T num_read = 0; - if (!ReadProcessMemory(child_handle, base_address, headers, 4096, &num_read) || - (num_read != 4096)) - { - FATAL("Error reading target memory\n"); - } - DWORD pe_offset; - pe_offset = *((DWORD *)(headers + 0x3C)); - unsigned char *pe = headers + pe_offset; - DWORD signature = *((DWORD *)pe); - if (signature != 0x00004550) { - FATAL("PE signature error\n"); - } - pe = pe + 0x18; - WORD magic = *((WORD *)pe); - if ((magic != 0x10b) && (magic != 0x20b)) { - FATAL("Unknown PE magic value\n"); - } - DWORD entrypoint_offset = *((DWORD *)(pe + 16)); - if (entrypoint_offset == 0) return NULL; - return (char *)base_address + entrypoint_offset; -} - -// parses PE headers and gets the image size -DWORD Debugger::GetImageSize(void *base_address) { - unsigned char headers[4096]; - SIZE_T num_read = 0; - if (!ReadProcessMemory(child_handle, base_address, headers, 4096, &num_read) || - (num_read != 4096)) - { - FATAL("Error reading target memory\n"); - } - DWORD pe_offset; - pe_offset = *((DWORD *)(headers + 0x3C)); - unsigned char *pe = headers + pe_offset; - DWORD signature = *((DWORD *)pe); - if (signature != 0x00004550) { - FATAL("PE signature error\n"); - } - pe = pe + 0x18; - WORD magic = *((WORD *)pe); - if ((magic != 0x10b) && (magic != 0x20b)) { - FATAL("Unknown PE magic value\n"); - } - DWORD SizeOfImage = *((DWORD *)(pe + 56)); - return SizeOfImage; -} - - -// parses PE headers and gets the image size -void Debugger::GetImageSize(void *base_address, size_t *min_address, size_t *max_address) { - *min_address = (size_t)base_address; - DWORD SizeOfImage = GetImageSize(base_address); - *max_address = *min_address + SizeOfImage; -} - -// adds a one-time breakpoint at a specified address -// type, is an arbitrary int -// that can be accessed later when the breakpoint gets hit -void Debugger::AddBreakpoint(void *address, int type) { - Breakpoint *new_breakpoint = new Breakpoint; - SIZE_T rwsize = 0; - if (!ReadProcessMemory(child_handle, address, &(new_breakpoint->original_opcode), 1, &rwsize) || - (rwsize != 1)) { - FATAL("Error reading target memory\n"); - } - rwsize = 0; - unsigned char cc = 0xCC; - if (!WriteProcessMemory(child_handle, address, &cc, 1, &rwsize) || (rwsize != 1)) { - FATAL("Error writing target memory\n"); - } - FlushInstructionCache(child_handle, address, 1); - new_breakpoint->address = address; - new_breakpoint->type = type; - breakpoints.push_back(new_breakpoint); -} - -// damn it Windows, why don't you have a GetProcAddress -// that works on another process -DWORD Debugger::GetProcOffset(HMODULE module, const char *name) { - char* base_of_dll = (char*)module; - DWORD size_of_image = GetImageSize(base_of_dll); - - // try the exported symbols next - char* modulebuf = (char*)malloc(size_of_image); - SIZE_T num_read; - if (!ReadProcessMemory(child_handle, base_of_dll, modulebuf, size_of_image, &num_read) || - (num_read != size_of_image)) - { - FATAL("Error reading target memory\n"); - } - - DWORD pe_offset; - pe_offset = *((DWORD *)(modulebuf + 0x3C)); - char *pe = modulebuf + pe_offset; - DWORD signature = *((DWORD *)pe); - if (signature != 0x00004550) { - free(modulebuf); - return 0; - } - pe = pe + 0x18; - WORD magic = *((WORD *)pe); - DWORD exporttableoffset; - if (magic == 0x10b) { - exporttableoffset = *(DWORD *)(pe + 96); - } else if (magic == 0x20b) { - exporttableoffset = *(DWORD *)(pe + 112); - } else { - free(modulebuf); - return 0; - } - - if (!exporttableoffset) { - free(modulebuf); - return 0; - } - - char *exporttable = modulebuf + exporttableoffset; - - DWORD numentries = *(DWORD *)(exporttable + 24); - DWORD addresstableoffset = *(DWORD *)(exporttable + 28); - DWORD nameptrtableoffset = *(DWORD *)(exporttable + 32); - DWORD ordinaltableoffset = *(DWORD *)(exporttable + 36); - DWORD *nameptrtable = (DWORD *)(modulebuf + nameptrtableoffset); - WORD *ordinaltable = (WORD *)(modulebuf + ordinaltableoffset); - DWORD *addresstable = (DWORD *)(modulebuf + addresstableoffset); - - DWORD i; - for (i = 0; i < numentries; i++) { - char *nameptr = modulebuf + nameptrtable[i]; - if (strcmp(name, nameptr) == 0) break; - } - - if (i == numentries) { - free(modulebuf); - return 0; - } - - WORD oridnal = ordinaltable[i]; - DWORD offset = addresstable[oridnal]; - - free(modulebuf); - return offset; -} - -// Gets the registered safe exception handlers for the module -void Debugger::GetExceptionHandlers(size_t module_haeder, std::unordered_set & handlers) { - // only present on x86 - if (child_ptr_size != 4) return; - - DWORD size_of_image = GetImageSize((void *)module_haeder); - - char* modulebuf = (char*)malloc(size_of_image); - SIZE_T num_read; - if (!ReadProcessMemory(child_handle, (void *)module_haeder, modulebuf, size_of_image, &num_read) || - (num_read != size_of_image)) - { - FATAL("Error reading target memory\n"); - } - - DWORD pe_offset; - pe_offset = *((DWORD*)(modulebuf + 0x3C)); - char* pe = modulebuf + pe_offset; - DWORD signature = *((DWORD*)pe); - if (signature != 0x00004550) { - free(modulebuf); - return; - } - pe = pe + 0x18; - WORD magic = *((WORD*)pe); - DWORD lc_offset; - DWORD lc_size; - if (magic == 0x10b) { - lc_offset = *(DWORD*)(pe + 176); - lc_size = *(DWORD*)(pe + 180); - } else if (magic == 0x20b) { - lc_offset = *(DWORD*)(pe + 192); - lc_size = *(DWORD*)(pe + 196); - } else { - free(modulebuf); - return; - } - - if (!lc_offset || (lc_size != 64)) { - free(modulebuf); - return; - } - - char* lc = modulebuf + lc_offset; - - size_t seh_table_address; - DWORD seh_count; - if (magic == 0x10b) { - seh_table_address = *(DWORD*)(lc + 64); - seh_count = *(DWORD*)(lc + 68); - } else if (magic == 0x20b) { - seh_table_address = *(uint64_t*)(lc + 96); - seh_count = *(DWORD*)(lc + 104); - } else { - free(modulebuf); - return; - } - - size_t seh_table_offset = seh_table_address - module_haeder; - - DWORD* seh_table = (DWORD *)(modulebuf + seh_table_offset); - for (DWORD i = 0; i < seh_count; i++) { - handlers.insert(module_haeder + seh_table[i]); - } - - free(modulebuf); -} - -// attempt to obtain the address of target function -// in various ways -char *Debugger::GetTargetAddress(HMODULE module) { - char* base_of_dll = (char *)module; - - // if persist_offset is defined, use that - if (target_offset) { - return base_of_dll + target_offset; - } - - DWORD offset = GetProcOffset(module, target_method.c_str()); - if (offset) { - return (char *)module + offset; - } - - // finally, try the debug symbols - char *method_address = NULL; - char base_name[MAX_PATH]; - GetModuleBaseNameA(child_handle, - module, - (LPSTR)(&base_name), - sizeof(base_name)); - - char module_path[MAX_PATH]; - if (!GetModuleFileNameExA(child_handle, - module, - module_path, - sizeof(module_path))) - return NULL; - - ULONG64 buffer[(sizeof(SYMBOL_INFO) + - MAX_SYM_NAME * sizeof(TCHAR) + - sizeof(ULONG64) - 1) / - sizeof(ULONG64)]; - PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; - pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); - pSymbol->MaxNameLen = MAX_SYM_NAME; - SymInitialize(child_handle, NULL, false); - DWORD64 sym_base_address = SymLoadModuleEx(child_handle, - NULL, - module_path, - NULL, - 0, - 0, - NULL, - 0); - if (SymFromName(child_handle, target_method.c_str(), pSymbol)) { - target_offset = (unsigned long)(pSymbol->Address - sym_base_address); - method_address = base_of_dll + target_offset; - } - SymCleanup(child_handle); - - return method_address; -} - -// called when a module gets loaded -void Debugger::OnModuleLoaded(void *module, char *module_name) { - // printf("In on_module_loaded, name: %s, base: %p\n", module_name, module_info.lpBaseOfDll); - - if (target_function_defined && _stricmp(module_name, target_module.c_str()) == 0) { - target_address = GetTargetAddress((HMODULE)module); - if (!target_address) { - FATAL("Error determining target method address\n"); - } - - AddBreakpoint(target_address, BREAKPOINT_TARGET); - } -} - -// called when a module gets unloaded -void Debugger::OnModuleUnloaded(void *module) { } - -// reads numitems entries from stack in remote process -// from stack_addr -// into buffer -void Debugger::ReadStack(void *stack_addr, void **buffer, size_t numitems) { - SIZE_T numrw = 0; -#ifdef _WIN64 - if (wow64_target) { - uint32_t *buf32 = (uint32_t *)malloc(numitems * child_ptr_size); - ReadProcessMemory(child_handle, stack_addr, buf32, numitems * child_ptr_size, &numrw); - for (size_t i = 0; i < numitems; i++) { - buffer[i] = (void *)((size_t)buf32[i]); - } - free(buf32); - return; - } -#endif - ReadProcessMemory(child_handle, stack_addr, buffer, numitems * child_ptr_size, &numrw); -} - -// writes numitems entries to stack in remote process -// from buffer -// into stack_addr -void Debugger::WriteStack(void *stack_addr, void **buffer, size_t numitems) { - SIZE_T numrw = 0; -#ifdef _WIN64 - if (wow64_target) { - uint32_t *buf32 = (uint32_t *)malloc(numitems * child_ptr_size); - for (size_t i = 0; i < numitems; i++) { - buf32[i] = (uint32_t)((size_t)buffer[i]); - } - WriteProcessMemory(child_handle, stack_addr, buf32, numitems * child_ptr_size, &numrw); - free(buf32); - return; - } -#endif - WriteProcessMemory(child_handle, stack_addr, buffer, numitems * child_ptr_size, &numrw); -} - -// called when the target method is reached -void Debugger::HandleTargetReachedInternal() { - // printf("in OnTargetMethod\n"); - - SIZE_T numrw = 0; - - CONTEXT lcContext; - lcContext.ContextFlags = CONTEXT_ALL; - HANDLE thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, thread_id); - GetThreadContext(thread_handle, &lcContext); - - // read out and save the params -#ifdef _WIN64 - saved_sp = (void *)lcContext.Rsp; -#else - saved_sp = (void *)lcContext.Esp; -#endif - - saved_return_address = 0; - ReadProcessMemory(child_handle, saved_sp, &saved_return_address, child_ptr_size, &numrw); - - if (loop_mode) { - switch (calling_convention) { -#ifdef _WIN64 - case CALLCONV_DEFAULT: - case CALLCONV_MICROSOFT_X64: - if (target_num_args > 0) saved_args[0] = (void *)lcContext.Rcx; - if (target_num_args > 1) saved_args[1] = (void *)lcContext.Rdx; - if (target_num_args > 2) saved_args[2] = (void *)lcContext.R8; - if (target_num_args > 3) saved_args[3] = (void *)lcContext.R9; - if (target_num_args > 4) { - ReadStack((void *)(lcContext.Rsp + 5 * child_ptr_size), saved_args + 4, target_num_args - 4); - } - break; - case CALLCONV_CDECL: - if (target_num_args > 0) { - ReadStack((void *)(lcContext.Rsp + child_ptr_size), saved_args, target_num_args); - } - break; - case CALLCONV_FASTCALL: - if (target_num_args > 0) saved_args[0] = (void *)lcContext.Rcx; - if (target_num_args > 1) saved_args[1] = (void *)lcContext.Rdx; - if (target_num_args > 3) { - ReadStack((void *)(lcContext.Rsp + child_ptr_size), saved_args + 2, target_num_args - 2); - } - break; - case CALLCONV_THISCALL: - if (target_num_args > 0) saved_args[0] = (void *)lcContext.Rcx; - if (target_num_args > 3) { - ReadStack((void *)(lcContext.Rsp + child_ptr_size), saved_args + 1, target_num_args - 1); - } - break; -#else - case CALLCONV_MICROSOFT_X64: - FATAL("X64 callong convention not supported for 32-bit targets"); - break; - case CALLCONV_DEFAULT: - case CALLCONV_CDECL: - if (target_num_args > 0) { - ReadStack((void *)(lcContext.Esp + child_ptr_size), saved_args, target_num_args); - } - break; - case CALLCONV_FASTCALL: - if (target_num_args > 0) saved_args[0] = (void *)lcContext.Ecx; - if (target_num_args > 1) saved_args[1] = (void *)lcContext.Edx; - if (target_num_args > 3) { - ReadStack((void *)(lcContext.Esp + child_ptr_size), saved_args + 2, target_num_args - 2); - } - break; - case CALLCONV_THISCALL: - if (target_num_args > 0) saved_args[0] = (void *)lcContext.Ecx; - if (target_num_args > 3) { - ReadStack((void *)(lcContext.Esp + child_ptr_size), saved_args + 1, target_num_args - 1); - } - break; -#endif - default: - break; - } - - // todo store any target-specific additional context here - } - - // modify the return address on the stack so that an exception is triggered - // when the target function finishes executing - // another option would be to allocate a block of executable memory - // and point return address over there, but this is quicker - size_t return_address = PERSIST_END_EXCEPTION; - WriteProcessMemory(child_handle, saved_sp, &return_address, child_ptr_size, &numrw); - - CloseHandle(thread_handle); - - if (!target_reached) { - target_reached = true; - OnTargetMethodReached(); - } -} - -// called every time the target method returns -void Debugger::HandleTargetEnded() { - // printf("in OnTargetMethodEnded\n"); - - CONTEXT lcContext; - lcContext.ContextFlags = CONTEXT_ALL; - HANDLE thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, thread_id); - GetThreadContext(thread_handle, &lcContext); - -#ifdef _WIN64 - target_return_value = (uint64_t)lcContext.Rax; -#else - target_return_value = (uint64_t)lcContext.Eax; -#endif - - if (loop_mode) { - // restore params -#ifdef _WIN64 - lcContext.Rip = (size_t)target_address; - lcContext.Rsp = (size_t)saved_sp; -#else - lcContext.Eip = (size_t)target_address; - lcContext.Esp = (size_t)saved_sp; -#endif - - // restore return address as it might have been overwritten by instrumentation - SIZE_T numrw = 0; - size_t return_address = PERSIST_END_EXCEPTION; - WriteProcessMemory(child_handle, saved_sp, &return_address, child_ptr_size, &numrw); - - switch (calling_convention) { -#ifdef _WIN64 - case CALLCONV_DEFAULT: - case CALLCONV_MICROSOFT_X64: - if (target_num_args > 0) lcContext.Rcx = (size_t)saved_args[0]; - if (target_num_args > 1) lcContext.Rdx = (size_t)saved_args[1]; - if (target_num_args > 2) lcContext.R8 = (size_t)saved_args[2]; - if (target_num_args > 3) lcContext.R9 = (size_t)saved_args[3]; - if (target_num_args > 4) { - WriteStack((void *)(lcContext.Rsp + 5 * child_ptr_size), saved_args + 4, target_num_args - 4); - } - break; - case CALLCONV_CDECL: - if (target_num_args > 0) { - WriteStack((void *)(lcContext.Rsp + child_ptr_size), saved_args, target_num_args); - } - break; - case CALLCONV_FASTCALL: - if (target_num_args > 0) lcContext.Rcx = (size_t)saved_args[0]; - if (target_num_args > 1) lcContext.Rdx = (size_t)saved_args[1]; - if (target_num_args > 3) { - WriteStack((void *)(lcContext.Rsp + child_ptr_size), saved_args + 2, target_num_args - 2); - } - break; - case CALLCONV_THISCALL: - if (target_num_args > 0) lcContext.Rcx = (size_t)saved_args[0]; - if (target_num_args > 3) { - WriteStack((void *)(lcContext.Rsp + child_ptr_size), saved_args + 1, target_num_args - 1); - } - break; -#else - case CALLCONV_MICROSOFT_X64: - FATAL("X64 callong convention not supported for 32-bit targets"); - break; - case CALLCONV_DEFAULT: - case CALLCONV_CDECL: - if (target_num_args > 0) { - WriteStack((void *)(lcContext.Esp + child_ptr_size), saved_args, target_num_args); - } - break; - case CALLCONV_FASTCALL: - if (target_num_args > 0) lcContext.Ecx = (size_t)saved_args[0]; - if (target_num_args > 1) lcContext.Edx = (size_t)saved_args[1]; - if (target_num_args > 3) { - WriteStack((void *)(lcContext.Esp + child_ptr_size), saved_args + 2, target_num_args - 2); - } - break; - case CALLCONV_THISCALL: - if (target_num_args > 0) lcContext.Ecx = (size_t)saved_args[0]; - if (target_num_args > 3) { - WriteStack((void *)(lcContext.Esp + child_ptr_size), saved_args + 1, target_num_args - 1); - } - break; -#endif - default: - break; - } - - // todo restore any target-specific additional context here - - } else { /* loop_mode == false */ - -#ifdef _WIN64 - lcContext.Rip = (size_t)saved_return_address; -#else - lcContext.Eip = (size_t)saved_return_address; -#endif - - // restore target entry breakpoint - // note that this time, the breakpoint address might be - // in instrumented code - // so we need to use translated address - AddBreakpoint((void *)GetTranslatedAddress((size_t)target_address), - BREAKPOINT_TARGET); - } - - SetThreadContext(thread_handle, &lcContext); - CloseHandle(thread_handle); -} - -// called when process entrypoint gets reached -void Debugger::OnEntrypoint() { - // printf("Entrypoint\n"); - - HMODULE *module_handles = NULL; - DWORD num_modules = GetLoadedModules(&module_handles); - for (DWORD i = 0; i < num_modules; i++) { - char base_name[MAX_PATH]; - GetModuleBaseNameA(child_handle, module_handles[i], (LPSTR)(&base_name), sizeof(base_name)); - if(trace_debug_events) - printf("Debugger: Loaded module %s at %p\n", base_name, (void *)module_handles[i]); - OnModuleLoaded((void *)module_handles[i], base_name); - } - if (module_handles) free(module_handles); - - child_entrypoint_reached = true; - - if (trace_debug_events) printf("Debugger: Process entrypoint reached\n"); -} - -// called when the debugger hits a breakpoint -int Debugger::HandleDebuggerBreakpoint(void *address) { - int ret = BREAKPOINT_UNKNOWN; - SIZE_T rwsize = 0; - - Breakpoint *breakpoint = NULL, *tmp_breakpoint; - for (auto iter = breakpoints.begin(); iter != breakpoints.end(); iter++) { - tmp_breakpoint = *iter; - if (tmp_breakpoint->address == address) { - breakpoint = tmp_breakpoint; - breakpoints.erase(iter); - break; - } - } - - if (!breakpoint) return ret; - - // restore address - if (!WriteProcessMemory(child_handle, address, &breakpoint->original_opcode, 1, &rwsize) || - (rwsize != 1)) - { - FATAL("Error writing child memory\n"); - } - FlushInstructionCache(child_handle, address, 1); - // restore context - CONTEXT lcContext; - lcContext.ContextFlags = CONTEXT_ALL; - HANDLE thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, thread_id); - GetThreadContext(thread_handle, &lcContext); -#ifdef _WIN64 - lcContext.Rip--; -#else - lcContext.Eip--; -#endif - SetThreadContext(thread_handle, &lcContext); - CloseHandle(thread_handle); - // handle breakpoint - switch (breakpoint->type) { - case BREAKPOINT_ENTRYPOINT: - OnEntrypoint(); - break; - case BREAKPOINT_TARGET: - if (trace_debug_events) printf("Target method reached\n"); - HandleTargetReachedInternal(); - break; - default: - break; - } - - // return the brekpoint type - ret = breakpoint->type; - - // delete the breakpoint object - free(breakpoint); - - return ret; -} - -// called when a dll gets loaded -void Debugger::HandleDllLoadInternal(LOAD_DLL_DEBUG_INFO *LoadDll) { - // Don't do anything until the processentrypoint is reached. - // Before that time we can't do much anyway, a lot of calls are going to fail - // Modules loaded before entrypoint is reached are going to be enumerated at that time - if (child_entrypoint_reached) { - char filename[MAX_PATH]; - GetFinalPathNameByHandleA(LoadDll->hFile, (LPSTR)(&filename), sizeof(filename), 0); - char *base_name = strrchr(filename, '\\'); - if (base_name) base_name += 1; - else base_name = filename; - if (trace_debug_events) - printf("Debugger: Loaded module %s at %p\n", - base_name, - (void *)LoadDll->lpBaseOfDll); - OnModuleLoaded(LoadDll->lpBaseOfDll, base_name); - } -} - -// called when a process gets created -// or attached to -void Debugger::OnProcessCreated() { - CREATE_PROCESS_DEBUG_INFO *info = &dbg_debug_event.u.CreateProcessInfo; - - if (attach_mode) { - // assume entrypoint has been reached already - child_handle = info->hProcess; - child_thread_handle = info->hThread; - child_entrypoint_reached = true; - GetProcessPlatform(); - - // In case of attaching to an existing process - // the dll load event for the main module - // will *not* be generated. - // Handle the main module load below - char filename[MAX_PATH]; - GetFinalPathNameByHandleA(info->hFile, (LPSTR)(&filename), sizeof(filename), 0); - char* base_name = strrchr(filename, '\\'); - if (base_name) base_name += 1; - else base_name = filename; - if (trace_debug_events) - printf("Debugger: Loaded module %s at %p\n", - base_name, - (void*)info->lpBaseOfImage); - OnModuleLoaded(info->lpBaseOfImage, base_name); - - } else { - // add a brekpoint to the process entrypoint - void *entrypoint = GetModuleEntrypoint(info->lpBaseOfImage); - AddBreakpoint(entrypoint, BREAKPOINT_ENTRYPOINT); - } -} - -// called when an exception in the target occurs -DebuggerStatus Debugger::HandleExceptionInternal(EXCEPTION_RECORD *exception_record) -{ - CreateException(exception_record, &last_exception); - - // note: instrumentation could have placed breakpoints - // on the same addresses as debugger - // handle one-time debugger breakpoints first - if ((exception_record->ExceptionCode == EXCEPTION_BREAKPOINT) || - (exception_record->ExceptionCode == 0x4000001f)) - { - void *address = exception_record->ExceptionAddress; - // printf("Breakpoint at address %p\n", address); - int breakpoint_type = HandleDebuggerBreakpoint(address); - if (breakpoint_type == BREAKPOINT_TARGET) { - return DEBUGGER_TARGET_START; - } else if (breakpoint_type != BREAKPOINT_UNKNOWN) { - return DEBUGGER_CONTINUE; - } - } - - // check if cleient can handle it - if (OnException(&last_exception)) { - return DEBUGGER_CONTINUE; - } - - // don't print exceptions handled by clients - if (trace_debug_events) - printf("Debugger: Exception %x at address %p\n", - exception_record->ExceptionCode, - exception_record->ExceptionAddress); - - switch (exception_record->ExceptionCode) - { - case EXCEPTION_BREAKPOINT: - case 0x4000001f: //STATUS_WX86_BREAKPOINT - // not handled above - dbg_continue_status = DBG_EXCEPTION_NOT_HANDLED; - return DEBUGGER_CONTINUE; - - case EXCEPTION_ACCESS_VIOLATION: { - if (target_function_defined && - ((size_t)exception_record->ExceptionAddress == PERSIST_END_EXCEPTION)) - { - if (trace_debug_events) printf("Debugger: Persistence method ended\n"); - HandleTargetEnded(); - return DEBUGGER_TARGET_END; - } else { - // Debug(&DebugEv->u.Exception.ExceptionRecord); - dbg_continue_status = DBG_EXCEPTION_NOT_HANDLED; - return DEBUGGER_CRASHED; - } - break; - } - - case EXCEPTION_ILLEGAL_INSTRUCTION: - case EXCEPTION_PRIV_INSTRUCTION: - case EXCEPTION_INT_DIVIDE_BY_ZERO: - case EXCEPTION_STACK_OVERFLOW: - case STATUS_HEAP_CORRUPTION: - case STATUS_STACK_BUFFER_OVERRUN: - case STATUS_FATAL_APP_EXIT: - dbg_continue_status = DBG_EXCEPTION_NOT_HANDLED; - return DEBUGGER_CRASHED; - break; - - default: - if (trace_debug_events) - printf("Unhandled exception %x\n", exception_record->ExceptionCode); - dbg_continue_status = DBG_EXCEPTION_NOT_HANDLED; - return DEBUGGER_CONTINUE; - } -} - -// standard debugger loop that listens to events in the target process -DebuggerStatus Debugger::DebugLoop(uint32_t timeout, bool killing) -{ - DebuggerStatus ret; - bool alive = true; - - if (dbg_continue_needed) { - ContinueDebugEvent(dbg_debug_event.dwProcessId, - dbg_debug_event.dwThreadId, - dbg_continue_status); - } - - LPDEBUG_EVENT DebugEv = &dbg_debug_event; - - while (alive) - { - have_thread_context = false; - - uint64_t begin_time = GetCurTime(); - BOOL wait_ret = WaitForDebugEvent(DebugEv, 100); - uint64_t end_time = GetCurTime(); - - uint64_t time_elapsed = end_time - begin_time; - timeout = ((uint64_t)timeout >= time_elapsed) ? timeout - (uint32_t)time_elapsed : 0; - - //printf("timeout: %u\n", timeout); - // printf("time: %lld\n", get_cur_time_us()); - - if (wait_ret) { - dbg_continue_needed = true; - } else { - dbg_continue_needed = false; - } - - if (timeout == 0) { - if(attach_mode) { - return DEBUGGER_PROCESS_EXIT; - } else { - return DEBUGGER_HANGED; - } - } - - if (!wait_ret) { - //printf("WaitForDebugEvent returned 0\n"); - continue; - } - - dbg_continue_status = DBG_CONTINUE; - - thread_id = DebugEv->dwThreadId; - - //printf("eventCode: %x\n", DebugEv->dwDebugEventCode); - - switch (DebugEv->dwDebugEventCode) - { - case EXCEPTION_DEBUG_EVENT: - if (!killing) { - ret = HandleExceptionInternal(&DebugEv->u.Exception.ExceptionRecord); - if (ret == DEBUGGER_CRASHED) OnCrashed(&last_exception); - if (ret != DEBUGGER_CONTINUE) return ret; - } else { - dbg_continue_status = DBG_EXCEPTION_NOT_HANDLED; - } - break; - - case CREATE_THREAD_DEBUG_EVENT: - break; - - case CREATE_PROCESS_DEBUG_EVENT: { - if (trace_debug_events) printf("Debugger: Process created or attached\n"); - OnProcessCreated(); - CloseHandle(DebugEv->u.CreateProcessInfo.hFile); - break; - } - - case EXIT_THREAD_DEBUG_EVENT: - break; - - case EXIT_PROCESS_DEBUG_EVENT: - if (trace_debug_events) printf("Debugger: Process exit\n"); - OnProcessExit(); - alive = false; - break; - - case LOAD_DLL_DEBUG_EVENT: { - if(!killing) HandleDllLoadInternal(&DebugEv->u.LoadDll); - CloseHandle(DebugEv->u.LoadDll.hFile); - break; - } - - case UNLOAD_DLL_DEBUG_EVENT: - if (trace_debug_events) - printf("Debugger: Unloaded module from %p\n", DebugEv->u.UnloadDll.lpBaseOfDll); - OnModuleUnloaded(DebugEv->u.UnloadDll.lpBaseOfDll); - break; - - default: - break; - } - - ContinueDebugEvent(DebugEv->dwProcessId, - DebugEv->dwThreadId, - dbg_continue_status); - } - - return DEBUGGER_PROCESS_EXIT; -} - -// starts the target process -void Debugger::StartProcess(char *cmd) { - dbg_continue_needed = false; - - STARTUPINFOA si; - STARTUPINFOEXA si_ex; - LPSTARTUPINFOA si_ptr; - LPSTARTUPINFOA si_basic_ptr; - LPPROC_THREAD_ATTRIBUTE_LIST attr_list_buf = NULL; - - DWORD creation_flags = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS; - - PROCESS_INFORMATION pi; - HANDLE hJob = NULL; - JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_limit; - - DeleteBreakpoints(); - - if (sinkhole_stds && devnul_handle == INVALID_HANDLE_VALUE) { - devnul_handle = CreateFile( - "nul", - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, - OPEN_EXISTING, - 0, - NULL); - - if (devnul_handle == INVALID_HANDLE_VALUE) { - FATAL("Unable to open the nul device."); - } - } - BOOL inherit_handles = TRUE; - - if (force_dep) { - ZeroMemory(&si_ex, sizeof(si_ex)); - si_ex.StartupInfo.cb = sizeof(si_ex); - - creation_flags |= EXTENDED_STARTUPINFO_PRESENT; - - SIZE_T attr_size = 0; - InitializeProcThreadAttributeList(NULL, 1, 0, &attr_size); - if (attr_size == 0) { - FATAL("Error getting attribute list size"); - } - - attr_list_buf = (LPPROC_THREAD_ATTRIBUTE_LIST)malloc(attr_size); - if (!InitializeProcThreadAttributeList(attr_list_buf, 1, 0, &attr_size)) { - FATAL("Error in InitializeProcThreadAttributeList"); - } - - DWORD flags = PROCESS_CREATION_MITIGATION_POLICY_DEP_ENABLE; - size_t flags_size = sizeof(flags); - - if (!UpdateProcThreadAttribute(attr_list_buf, - 0, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, - &flags, flags_size, NULL, NULL)) - { - FATAL("Error in UpdateProcThreadAttribute"); - } - - si_ex.lpAttributeList = attr_list_buf; - - si_ptr = (LPSTARTUPINFOA)&si_ex; - si_basic_ptr = &si_ex.StartupInfo; - } else { - ZeroMemory(&si, sizeof(si)); - si.cb = sizeof(si); - si_ptr = &si; - si_basic_ptr = &si; - } - - if (sinkhole_stds) { - si_basic_ptr->hStdOutput = si_basic_ptr->hStdError = devnul_handle; - si_basic_ptr->dwFlags |= STARTF_USESTDHANDLES; - } else { - inherit_handles = FALSE; - } - - ZeroMemory(&pi, sizeof(pi)); - - if (mem_limit || cpu_aff) { - hJob = CreateJobObject(NULL, NULL); - if (hJob == NULL) { - FATAL("CreateJobObject failed, GLE=%d.\n", GetLastError()); - } - - ZeroMemory(&job_limit, sizeof(job_limit)); - if (mem_limit) { - job_limit.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_PROCESS_MEMORY; - job_limit.ProcessMemoryLimit = (size_t)(mem_limit * 1024 * 1024); - } - - if (cpu_aff) { - job_limit.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_AFFINITY; - job_limit.BasicLimitInformation.Affinity = (DWORD_PTR)cpu_aff; - } - - if (!SetInformationJobObject( - hJob, - JobObjectExtendedLimitInformation, - &job_limit, - sizeof(job_limit) - )) { - FATAL("SetInformationJobObject failed, GLE=%d.\n", GetLastError()); - } - } - - if (!CreateProcessA(NULL, - cmd, - NULL, - NULL, - inherit_handles, - creation_flags, - NULL, - NULL, - si_ptr, - &pi)) - { - FATAL("CreateProcess failed, GLE=%d.\n", GetLastError()); - } - - if (attr_list_buf) { - DeleteProcThreadAttributeList(attr_list_buf); - free(attr_list_buf); - } - - child_handle = pi.hProcess; - child_thread_handle = pi.hThread; - child_entrypoint_reached = false; - target_reached = false; - have_thread_context = false; - - if (mem_limit || cpu_aff) { - if (!AssignProcessToJobObject(hJob, child_handle)) { - FATAL("AssignProcessToJobObject failed, GLE=%d.\n", GetLastError()); - } - } - - GetProcessPlatform(); -} - -void Debugger::GetProcessPlatform() { - BOOL wow64current, wow64remote; - if (!IsWow64Process(child_handle, &wow64remote)) { - FATAL("IsWow64Process failed"); - } - if (wow64remote) { - wow64_target = 1; - child_ptr_size = 4; - if (calling_convention == CALLCONV_DEFAULT) { - calling_convention = CALLCONV_CDECL; - } - } - if (!IsWow64Process(GetCurrentProcess(), &wow64current)) { - FATAL("IsWow64Process failed"); - } - // Will probably fail before we reach this, but oh well - if (sizeof(void*) < child_ptr_size) { - FATAL("64-bit build is needed to run 64-bit targets\n"); - } -} - -// kills the target process -// (if not dead already) -DebuggerStatus Debugger::Kill() { - if (!child_handle) return DEBUGGER_PROCESS_EXIT; - - TerminateProcess(child_handle, 0); - - dbg_last_status = DebugLoop(0xFFFFFFFFUL, true); - if (dbg_last_status != DEBUGGER_PROCESS_EXIT) { - FATAL("Error killing target process\n"); - } - - CloseHandle(child_handle); - CloseHandle(child_thread_handle); - - child_handle = NULL; - child_thread_handle = NULL; - have_thread_context = false; - - // delete any breakpoints that weren't hit - DeleteBreakpoints(); - - return dbg_last_status; -} - -// attaches to an active process -DebuggerStatus Debugger::Attach(unsigned int pid, uint32_t timeout) { - attach_mode = true; - - if (!DebugActiveProcess(pid)) { - DWORD error_code = GetLastError(); - if(error_code == 5) { - HANDLE hToken = NULL; - LUID luid; - TOKEN_PRIVILEGES tp; - - if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { - FATAL("OpenProcessToken() failed, error code = %d\n", GetLastError()); - } - - if(!LookupPrivilegeValueA(NULL, "SeDebugPrivilege", &luid)) { - FATAL("LookupPrivilegeValueA() failed, error code = %d\n", GetLastError()); - } - - tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - tp.Privileges[0].Luid = luid; - tp.PrivilegeCount = 1; - - if(!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) { - FATAL("AdjustTokenPrivileges() failed, error code = %d\n", GetLastError()); - } - - if(!DebugActiveProcess(pid)) { - FATAL("Could not attach to the process.\n" - "Make sure the process exists and you have permissions to debug it.\n"); - } - - } else { - FATAL("DebugActiveProcess() failed, error code = %d\n", error_code); - } - } - - dbg_last_status = DEBUGGER_ATTACHED; - - return Continue(timeout); -} - -// starts the process and waits for the next event -DebuggerStatus Debugger::Run(char *cmd, uint32_t timeout) { - attach_mode = false; - - StartProcess(cmd); - - return Continue(timeout); -} - -DebuggerStatus Debugger::Run(int argc, char **argv, uint32_t timeout) { - char* cmd = NULL; - cmd = ArgvToCmd(argc, argv); - - DebuggerStatus ret_dbg_status = Run(cmd, timeout); - free(cmd); - - return ret_dbg_status; -} - -// continues after Run() or previous Continue() -// return with a non-terminal status -DebuggerStatus Debugger::Continue(uint32_t timeout) { - if (!child_handle && (dbg_last_status != DEBUGGER_ATTACHED)) - return DEBUGGER_PROCESS_EXIT; - - if (loop_mode && (dbg_last_status == DEBUGGER_TARGET_END)) { - // saves us a breakpoint - dbg_last_status = DEBUGGER_TARGET_START; - return dbg_last_status; - } - if (script != NULL) { - HANDLE thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)system, script, 0, NULL); - CloseHandle(thread_handle); - } - - dbg_last_status = DebugLoop(timeout); - - if (dbg_last_status == DEBUGGER_PROCESS_EXIT) { - if (!attach_mode) { - CloseHandle(child_handle); - CloseHandle(child_thread_handle); - child_handle = NULL; - child_thread_handle = NULL; - } - } - - return dbg_last_status; -} - -// initializes options from command line -void Debugger::Init(int argc, char **argv) { - have_thread_context = false; - sinkhole_stds = false; - mem_limit = 0; - cpu_aff = 0; - - attach_mode = false; - trace_debug_events = false; - loop_mode = false; - target_function_defined = false; - - target_return_value = 0; - - child_handle = NULL; - child_thread_handle = NULL; - - target_module[0] = 0; - target_method[0] = 0; - target_offset = 0; - saved_args = NULL; - target_num_args = 0; - calling_convention = CALLCONV_DEFAULT; - target_address = NULL; - - char *option; - - trace_debug_events = GetBinaryOption("-trace_debug_events", - argc, argv, - trace_debug_events); - - option = GetOption("-target_module", argc, argv); - if (option) target_module = option; - - option = GetOption("-target_method", argc, argv); - if (option) target_method = option; - - loop_mode = GetBinaryOption("-loop", argc, argv, loop_mode); - - option = GetOption("-nargs", argc, argv); - if (option) target_num_args = atoi(option); - - option = GetOption("-target_offset", argc, argv); - if (option) target_offset = strtoul(option, NULL, 0); - - option = GetOption("-callconv", argc, argv); - if (option) { - if (strcmp(option, "stdcall") == 0) - calling_convention = CALLCONV_CDECL; - else if (strcmp(option, "fastcall") == 0) - calling_convention = CALLCONV_FASTCALL; - else if (strcmp(option, "thiscall") == 0) - calling_convention = CALLCONV_THISCALL; - else if (strcmp(option, "ms64") == 0) - calling_convention = CALLCONV_MICROSOFT_X64; - else - FATAL("Unknown calling convention"); - } - - force_dep = GetBinaryOption("-force_dep", argc, argv, false); - - // check if we are running in persistence mode - if (target_module[0] || target_offset || target_method[0]) { - target_function_defined = true; - if ((target_module[0] == 0) || ((target_offset == 0) && (target_method[0] == 0))) { - FATAL("target_module and either target_offset or target_method must be specified together\n"); - } - } - - if (loop_mode && !target_function_defined) { - FATAL("Target function needs to be defined to use the loop mode\n"); - } - - if (target_num_args) { - saved_args = (void **)malloc(target_num_args * sizeof(void *)); - } - - // get allocation granularity - SYSTEM_INFO system_info; - GetSystemInfo(&system_info); - allocation_granularity = system_info.dwAllocationGranularity; -} diff --git a/debugger.h b/debugger.h deleted file mode 100644 index 9b19b0c..0000000 --- a/debugger.h +++ /dev/null @@ -1,237 +0,0 @@ -/* -Copyright 2020 Google LLC - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -https ://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -#ifndef DEBUGGER_H -#define DEBUGGER_H - -#include -#include -#include -#include - -#include "common.h" -#include "windows.h" -#include "arch/x86/reg.h" - - -enum DebuggerStatus { - DEBUGGER_NONE, - DEBUGGER_CONTINUE, - DEBUGGER_PROCESS_EXIT, - DEBUGGER_TARGET_START, - DEBUGGER_TARGET_END, - DEBUGGER_CRASHED, - DEBUGGER_HANGED, - DEBUGGER_ATTACHED -}; - -struct SavedRegisters { - CONTEXT saved_context; -}; - -class Debugger { -public: - - virtual void Init(int argc, char **argv); - DebuggerStatus Run(char *cmd, uint32_t timeout); - DebuggerStatus Run(int argc, char **argv, uint32_t timeout); - DebuggerStatus Kill(); - DebuggerStatus Continue(uint32_t timeout); - DebuggerStatus Attach(unsigned int pid, uint32_t timeout); - - bool IsTargetAlive() { return (child_handle != NULL); }; - bool IsTargetFunctionDefined() { return target_function_defined; } - - uint64_t GetTargetReturnValue() { return target_return_value; } - - enum ExceptionType { - BREAKPOINT, - ACCESS_VIOLATION, - ILLEGAL_INSTRUCTION, - STACK_OVERFLOW, - OTHER - }; - - struct Exception { - ExceptionType type; - void *ip; - bool maybe_write_violation; - bool maybe_execute_violation; - void *access_address; - }; - - Exception GetLastException() { - return last_exception; - } - - char * script; - -protected: - - enum MemoryProtection { - READONLY, - READWRITE, - READEXECUTE, - READWRITEEXECUTE - }; - - virtual void OnModuleLoaded(void *module, char *module_name); - virtual void OnModuleUnloaded(void *module); - virtual void OnTargetMethodReached() {} - virtual void OnProcessCreated(); - virtual void OnProcessExit() {}; - virtual void OnEntrypoint(); - - // should return true if the exception has been handled - virtual bool OnException(Exception *exception_record) { - return false; - } - - virtual void OnCrashed(Exception *exception_record) { } - - void *GetModuleEntrypoint(void *base_address); - void ReadStack(void *stack_addr, void **buffer, size_t numitems); - void WriteStack(void *stack_addr, void **buffer, size_t numitems); - void GetImageSize(void *base_address, size_t *min_address, size_t *max_address); - - // helper functions - void *RemoteAllocateNear(uint64_t region_min, - uint64_t region_max, - size_t size, - MemoryProtection protection, - bool use_shared_memory = false); - - void ExtractCodeRanges(void *module_base, - size_t min_address, - size_t max_address, - std::list *executable_ranges, - size_t *code_size); - - void ProtectCodeRanges(std::list *executable_ranges); - - // returns address in (potentially) instrumented code - virtual size_t GetTranslatedAddress(size_t address) { return address; } - - void RemoteFree(void *address, size_t size); - void RemoteWrite(void *address, void *buffer, size_t size); - void RemoteRead(void *address, void *buffer, size_t size); - void RemoteProtect(void *address, size_t size, MemoryProtection protect); - - size_t GetRegister(Register r); - void SetRegister(Register r, size_t value); - - void *GetTargetMethodAddress() { return target_address; } - - DWORD GetProcOffset(HMODULE module, const char* name); - - void SaveRegisters(SavedRegisters* registers); - void RestoreRegisters(SavedRegisters* registers); - - void GetExceptionHandlers(size_t module_haeder, std::unordered_set & handlers); - - void PatchPointersRemote(size_t min_address, size_t max_address, std::unordered_map& search_replace); - template - void PatchPointersRemoteT(size_t min_address, size_t max_address, std::unordered_map& search_replace); - -private: - struct Breakpoint { - void *address; - int type; - unsigned char original_opcode; - }; - std::list breakpoints; - - void StartProcess(char *cmd); - void GetProcessPlatform(); - DebuggerStatus DebugLoop(uint32_t timeout, bool killing=false); - int HandleDebuggerBreakpoint(void *address); - void HandleDllLoadInternal(LOAD_DLL_DEBUG_INFO *LoadDll); - DebuggerStatus HandleExceptionInternal(EXCEPTION_RECORD *exception_record); - void HandleTargetReachedInternal(); - void HandleTargetEnded(); - char *GetTargetAddress(HMODULE module); - void AddBreakpoint(void *address, int type); - DWORD GetLoadedModules(HMODULE **modules); - void DeleteBreakpoints(); - DWORD WindowsProtectionFlags(MemoryProtection protection); - DWORD GetImageSize(void *base_address); - void *RemoteAllocateBefore(uint64_t min_address, - uint64_t max_address, - size_t size, - MemoryProtection protection); - void *RemoteAllocateAfter(uint64_t min_address, - uint64_t max_address, - size_t size, - MemoryProtection protection); - -protected: - - bool child_entrypoint_reached; - bool target_reached; - - int32_t child_ptr_size = sizeof(void *); - -private: - HANDLE child_handle, child_thread_handle; - - HANDLE devnul_handle = INVALID_HANDLE_VALUE; - - DEBUG_EVENT dbg_debug_event; - DWORD dbg_continue_status; - bool dbg_continue_needed; - DebuggerStatus dbg_last_status; - - int wow64_target = 0; - -protected: - bool target_function_defined; - bool loop_mode; - bool attach_mode; - bool trace_debug_events; - - bool sinkhole_stds; - uint64_t mem_limit; - uint64_t cpu_aff; - -private: - // persistence related - int target_num_args; - uint64_t target_offset; - std::string target_module; - std::string target_method; - int calling_convention; - void *target_address; - void *saved_sp; - void *saved_return_address; - void **saved_args; - - uint64_t target_return_value; - - void RetrieveThreadContext(); - void CreateException(EXCEPTION_RECORD *win_exception_record, - Exception *exception); - - Exception last_exception; - // thread id of the last event - DWORD thread_id; - CONTEXT lcContext; - bool have_thread_context; - size_t allocation_granularity; - - bool force_dep; -}; - -#endif // DEBUGGER_H From e7fc4aa64e06c807351d8c4fb40f54fdb6379dc0 Mon Sep 17 00:00:00 2001 From: Akshat Parikh <68412398+parikhakshat@users.noreply.github.com> Date: Tue, 30 Aug 2022 21:06:53 -0400 Subject: [PATCH 5/8] Fix debug loop, remove unused variable, and implement windows specific checks --- Windows/debugger.cpp | 9 +-------- Windows/debugger.h | 2 -- common.cpp | 46 ++++++++++++++++++++++++-------------------- common.h | 4 +++- 4 files changed, 29 insertions(+), 32 deletions(-) diff --git a/Windows/debugger.cpp b/Windows/debugger.cpp index ca6b23b..4bdb5a7 100644 --- a/Windows/debugger.cpp +++ b/Windows/debugger.cpp @@ -1480,13 +1480,7 @@ DebuggerStatus Debugger::DebugLoop(uint32_t timeout, bool killing) dbg_continue_needed = false; } - if (timeout == 0) { - if(attach_mode) { - return DEBUGGER_PROCESS_EXIT; - } else { - return DEBUGGER_HANGED; - } - } + if (timeout == 0) return DEBUGGER_HANGED; if (!wait_ret) { //printf("WaitForDebugEvent returned 0\n"); @@ -1745,7 +1739,6 @@ DebuggerStatus Debugger::Kill() { // attaches to an active process DebuggerStatus Debugger::Attach(unsigned int pid, uint32_t timeout) { attach_mode = true; - processID = pid; if (!DebugActiveProcess(pid)) { DWORD error_code = GetLastError(); diff --git a/Windows/debugger.h b/Windows/debugger.h index bc57cbd..9b19b0c 100644 --- a/Windows/debugger.h +++ b/Windows/debugger.h @@ -81,8 +81,6 @@ class Debugger { protected: - DWORD processID; - enum MemoryProtection { READONLY, READWRITE, diff --git a/common.cpp b/common.cpp index 6ce23b9..0281712 100755 --- a/common.cpp +++ b/common.cpp @@ -20,8 +20,10 @@ limitations under the License. #include #include "common.h" -#include -#include +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) + #include + #include +#endif #include #include #include @@ -102,25 +104,27 @@ int GetIntOption(const char *name, int argc, char** argv, int default_value) { return (int)strtol(option, NULL, 0); } -DWORD FindProcessId(char * process_name) -{ - PROCESSENTRY32 entry; - entry.dwSize = sizeof(PROCESSENTRY32); - - HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); - - if (Process32First(snapshot, &entry) == TRUE) - { - while (Process32Next(snapshot, &entry) == TRUE) - { - if (stricmp(entry.szExeFile, process_name) == 0) - { - CloseHandle(snapshot); - return entry.th32ProcessID; - } - } - } -} +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) + DWORD FindProcessId(char * process_name) + { + PROCESSENTRY32 entry; + entry.dwSize = sizeof(PROCESSENTRY32); + + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); + + if (Process32First(snapshot, &entry) == TRUE) + { + while (Process32Next(snapshot, &entry) == TRUE) + { + if (stricmp(entry.szExeFile, process_name) == 0) + { + CloseHandle(snapshot); + return entry.th32ProcessID; + } + } + } + } +#endif //quoting on Windows is weird size_t ArgvEscapeWindows(char *in, char *out) { diff --git a/common.h b/common.h index 5720e6d..f1e2c07 100755 --- a/common.h +++ b/common.h @@ -75,7 +75,9 @@ uint64_t GetCurTime(void); char *GetOption(const char *name, int argc, char** argv); void GetOptionAll(const char *name, int argc, char** argv, std::list *results); bool GetBinaryOption(const char *name, int argc, char** argv, bool default_value); -DWORD FindProcessId(char * process_name); +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) + DWORD FindProcessId(char * process_name); +#endif int GetIntOption(const char *name, int argc, char** argv, int default_value); char *ArgvToCmd(int argc, char** argv); From 2fce951ad2f1f7ddd5776c382cdf593f8f8e0f90 Mon Sep 17 00:00:00 2001 From: Akshat Parikh <68412398+parikhakshat@users.noreply.github.com> Date: Mon, 5 Sep 2022 21:43:23 -0400 Subject: [PATCH 6/8] Remove some extraneous code and move it to the Jackalope repository --- Windows/debugger.cpp | 18 ++++++------------ common.cpp | 1 - 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/Windows/debugger.cpp b/Windows/debugger.cpp index 4bdb5a7..cfc83f3 100644 --- a/Windows/debugger.cpp +++ b/Windows/debugger.cpp @@ -1471,7 +1471,7 @@ DebuggerStatus Debugger::DebugLoop(uint32_t timeout, bool killing) uint64_t time_elapsed = end_time - begin_time; timeout = ((uint64_t)timeout >= time_elapsed) ? timeout - (uint32_t)time_elapsed : 0; - //printf("timeout: %u\n", timeout); + // printf("timeout: %u\n", timeout); // printf("time: %lld\n", get_cur_time_us()); if (wait_ret) { @@ -1491,7 +1491,7 @@ DebuggerStatus Debugger::DebugLoop(uint32_t timeout, bool killing) thread_id = DebugEv->dwThreadId; - //printf("eventCode: %x\n", DebugEv->dwDebugEventCode); + // printf("eventCode: %x\n", DebugEv->dwDebugEventCode); switch (DebugEv->dwDebugEventCode) { @@ -1808,20 +1808,14 @@ DebuggerStatus Debugger::Continue(uint32_t timeout) { dbg_last_status = DEBUGGER_TARGET_START; return dbg_last_status; } - if (script != NULL) { - HANDLE thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)system, script, 0, NULL); - CloseHandle(thread_handle); - } dbg_last_status = DebugLoop(timeout); if (dbg_last_status == DEBUGGER_PROCESS_EXIT) { - if (!attach_mode) { - CloseHandle(child_handle); - CloseHandle(child_thread_handle); - child_handle = NULL; - child_thread_handle = NULL; - } + CloseHandle(child_handle); + CloseHandle(child_thread_handle); + child_handle = NULL; + child_thread_handle = NULL; } return dbg_last_status; diff --git a/common.cpp b/common.cpp index 0281712..938f93f 100755 --- a/common.cpp +++ b/common.cpp @@ -21,7 +21,6 @@ limitations under the License. #include "common.h" #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) - #include #include #endif #include From d2f1619f43fa6fc23cbe9ab0c92284556be19f99 Mon Sep 17 00:00:00 2001 From: Akshat Parikh <68412398+parikhakshat@users.noreply.github.com> Date: Mon, 5 Sep 2022 22:20:40 -0400 Subject: [PATCH 7/8] Update tinyinst repository by removing extraneous code --- common.cpp | 40 ++++++++++++++++++++-------------------- common.h | 2 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/common.cpp b/common.cpp index 938f93f..6cf08fe 100755 --- a/common.cpp +++ b/common.cpp @@ -21,7 +21,7 @@ limitations under the License. #include "common.h" #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) - #include + #include #endif #include #include @@ -104,25 +104,25 @@ int GetIntOption(const char *name, int argc, char** argv, int default_value) { } #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) - DWORD FindProcessId(char * process_name) - { - PROCESSENTRY32 entry; - entry.dwSize = sizeof(PROCESSENTRY32); - - HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); - - if (Process32First(snapshot, &entry) == TRUE) - { - while (Process32Next(snapshot, &entry) == TRUE) - { - if (stricmp(entry.szExeFile, process_name) == 0) - { - CloseHandle(snapshot); - return entry.th32ProcessID; - } - } - } - } + DWORD FindProcessId(char * process_name) + { + PROCESSENTRY32 entry; + entry.dwSize = sizeof(PROCESSENTRY32); + + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); + + if (Process32First(snapshot, &entry) == TRUE) + { + while (Process32Next(snapshot, &entry) == TRUE) + { + if (stricmp(entry.szExeFile, process_name) == 0) + { + CloseHandle(snapshot); + return entry.th32ProcessID; + } + } + } + } #endif //quoting on Windows is weird diff --git a/common.h b/common.h index f1e2c07..847e866 100755 --- a/common.h +++ b/common.h @@ -76,7 +76,7 @@ char *GetOption(const char *name, int argc, char** argv); void GetOptionAll(const char *name, int argc, char** argv, std::list *results); bool GetBinaryOption(const char *name, int argc, char** argv, bool default_value); #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) - DWORD FindProcessId(char * process_name); + DWORD FindProcessId(char * process_name); #endif int GetIntOption(const char *name, int argc, char** argv, int default_value); From fade015332a47d32a57decc95d4dc003455498a3 Mon Sep 17 00:00:00 2001 From: Akshat Parikh <68412398+parikhakshat@users.noreply.github.com> Date: Tue, 6 Sep 2022 09:34:40 -0400 Subject: [PATCH 8/8] Remove unused script variable --- Windows/debugger.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Windows/debugger.h b/Windows/debugger.h index 9b19b0c..6604b8e 100644 --- a/Windows/debugger.h +++ b/Windows/debugger.h @@ -76,9 +76,7 @@ class Debugger { Exception GetLastException() { return last_exception; } - - char * script; - + protected: enum MemoryProtection {