diff --git a/src/main.rs b/src/main.rs index d35e662..df8efb0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,52 +1,104 @@ mod child; +use nix::libc::user_regs_struct; use nix::sys::ptrace::*; -use nix::sys::wait::{waitpid, WaitStatus}; +use nix::sys::signal::Signal::*; +use nix::sys::wait::{waitid, waitpid, Id, WaitPidFlag, WaitStatus}; use nix::unistd::{fork, ForkResult, Pid}; -use std::ffi::CString; +use std::ffi::{c_long, c_void, CString}; use std::process::ExitCode; +#[allow(dead_code)] fn single_step_all(child_pid: Pid) -> ExitCode { - let mut instruction_number = 0; - loop { - let wait_status = waitpid(child_pid, None); - match wait_status { - Ok(WaitStatus::Stopped(_, _)) => { - instruction_number += 1; - let regs = getregs(child_pid).unwrap(); - println!( - "🔎 [==> {}] rip= 0x{:016X}, rax = 0x{rax:X} ({rax})", - instruction_number, - regs.rip, - rax = regs.rax - ); - step(child_pid, None).unwrap(); - } - Ok(WaitStatus::Exited(_, exit_code)) => { - println!("👋 Child exited with code {exit_code}"); - return ExitCode::SUCCESS; - } - other => { - println!("⚠️ Other (unexpected) wait status: {other:?}"); - return ExitCode::FAILURE; - } - } - } + let mut instruction_number = 0; + loop { + let wait_status = waitpid(child_pid, None); + match wait_status { + Ok(WaitStatus::Stopped(_, _)) => { + instruction_number += 1; + let regs = getregs(child_pid).unwrap(); + println!("🔎 [==> {}] rip= 0x{:016X}, rax = 0x{rax:X} ({rax})", instruction_number, regs.rip, rax = regs.rax); + step(child_pid, None).unwrap(); + } + Ok(WaitStatus::Exited(_, exit_code)) => { + println!("👋 Child exited with code {exit_code}"); + return ExitCode::SUCCESS; + } + other => { + println!("⚠️ Other (unexpected) wait status: {other:?}"); + return ExitCode::FAILURE; + } + } + } +} + +#[allow(dead_code)] +fn breakpoint_fun(child_pid: Pid) -> ExitCode { + println!("⏳️ Waiting for child to be ready"); + waitid(Id::Pid(child_pid), WaitPidFlag::WSTOPPED).unwrap(); + + let address: u64 = 0x0000000000401019; + println!("🚧 Setting breakpoint at location 0x{address:x}"); + let orig_bytes: [u8; 8] = read(child_pid, address as *mut c_void).expect("Breakpoint memory read").to_le_bytes(); + println!("\t🔎 Original content is: {}", orig_bytes.map(|b| format!("{:#04x}", b)).join(" ")); + let mut new_bytes = orig_bytes.clone(); + new_bytes[0] = 0xCC; + println!( + "\t🌟 New content will be: {}", + new_bytes.map(|b| format!("{:#04x}", b)).join(" ") + ); + write(child_pid, address as *mut c_void, c_long::from_le_bytes(new_bytes)).expect("Breakpoint memory write"); + println!("\t🖍️ Breakpoint set"); + + println!("⚙️ Continuing execution waiting for breakpoint"); + cont(child_pid, None).unwrap(); + match waitpid(child_pid, None).unwrap() { + WaitStatus::Stopped(_, SIGTRAP) => { + let registers = getregs(child_pid).unwrap(); + let breakpoint_addr = registers.rip - 1; + println!("🛑 Stopped at breakpoint ({:#018x})!", breakpoint_addr); + println!("\t🔎 Registers content: {:?}", registers); + println!("\t🖍️ Restoring instructions to original"); + write(child_pid, address as *mut c_void, c_long::from_le_bytes(orig_bytes)).expect("breakpoint restore memory"); + println!("\t↪️ Rolling back instruction pointer"); + setregs(child_pid, user_regs_struct { rip: breakpoint_addr, ..registers }).unwrap(); + println!("\t⚙️ One more instruction"); + step(child_pid, None).unwrap(); + waitid(Id::Pid(child_pid), WaitPidFlag::WSTOPPED).unwrap(); + println!("\t⚙️ Continuing execution"); + cont(child_pid, None).unwrap(); + } + other => { + println!("⚠️ Other (unexpected) wait status: {other:?}"); + return ExitCode::FAILURE; + } + } + + match waitpid(child_pid, None) { + Ok(WaitStatus::Exited(_, exit_code)) => { + println!("👋 Child exited with code {exit_code}"); + ExitCode::SUCCESS + } + other => { + println!("⚠️ Other (unexpected) wait status: {other:?}"); + ExitCode::FAILURE + } + } } fn main() -> ExitCode { - let child_exec_path = CString::new(env!("TEST_PROG_PATH")).unwrap(); + 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!("✔️ Created child {child_pid}"); - return single_step_all(child_pid); - // todo!("Something interesting"); - } - Err(e) => { - println!("❌ Fork failed: {e}"); - ExitCode::FAILURE - } - } + match unsafe { fork() } { + Ok(ForkResult::Child) => child::starti(child_exec_path), + Ok(ForkResult::Parent { child: child_pid }) => { + println!("✔️ Created child {child_pid}"); + // return single_step_all(child_pid); + return breakpoint_fun(child_pid); + } + Err(e) => { + println!("❌ Fork failed: {e}"); + ExitCode::FAILURE + } + } }