Compare commits

..

No commits in common. "custom_clib_wrap" and "dev" have entirely different histories.

8 changed files with 23 additions and 269 deletions

5
Cargo.lock generated
View File

@ -36,7 +36,6 @@ dependencies = [
name = "bdb"
version = "0.0.0"
dependencies = [
"bitflags",
"color-eyre",
"derive_more",
"either",
@ -47,9 +46,9 @@ dependencies = [
[[package]]
name = "bitflags"
version = "2.9.1"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
[[package]]
name = "cc"

View File

@ -11,7 +11,6 @@ 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"

View File

@ -1,26 +0,0 @@
use crate::clibwrap::{ErrnoError, Pid};
mod external {
use crate::clibwrap::Pid;
unsafe extern "C" {
pub fn fork() -> Pid;
}
}
#[derive(Debug)]
pub enum ForkResult {
Parent { child_pid: Pid },
Child,
}
pub unsafe fn fork() -> Result<ForkResult, ErrnoError> {
let return_value = unsafe { external::fork() };
if return_value == -1 {
unsafe { Err(ErrnoError::from_current_errno()) }
} else if return_value == 0 {
Ok(ForkResult::Child)
} else {
Ok(ForkResult::Parent { child_pid: return_value })
}
}

View File

@ -1,49 +0,0 @@
use std::ffi::{c_char, c_int, CStr};
pub mod ptrace;
pub mod fork;
pub type Pid = i32;
unsafe extern "C" {
fn __errno_location() -> *mut c_int;
fn strerror(errnum: c_int) -> *const c_char;
fn strerrorname_np(errnum: c_int) -> *const c_char;
}
unsafe fn errno() -> c_int {
unsafe { *__errno_location() }
}
unsafe fn set_errno(errno: c_int) {
unsafe { *__errno_location() = errno; }
}
#[derive(Debug, thiserror::Error)]
#[error("{}", self.error_message())]
pub struct ErrnoError {
errno: i32,
}
impl ErrnoError {
pub unsafe fn from_current_errno() -> Self {
unsafe { Self { errno: errno() } }
}
fn error_message(&self) -> String {
unsafe {
let error_name = {
let c_error_name = strerrorname_np(self.errno);
if c_error_name.is_null() {
"???"
} else {
&CStr::from_ptr(c_error_name).to_string_lossy().into_owned()
}
};
let error_message = {
let c_str = strerror(self.errno);
CStr::from_ptr(c_str).to_string_lossy().into_owned()
};
format!("{}: {}", error_name, error_message)
}
}
}

View File

@ -1,166 +0,0 @@
use crate::clibwrap::{errno, set_errno, ErrnoError, Pid};
use bitflags::bitflags;
use std::ffi::{c_long, c_ulonglong, c_void};
use std::mem::MaybeUninit;
#[allow(non_camel_case_types)]
#[repr(u32)]
enum PTraceRequest {
PTRACE_PEEKDATA = 2,
PTRACE_POKEDATA = 5,
PTRACE_CONT = 7,
PTRACE_SINGLESTEP = 9,
PTRACE_GETREGS = 12,
PTRACE_SETREGS = 13,
PTRACE_SYSCALL = 24,
PTRACE_SETOPTIONS = 0x4200,
}
unsafe extern "C" {
fn ptrace(request: PTraceRequest, pid: Pid, addr: *mut c_void, data: *mut c_void) -> c_long;
}
pub fn cont(pid: Pid) -> Result<(), ErrnoError> {
let return_code = unsafe { ptrace(PTraceRequest::PTRACE_CONT, pid, 0 as _, 0 as _) };
if return_code == 0 {
Ok(())
} else {
unsafe { Err(ErrnoError::from_current_errno()) }
}
}
pub fn single_step(pid: Pid) -> Result<(), ErrnoError> {
let return_code = unsafe { ptrace(PTraceRequest::PTRACE_SINGLESTEP, pid, 0 as _, 0 as _) };
if return_code == 0 {
Ok(())
} else {
unsafe { Err(ErrnoError::from_current_errno()) }
}
}
pub fn syscall(pid: Pid) -> Result<(), ErrnoError> {
let return_code = unsafe { ptrace(PTraceRequest::PTRACE_SYSCALL, pid, 0 as _, 0 as _) };
if return_code == 0 {
Ok(())
} else {
unsafe { Err(ErrnoError::from_current_errno()) }
}
}
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<(), ErrnoError> {
let return_code = unsafe { ptrace(PTraceRequest::PTRACE_SETOPTIONS, pid, 0 as _, options.bits() as _) };
if return_code == 0 {
Ok(())
} else {
unsafe { Err(ErrnoError::from_current_errno()) }
}
}
#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(derive_more::Debug)]
pub struct registers {
#[debug("{r15:#x} ({r15})")]
pub r15: c_ulonglong,
#[debug("{r14:#x} ({r14})")]
pub r14: c_ulonglong,
#[debug("{r13:#x} ({r13})")]
pub r13: c_ulonglong,
#[debug("{r12:#x} ({r12})")]
pub r12: c_ulonglong,
#[debug("{rbp:#x} ({rbp})")]
pub rbp: c_ulonglong,
#[debug("{rbx:#x} ({rbx})")]
pub rbx: c_ulonglong,
#[debug("{r11:#x} ({r11})")]
pub r11: c_ulonglong,
#[debug("{r10:#x} ({r10})")]
pub r10: c_ulonglong,
#[debug("{r9:#x} ({r9})")]
pub r9: c_ulonglong,
#[debug("{r8:#x} ({r8})")]
pub r8: c_ulonglong,
#[debug("{rax:#x} ({rax})")]
pub rax: c_ulonglong,
#[debug("{rcx:#x} ({rcx})")]
pub rcx: c_ulonglong,
#[debug("{rdx:#x} ({rdx})")]
pub rdx: c_ulonglong,
#[debug("{rsi:#x} ({rsi})")]
pub rsi: c_ulonglong,
#[debug("{rdi:#x} ({rdi})")]
pub rdi: c_ulonglong,
#[debug(skip)]
pub orig_rax: c_ulonglong,
#[debug("{rip:#x} ({rip})")]
pub rip: c_ulonglong,
#[debug(skip)]
pub cs: c_ulonglong,
#[debug("{eflags:#x} ({eflags:#b})")]
pub eflags: c_ulonglong,
#[debug("{rsp:#x} ({rsp})")]
pub rsp: c_ulonglong,
#[debug(skip)]
pub ss: c_ulonglong,
#[debug(skip)]
pub fs_base: c_ulonglong,
#[debug(skip)]
pub gs_base: c_ulonglong,
#[debug(skip)]
pub ds: c_ulonglong,
#[debug(skip)]
pub es: c_ulonglong,
#[debug(skip)]
pub fs: c_ulonglong,
#[debug(skip)]
pub gs: c_ulonglong,
}
pub fn get_regs(pid: Pid) -> Result<registers, ErrnoError> {
let mut registers = MaybeUninit::<registers>::uninit();
let return_code = unsafe { ptrace(PTraceRequest::PTRACE_GETREGS, pid, 0 as _, registers.as_mut_ptr() as _) };
if return_code == 0 {
unsafe { Ok(registers.assume_init()) }
} else {
unsafe { Err(ErrnoError::from_current_errno()) }
}
}
pub fn set_regs(pid: Pid, registers: registers) -> Result<(), ErrnoError> {
let register_address: *const registers = &registers;
let return_code = unsafe { ptrace(PTraceRequest::PTRACE_SETREGS, pid, 0 as _, register_address as _) };
if return_code == 0 {
Ok(())
} else {
unsafe { Err(ErrnoError::from_current_errno()) }
}
}
pub fn peek_data(pid: Pid, address: u64) -> Result<c_long, ErrnoError> {
unsafe {
set_errno(0);
let data = ptrace(PTraceRequest::PTRACE_PEEKDATA, pid, address as _, 0 as _);
let errno = errno();
if errno != 0 {
Err(ErrnoError::from_current_errno())
} else {
Ok(data)
}
}
}
pub fn poke_data(pid: Pid, address: u64, data: c_long) -> Result<(), ErrnoError> {
let return_code = unsafe { ptrace(PTraceRequest::PTRACE_POKEDATA, pid, address as _, data as _) };
if return_code == 0 {
Ok(())
} else {
unsafe { Err(ErrnoError::from_current_errno()) }
}
}

View File

@ -1,14 +1,13 @@
mod stopped_target;
mod running_target;
use crate::clibwrap;
use nix::unistd::Pid;
pub use running_target::*;
pub use stopped_target::*;
#[derive(thiserror::Error, Debug)]
#[error("Error when calling ptrace: {0}")]
pub struct PTraceError(#[from] clibwrap::ErrnoError);
pub struct PTraceError(#[from] nix::errno::Errno);
#[derive(thiserror::Error, Debug)]
#[error("Error when waiting on child process: {0}")]

View File

@ -1,10 +1,10 @@
use crate::clibwrap;
use crate::debug_target::{DebugError, PTraceError, RunningTarget, WaitError};
use crate::syscall_info::{syscall_info, SyscallInfo, SyscallInfoError};
use libc::c_long;
use libc::{c_long, user_regs_struct};
use nix::sys::wait::{waitid, Id, WaitPidFlag};
use nix::unistd::Pid;
use std::collections::HashMap;
use std::ffi::c_void;
pub struct StoppedTarget {
pub pid: Pid,
@ -21,7 +21,7 @@ impl StoppedTarget {
pub fn new(pid: Pid) -> Result<Self, DebugError> {
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)
clibwrap::ptrace::set_options(pid.as_raw(), clibwrap::ptrace::PTraceOptions::PTRACE_O_TRACESYSGOOD).map_err(PTraceError)?;
nix::sys::ptrace::setoptions(pid, nix::sys::ptrace::Options::PTRACE_O_TRACESYSGOOD).map_err(PTraceError)?;
Ok(Self { pid, breakpoints: HashMap::new() })
}
@ -34,7 +34,7 @@ impl StoppedTarget {
let mut registers = self.get_registers()?;
self.remove_breakpoint(registers.rip - 1)?;
registers.rip -= 1;
clibwrap::ptrace::set_regs(self.pid.as_raw(), registers).map_err(PTraceError)?;
nix::sys::ptrace::setregs(self.pid, registers).map_err(PTraceError)?;
Ok(())
}
@ -42,7 +42,7 @@ impl StoppedTarget {
if self.on_breakpoint()? {
self.breakpoint_remove_and_rewind()?;
}
clibwrap::ptrace::cont(self.pid.as_raw()).map_err(PTraceError)?;
nix::sys::ptrace::cont(self.pid, None).map_err(PTraceError)?;
Ok(self.to_running())
}
@ -50,7 +50,7 @@ impl StoppedTarget {
if self.on_breakpoint()? {
self.breakpoint_remove_and_rewind()?;
}
clibwrap::ptrace::single_step(self.pid.as_raw()).map_err(PTraceError)?;
nix::sys::ptrace::step(self.pid, None).map_err(PTraceError)?;
Ok(self.to_running())
}
@ -58,12 +58,12 @@ impl StoppedTarget {
if self.on_breakpoint()? {
self.breakpoint_remove_and_rewind()?;
}
clibwrap::ptrace::syscall(self.pid.as_raw()).map_err(PTraceError)?;
nix::sys::ptrace::syscall(self.pid, None).map_err(PTraceError)?;
Ok(self.to_running())
}
pub fn get_registers(&self) -> Result<clibwrap::ptrace::registers, PTraceError> {
clibwrap::ptrace::get_regs(self.pid.as_raw()).map_err(PTraceError)
pub fn get_registers(&self) -> Result<user_regs_struct, PTraceError> {
Ok(nix::sys::ptrace::getregs(self.pid)?)
}
pub fn get_syscall_info(&self) -> Result<SyscallInfo, SyscallInfoError> {
@ -75,10 +75,10 @@ impl StoppedTarget {
if self.breakpoints.contains_key(&address) {
return Err(DebugError::DuplicateBreakpoint { address });
}
let orig_bytes = clibwrap::ptrace::peek_data(self.pid.as_raw(), address).map_err(PTraceError)?;
let orig_bytes = nix::sys::ptrace::read(self.pid, address as *mut c_void).map_err(PTraceError)?;
let target_byte: u8 = (orig_bytes & 0xFF as c_long) as u8;
let new_content = (orig_bytes & (!0xff as c_long)) | (0xCC as c_long);
clibwrap::ptrace::poke_data(self.pid.as_raw(), address, new_content).map_err(PTraceError)?;
nix::sys::ptrace::write(self.pid, address as *mut c_void, new_content).map_err(PTraceError)?;
self.breakpoints.insert(address, target_byte);
Ok(())
@ -89,9 +89,9 @@ impl StoppedTarget {
match self.breakpoints.remove(&address) {
None => Err(DebugError::NonExistingBreakpoint { address }),
Some(original_byte) => {
let content = clibwrap::ptrace::peek_data(self.pid.as_raw(), address).map_err(PTraceError)?;
let content = nix::sys::ptrace::read(self.pid, address as *mut c_void).map_err(PTraceError)?;
let new_content = (content & (!0xff as c_long)) | (original_byte as c_long);
clibwrap::ptrace::poke_data(self.pid.as_raw(), address, new_content).map_err(PTraceError)?;
nix::sys::ptrace::write(self.pid, address as *mut c_void, new_content).map_err(PTraceError)?;
Ok(())
}
}

View File

@ -1,12 +1,10 @@
mod debug_target;
mod syscall_info;
mod clibwrap;
use crate::debug_target::{ExitedTarget, StoppedTarget};
use clibwrap::fork::{fork, ForkResult};
use color_eyre::eyre::eyre;
use either::Either;
use nix::unistd::Pid;
use nix::unistd::{fork, ForkResult};
use std::ffi::CString;
#[allow(dead_code)]
@ -15,7 +13,7 @@ fn single_step_all(mut target: StoppedTarget) -> color_eyre::Result<()> {
loop {
instruction_number += 1;
let regs = target.get_registers()?;
println!("🔎 [==> {instruction_number} {regs:?}");
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(ExitedTarget { exit_code, was_pid: _was_pid }) => {
@ -53,10 +51,10 @@ fn main() -> color_eyre::Result<()> {
nix::unistd::execv(&child_exec_path, &[&child_exec_path])?;
unreachable!();
}
Ok(ForkResult::Parent { child_pid }) => {
Ok(ForkResult::Parent { child: child_pid }) => {
println!("✔️ Created child {child_pid}");
let mut target = StoppedTarget::new(Pid::from_raw(child_pid))?;
let mut target = StoppedTarget::new(child_pid)?;
println!("✔️ Child ready!");
let breakpoint1 = 0x40101b;
@ -69,8 +67,8 @@ fn main() -> color_eyre::Result<()> {
println!("🚧 We are on a breakpoint!")
}
println!("🔎 rip: {:#x}", t.get_registers()?.rip);
single_step_all(t)?;
// strace(t)?;
// single_step_all(t)?;
strace(t)?;
}
Either::Right(ExitedTarget { exit_code, .. }) => {
println!("👋 Child exited with code {exit_code}");