Skip to content

Commit 77cbed3

Browse files
committed
Added support to spawn a process, rather than just attach
1 parent 1b36b15 commit 77cbed3

File tree

3 files changed

+117
-32
lines changed

3 files changed

+117
-32
lines changed

Cargo.lock

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

libs/debugger/src/debugger.rs

+53-4
Original file line numberDiff line numberDiff line change
@@ -24,28 +24,34 @@ use winapi::um::debugapi::DebugActiveProcessStop;
2424
use winapi::um::debugapi::DebugActiveProcess;
2525
use winapi::um::debugapi::ContinueDebugEvent;
2626
use winapi::um::winbase::InitializeContext;
27+
use winapi::um::processthreadsapi::GetProcessId;
2728
use winapi::um::processthreadsapi::GetCurrentProcess;
2829
use winapi::um::processthreadsapi::SetThreadContext;
2930
use winapi::um::processthreadsapi::GetThreadContext;
3031
use winapi::um::processthreadsapi::FlushInstructionCache;
3132
use winapi::um::processthreadsapi::TerminateProcess;
3233
use winapi::um::processthreadsapi::OpenProcess;
34+
use winapi::um::processthreadsapi::CreateProcessA;
3335
use winapi::um::wow64apiset::IsWow64Process;
3436
use winapi::um::winnt::PROCESS_QUERY_LIMITED_INFORMATION;
3537
use winapi::um::psapi::GetMappedFileNameW;
3638
use winapi::um::winnt::HANDLE;
3739
use winapi::um::minwinbase::DEBUG_EVENT;
40+
use winapi::um::winbase::DEBUG_PROCESS;
41+
use winapi::um::winbase::DEBUG_ONLY_THIS_PROCESS;
3842

3943
use std::time::{Duration, Instant};
4044
use std::collections::{HashSet, HashMap};
4145
use std::path::Path;
4246
use std::sync::Arc;
4347
use std::fs::File;
48+
use std::ffi::CString;
4449
use std::io::Write;
4550
use std::io::BufWriter;
4651
use std::sync::atomic::{AtomicBool, Ordering};
4752
use winapi::um::consoleapi::SetConsoleCtrlHandler;
4853

54+
4955
use crate::minidump::dump;
5056
use crate::handles::Handle;
5157

@@ -201,6 +207,47 @@ macro_rules! mprint {
201207
impl<'a> Debugger<'a> {
202208
/// Create a new debugger and attach to `pid`
203209
pub fn attach(pid: u32) -> Debugger<'a> {
210+
Debugger::attach_internal(pid, false)
211+
}
212+
213+
/// Create a new process argv[0], with arguments argv[1..] and attach to it
214+
pub fn spawn_proc(argv: &[String], follow_fork: bool) -> Debugger<'a> {
215+
let mut startup_info = unsafe { std::mem::zeroed() };
216+
let mut proc_info = unsafe { std::mem::zeroed() };
217+
218+
let cmdline = CString::new(argv.join(" ")).unwrap();
219+
220+
let cmdline_ptr = cmdline.into_raw();
221+
222+
let flags = if follow_fork {
223+
DEBUG_PROCESS
224+
}
225+
else {
226+
DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS
227+
};
228+
229+
unsafe {
230+
assert!(CreateProcessA(
231+
std::ptr::null_mut(), // lpApplicationName
232+
cmdline_ptr, // lpCommandLine
233+
std::ptr::null_mut(), // lpProcessAttributes
234+
std::ptr::null_mut(), // lpThreadAttributes
235+
0, // bInheritHandles
236+
flags, // dwCreationFlags
237+
std::ptr::null_mut(), // lpEnvironment
238+
std::ptr::null_mut(), // lpCurrentDirectory
239+
&mut startup_info, // lpStartupInfo
240+
&mut proc_info) != 0, // lpProcessInformation
241+
"Failed to create process.");
242+
}
243+
244+
let pid = unsafe { GetProcessId(proc_info.hProcess) };
245+
246+
Debugger::attach_internal(pid, true)
247+
}
248+
249+
/// Create a new debugger
250+
pub fn attach_internal(pid: u32, attached: bool) -> Debugger<'a> {
204251
// Save the start time
205252
let start_time = Instant::now();
206253

@@ -238,10 +285,12 @@ impl<'a> Debugger<'a> {
238285
assert!(cur_is_wow64 == target_is_wow64,
239286
"Target process does not match mesos bitness");
240287

241-
// Attach to the target!
242-
assert!(DebugActiveProcess(pid) != 0,
243-
"Failed to attach to process, is your PID valid \
244-
and do you have correct permissions?");
288+
if !attached {
289+
// Attach to the target!
290+
assert!(DebugActiveProcess(pid) != 0,
291+
"Failed to attach to process, is your PID valid \
292+
and do you have correct permissions?");
293+
}
245294
}
246295

247296
// Correctly initialize a context so it's aligned. We overcommit

src/main.rs

+62-28
Original file line numberDiff line numberDiff line change
@@ -15,56 +15,90 @@ fn modload_handler(dbg: &mut Debugger, modname: &str, base: usize) {
1515
}
1616

1717
fn main() {
18+
19+
// mesos.exe -p pid mesos_file0 mesos_file1 mesos_file2
20+
// or mesos.exe mesos_file0 mesos_file1 mesos_file2 -- ./exe arg0 arg1 arg2
21+
1822
// Usage and argument parsing
1923
let args: Vec<String> = std::env::args().collect();
2024
if args.len() < 2 {
21-
print!("Usage: mesos.exe <pid> [--freq | --verbose | --print] \
22-
<explicit meso file 1> <explicit meso file ...>\n");
25+
print!("Usage: mesos.exe -p <pid> <options> <mesos files>\n");
26+
print!(" or mesos.exe <options> <mesos files> -- program.exe <arguments>\n");
2327
print!(" --freq - \
2428
Treats all breakpoints as frequency breakpoints\n");
2529
print!(" --verbose - \
2630
Enables verbose prints for debugging\n");
2731
print!(" --print - \
2832
Prints breakpoint info on every single breakpoint\n");
33+
print!(" --follow-fork - \
34+
Capture coverage for child processes\n");
2935
print!(" [explicit meso file] - \
3036
Load a specific meso file regardless of loaded modules\n\n");
3137

32-
print!("Standard usage: mesos.exe <pid>\n");
3338
return;
3439
}
3540

36-
// Attach to process
37-
let mut dbg = Debugger::attach(args[1].parse().unwrap());
3841

39-
// Register callback routine for module loads so we can attempt to apply
40-
// breakpoints to it from the meso file cache
41-
dbg.register_modload_callback(Box::new(modload_handler));
42+
let mut pid: i32 = -1;
43+
let mut frequency_mode_enabled = false;
44+
let mut verbose_mode_enabled = false;
45+
let mut follow_fork_enabled = false;
46+
let mut print_breakpoints_enabled = false;
47+
let mut mesos: Vec<&Path> = Vec::new();
48+
49+
let mut argv: Vec<String> = Vec::new();
4250

43-
// Process arguments
4451
if args.len() > 2 {
45-
for meso in &args[2..] {
46-
if meso == "--freq" {
47-
// Always do frequency
48-
print!("Always frequency mode enabled\n");
49-
dbg.set_always_freq(true);
50-
continue;
51-
} else if meso == "--verbose" {
52-
// Enable verbose mode
53-
print!("Verbose mode enabled\n");
54-
dbg.set_verbose(true);
55-
continue;
56-
} else if meso == "--print" {
57-
// Enable breakpoint print mode
58-
print!("Breakpoint print mode enabled\n");
59-
dbg.set_bp_print(true);
60-
continue;
52+
for (i, arg) in args[1..].iter().enumerate() {
53+
if arg == "-p" {
54+
pid = arg.parse().unwrap();
55+
}
56+
else if arg == "--verbose" {
57+
verbose_mode_enabled = true;
58+
}
59+
else if arg == "--print" {
60+
print_breakpoints_enabled = true;
61+
}
62+
else if arg == "--freq" {
63+
frequency_mode_enabled = true;
64+
}
65+
else if arg == "--follow-fork" {
66+
follow_fork_enabled = true;
67+
}
68+
else if arg == "--" {
69+
argv.extend_from_slice(&args[i + 2..]);
70+
break;
71+
}
72+
else { // Has to be a mesofile
73+
//mesofile::load_meso(&mut dbg, Path::new(arg));
74+
mesos.push(Path::new(arg));
6175
}
62-
63-
// Load explicit meso file
64-
mesofile::load_meso(&mut dbg, Path::new(meso));
6576
}
6677
}
6778

79+
let mut dbg:Debugger;
80+
if pid == -1 && argv.len() > 0 {
81+
dbg = Debugger::spawn_proc(&argv, follow_fork_enabled);
82+
}
83+
else {
84+
dbg = Debugger::attach(args[1].parse().unwrap());
85+
}
86+
87+
// Attach to process
88+
89+
dbg.set_always_freq(frequency_mode_enabled);
90+
dbg.set_verbose(verbose_mode_enabled);
91+
dbg.set_bp_print(print_breakpoints_enabled);
92+
93+
for mesofile in mesos {
94+
mesofile::load_meso(&mut dbg, mesofile);
95+
}
96+
97+
// Register callback routine for module loads so we can attempt to apply
98+
// breakpoints to it from the meso file cache
99+
dbg.register_modload_callback(Box::new(modload_handler));
100+
68101
// Debug forever
69102
dbg.run();
70103
}
104+

0 commit comments

Comments
 (0)