made things better

This commit is contained in:
DB
2026-03-08 13:21:47 +05:30
parent 1c18c58b21
commit 2de212bc9b
5 changed files with 230 additions and 67 deletions
Generated
+123
View File
@@ -2,13 +2,121 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "anstream"
version = "0.6.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
[[package]]
name = "anstyle-parse"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys",
]
[[package]]
name = "clap"
version = "4.5.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.5.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_lex"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
[[package]]
name = "colorchoice"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]]
name = "once_cell_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "stackfinder"
version = "0.1.0"
dependencies = [
"clap",
"winapi",
]
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "winapi"
version = "0.3.9"
@@ -30,3 +138,18 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
+2
View File
@@ -2,8 +2,10 @@
name = "stackfinder"
version = "0.1.0"
edition = "2024"
description = "A rust program to print the stack trace of a given thread"
[dependencies]
clap = { version = "4.5.60" }
winapi = { version = "0.3.9", features = [
"tlhelp32",
"processthreadsapi",
+63
View File
@@ -0,0 +1,63 @@
use clap::{Arg, ArgAction, Command};
pub struct CliArgs {
pub pid: u32,
pub tid: u32,
pub hide_banner: bool,
}
const BANNER: &str = r#" _____ _ _ _______
/ ____| | | |__ __|
| (___ | |_ __ _ ___| | _| |_ __ __ _ ___ ___ _ __
\___ \| __/ _` |/ __| |/ / | '__/ _` |/ __/ _ \ '__|
____) | || (_| | (__| <| | | | (_| | (_| __/ |
|_____/ \__\__,_|\___|_|\_\_|_| \__,_|\___\___|_|
DB @whokilleddb"#;
impl CliArgs {
pub fn banner() {
// Load the standard FIGlet font
println!("{}", BANNER);
}
pub fn parse() -> Self {
let matches = Command::new(env!("CARGO_PKG_NAME"))
.version(env!("CARGO_PKG_VERSION"))
.about(env!("CARGO_PKG_DESCRIPTION"))
.author(env!("CARGO_PKG_AUTHORS"))
.help_template(
"{before-help}\
{about}\n\n\
{usage-heading} {usage}\n\n\
{all-args}",
)
.before_help(BANNER)
.arg(
Arg::new("pid")
.long("pid")
.help("Process ID")
.required(true)
.value_parser(clap::value_parser!(u32)),
)
.arg(
Arg::new("tid")
.long("tid")
.help("Thread ID (defaults to 0)")
.default_value("0")
.value_parser(clap::value_parser!(u32)),
)
.arg(
Arg::new("hide-banner")
.long("hide-banner")
.help("Hide the banner")
.action(ArgAction::SetTrue),
)
.get_matches();
CliArgs {
pid: *matches.get_one::<u32>("pid").unwrap(),
tid: *matches.get_one::<u32>("tid").unwrap(),
hide_banner: matches.get_flag("hide-banner"),
}
}
}
+25 -44
View File
@@ -1,25 +1,18 @@
use std;
mod procenum;
mod trace;
#![cfg(all(target_os = "windows", target_arch = "x86_64"))]
use std;
use winapi::shared::minwindef::{FALSE, TRUE};
use winapi::um::dbghelp::{
SYMBOL_INFOW, SYMOPT_DEFERRED_LOADS, SYMOPT_UNDNAME, SymInitializeW, SymSetOptions,
SYMOPT_DEFERRED_LOADS, SYMOPT_UNDNAME, SymCleanup, SymInitializeW, SymSetOptions,
};
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::handleapi::CloseHandle;
use winapi::um::processthreadsapi::OpenProcess;
use winapi::um::winnt::HANDLE;
use winapi::um::winnt::{PROCESS_QUERY_INFORMATION, PROCESS_VM_READ};
use winapi::um::winnt::{HANDLE, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ};
fn usage() {
println!("[+] Usage:\n\tstackfinder.exe PID <TID>\n");
println!("[+] Description:\n\tPrint the stack trace of a Thread of a given process\n");
println!(
"[+] Values:\n\tPID\tPID of process to target\n\tTID\t[Optional] TID of the target thread. If not specified,\n\t\tit will print the stack trace of all current threads."
);
std::process::exit(-1);
}
mod cli;
mod procenum;
mod trace;
fn stacktrace(pid: u32, tid: u32) {
let target_tids = procenum::collect_threads(pid, tid);
@@ -36,7 +29,7 @@ fn stacktrace(pid: u32, tid: u32) {
);
return;
}
println!("[+] Opened handle to Process:\t{:?}", h_process);
// println!("[+] Opened handle to Process:\t{:?}", h_process);
// Initialize symbols
SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS);
@@ -45,10 +38,18 @@ fn stacktrace(pid: u32, tid: u32) {
CloseHandle(h_process);
return;
}
println!("[+] Initialized Symbols!");
// println!("[+] Initialized Symbols!");
for x in &target_tids {
trace::trace_thread_stack(h_process, *x);
trace::trace_thread_stack(pid, h_process, *x);
}
if FALSE == SymCleanup(h_process) {
eprintln!(
"[!] SymCleanup failed for PID {} (error {})",
pid,
GetLastError()
);
}
if !h_process.is_null() {
CloseHandle(h_process);
@@ -57,37 +58,17 @@ fn stacktrace(pid: u32, tid: u32) {
}
fn main() {
let args: Vec<String> = std::env::args().collect();
if (args.len() < 2) || (args.len() > 3) {
usage();
let c_args: cli::CliArgs = cli::CliArgs::parse();
if !c_args.hide_banner {
cli::CliArgs::banner();
println!();
}
let pid: u32;
let mut tid: u32 = 0;
// println!("[+] Targeting PID:\t\t{}", c_args.pid);
pid = match args[1].parse() {
Ok(id) => id,
Err(_e) => {
eprintln!("[-] Provided PID is invalid: {:#?}", args[1]);
std::process::exit(-1);
}
};
println!("[+] Targeting PID:\t\t{}", pid);
if args.len() == 3 {
tid = match args[2].parse() {
Ok(id) => id,
Err(_e) => {
eprintln!("[-] Provided TID is invalid: {:#?}", args[1]);
std::process::exit(-1);
}
};
}
if pid == std::process::id() {
if c_args.pid == std::process::id() {
eprintln!("[-] DO NOT RUN THIS PROGRAM AGAINST THE CURRENT PROCESS");
std::process::exit(-1);
}
stacktrace(pid, tid);
stacktrace(c_args.pid, c_args.tid);
}
+17 -23
View File
@@ -1,24 +1,22 @@
#![allow(unused_imports)]
use std::ffi::CStr;
// use std::ffi::CStr;
use std::mem;
use std::path::Path;
use std::ptr;
use winapi::shared::minwindef::{DWORD, FALSE, TRUE};
use winapi::shared::ntdef::NULL;
// use std::ptr;
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, SymCleanup, SymFromAddrW, SymFunctionTableAccess64,
SymGetModuleBase64, SymGetModuleInfoW64, SymInitializeW, SymSetOptions,
SYMOPT_UNDNAME, StackWalk64, SymFromAddrW, SymFunctionTableAccess64, SymGetModuleBase64,
SymGetModuleInfoW64,
};
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::handleapi::CloseHandle;
use winapi::um::processthreadsapi::{
GetThreadContext, OpenProcess, OpenThread, ResumeThread, SuspendThread,
};
use winapi::um::processthreadsapi::{GetThreadContext, OpenThread};
use winapi::um::winnt::{
CONTEXT, CONTEXT_FULL, HANDLE, IMAGE_FILE_MACHINE_AMD64, PROCESS_QUERY_INFORMATION,
PROCESS_VM_READ, THREAD_GET_CONTEXT, THREAD_QUERY_INFORMATION, THREAD_SUSPEND_RESUME,
CONTEXT, CONTEXT_FULL, HANDLE, IMAGE_FILE_MACHINE_AMD64, THREAD_GET_CONTEXT,
THREAD_QUERY_INFORMATION,
};
const MAX_SYM_NAME_LEN: usize = 512;
// ---------------------------------------------------------------------------
// Resolve a single PC address to "module.dll!Symbol+0xNN" / "module.dll+0xNN"
@@ -82,15 +80,11 @@ fn wide_to_string(buf: &[u16]) -> String {
String::from_utf16_lossy(&buf[..len])
}
pub fn trace_thread_stack(h_process: HANDLE, tid: u32) {
pub fn trace_thread_stack(pid: u32, h_process: HANDLE, tid: u32) {
let h_thread: HANDLE;
println!("[+] Targeting TID:\t\t{}", tid);
// println!("[+] Targeting TID:\t\t{}", tid);
unsafe {
h_thread = OpenThread(
THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION,
FALSE,
tid,
);
h_thread = OpenThread(THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, FALSE, tid);
if h_thread.is_null() {
eprintln!(
"[-] OpenThread failed for TID {} (error {})",
@@ -99,7 +93,7 @@ pub fn trace_thread_stack(h_process: HANDLE, tid: u32) {
);
return;
}
println!("[+] Opened handle to Thread:\t{:?}", h_thread);
// println!("[+] Opened handle to Thread:\t{:?}", h_thread);
loop {
let mut ctx: CONTEXT = mem::zeroed();
@@ -109,7 +103,7 @@ pub fn trace_thread_stack(h_process: HANDLE, tid: u32) {
eprintln!("[!] GetThreadContext failed (error {})", GetLastError());
break;
}
println!("[+] Fetched thread context");
// println!("[+] Fetched thread context");
let mut frame: STACKFRAME64 = mem::zeroed();
frame.AddrPC.Offset = ctx.Rip;
frame.AddrPC.Mode = AddrModeFlat;
@@ -141,9 +135,9 @@ pub fn trace_thread_stack(h_process: HANDLE, tid: u32) {
}
// ── Output ──
if entries.is_empty() {
println!("(no frames captured)");
println!("{} | {} | ", pid, tid);
} else {
println!("{}", entries.join(","));
println!("{} | {} | {}", pid, tid, entries.join(","));
}
break;
}