From 77a7197718d18eb9a5024d1f7228a05b63c144ee Mon Sep 17 00:00:00 2001 From: Elnath Date: Sun, 18 May 2025 23:33:44 +0200 Subject: [PATCH] Added syscall and options to custom ptrace wrapper --- Cargo.lock | 5 +++-- Cargo.toml | 3 ++- src/clibwrap/ptrace.rs | 29 +++++++++++++++++++++++++++++ src/debug_target/mod.rs | 6 +++++- src/debug_target/stopped_target.rs | 10 +++++----- 5 files changed, 44 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a80866..79a0fb7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -36,6 +36,7 @@ dependencies = [ name = "bdb" version = "0.0.0" dependencies = [ + "bitflags", "color-eyre", "derive_more", "either", @@ -46,9 +47,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "cc" diff --git a/Cargo.toml b/Cargo.toml index a5b4426..c6aaf90 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ color-eyre = "0.6.3" either = "1.15.0" libc = "0.2.172" derive_more = { version = "2.0.1", features = ["debug"] } +bitflags = "2.9.1" [build-dependencies] -color-eyre = "0.6.3" \ No newline at end of file +color-eyre = "0.6.3" diff --git a/src/clibwrap/ptrace.rs b/src/clibwrap/ptrace.rs index 98b33e9..fc20fa5 100644 --- a/src/clibwrap/ptrace.rs +++ b/src/clibwrap/ptrace.rs @@ -1,4 +1,5 @@ use crate::clibwrap::{CError, Pid}; +use bitflags::bitflags; use std::ffi::{c_long, c_void}; #[allow(non_camel_case_types)] @@ -6,6 +7,8 @@ use std::ffi::{c_long, c_void}; enum PTraceRequest { PTRACE_CONT = 7, PTRACE_SINGLESTEP = 9, + PTRACE_SYSCALL = 24, + PTRACE_SETOPTIONS = 0x4200, } unsafe extern "C" { @@ -29,3 +32,29 @@ pub fn single_step(pid: Pid) -> Result<(), CError> { Err(CError { error_code: return_code }) } } + +pub fn syscall(pid: Pid) -> Result<(), CError> { + let return_code = unsafe { ptrace(PTraceRequest::PTRACE_SYSCALL, pid, 0 as _, 0 as _) }; + if return_code == 0 { + Ok(()) + } else { + Err(CError { error_code: return_code }) + } +} + +bitflags! { + #[derive(Debug)] + pub struct PTraceOptions: u32 { + const PTRACE_O_TRACESYSGOOD = 1; + // Other variants not implemented + } +} + +pub fn set_options(pid: Pid, options: PTraceOptions) -> Result<(), CError> { + let return_code = unsafe { ptrace(PTraceRequest::PTRACE_SETOPTIONS, pid, 0 as _, options.bits() as _) }; + if return_code == 0 { + Ok(()) + } else { + Err(CError { error_code: return_code }) + } +} diff --git a/src/debug_target/mod.rs b/src/debug_target/mod.rs index d35c951..1433eb5 100644 --- a/src/debug_target/mod.rs +++ b/src/debug_target/mod.rs @@ -10,6 +10,10 @@ pub use stopped_target::*; #[error("Error when calling ptrace: {0}")] pub struct PTraceError(#[from] nix::errno::Errno); +#[derive(thiserror::Error, Debug)] +#[error(transparent)] +pub struct CustomPTraceError(#[from] clibwrap::CError); + #[derive(thiserror::Error, Debug)] #[error("Error when waiting on child process: {0}")] pub struct WaitError(#[from] nix::errno::Errno); @@ -20,7 +24,7 @@ pub enum DebugError { PTraceError(#[from] PTraceError), #[error(transparent)] - CustomPTraceError(#[from] clibwrap::CError), + CustomPTraceError(#[from] CustomPTraceError), #[error(transparent)] WaitError(#[from] WaitError), diff --git a/src/debug_target/stopped_target.rs b/src/debug_target/stopped_target.rs index f146c33..b8050dc 100644 --- a/src/debug_target/stopped_target.rs +++ b/src/debug_target/stopped_target.rs @@ -1,5 +1,5 @@ use crate::clibwrap; -use crate::debug_target::{DebugError, PTraceError, RunningTarget, WaitError}; +use crate::debug_target::{CustomPTraceError, DebugError, PTraceError, RunningTarget, WaitError}; use crate::syscall_info::{syscall_info, SyscallInfo, SyscallInfoError}; use libc::{c_long, user_regs_struct}; use nix::sys::wait::{waitid, Id, WaitPidFlag}; @@ -22,7 +22,7 @@ impl StoppedTarget { 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)?; + clibwrap::ptrace::set_options(pid.as_raw(), clibwrap::ptrace::PTraceOptions::PTRACE_O_TRACESYSGOOD).map_err(CustomPTraceError)?; Ok(Self { pid, breakpoints: HashMap::new() }) } @@ -43,7 +43,7 @@ impl StoppedTarget { if self.on_breakpoint()? { self.breakpoint_remove_and_rewind()?; } - clibwrap::ptrace::cont(self.pid.as_raw())?; + clibwrap::ptrace::cont(self.pid.as_raw()).map_err(CustomPTraceError)?; Ok(self.to_running()) } @@ -51,7 +51,7 @@ impl StoppedTarget { if self.on_breakpoint()? { self.breakpoint_remove_and_rewind()?; } - clibwrap::ptrace::single_step(self.pid.as_raw())?; + clibwrap::ptrace::single_step(self.pid.as_raw()).map_err(CustomPTraceError)?; Ok(self.to_running()) } @@ -59,7 +59,7 @@ impl StoppedTarget { if self.on_breakpoint()? { self.breakpoint_remove_and_rewind()?; } - nix::sys::ptrace::syscall(self.pid, None).map_err(PTraceError)?; + clibwrap::ptrace::syscall(self.pid.as_raw()).map_err(CustomPTraceError)?; Ok(self.to_running()) }