diff --git a/src/debug_target.rs b/src/debug_target.rs index a875715..ec1e1dd 100644 --- a/src/debug_target.rs +++ b/src/debug_target.rs @@ -1,8 +1,8 @@ +use crate::syscall_info::{syscall_info, SyscallInfo, SyscallInfoError}; use either::{Either, Left, Right}; use nix::libc::user_regs_struct; use nix::sys::wait::{waitid, Id, WaitPidFlag, WaitStatus}; use nix::unistd::Pid; -use crate::syscall_info::{SyscallInfo, SyscallInfoError, syscall_info}; #[derive(thiserror::Error, Debug)] #[error("Error when calling ptrace: {0}")] @@ -24,85 +24,82 @@ pub enum DebugError { UnexpectedWaitStatus(nix::sys::wait::WaitStatus), } -pub struct DebugTarget { - state: S, +pub struct StoppedTarget { + pub pid: Pid, } -pub trait DebugState { - #[allow(dead_code)] - fn pid(&self) -> Pid; -} - -pub struct Stopped { - pid: Pid, -} - -impl DebugState for Stopped { - fn pid(&self) -> Pid { self.pid } -} #[allow(dead_code)] -impl DebugTarget { +impl StoppedTarget { + fn to_running(self) -> RunningTarget { + RunningTarget { pid: self.pid } + } + pub fn new(pid: Pid) -> Result { waitid(Id::Pid(pid), WaitPidFlag::WSTOPPED).map_err(WaitError)?; // Needed for waiting on syscalls and apparently also for getting syscall info (can not get it to work without this) nix::sys::ptrace::setoptions(pid, nix::sys::ptrace::Options::PTRACE_O_TRACESYSGOOD).map_err(PTraceError)?; - Ok(DebugTarget { state: Stopped { pid } }) + Ok(Self { pid }) } - pub fn cont(self) -> Result, PTraceError> { - nix::sys::ptrace::cont(self.state.pid, None).map_err(PTraceError)?; - Ok(DebugTarget { state: Running { pid: self.state.pid } }) + pub fn cont(self) -> Result { + nix::sys::ptrace::cont(self.pid, None).map_err(PTraceError)?; + Ok(self.to_running()) } - pub fn stepi(self) -> Result, PTraceError> { - nix::sys::ptrace::step(self.state.pid, None).map_err(PTraceError)?; - Ok(DebugTarget { state: Running { pid: self.state.pid } }) + pub fn stepi(self) -> Result { + nix::sys::ptrace::step(self.pid, None).map_err(PTraceError)?; + Ok(self.to_running()) } - pub fn cont_syscall(self) -> Result, PTraceError> { - nix::sys::ptrace::syscall(self.state.pid, None).map_err(PTraceError)?; - Ok(DebugTarget { state: Running { pid: self.state.pid } }) + pub fn cont_syscall(self) -> Result { + nix::sys::ptrace::syscall(self.pid, None).map_err(PTraceError)?; + Ok(self.to_running()) } pub fn get_registers(&self) -> Result { - Ok(nix::sys::ptrace::getregs(self.state.pid)?) + Ok(nix::sys::ptrace::getregs(self.pid)?) } pub fn get_syscall_info(&self) -> Result { - Ok(syscall_info(self.state.pid.as_raw())?) + Ok(syscall_info(self.pid.as_raw())?) } } -pub struct Running { - pid: Pid, -} - -impl DebugState for Running { - fn pid(&self) -> Pid { self.pid } +pub struct RunningTarget { + pub pid: Pid, } #[allow(dead_code)] -impl DebugTarget { - pub fn wait_for_something(self) -> Result, i32>, DebugError> { - match waitid(Id::Pid(self.state.pid), WaitPidFlag::WSTOPPED | WaitPidFlag::WEXITED).map_err(WaitError)? { - WaitStatus::Exited(_pid, exit_code) => Ok(Right(exit_code)), - WaitStatus::PtraceEvent(..) | WaitStatus::PtraceSyscall(..) => Ok(Left(DebugTarget { state: Stopped { pid: self.state.pid } })), +impl RunningTarget { + fn to_stopped(self) -> StoppedTarget { + StoppedTarget { pid: self.pid } + } + + pub fn wait_for_something(self) -> Result, DebugError> { + match waitid(Id::Pid(self.pid), WaitPidFlag::WSTOPPED | WaitPidFlag::WEXITED).map_err(WaitError)? { + WaitStatus::Exited(_pid, exit_code) => Ok(Right(ExitedTarget { exit_code, was_pid: self.pid })), + WaitStatus::PtraceEvent(..) | WaitStatus::PtraceSyscall(..) => Ok(Left(self.to_stopped())), status => Err(DebugError::UnexpectedWaitStatus(status)), } } - pub fn wait_for_syscall(self) -> Result, DebugError> { - match waitid(Id::Pid(self.state.pid), WaitPidFlag::WSTOPPED).map_err(WaitError)? { - WaitStatus::PtraceSyscall(..) => Ok(DebugTarget { state: Stopped { pid: self.state.pid } }), + pub fn wait_for_syscall(self) -> Result { + match waitid(Id::Pid(self.pid), WaitPidFlag::WSTOPPED).map_err(WaitError)? { + WaitStatus::PtraceSyscall(..) => Ok(self.to_stopped()), status => Err(DebugError::UnexpectedWaitStatus(status)), } } - pub fn wait_for_exit(self) -> Result { - match waitid(Id::Pid(self.state.pid), WaitPidFlag::WEXITED).map_err(WaitError)? { - WaitStatus::Exited(_pid, exit_code) => Ok(exit_code), + pub fn wait_for_exit(self) -> Result { + match waitid(Id::Pid(self.pid), WaitPidFlag::WEXITED).map_err(WaitError)? { + WaitStatus::Exited(_pid, exit_code) => Ok(ExitedTarget { exit_code, was_pid: self.pid }), status => Err(DebugError::UnexpectedWaitStatus(status)) } } +} + +pub struct ExitedTarget { + pub exit_code: i32, + pub was_pid: Pid, } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 25f9f80..fc66a57 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,7 @@ mod debug_target; mod syscall_info; mod syscall_names; -use crate::debug_target::{DebugTarget, Stopped}; +use crate::debug_target::{ExitedTarget, StoppedTarget}; use color_eyre::eyre::eyre; use either::Either; use nix::libc::user_regs_struct; @@ -14,7 +14,7 @@ use nix::unistd::{fork, ForkResult, Pid}; use std::ffi::{c_long, c_void, CString}; #[allow(dead_code)] -fn single_step_all(mut target: DebugTarget) -> color_eyre::Result<()> { +fn single_step_all(mut target: StoppedTarget) -> color_eyre::Result<()> { let mut instruction_number = 0; loop { instruction_number += 1; @@ -22,7 +22,7 @@ fn single_step_all(mut target: DebugTarget) -> color_eyre::Result<()> { println!("🔎 [==> {}] rip= {:#016x}, rax = 0x{rax:x} ({rax})", instruction_number, regs.rip, rax = regs.rax); match target.stepi()?.wait_for_something()? { Either::Left(t) => target = t, - Either::Right(exit_code) => { + Either::Right(ExitedTarget { exit_code, was_pid: _was_pid }) => { println!("👋 Child exited with code {exit_code}"); return Ok(()); } @@ -31,14 +31,14 @@ fn single_step_all(mut target: DebugTarget) -> color_eyre::Result<()> { } #[allow(dead_code)] -fn strace(mut target: DebugTarget) -> color_eyre::Result<()> { +fn strace(mut target: StoppedTarget) -> color_eyre::Result<()> { loop { match target.cont_syscall()?.wait_for_something()? { Either::Left(t) => { println!("{:?}", t.get_syscall_info()); target = t; } - Either::Right(exit_code) => { + Either::Right(ExitedTarget { exit_code, was_pid: _was_pid }) => { println!("👋 Child exited with code {exit_code}"); return Ok(()); } @@ -100,16 +100,17 @@ fn breakpoint_fun(child_pid: Pid) -> color_eyre::Result<()> { fn main() -> color_eyre::Result<()> { color_eyre::install()?; - let child_exec_path = CString::new(env!("C_PROG_PATH"))?; + let child_exec_path = CString::new(env!("ASM_PROG_PATH"))?; match unsafe { fork() } { Ok(ForkResult::Child) => child::starti(child_exec_path), Ok(ForkResult::Parent { child: child_pid }) => { println!("✔️ Created child {child_pid}"); - let target = DebugTarget::new(child_pid)?; + let target = StoppedTarget::new(child_pid)?; println!("✔️ Child ready!"); + // println!("🔎 rip: {:#x}", target.get_registers()?.rip); // //