[FEATURE] Allow to print full callstack in the report

This commit is contained in:
hasherezade
2025-02-09 02:04:02 +01:00
parent cfb99d9c80
commit 1d8419d9ad
3 changed files with 62 additions and 47 deletions
+3 -3
View File
@@ -153,11 +153,11 @@ std::string pesieve::translate_json_level(const pesieve::t_json_level &mode)
{
switch (mode) {
case pesieve::JSON_BASIC:
return "basic";
return "basic (default)";
case pesieve::JSON_DETAILS:
return "details #1 (list patches)";
return "details #1 (medium)";
case pesieve::JSON_DETAILS2:
return "details #2 (list patches: extended)";
return "details #2 (verbose)";
}
return "";
}
+29 -32
View File
@@ -306,17 +306,16 @@ bool pesieve::ThreadScanner::checkReturnAddrIntegrity(IN const std::vector<ULONG
return false;
}
size_t pesieve::ThreadScanner::_analyzeCallStack(IN const std::vector<ULONGLONG> &call_stack, IN OUT ctx_details& cDetails, OUT IN std::set<ULONGLONG> &shcCandidates)
size_t pesieve::ThreadScanner::_analyzeCallStack(IN OUT ctx_details& cDetails, OUT IN std::set<ULONGLONG> &shcCandidates)
{
size_t processedCntr = 0;
cDetails.is_managed = false;
cDetails.stackFramesCount = call_stack.size();
cDetails.is_ret_in_frame = false;
#ifdef _SHOW_THREAD_INFO
std::cout << "\n" << "Stack frame Size: " << std::dec << call_stack.size() << "\n===\n";
std::cout << "\n" << "Stack frame Size: " << std::dec << cDetails.callStack.size() << "\n===\n";
#endif //_SHOW_THREAD_INFO
for (auto itr = call_stack.rbegin(); itr != call_stack.rend() ;++itr, ++processedCntr) {
for (auto itr = cDetails.callStack.rbegin(); itr != cDetails.callStack.rend() ;++itr, ++processedCntr) {
const ULONGLONG next_return = *itr;
if (cDetails.ret_on_stack == next_return) {
cDetails.is_ret_in_frame = true;
@@ -362,6 +361,15 @@ size_t pesieve::ThreadScanner::_analyzeCallStack(IN const std::vector<ULONGLONG>
return processedCntr;
}
size_t pesieve::ThreadScanner::analyzeCallStackInfo(IN OUT ThreadScanReport& my_report)
{
const size_t analyzedCount = _analyzeCallStack(my_report.cDetails, my_report.shcCandidates);
if (!my_report.cDetails.is_managed) {
my_report.cDetails.is_ret_as_syscall = checkReturnAddrIntegrity(my_report.cDetails.callStack);
}
return analyzedCount;
}
size_t pesieve::ThreadScanner::fillCallStackInfo(IN HANDLE hProcess, IN HANDLE hThread, IN LPVOID ctx, IN OUT ThreadScanReport& my_report)
{
// do it in a new thread to prevent stucking...
@@ -391,31 +399,22 @@ size_t pesieve::ThreadScanner::fillCallStackInfo(IN HANDLE hProcess, IN HANDLE h
if (!args.is_ok) {
return 0;
}
my_report.callStack = args.callStack;
my_report.cDetails.callStack = args.callStack;
return args.callStack.size();
}
size_t pesieve::ThreadScanner::analyzeCallStackInfo(IN OUT ThreadScanReport& my_report)
{
const size_t analyzedCount = _analyzeCallStack(my_report.callStack, my_report.cDetails, my_report.shcCandidates);
if (!my_report.cDetails.is_managed) {
my_report.cDetails.is_ret_as_syscall = checkReturnAddrIntegrity(my_report.callStack);
namespace pesieve {
template <typename PTR_T>
bool read_return_ptr(IN HANDLE hProcess, IN OUT ctx_details& cDetails) {
PTR_T ret_addr = 0;
cDetails.ret_on_stack = 0;
if (peconv::read_remote_memory(hProcess, (LPVOID)cDetails.rsp, (BYTE*)&ret_addr, sizeof(ret_addr)) == sizeof(ret_addr)) {
cDetails.ret_on_stack = (ULONGLONG)ret_addr;
return true;
}
return false;
}
return analyzedCount;
}
template <typename PTR_T>
bool read_return_ptr(IN HANDLE hProcess, IN OUT ctx_details& cDetails) {
PTR_T ret_addr = 0;
cDetails.ret_on_stack = 0;
if (peconv::read_remote_memory(hProcess, (LPVOID)cDetails.rsp, (BYTE*)&ret_addr, sizeof(ret_addr)) == sizeof(ret_addr)) {
cDetails.ret_on_stack = (ULONGLONG)ret_addr;
return true;
}
return false;
}
}; //namespace pesieve
bool pesieve::ThreadScanner::fetchThreadCtxDetails(IN HANDLE hProcess, IN HANDLE hThread, OUT ThreadScanReport& my_report)
{
@@ -610,14 +609,12 @@ bool pesieve::ThreadScanner::scanRemoteThreadCtx(HANDLE hThread, ThreadScanRepor
my_report.status = SCAN_NOT_SUSPICIOUS;
return false;
}
if (!is_ok) {
if (!is_ok || !analyzeCallStackInfo(my_report)) {
// could not fetch the thread context and information
my_report.status = SCAN_ERROR;
return false;
}
analyzeCallStackInfo(my_report);
my_report.stack_ptr = cDetails.rsp;
bool isModified = false;
bool is_unnamed = !isAddrInNamedModule(cDetails.rip);
@@ -692,7 +689,7 @@ bool pesieve::ThreadScanner::scanRemoteThreadCtx(HANDLE hThread, ThreadScanRepor
}
if (hasEmptyGUI &&
cDetails.stackFramesCount == 1
cDetails.callStack.size() == 1
&& this->info.is_extended && info.ext.state == Waiting && info.ext.wait_reason == UserRequest)
{
my_report.indicators.insert(THI_SUS_CALLSTACK_CORRUPT);
@@ -743,14 +740,14 @@ ThreadScanReport* pesieve::ThreadScanner::scanRemote()
}
}
}
if (!should_scan_context(info)) {
return my_report;
}
if (this->info.is_extended) {
my_report->thread_state = info.ext.state;
my_report->thread_wait_reason = info.ext.wait_reason;
my_report->thread_wait_time = info.ext.wait_time;
}
if (!should_scan_context(info)) {
return my_report;
}
// proceed with detailed checks:
HANDLE hThread = OpenThread(
THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION | SYNCHRONIZE,
+30 -12
View File
@@ -7,6 +7,7 @@
#include "../utils/process_symbols.h"
#include "../stats/stats.h"
#include "../stats/entropy_stats.h"
#include <vector>
namespace pesieve {
@@ -46,11 +47,10 @@ namespace pesieve {
bool is_ret_as_syscall;
bool is_ret_in_frame;
bool is_managed; // does it contain .NET modules
size_t stackFramesCount;
std::vector<ULONGLONG> callStack;
_ctx_details(bool _is64b = false, ULONGLONG _rip = 0, ULONGLONG _rsp = 0, ULONGLONG _rbp = 0, ULONGLONG _ret_addr = 0)
: is64b(_is64b), rip(_rip), rsp(_rsp), rbp(_rbp), last_ret(_ret_addr), ret_on_stack(0), is_ret_as_syscall(false), is_ret_in_frame(false),
stackFramesCount(0),
is_managed(false)
{
}
@@ -87,6 +87,29 @@ namespace pesieve {
{
}
const virtual void callstackToJSON(std::stringstream& outs, size_t level, const pesieve::t_json_level& jdetails)
{
OUT_PADDED(outs, level, "\"stack_ptr\" : ");
outs << "\"" << std::hex << stack_ptr << "\"";
if (cDetails.callStack.size()) {
outs << ",\n";
OUT_PADDED(outs, level, "\"frames_count\" : ");
outs << std::dec << cDetails.callStack.size();
if (jdetails >= JSON_DETAILS) {
outs << ",\n";
OUT_PADDED(outs, level, "\"frames\" : [");
for (auto itr = cDetails.callStack.begin(); itr != cDetails.callStack.end(); ++itr) {
if (itr != cDetails.callStack.begin()) {
outs << ", ";
}
const ULONGLONG addr = *itr;
outs << "\"" << std::hex << addr << "\"";
}
outs << "]";
}
}
}
const virtual void fieldsToJSON(std::stringstream &outs, size_t level, const pesieve::t_json_level &jdetails)
{
ModuleScanReport::_toJSON(outs, level);
@@ -105,13 +128,10 @@ namespace pesieve {
outs << "]";
if (stack_ptr) {
outs << ",\n";
OUT_PADDED(outs, level, "\"stack_ptr\" : ");
outs << "\"" << std::hex << stack_ptr << "\"";
}
if (callStack.size()) {
outs << ",\n";
OUT_PADDED(outs, level, "\"frames_count\" : ");
outs << std::dec << callStack.size();
OUT_PADDED(outs, level, "\"callstack\" : {\n");
callstackToJSON(outs, level + 1, jdetails);
outs << "\n";
OUT_PADDED(outs, level, "}");
}
if (thread_state != THREAD_STATE_UNKNOWN) {
outs << ",\n";
@@ -158,13 +178,11 @@ namespace pesieve {
ULONGLONG susp_addr;
DWORD protection;
ULONGLONG stack_ptr;
//size_t frames_count;
DWORD thread_state;
DWORD thread_wait_reason;
DWORD thread_wait_time;
ctx_details cDetails;
std::vector<ULONGLONG> callStack;
std::set<ULONGLONG> shcCandidates;
std::set<ThSusIndicator> indicators;
@@ -197,7 +215,7 @@ namespace pesieve {
size_t fillCallStackInfo(IN HANDLE hProcess, IN HANDLE hThread, IN LPVOID ctx, IN OUT ThreadScanReport& my_report);
size_t analyzeCallStackInfo(IN OUT ThreadScanReport& my_report);
size_t _analyzeCallStack(IN const std::vector<ULONGLONG>& call_stack, IN OUT ctx_details& cDetails, OUT IN std::set<ULONGLONG>& shcCandidates);
size_t _analyzeCallStack(IN OUT ctx_details& cDetails, OUT IN std::set<ULONGLONG>& shcCandidates);
bool checkReturnAddrIntegrity(IN const std::vector<ULONGLONG>& callStack);