diff --git a/scanners/scanner.cpp b/scanners/scanner.cpp index 014bebc9e..f73825c5c 100644 --- a/scanners/scanner.cpp +++ b/scanners/scanner.cpp @@ -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()) { @@ -487,7 +486,7 @@ size_t pesieve::ProcessScanner::scanThreads(ProcessScanReport& pReport) //throws } DWORD start_tick = GetTickCount(); - std::vector threads_info; + std::map 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.. @@ -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::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(); diff --git a/scanners/thread_scanner.cpp b/scanners/thread_scanner.cpp index e8c382fa8..5479f49cb 100644 --- a/scanners/thread_scanner.cpp +++ b/scanners/thread_scanner.cpp @@ -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; @@ -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)) { @@ -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 @@ -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; @@ -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 @@ -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, @@ -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); @@ -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"; @@ -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; } } diff --git a/scanners/thread_scanner.h b/scanners/thread_scanner.h index 7307170c3..9e79cf1cb 100644 --- a/scanners/thread_scanner.h +++ b/scanners/thread_scanner.h @@ -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) { } @@ -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\" : "); @@ -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; @@ -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; diff --git a/utils/threads_util.cpp b/utils/threads_util.cpp index af64ec04f..1777f3e84 100644 --- a/utils/threads_util.cpp +++ b/utils/threads_util.cpp @@ -7,21 +7,51 @@ #ifdef _DEBUG #include +#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(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& 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& threads_info) +bool pesieve::util::fetch_threads_info(IN DWORD pid, OUT std::map& threads_info) { AutoBuffer bBuf; @@ -70,25 +100,25 @@ bool pesieve::util::fetch_threads_info(DWORD pid, std::vector& 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& threads_info) +bool pesieve::util::fetch_threads_by_snapshot(IN DWORD pid, OUT std::map& threads_info) { HANDLE hThreadSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (hThreadSnapShot == INVALID_HANDLE_VALUE) { @@ -113,12 +143,11 @@ bool pesieve::util::fetch_threads_by_snapshot(DWORD pid, std::vector -#include +#include namespace pesieve { namespace util { typedef struct _thread_info_ext { - ULONGLONG start_addr; + ULONGLONG sys_start_addr; DWORD state; DWORD wait_reason; DWORD wait_time; _thread_info_ext() { - this->start_addr = 0; + this->sys_start_addr = 0; this->state = 0; this->wait_reason = 0; this->wait_time = 0; @@ -23,7 +23,7 @@ namespace pesieve { _thread_info_ext(const _thread_info_ext& other) { - this->start_addr = other.start_addr; + this->sys_start_addr = other.sys_start_addr; this->state = other.state; this->wait_reason = other.wait_reason; this->wait_time = other.wait_time; @@ -34,27 +34,35 @@ namespace pesieve { typedef struct _thread_info { DWORD tid; + ULONGLONG start_addr; bool is_extended; thread_info_ext ext; _thread_info() + : tid(0), start_addr(0), is_extended(false) + { + } + + _thread_info(DWORD _tid) + : tid(_tid), start_addr(0), is_extended(false) { - this->tid = 0; - this->is_extended = false; } _thread_info(const _thread_info& other) { this->tid = other.tid; + this->start_addr = other.start_addr; this->is_extended = other.is_extended; this->ext = other.ext; } } thread_info; - bool fetch_threads_info(DWORD pid, std::vector& threads_info); + bool query_thread_details(IN OUT std::map& threads_info); + + bool fetch_threads_info(IN DWORD pid, OUT std::map& threads_info); - bool fetch_threads_by_snapshot(DWORD pid, std::vector& threads_info); + bool fetch_threads_by_snapshot(IN DWORD pid, OUT std::map& threads_info); }; // namespace util }; // namespace pesieve