mod child; use nix::libc::user_regs_struct; use nix::sys::ptrace::*; use nix::sys::wait::waitpid; use nix::unistd::{fork, ForkResult}; use std::ffi::CString; use std::process::ExitCode; fn main() -> ExitCode { let child_exec_path = CString::new(env!("TEST_PROG_PATH")).unwrap(); match unsafe { fork() } { Ok(ForkResult::Child) => child::starti(child_exec_path), Ok(ForkResult::Parent { child: child_pid }) => { println!("✔️ Started child {child_pid}"); let mut before_instruction = 0; loop { use nix::sys::signal::Signal::*; use nix::sys::wait::WaitStatus::*; match waitpid(child_pid, None) { Err(e) => { println!("❌ Wait failed: {e}"); return ExitCode::FAILURE; } Ok(status) => match status { Exited(_pid, exit_code) => { println!("👋 Child exited with code {exit_code}"); return ExitCode::SUCCESS; } Signaled(_, signal, _) => match signal { SIGKILL => { println!("💀 Child killed by SIGKILL"); return ExitCode::SUCCESS; } s => println!("💡 Child received signal {s:?}"), }, Stopped(_pid, _signal) => { let regs = getregs(child_pid).unwrap(); println!( "🔎 [{}] rip= 0x{:016X}, rax = 0x{rax:X} ({rax})", before_instruction, regs.rip, rax = regs.rax ); if regs.rax == 60 { println!("Let's change a register!"); let new_regs = user_regs_struct { rdi: 54, ..regs }; setregs(child_pid, new_regs).unwrap(); } before_instruction += 1; step(child_pid, None).unwrap(); } status => { println!("⚠️ Other (unexpected) wait status: {status:?}"); } }, } } } Err(e) => { println!("❌ Fork failed: {e}"); ExitCode::FAILURE } } }