mirror of
https://github.com/hasherezade/pe-sieve.git
synced 2026-10-11 21:49:57 +00:00
[FEATURE] In ThreadScanner: added native callstack for WoW64 processes
This commit is contained in:
+64
-46
@@ -331,8 +331,7 @@ size_t pesieve::ThreadScanner::analyzeCallStackInfo(IN OUT ThreadScanReport& my_
|
||||
return analyzedCount;
|
||||
}
|
||||
|
||||
template<typename T_STACKFRAME>
|
||||
size_t enum_callstack(IN ProcessSymbolsManager* symbols, const pesieve::ctx_details& cDetails,IN HANDLE hThread, IN LPVOID ctx, DWORD MachineType, std::vector<ULONGLONG> &callStack)
|
||||
size_t enum_callstack(IN ProcessSymbolsManager* symbols, const pesieve::ctx_details& cDetails, IN HANDLE hThread, IN LPVOID ctx, DWORD MachineType, std::vector<ULONGLONG>& 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<ULONGLONG> callStack;
|
||||
size_t fetched = 0;
|
||||
|
||||
const pesieve::ctx_details& cDetails = my_report.cDetails;
|
||||
#ifdef _WIN64
|
||||
if (cDetails.is64b) {
|
||||
fetched = enum_callstack<STACKFRAME64>(this->symbols, cDetails, hThread, ctx, IMAGE_FILE_MACHINE_AMD64, callStack);
|
||||
}
|
||||
#endif
|
||||
if (!cDetails.is64b) {
|
||||
fetched = enum_callstack<STACKFRAME>(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<ULONGLONG>(hProcess, cDetails);
|
||||
#else
|
||||
cDetails.init(false, ctx.Eip, ctx.Esp, ctx.Ebp);
|
||||
read_return_ptr<DWORD>(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<DWORD>(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<DWORD>(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<ULONGLONG>(hProcess, my_report.cDetails);
|
||||
#else
|
||||
my_report.cDetails.init(false, ctx.Eip, ctx.Esp, ctx.Ebp);
|
||||
read_return_ptr<DWORD>(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()
|
||||
|
||||
@@ -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<ULONGLONG, std::string> addrToSymbol;
|
||||
std::set<ULONGLONG> shcCandidates;
|
||||
std::set<ThSusIndicator> 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<ULONGLONG>& shcCandidates);
|
||||
|
||||
|
||||
@@ -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<std::mutex> 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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user