mirror of
https://github.com/whokilleddb/stacktracer
synced 2026-06-06 17:00:04 +00:00
made things better
This commit is contained in:
Generated
+123
@@ -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,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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user