diff --git a/scanners/thread_scanner.cpp b/scanners/thread_scanner.cpp index e2f98641..7030ea9b 100644 --- a/scanners/thread_scanner.cpp +++ b/scanners/thread_scanner.cpp @@ -331,8 +331,7 @@ size_t pesieve::ThreadScanner::analyzeCallStackInfo(IN OUT ThreadScanReport& my_ return analyzedCount; } -template -size_t enum_callstack(IN ProcessSymbolsManager* symbols, const pesieve::ctx_details& cDetails,IN HANDLE hThread, IN LPVOID ctx, DWORD MachineType, std::vector &callStack) +size_t enum_callstack(IN ProcessSymbolsManager* symbols, const pesieve::ctx_details& cDetails, IN HANDLE hThread, IN LPVOID ctx, DWORD MachineType, std::vector& callStack) { if (!ctx || !symbols || !symbols->IsInitialized()) { return 0; @@ -340,7 +339,7 @@ size_t enum_callstack(IN ProcessSymbolsManager* symbols, const pesieve::ctx_deta const size_t max_frames = 128; size_t fetched = 0; - T_STACKFRAME frame = { 0 }; + STACKFRAME64 frame = { 0 }; frame.AddrPC.Offset = cDetails.rip; frame.AddrPC.Mode = AddrModeFlat; @@ -348,11 +347,11 @@ size_t enum_callstack(IN ProcessSymbolsManager* symbols, const pesieve::ctx_deta frame.AddrStack.Mode = AddrModeFlat; frame.AddrFrame.Offset = cDetails.rbp; frame.AddrFrame.Mode = AddrModeFlat; - + DWORD64 prevPc = 0; size_t frameCount = 0; - while (symbols->RunStackWalk(MachineType, hThread, &frame, ctx, NULL, SymFunctionTableAccess, SymGetModuleBase, NULL)) { + while (symbols->RunStackWalk64(MachineType, hThread, &frame, ctx, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) { DWORD64 next_return = frame.AddrPC.Offset; if (next_return == 0) break; @@ -368,7 +367,7 @@ size_t enum_callstack(IN ProcessSymbolsManager* symbols, const pesieve::ctx_deta return fetched; } -size_t pesieve::ThreadScanner::fillCallStackInfo(IN HANDLE hProcess, IN HANDLE hThread, IN const LPVOID ctx, IN OUT ThreadScanReport& my_report) +size_t pesieve::ThreadScanner::fillCallStackInfo(IN HANDLE hThread, IN const LPVOID ctx, IN OUT ctx_details& cDetails) { if (!ctx || !this->symbols || !this->symbols->IsInitialized()) @@ -376,21 +375,12 @@ size_t pesieve::ThreadScanner::fillCallStackInfo(IN HANDLE hProcess, IN HANDLE h return 0; } std::vector callStack; - size_t fetched = 0; - - const pesieve::ctx_details& cDetails = my_report.cDetails; -#ifdef _WIN64 - if (cDetails.is64b) { - fetched = enum_callstack(this->symbols, cDetails, hThread, ctx, IMAGE_FILE_MACHINE_AMD64, callStack); - } -#endif - if (!cDetails.is64b) { - fetched = enum_callstack(this->symbols, cDetails, hThread, ctx, IMAGE_FILE_MACHINE_I386, callStack); - } + const DWORD machineType = cDetails.is64b ? IMAGE_FILE_MACHINE_AMD64 : IMAGE_FILE_MACHINE_I386; + const size_t fetched = enum_callstack(this->symbols, cDetails, hThread, ctx, machineType, callStack); if (!fetched) { return 0; } - my_report.cDetails.callStack = callStack; + cDetails.callStack = callStack; return callStack.size(); } @@ -407,43 +397,67 @@ namespace pesieve { } }; //namespace pesieve +bool pesieve::ThreadScanner::fetchNativeThreadCtxDetails(IN HANDLE hProcess, IN HANDLE hThread, IN OUT ctx_details& cDetails) +{ + CONTEXT ctx = { 0 }; + ctx.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL; + if (!GetThreadContext(hThread, &ctx)) { + return false; + } +#ifdef _WIN64 + cDetails.init(true, ctx.Rip, ctx.Rsp, ctx.Rbp); + read_return_ptr(hProcess, cDetails); +#else + cDetails.init(false, ctx.Eip, ctx.Esp, ctx.Ebp); + read_return_ptr(hProcess, cDetails); +#endif + fillCallStackInfo(hThread, &ctx, cDetails); + return true; +} + +#ifdef _WIN64 +bool pesieve::ThreadScanner::fetchWow64ThreadCtxDetails(IN HANDLE hProcess, IN HANDLE hThread, IN OUT ctx_details& cDetails) +{ + WOW64_CONTEXT ctx = { 0 }; + ctx.ContextFlags = WOW64_CONTEXT_INTEGER | WOW64_CONTEXT_CONTROL; + if (!pesieve::util::wow64_get_thread_context(hThread, &ctx)) { + return false; + } + cDetails.init(false, ctx.Eip, ctx.Esp, ctx.Ebp); + read_return_ptr(hProcess, cDetails); + fillCallStackInfo(hThread, &ctx, cDetails); + return true; +} +#endif + bool pesieve::ThreadScanner::fetchThreadCtxDetails(IN HANDLE hProcess, IN HANDLE hThread, OUT ThreadScanReport& my_report) { - bool is_ok = false; - BOOL is_wow64 = FALSE; - size_t retrieved = 0; #ifdef _WIN64 + BOOL is_wow64 = FALSE; pesieve::util::is_process_wow64(hProcess, &is_wow64); if (is_wow64) { - WOW64_CONTEXT ctx = { 0 }; - ctx.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL; - if (pesieve::util::wow64_get_thread_context(hThread, &ctx)) { - is_ok = true; - my_report.cDetails.init(false, ctx.Eip, ctx.Esp, ctx.Ebp); - read_return_ptr(hProcess, my_report.cDetails); - retrieved = fillCallStackInfo(hProcess, hThread, &ctx, my_report); - } - } -#endif - if (!is_ok) { + const bool native_ok = fetchNativeThreadCtxDetails(hProcess, hThread, my_report.nativeWow64Details); + my_report.has_native_wow64_context = native_ok; - CONTEXT ctx = { 0 }; - ctx.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL; - if (GetThreadContext(hThread, &ctx)) { - is_ok = true; -#ifdef _WIN64 - my_report.cDetails.init(true, ctx.Rip, ctx.Rsp, ctx.Rbp); - read_return_ptr(hProcess, my_report.cDetails); -#else - my_report.cDetails.init(false, ctx.Eip, ctx.Esp, ctx.Ebp); - read_return_ptr(hProcess, my_report.cDetails); -#endif - retrieved = fillCallStackInfo(hProcess, hThread, &ctx, my_report); + const bool wow64_ok = fetchWow64ThreadCtxDetails(hProcess, hThread, my_report.cDetails); + if (!wow64_ok && native_ok) { + // Preserve a usable primary stack if the WOW64 guest context could not be fetched. + // Avoid reporting the same native stack twice. + my_report.cDetails = my_report.nativeWow64Details; + my_report.has_native_wow64_context = false; } - } #ifdef _DEBUG - std::cout << std::dec << "[" << GetThreadId(hThread) << "] Retrieved callstack: " << retrieved << std::endl; + std::cout << std::dec << "[" << GetThreadId(hThread) << "] Retrieved WOW64 guest callstack: " << my_report.cDetails.callStack.size() + << ", native callstack: " << my_report.nativeWow64Details.callStack.size() << std::endl; +#endif //_DEBUG + return wow64_ok || native_ok; + } +#endif + + const bool is_ok = fetchNativeThreadCtxDetails(hProcess, hThread, my_report.cDetails); +#ifdef _DEBUG + std::cout << std::dec << "[" << GetThreadId(hThread) << "] Retrieved callstack: " << my_report.cDetails.callStack.size() << std::endl; #endif //_DEBUG return is_ok; } @@ -836,6 +850,10 @@ void pesieve::ThreadScanner::reportResolvedCallstack(ThreadScanReport& my_report ULONGLONG addr = *itr; my_report.addrToSymbol[addr] = this->resolveAddrToString(addr); } + for (auto itr = my_report.nativeWow64Details.callStack.begin(); itr != my_report.nativeWow64Details.callStack.end(); ++itr) { + ULONGLONG addr = *itr; + my_report.addrToSymbol[addr] = this->resolveAddrToString(addr); + } } ThreadScanReport* pesieve::ThreadScanner::scanRemote() diff --git a/scanners/thread_scanner.h b/scanners/thread_scanner.h index 813e4dfa..46cf33a7 100644 --- a/scanners/thread_scanner.h +++ b/scanners/thread_scanner.h @@ -197,7 +197,8 @@ namespace pesieve { : ModuleScanReport(0, 0), tid(_tid), stack_ptr(0), thread_state(THREAD_STATE_UNKNOWN), - thread_wait_reason(0), thread_wait_time(0) + thread_wait_reason(0), thread_wait_time(0), + has_native_wow64_context(false) { } @@ -226,7 +227,7 @@ namespace pesieve { return nullptr; } - const virtual void callstackToJSON(std::stringstream& outs, size_t level, const pesieve::t_json_level& jdetails) + const virtual void callstackToJSON(std::stringstream& outs, size_t level, const pesieve::t_json_level& jdetails, const ctx_details& details) { bool printCallstack = (jdetails >= JSON_DETAILS) ? true : false; if (this->indicators.find(THI_SUS_CALLSTACK_CORRUPT) != this->indicators.end()) { @@ -236,16 +237,16 @@ namespace pesieve { printCallstack = true; } OUT_PADDED(outs, level, "\"stack_ptr\" : "); - outs << "\"" << std::hex << stack_ptr << "\""; - if (cDetails.callStack.size()) { + outs << "\"" << std::hex << details.rsp << "\""; + if (details.callStack.size()) { outs << ",\n"; OUT_PADDED(outs, level, "\"frames_count\" : "); - outs << std::dec << cDetails.callStack.size(); + outs << std::dec << details.callStack.size(); if (printCallstack) { outs << ",\n"; OUT_PADDED(outs, level, "\"frames\" : ["); - for (auto itr = cDetails.callStack.rbegin(); itr != cDetails.callStack.rend(); ++itr) { - if (itr != cDetails.callStack.rbegin()) { + for (auto itr = details.callStack.rbegin(); itr != details.callStack.rend(); ++itr) { + if (itr != details.callStack.rbegin()) { outs << ", "; } const ULONGLONG addr = *itr; @@ -281,7 +282,14 @@ namespace pesieve { if (stack_ptr) { outs << ",\n"; OUT_PADDED(outs, level, "\"callstack\" : {\n"); - callstackToJSON(outs, level + 1, jdetails); + callstackToJSON(outs, level + 1, jdetails, cDetails); + outs << "\n"; + OUT_PADDED(outs, level, "}"); + } + if (has_native_wow64_context && nativeWow64Details.rsp) { + outs << ",\n"; + OUT_PADDED(outs, level, "\"native_callstack\" : {\n"); + callstackToJSON(outs, level + 1, jdetails, nativeWow64Details); outs << "\n"; OUT_PADDED(outs, level, "}"); } @@ -374,7 +382,15 @@ namespace pesieve { std::string lastSyscall; std::string lastFunction; + // Primary context used by the existing analysis logic. For a WOW64 target + // scanned by a 64-bit build, this remains the 32-bit guest context. ctx_details cDetails; + + // Additional native AMD64 context exposed for WOW64 targets. It is report-only + // in this narrow refactor; indicator semantics remain unchanged. + bool has_native_wow64_context; + ctx_details nativeWow64Details; + std::map addrToSymbol; std::set shcCandidates; std::set indicators; @@ -406,7 +422,11 @@ namespace pesieve { std::string resolveLowLevelFuncName(IN const ULONGLONG addr, OUT OPTIONAL size_t* disp = nullptr); std::string resolveAddrToString(IN ULONGLONG addr); bool printResolvedAddr(const ULONGLONG addr); - size_t fillCallStackInfo(IN HANDLE hProcess, IN HANDLE hThread, const IN LPVOID ctx, IN OUT ThreadScanReport& my_report); + size_t fillCallStackInfo(IN HANDLE hThread, const IN LPVOID ctx, IN OUT ctx_details& cDetails); + bool fetchNativeThreadCtxDetails(IN HANDLE hProcess, IN HANDLE hThread, IN OUT ctx_details& cDetails); +#ifdef _WIN64 + bool fetchWow64ThreadCtxDetails(IN HANDLE hProcess, IN HANDLE hThread, IN OUT ctx_details& cDetails); +#endif size_t analyzeCallStackInfo(IN OUT ThreadScanReport& my_report); size_t _analyzeCallStack(IN OUT ctx_details& cDetails, OUT IN std::set& shcCandidates); diff --git a/utils/dbg_help_wrapper.h b/utils/dbg_help_wrapper.h index 448eb552..939f96aa 100644 --- a/utils/dbg_help_wrapper.h +++ b/utils/dbg_help_wrapper.h @@ -78,20 +78,20 @@ public: return true; } - static bool RunStackWalk( + static bool RunStackWalk64( _In_ DWORD MachineType, _In_ HANDLE hProcess, _In_ HANDLE hThread, - _Inout_ LPSTACKFRAME StackFrame, + _Inout_ LPSTACKFRAME64 StackFrame, _Inout_ PVOID ContextRecord, - _In_opt_ PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine, - _In_opt_ PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine, - _In_opt_ PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine, - _In_opt_ PTRANSLATE_ADDRESS_ROUTINE TranslateAddress + _In_opt_ PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, + _In_opt_ PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, + _In_opt_ PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, + _In_opt_ PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress ) { std::lock_guard guard(m_Mutex); - if (StackWalk(MachineType, hProcess, hThread, StackFrame, ContextRecord, ReadMemoryRoutine, FunctionTableAccessRoutine, GetModuleBaseRoutine, TranslateAddress)) { + if (StackWalk64(MachineType, hProcess, hThread, StackFrame, ContextRecord, ReadMemoryRoutine, FunctionTableAccessRoutine, GetModuleBaseRoutine, TranslateAddress)) { return true; } return false; diff --git a/utils/process_symbols.h b/utils/process_symbols.h index 7d131105..e38937db 100644 --- a/utils/process_symbols.h +++ b/utils/process_symbols.h @@ -204,21 +204,21 @@ public: return DbgHelpWrapper::RefreshModuleList(hProcess); } - bool RunStackWalk( + bool RunStackWalk64( _In_ DWORD MachineType, _In_ HANDLE hThread, - _Inout_ LPSTACKFRAME StackFrame, + _Inout_ LPSTACKFRAME64 StackFrame, _Inout_ PVOID ContextRecord, - _In_opt_ PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine, - _In_opt_ PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine, - _In_opt_ PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine, - _In_opt_ PTRANSLATE_ADDRESS_ROUTINE TranslateAddress + _In_opt_ PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, + _In_opt_ PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, + _In_opt_ PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, + _In_opt_ PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress ) { if (!isInit) { return false; } - return DbgHelpWrapper::RunStackWalk(MachineType, + return DbgHelpWrapper::RunStackWalk64(MachineType, this->hProcess, hThread, StackFrame,