From 45c76ad2f5c9f36e0bd3c5dbfd36f07e870a828c Mon Sep 17 00:00:00 2001 From: DB Date: Sun, 8 Mar 2026 15:14:19 +0530 Subject: [PATCH] Update README --- README.md | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/trace.rs | 23 +++++++----------- 2 files changed, 73 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 21c50bb..91475fe 100644 --- a/README.md +++ b/README.md @@ -1 +1,65 @@ -# StackTracer +# StackTracer + +[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/whokilleddb) + +The goal of this project is find valid stack frames which you can replicate in your payloads. In C2s like BRC4, you can specify a stack frame which looks legit - and this can help in increasing the evasiveness of your shellcode. Using this tool you can examine legit processes for their stack frames and copy them over. + +## Building + +Building this tool is as easy as rust makes it: + +``` +$ cargo build --release +``` + +Note that you might need to install the `x86_64-pc-windows-gnu` toolchain + +## Usage: + +To view a detailed usage guide, you can pass the `--help` flag: + +``` +Z:\> stackfinder.exe --help + _____ _ _ _______ + / ____| | | |__ __| +| (___ | |_ __ _ ___| | _| |_ __ __ _ ___ ___ _ __ + \___ \| __/ _` |/ __| |/ / | '__/ _` |/ __/ _ \ '__| + ____) | || (_| | (__| <| | | | (_| | (_| __/ | +|_____/ \__\__,_|\___|_|\_\_|_| \__,_|\___\___|_| + DB @whokilleddb + +A rust program to print the stack trace of a given thread + +Usage: stackfinder.exe [OPTIONS] --pid + +Options: + --pid Process ID + --tid Thread ID (defaults to 0) [default: 0] + --hide-banner Hide the banner + -h, --help Print help + -V, --version Print version +``` + +The only required option is the `--pid` flag. To enumerate a process without targetting a particular thread, you go like: + +``` +Z:\> stackfinder.exe --pid 1234 +``` + +To enumerate a specific thread, you can specify the ThreadID using the `--tid` flag: + +``` +Z:\>.\stackfinder.exe --pid 3688 --tid 7336 + _____ _ _ _______ + / ____| | | |__ __| +| (___ | |_ __ _ ___| | _| |_ __ __ _ ___ ___ _ __ + \___ \| __/ _` |/ __| |/ / | '__/ _` |/ __/ _ \ '__| + ____) | || (_| | (__| <| | | | (_| | (_| __/ | +|_____/ \__\__,_|\___|_|\_\_|_| \__,_|\___\___|_| + DB @whokilleddb + +PID | TID | STACK FRAME +3688 | 7336 | win32u.dll!NtUserWaitMessage+0x14,USER32.DLL!IsDialogMessageA+0x3ba,USER32.DLL!Ordinal2635+0x267,USER32.DLL!SoftModalMessageBox+0x5b6,USER32.DLL!MessageBoxIndirectA+0x562,USER32.DLL!MessageBoxTimeoutW+0x18f,USER32.DLL!MessageBoxTimeoutA+0x100,USER32.DLL!MessageBoxA+0x45,0x25c70680050,KERNEL32.DLL+0x0 +``` + +_Hope this helps!_ diff --git a/src/trace.rs b/src/trace.rs index c4397de..070850e 100644 --- a/src/trace.rs +++ b/src/trace.rs @@ -5,9 +5,8 @@ use std::path::Path; use winapi::shared::minwindef::{DWORD, FALSE}; // use winapi::shared::ntdef::NULL; use winapi::um::dbghelp::{ - AddrModeFlat, IMAGEHLP_MODULEW64, STACKFRAME64, SYMBOL_INFOW, SYMOPT_DEFERRED_LOADS, - SYMOPT_UNDNAME, StackWalk64, SymFromAddrW, SymFunctionTableAccess64, SymGetModuleBase64, - SymGetModuleInfoW64, + AddrModeFlat, IMAGEHLP_MODULEW64, STACKFRAME64, SYMBOL_INFOW, StackWalk64, SymFromAddrW, + SymFunctionTableAccess64, SymGetModuleBase64, SymGetModuleInfoW64, }; use winapi::um::errhandlingapi::GetLastError; use winapi::um::handleapi::CloseHandle; @@ -18,9 +17,6 @@ use winapi::um::winnt::{ }; const MAX_SYM_NAME_LEN: usize = 512; -// --------------------------------------------------------------------------- -// Resolve a single PC address to "module.dll!Symbol+0xNN" / "module.dll+0xNN" -// --------------------------------------------------------------------------- fn resolve_address(h_process: HANDLE, addr: u64) -> String { unsafe { @@ -41,9 +37,6 @@ fn resolve_address(h_process: HANDLE, addr: u64) -> String { String::new() }; - // ── 2. Try to resolve to a symbol via SymFromAddrW ── - // SYMBOL_INFOW has a trailing `Name[1]` flexible-array member; - // we over-allocate to hold up to MAX_SYM_NAME_LEN wide characters. let buf_size = mem::size_of::() + MAX_SYM_NAME_LEN * mem::size_of::(); let mut buf = vec![0u8; buf_size]; let sym = buf.as_mut_ptr() as *mut SYMBOL_INFOW; @@ -52,6 +45,7 @@ fn resolve_address(h_process: HANDLE, addr: u64) -> String { let mut sym_displacement: u64 = 0; + // https://learn.microsoft.com/en-us/windows/win32/debug/retrieving-symbol-information-by-address if SymFromAddrW(h_process, addr, &mut sym_displacement, sym) != 0 { // We have a symbol name (wide) let name_slice = @@ -113,7 +107,7 @@ pub fn trace_thread_stack(pid: u32, h_process: HANDLE, tid: u32) { frame.AddrStack.Mode = AddrModeFlat; let mut entries: Vec = Vec::new(); - // ── Walk the stack ── + loop { let ok = StackWalk64( IMAGE_FILE_MACHINE_AMD64 as DWORD, @@ -133,16 +127,15 @@ pub fn trace_thread_stack(pid: u32, h_process: HANDLE, tid: u32) { entries.push(resolve_address(h_process, frame.AddrPC.Offset)); } - // ── Output ── + + println!("PID\t| TID\t| STACK FRAME"); if entries.is_empty() { - println!("{} | {} | ", pid, tid); + println!("{}\t| {}\t| ", pid, tid); } else { - println!("{} | {} | {}", pid, tid, entries.join(",")); + println!("{}\t| {}\t| {}", pid, tid, entries.join(",")); } break; } - // Initialize DbgHelp symbol handler - // let opts = SymGetOptions(); if !h_thread.is_null() { CloseHandle(h_thread);