Skip to content

Commit

Permalink
[FEATURE] Scan real thread start address
Browse files Browse the repository at this point in the history
  • Loading branch information
hasherezade committed Aug 31, 2024
1 parent f9e1571 commit 313c311
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 80 deletions.
13 changes: 8 additions & 5 deletions scanners/scanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,6 @@ size_t pesieve::ProcessScanner::scanModulesIATs(ProcessScanReport &pReport) //th
return counter;
}


size_t pesieve::ProcessScanner::scanThreads(ProcessScanReport& pReport) //throws exceptions
{
if (!this->symbols.InitSymbols()) {
Expand All @@ -487,7 +486,7 @@ size_t pesieve::ProcessScanner::scanThreads(ProcessScanReport& pReport) //throws
}
DWORD start_tick = GetTickCount();

std::vector<thread_info> threads_info;
std::map<DWORD, thread_info> threads_info;
if (!pesieve::util::fetch_threads_info(pid, threads_info)) { //extended info, but doesn't work on old Windows...

if (!pesieve::util::fetch_threads_by_snapshot(pid, threads_info)) { // works on old Windows, but gives less data..
Expand All @@ -498,10 +497,14 @@ size_t pesieve::ProcessScanner::scanThreads(ProcessScanReport& pReport) //throws
return 0;
}
}
if (!pesieve::util::query_thread_details(threads_info)) {
if (!args.quiet) {
std::cout << "[-] Failed quering thread details." << std::endl;
}
}

std::vector<thread_info>::iterator itr;
for (itr = threads_info.begin(); itr != threads_info.end(); ++itr) {
const thread_info &info = *itr;
for (auto itr = threads_info.begin(); itr != threads_info.end(); ++itr) {
const thread_info &info = itr->second;

ThreadScanner scanner(this->processHandle, this->isReflection, info, pReport.modulesInfo, pReport.exportsMap);
ThreadScanReport* report = scanner.scanRemote();
Expand Down
89 changes: 53 additions & 36 deletions scanners/thread_scanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,8 @@ bool pesieve::ThreadScanner::isAddrInShellcode(ULONGLONG addr)
bool pesieve::ThreadScanner::resolveAddr(ULONGLONG addr)
{
bool is_resolved = false;
std::cout << std::hex << addr;
ScannedModule* mod = modulesInfo.findModuleContaining(addr);
std::cout << " > " << std::hex << addr;
if (mod) {
std::cout << " : " << mod->getModName();
is_resolved = true;
Expand Down Expand Up @@ -308,7 +308,7 @@ bool pesieve::ThreadScanner::fillAreaStats(ThreadScanReport* my_report)
return calc.fill(my_report->stats, nullptr);
}

bool pesieve::ThreadScanner::reportSuspiciousAddr(ThreadScanReport* my_report, ULONGLONG susp_addr, thread_ctx &c)
bool pesieve::ThreadScanner::reportSuspiciousAddr(ThreadScanReport* my_report, ULONGLONG susp_addr)
{
MEMORY_BASIC_INFORMATION page_info = { 0 };
if (!get_page_details(processHandle, (LPVOID)susp_addr, page_info)) {
Expand All @@ -326,7 +326,7 @@ bool pesieve::ThreadScanner::reportSuspiciousAddr(ThreadScanReport* my_report, U
my_report->moduleSize = page_info.RegionSize;
my_report->protection = page_info.AllocationProtect;

my_report->thread_ip = susp_addr;
my_report->susp_addr = susp_addr;
my_report->status = SCAN_SUSPICIOUS;
const bool isStatFilled = fillAreaStats(my_report);
#ifndef NO_ENTROPY_CHECK
Expand All @@ -338,7 +338,7 @@ bool pesieve::ThreadScanner::reportSuspiciousAddr(ThreadScanReport* my_report, U
}

// if extended info given, allow to filter out from the scan basing on the thread state and conditions
bool should_scan(const util::thread_info& info)
bool should_scan_context(const util::thread_info& info)
{
if (!info.is_extended) {
return true;
Expand All @@ -351,7 +351,7 @@ bool should_scan(const util::thread_info& info)
return false;
}
if (state == Waiting) {
if (info.ext.start_addr == 0) {
if (info.ext.sys_start_addr == 0) {
return true;
}
if (info.ext.wait_reason == DelayExecution
Expand All @@ -367,8 +367,44 @@ bool should_scan(const util::thread_info& info)
return false;
}

void pesieve::ThreadScanner::printInfo(const pesieve::util::thread_info& threadi)
{
std::cout << std::dec << "TID: " << threadi.tid << "\n";
std::cout << std::hex << "\tStart : ";
resolveAddr(threadi.start_addr);
if (threadi.is_extended) {
std::cout << std::hex << "\tSysStart: ";
resolveAddr(threadi.ext.sys_start_addr);
std::cout << "\tState: " << threadi.ext.state;
if (threadi.ext.state == Waiting) {
std::cout << " Reason: " << threadi.ext.wait_reason << " Time: " << threadi.ext.wait_time;
}
std::cout << "\n";
}
std::cout << "\n";
}

ThreadScanReport* pesieve::ThreadScanner::scanRemote()
{
ThreadScanReport* my_report = new ThreadScanReport(info.tid);
if (!my_report) return nullptr;
#ifdef _DEBUG
printInfo(info);
#endif
bool is_shc = isAddrInShellcode(info.start_addr);
if (is_shc) {
if (reportSuspiciousAddr(my_report, info.start_addr)) {
return my_report;
}
}
#ifndef _DEBUG
// if NOT compiled in a debug mode, make this check BEFORE scan
if (!should_scan_context(info)) {
my_report->status = SCAN_NOT_SUSPICIOUS;
return my_report;
}
#endif
// proceed with detailed checks:
HANDLE hThread = OpenThread(
THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION | SYNCHRONIZE,
FALSE,
Expand All @@ -380,29 +416,9 @@ ThreadScanReport* pesieve::ThreadScanner::scanRemote()
#endif
return nullptr;
}
#ifdef _DEBUG
std::cout << std::dec << "---\nTid: " << info.tid << "\n";
if (info.is_extended) {
std::cout << " Start: " << std::hex << info.ext.start_addr << std::dec << " State: " << info.ext.state;
if (info.ext.state == Waiting) {
std::cout << " WaitReason: " << info.ext.wait_reason
<< " WaitTime: " << info.ext.wait_time;
}
std::cout << "\n";
resolveAddr(info.ext.start_addr);
}
#endif
ThreadScanReport* my_report = new ThreadScanReport(info.tid);
#ifndef _DEBUG
// if NOT compiled in a debug mode, make this check BEFORE scan
if (!should_scan(info)) {
CloseHandle(hThread); // close the opened thread
my_report->status = SCAN_NOT_SUSPICIOUS;
return my_report;
}
#endif
thread_ctx c = { 0 };
const bool is_ok = fetchThreadCtx(processHandle, hThread, c);

thread_ctx ctx = { 0 };
const bool is_ok = fetchThreadCtx(processHandle, hThread, ctx);

DWORD exit_code = 0;
GetExitCodeThread(hThread, &exit_code);
Expand All @@ -414,11 +430,11 @@ ThreadScanReport* pesieve::ThreadScanner::scanRemote()
return my_report;
}
#ifdef _DEBUG
std::cout << " b:" << c.is64b << std::hex << " Rip: " << c.rip << " Rsp: " << c.rsp;
std::cout << " b:" << ctx.is64b << std::hex << " Rip: " << ctx.rip << " Rsp: " << c.rsp;
if (exit_code != STILL_ACTIVE)
std::cout << " ExitCode: " << exit_code;

if (c.ret_addr != 0) {
if (ctx.ret_addr != 0) {
std::cout << std::hex << " Ret: " << c.ret_addr;
}
std::cout << "\n";
Expand All @@ -431,21 +447,22 @@ ThreadScanReport* pesieve::ThreadScanner::scanRemote()
#ifdef _DEBUG
// if compiled in a debug mode, make this check AFTER scan
// (so that we can see first what was skipped)
if (!should_scan(info)) {
if (!should_scan_context(info)) {
my_report->status = SCAN_NOT_SUSPICIOUS;
return my_report;
}
#endif
bool is_shc = isAddrInShellcode(c.rip);

is_shc = isAddrInShellcode(ctx.rip);
if (is_shc) {
if (reportSuspiciousAddr(my_report, c.rip, c)) {
if (reportSuspiciousAddr(my_report, ctx.rip)) {
return my_report;
}
}
if ((c.ret_addr != 0) && (c.is_managed == false)) {
is_shc = isAddrInShellcode(c.ret_addr);
if ((ctx.ret_addr != 0) && (ctx.is_managed == false)) {
is_shc = isAddrInShellcode(ctx.ret_addr);
if (is_shc) {
if (reportSuspiciousAddr(my_report, c.ret_addr, c)) {
if (reportSuspiciousAddr(my_report, ctx.ret_addr)) {
return my_report;
}
}
Expand Down
12 changes: 7 additions & 5 deletions scanners/thread_scanner.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ namespace pesieve {

ThreadScanReport(DWORD _tid)
: ModuleScanReport(0, 0),
tid(_tid), thread_ip(0), protection(0),
tid(_tid),
susp_addr(0), protection(0),
thread_state(THREAD_STATE_UNKNOWN), thread_wait_reason(0)
{
}
Expand All @@ -31,8 +32,8 @@ namespace pesieve {
OUT_PADDED(outs, level, "\"thread_id\" : ");
outs << std::dec << tid;
outs << ",\n";
OUT_PADDED(outs, level, "\"thread_ip\" : ");
outs << "\"" << std::hex << thread_ip << "\"";
OUT_PADDED(outs, level, "\"susp_addr\" : ");
outs << "\"" << std::hex << susp_addr << "\"";
outs << ",\n";
if (thread_state != THREAD_STATE_UNKNOWN) {
OUT_PADDED(outs, level, "\"thread_state\" : ");
Expand Down Expand Up @@ -62,8 +63,8 @@ namespace pesieve {
return true;
}

ULONGLONG thread_ip;
DWORD tid;
ULONGLONG susp_addr;
DWORD protection;
DWORD thread_state;
DWORD thread_wait_reason;
Expand Down Expand Up @@ -99,11 +100,12 @@ namespace pesieve {
protected:

bool isAddrInShellcode(ULONGLONG addr);
void printInfo(const util::thread_info& threadi);
bool resolveAddr(ULONGLONG addr);
bool fetchThreadCtx(IN HANDLE hProcess, IN HANDLE hThread, OUT thread_ctx& c);
size_t enumStackFrames(IN HANDLE hProcess, IN HANDLE hThread, IN LPVOID ctx, IN OUT thread_ctx& c);
bool fillAreaStats(ThreadScanReport* my_report);
bool reportSuspiciousAddr(ThreadScanReport* my_report, ULONGLONG susp_addr, thread_ctx& c);
bool reportSuspiciousAddr(ThreadScanReport* my_report, ULONGLONG susp_addr);

bool isReflection;
const util::thread_info& info;
Expand Down
81 changes: 55 additions & 26 deletions utils/threads_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,51 @@

#ifdef _DEBUG
#include <iostream>
#endif

void print_info(const pesieve::util::thread_info &threadi)
{
std::cout << std::dec << "TID: " << threadi.tid;
if (threadi.is_extended) {
std::cout << std::hex << " Start: " << threadi.ext.start_addr << " State: " << threadi.ext.state;
if (threadi.ext.state == Waiting) {
std::cout << " Reason: " << threadi.ext.wait_reason << " Time: " << threadi.ext.wait_time;
namespace pesieve {
namespace util {

bool query_thread_start(IN DWORD tid, OUT ULONGLONG& startAddr)
{
static auto mod = GetModuleHandleA("ntdll.dll");
if (!mod) return false;

static auto pNtQueryInformationThread = reinterpret_cast<decltype(&NtQueryInformationThread)>(GetProcAddress(mod, "NtQueryInformationThread"));
if (!pNtQueryInformationThread) return false;

DWORD thAccess = THREAD_QUERY_INFORMATION;
HANDLE hThread = OpenThread(thAccess, 0, tid);
if (!hThread) return false;

ULONG returnedLen = 0;
NTSTATUS status = pNtQueryInformationThread(hThread, ThreadQuerySetWin32StartAddress, &startAddr, sizeof(startAddr), &returnedLen);
CloseHandle(hThread);

if (status != 0 || returnedLen != sizeof(startAddr)) {
#ifdef _DEBUG
std::cerr << "Failed to query thread: " << std::hex << status << "\n";
#endif
return false;
}
//std::cout << "\tStart: " << std::hex << startAddr;
return true;
}

}; // namespace util
}; // namespace pesieve


bool pesieve::util::query_thread_details(IN OUT std::map<DWORD, pesieve::util::thread_info>& threads_info)
{
for (auto itr = threads_info.begin(); itr != threads_info.end(); ++itr) {
pesieve::util::thread_info& info = itr->second;
if (!query_thread_start(info.tid, info.start_addr)) return false;
}
std::cout << "\n";
return true;
}
#endif

bool pesieve::util::fetch_threads_info(DWORD pid, std::vector<thread_info>& threads_info)
bool pesieve::util::fetch_threads_info(IN DWORD pid, OUT std::map<DWORD, thread_info>& threads_info)
{
AutoBuffer bBuf;

Expand Down Expand Up @@ -70,25 +100,25 @@ bool pesieve::util::fetch_threads_info(DWORD pid, std::vector<thread_info>& thre
return false;
}

size_t thread_count = info->NumberOfThreads;
const size_t thread_count = info->NumberOfThreads;
for (size_t i = 0; i < thread_count; i++) {
thread_info threadi;

threadi.tid = MASK_TO_DWORD((ULONGLONG)info->Threads[i].ClientId.UniqueThread);

const DWORD tid = MASK_TO_DWORD((ULONGLONG)info->Threads[i].ClientId.UniqueThread);
auto itr = threads_info.find(tid);
if (itr == threads_info.end()) {
threads_info[tid] = thread_info(tid);
}
thread_info &threadi = threads_info[tid];
threadi.is_extended = true;
threadi.ext.start_addr = (ULONG_PTR)info->Threads[i].StartAddress;
threadi.ext.sys_start_addr = (ULONG_PTR)info->Threads[i].StartAddress;
threadi.ext.state = info->Threads[i].ThreadState;
threadi.ext.wait_reason = info->Threads[i].WaitReason;
threadi.ext.wait_time = info->Threads[i].WaitTime;
threads_info.push_back(threadi);
#ifdef _DEBUG
print_info(threadi);
#endif
}
return true;
}

bool pesieve::util::fetch_threads_by_snapshot(DWORD pid, std::vector<thread_info>& threads_info)
bool pesieve::util::fetch_threads_by_snapshot(IN DWORD pid, OUT std::map<DWORD, thread_info>& threads_info)
{
HANDLE hThreadSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (hThreadSnapShot == INVALID_HANDLE_VALUE) {
Expand All @@ -113,12 +143,11 @@ bool pesieve::util::fetch_threads_by_snapshot(DWORD pid, std::vector<thread_info
if (th32.th32OwnerProcessID != pid) {
continue;
}

thread_info threadi;
threadi.tid = th32.th32ThreadID;
threadi.is_extended = false;
threads_info.push_back(threadi);

const DWORD tid = th32.th32ThreadID;
auto itr = threads_info.find(tid);
if (itr == threads_info.end()) {
threads_info[tid] = thread_info(tid);
}
} while (Thread32Next(hThreadSnapShot, &th32));

CloseHandle(hThreadSnapShot);
Expand Down
Loading

0 comments on commit 313c311

Please sign in to comment.