5#include "../utils/ntddk.h"
12#define ENTROPY_TRESHOLD 3.0
16#define _SHOW_THREAD_INFO
44 if (GetExitCodeThread(hThread, &exit_code)) {
45 if (exit_code != STILL_ACTIVE) {
47 std::cout <<
" Thread ExitCode: " << std::dec << exit_code <<
"\n";
57bool get_page_details(HANDLE processHandle, LPVOID start_va, MEMORY_BASIC_INFORMATION& page_info)
59 size_t page_info_size =
sizeof(MEMORY_BASIC_INFORMATION);
60 const SIZE_T out = VirtualQueryEx(processHandle, (LPCVOID)start_va, &page_info, page_info_size);
61 const bool is_read = (out == page_info_size) ?
true :
false;
62 const DWORD error = is_read ? ERROR_SUCCESS : GetLastError();
63 if (error != ERROR_SUCCESS) {
74 return STATUS_INVALID_PARAMETER;
80 STACKFRAME64 frame = { 0 };
82 frame.AddrPC.Offset = cDetails.
rip;
83 frame.AddrPC.Mode = AddrModeFlat;
84 frame.AddrStack.Offset = cDetails.
rsp;
85 frame.AddrStack.Mode = AddrModeFlat;
86 frame.AddrFrame.Offset = cDetails.
rbp;
87 frame.AddrFrame.Mode = AddrModeFlat;
89 while (StackWalk64(IMAGE_FILE_MACHINE_AMD64, args->
hProcess, args->
hThread, &frame, args->
ctx, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) {
90 const ULONGLONG next_addr = frame.AddrPC.Offset;
96 if (!cDetails.
is64b) {
97 STACKFRAME frame = { 0 };
99 frame.AddrPC.Offset = cDetails.
rip;
100 frame.AddrPC.Mode = AddrModeFlat;
101 frame.AddrStack.Offset = cDetails.
rsp;
102 frame.AddrStack.Mode = AddrModeFlat;
103 frame.AddrFrame.Offset = cDetails.
rbp;
104 frame.AddrFrame.Mode = AddrModeFlat;
106 while (StackWalk(IMAGE_FILE_MACHINE_I386, args->
hProcess, args->
hThread, &frame, args->
ctx, NULL, SymFunctionTableAccess, SymGetModuleBase, NULL)) {
107 const ULONGLONG next_return = frame.AddrPC.Offset;
114 return STATUS_SUCCESS;
116 return STATUS_UNSUCCESSFUL;
121 GUITHREADINFO gui = { 0 };
122 gui.cbSize =
sizeof(GUITHREADINFO);
123 if (!GetGUIThreadInfo(tid, &gui)) {
126 bool hasWindows = gui.hwndActive || gui.hwndCapture || gui.hwndCaret || gui.hwndMenuOwner || gui.hwndMoveSize;
127 bool hasRcCaret = gui.rcCaret.left || gui.rcCaret.right || gui.rcCaret.bottom || gui.rcCaret.top;
128 if (hasWindows || hasRcCaret) {
139 case DelayExecution:
return "DelayExecution";
140 case Suspended:
return "Suspended";
141 case Executive:
return "Executive";
142 case UserRequest:
return "UserRequest";
143 case WrUserRequest:
return "WrUserRequest";
144 case WrEventPair:
return "WrEventPair";
145 case WrQueue:
return "WrQueue";
147 std::stringstream ss;
155 case Initialized:
return "Initialized";
156 case Ready:
return "Ready";
157 case Running:
return "Running";
158 case Standby:
return "Standby";
159 case Terminated:
return "Terminated";
160 case Waiting:
return "Waiting";
161 case Transition:
return "Transition";
162 case DeferredReady:
return "DeferredReady";
163 case GateWaitObsolete:
return "GateWaitObsolete";
164 case WaitingForProcessInSwap:
return "WaitingForProcessInSwap";
166 std::stringstream ss;
180 const ULONGLONG lastCalled = *callStack.begin();
181 const std::string lastFuncCalled = symbols->funcNameFromAddr(lastCalled);
182 if (callStack.size() == 1) {
183 if (this->
info.ext.wait_reason == Suspended && lastFuncCalled ==
"RtlUserThreadStart" && this->info.last_syscall == 0) {
190 if (!isWow64 && lastFuncCalled ==
"KiFastSystemCallRet") {
198 if (this->
info.ext.wait_reason == UserRequest && syscallFuncName ==
"NtWaitForSingleObject") {
199 if (lastFuncCalled.rfind(
"NtQuery", 0) == 0 || lastFuncCalled.rfind(
"ZwQuery", 0) == 0) {
203 if (syscallFuncName ==
"NtCallbackReturn") {
204 const ScannedModule* mod = modulesInfo.findModuleContaining(lastCalled);
205 if (mod && mod->
getModName() ==
"win32u.dll")
return true;
207#ifdef _SHOW_THREAD_INFO
208 std::cout <<
"\n#### TID=" << std::dec <<
info.tid <<
" " << syscallFuncName <<
" VS " << lastFuncCalled <<
" DIFFERENT"<< std::endl;
209 printThreadInfo(
info);
210 std::cout <<
"STACK:\n";
211 for (
auto itr = callStack.rbegin(); itr != callStack.rend(); ++itr) {
212 ULONGLONG next_return = *itr;
213 symbols->dumpSymbolInfo(next_return);
215 printResolvedAddr(next_return);
217 std::cout << std::endl;
224 size_t processedCntr = 0;
226 cDetails.is_managed =
false;
227 cDetails.stackFramesCount = call_stack.size();
228 cDetails.is_ret_in_frame =
false;
229#ifdef _SHOW_THREAD_INFO
230 std::cout <<
"\n" <<
"Stack frame Size: " << std::dec << call_stack.size() <<
"\n===\n";
232 for (
auto itr = call_stack.rbegin(); itr != call_stack.rend() ;++itr, ++processedCntr) {
233 const ULONGLONG next_return = *itr;
234 if (cDetails.ret_on_stack == next_return) {
235 cDetails.is_ret_in_frame =
true;
237#ifdef _SHOW_THREAD_INFO
239 symbols->dumpSymbolInfo(next_return);
242 printResolvedAddr(next_return);
244 bool is_curr_shc =
false;
245 const ScannedModule* mod = modulesInfo.findModuleContaining(next_return);
246 const std::string mod_name = mod ? mod->
getModName() :
"";
247 if (mod_name.length() == 0) {
248 if (!cDetails.is_managed) {
250 cDetails.shcCandidates.insert(next_return);
251#ifdef _SHOW_THREAD_INFO
252 std::cout <<
"\t" << std::hex << next_return <<
" <=== SHELLCODE\n";
255#ifdef _SHOW_THREAD_INFO
256 std::cout <<
"\t" << std::hex << next_return <<
" <=== .NET JIT\n";
262 cDetails.last_ret = next_return;
265 if (mod_name ==
"clr.dll" || mod_name ==
"coreclr.dll") {
266 cDetails.is_managed =
true;
267#ifdef _SHOW_THREAD_INFO
268 std::cout <<
"\t" << std::hex << next_return <<
" <--- .NET\n";
272#ifdef _SHOW_THREAD_INFO
273 std::cout <<
"\n===\n";
275 return processedCntr;
283 const size_t max_wait = 1000;
285 HANDLE enumThread = CreateThread(
294 DWORD wait_result = WaitForSingleObject(enumThread, max_wait);
295 if (wait_result == WAIT_TIMEOUT) {
296 std::cerr <<
"[!] Cannot retrieve stack frame: timeout passed!\n";
297 TerminateThread(enumThread, 0);
298 CloseHandle(enumThread);
301 CloseHandle(enumThread);
307#ifdef _SHOW_THREAD_INFO
308 std::cout <<
"\n=== TID " << std::dec << GetThreadId(hThread) <<
" ===\n";
310 const size_t analyzedCount = analyzeCallStack(args.
callStack, cDetails);
311 if (!cDetails.is_managed) {
312 cDetails.is_ret_as_syscall = checkReturnAddrIntegrity(args.
callStack);
314 return analyzedCount;
317template <
typename PTR_T>
320 cDetails.ret_on_stack = 0;
321 if (peconv::read_remote_memory(hProcess, (LPVOID)cDetails.rsp, (BYTE*)&ret_addr,
sizeof(ret_addr)) ==
sizeof(ret_addr)) {
322 cDetails.ret_on_stack = (ULONGLONG)ret_addr;
331 BOOL is_wow64 = FALSE;
332 size_t retrieved = 0;
337 WOW64_CONTEXT ctx = { 0 };
338 ctx.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
341 cDetails.init(
false, ctx.Eip, ctx.Esp, ctx.Ebp);
343 retrieved = fillCallStackInfo(hProcess, hThread, &ctx, cDetails);
350 ctx.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
351 if (GetThreadContext(hThread, &ctx)) {
354 cDetails.init(
true, ctx.Rip, ctx.Rsp, ctx.Rbp);
357 cDetails.init(
false, ctx.Eip, ctx.Esp, ctx.Ebp);
360 retrieved = fillCallStackInfo(hProcess, hThread, &ctx, cDetails);
363 if (!retrieved) is_ok =
false;
370 if (!mod)
return true;
381 bool is_resolved =
false;
382 std::cout << std::hex << addr;
388 if (exportsMap && is_resolved) {
389 bool search_name =
false;
393 for (
size_t i = 0; i < 25; i++) {
394 const peconv::ExportedFunc* exp = exportsMap->find_export_by_va(addr - i);
396 std::cout <<
" : " << exp->toString();
405 std::cout << std::endl;
411 std::cout << std::dec <<
"TID: " << threadi.
tid <<
"\n";
412 std::cout << std::hex <<
"\tStart : ";
416 std::cout << std::hex <<
"\tSysStart: ";
432 if (!my_report)
return false;
435 MemPageData mem(this->processHandle, this->isReflection, (ULONG_PTR)my_report->
module, end_va);
440 return calc.
fill(my_report->
stats,
nullptr);
445 MEMORY_BASIC_INFORMATION page_info = { 0 };
449 if (page_info.State & MEM_FREE) {
452 ULONGLONG base = (ULONGLONG)page_info.BaseAddress;
453 if (this->
info.is_extended) {
458 my_report->
module = (HMODULE)base;
460 my_report->
protection = page_info.AllocationProtect;
464 const bool isStatFilled = fillAreaStats(my_report);
465#ifndef NO_ENTROPY_CHECK
476 if (!
info.is_extended) {
479 const KTHREAD_STATE state = (KTHREAD_STATE)
info.ext.state;
480 if (state == Ready) {
483 if (state == Terminated) {
486 if (state == Waiting &&
info.ext.wait_reason <= WrQueue) {
494 const DWORD tid = GetThreadId(hThread);
496 const bool is_ok = fetchThreadCtxDetails(processHandle, hThread, cDetails);
509 bool is_shc = isAddrInShellcode(cDetails.
rip);
511 if (reportSuspiciousAddr(my_report, cDetails.
rip)) {
519 const ULONGLONG addr = *itr;
520#ifdef _SHOW_THREAD_INFO
521 std::cout <<
"Checking shc candidate: " << std::hex << addr <<
"\n";
524 if (reportSuspiciousAddr(my_report, addr)) {
526#ifdef _SHOW_THREAD_INFO
527 std::cout <<
"Found! " << std::hex << addr <<
"\n";
539 is_shc = isAddrInShellcode(ret_addr);
540#ifdef _SHOW_THREAD_INFO
541 std::cout <<
"Return addr: " << std::hex << ret_addr <<
"\n";
542 printResolvedAddr(ret_addr);
544 if (is_shc && reportSuspiciousAddr(my_report, (ULONGLONG)ret_addr)) {
559 bool isStackCorrupt =
false;
563 isStackCorrupt =
true;
568 && this->info.is_extended &&
info.ext.state == Waiting &&
info.ext.wait_reason == UserRequest)
570 isStackCorrupt =
true;
573 if (isStackCorrupt) {
586 if (GetCurrentThreadId() ==
info.tid) {
593#ifdef _SHOW_THREAD_INFO
594 printThreadInfo(
info);
597 bool is_shc = isAddrInShellcode(
info.start_addr);
599 if (reportSuspiciousAddr(my_report,
info.start_addr)) {
610 HANDLE hThread = OpenThread(
611 THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION | SYNCHRONIZE,
617 std::cerr <<
"[-] Could not OpenThread. Error: " << GetLastError() << std::endl;
622 scanRemoteThreadCtx(hThread, my_report);
623 CloseHandle(hThread);
A class responsible for filling in the statistics with the data from the particular buffer.
bool fill(AreaStats &stats, StatsSettings *settings)
util::ByteBuffer loadedData
Represents a basic info about the scanned module, such as its base offset, size, and the status.
std::string getModName() const
A report from the thread scan, generated by ThreadScanner.
static std::string translate_wait_reason(DWORD thread_wait_reason)
static std::string translate_thread_state(DWORD thread_state)
virtual ThreadScanReport * scanRemote()
bool reportSuspiciousAddr(ThreadScanReport *my_report, ULONGLONG susp_addr)
size_t fillCallStackInfo(IN HANDLE hProcess, IN HANDLE hThread, IN LPVOID ctx, IN OUT ctx_details &cDetails)
void printThreadInfo(const util::thread_info &threadi)
bool fillAreaStats(ThreadScanReport *my_report)
bool isAddrInShellcode(ULONGLONG addr)
size_t analyzeCallStack(IN const std::vector< ULONGLONG > &stack_frame, IN OUT ctx_details &cDetails)
bool fetchThreadCtxDetails(IN HANDLE hProcess, IN HANDLE hThread, OUT ctx_details &c)
bool printResolvedAddr(ULONGLONG addr)
bool scanRemoteThreadCtx(HANDLE hThread, ThreadScanReport *my_report)
bool checkReturnAddrIntegrity(IN const std::vector< ULONGLONG > &callStack)
BOOL is_process_wow64(IN HANDLE processHandle, OUT BOOL *isProcWow64)
BOOL wow64_get_thread_context(IN HANDLE hThread, IN OUT PWOW64_CONTEXT lpContext)
bool is_thread_running(HANDLE hThread)
std::string info()
The string with the basic information about the scanner.
pesieve::SyscallTable g_SyscallTable
_t_stack_enum_params(IN HANDLE _hProcess=NULL, IN HANDLE _hThread=NULL, IN LPVOID _ctx=NULL, IN const pesieve::ctx_details *_cDetails=NULL)
const pesieve::ctx_details * cDetails
std::vector< ULONGLONG > callStack
A custom structure keeping a fragment of a thread context.
std::set< ULONGLONG > shcCandidates
static bool isSameSyscallFunc(std::string func1, std::string func2)
std::string getSyscallName(DWORD id)
bool has_empty_gui_info(DWORD tid)
struct _t_stack_enum_params t_stack_enum_params
bool get_page_details(HANDLE processHandle, LPVOID start_va, MEMORY_BASIC_INFORMATION &page_info)
bool read_return_ptr(IN HANDLE hProcess, IN OUT ctx_details &cDetails)
DWORD WINAPI enum_stack_thread(LPVOID lpParam)
bool should_scan_context(const util::thread_info &info)
pesieve::SyscallTable g_SyscallTable