Implemented custom wrapper to get syscall info

This commit is contained in:
Elnath 2025-05-17 20:26:27 +02:00
parent c968c0d09a
commit b4e7e6864a
5 changed files with 132 additions and 16 deletions

29
Cargo.lock generated
View File

@ -37,7 +37,9 @@ name = "bdb"
version = "0.0.0"
dependencies = [
"color-eyre",
"derive_more",
"either",
"libc",
"nix",
"thiserror",
]
@ -96,6 +98,27 @@ dependencies = [
"tracing-error",
]
[[package]]
name = "derive_more"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
dependencies = [
"derive_more-impl",
]
[[package]]
name = "derive_more-impl"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
dependencies = [
"proc-macro2",
"quote",
"syn",
"unicode-xid",
]
[[package]]
name = "either"
version = "1.15.0"
@ -317,6 +340,12 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "valuable"
version = "0.1.1"

View File

@ -9,6 +9,8 @@ nix = { version = "0.30.1", features = ["process", "ptrace"] }
thiserror = "2.0.12"
color-eyre = "0.6.3"
either = "1.15.0"
libc = "0.2.172"
derive_more = { version = "2.0.1", features = ["debug"] }
[build-dependencies]
color-eyre = "0.6.3"

View File

@ -2,6 +2,7 @@ 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}")]
@ -67,6 +68,10 @@ impl DebugTarget<Stopped> {
pub fn get_registers(&self) -> Result<user_regs_struct, PTraceError> {
Ok(nix::sys::ptrace::getregs(self.state.pid)?)
}
pub fn get_syscall_info(&self) -> Result<SyscallInfo, SyscallInfoError> {
Ok(syscall_info(self.state.pid.as_raw())?)
}
}
pub struct Running {

View File

@ -1,5 +1,6 @@
mod child;
mod debug_target;
mod syscall_info;
use crate::debug_target::{DebugTarget, Stopped};
use color_eyre::eyre::eyre;
@ -92,24 +93,24 @@ fn main() -> color_eyre::Result<()> {
let target = DebugTarget::new(child_pid)?;
println!("✔️ Child ready!");
// println!("🔎 rip: {:#x}", target.get_registers()?.rip);
// println!("➡️➡️➡️ Stepping three times");
// let mut target = target;
// for _i in 0..3 {
// target = target.stepi()?;
// }
// println!("🔎 rip: {:#x}", target.get_registers()?.rip);
//
// println!("⚙️ Continuing execution");
// let target = target.cont()?;
// let exit_code = target.wait_for_exit()?;
//
// println!("👋 Child exited with code {exit_code}");
//
// Ok(())
println!("🔎 rip: {:#x}", target.get_registers()?.rip);
single_step_all(target)
println!("⚙️ Executing until next syscall");
let target = target.cont_syscall()?.wait_for_syscall()?;
println!("{:?}", target.get_syscall_info()?);
let target = target.cont_syscall()?.wait_for_syscall()?;
println!("{:?}", target.get_syscall_info()?);
println!("🔎 rip: {}", target.get_registers()?.rip);
println!("⚙️ Continuing execution");
let exit_code = target.cont()?.wait_for_exit()?;
println!("👋 Child exited with code {exit_code}");
Ok(())
// single_step_all(target)
// breakpoint_fun(child_pid)
}
Err(e) => {

79
src/syscall_info.rs Normal file
View File

@ -0,0 +1,79 @@
/*!
Currently nix's (v0.30.1) implementation for PTRACE_GET_SYSCALL_INFO does not seem to work, always returning a 0-filled struct.
It also returns the raw libc struct containing a union, while this parses it in a custom union-free type.
This is an alternative implementation that relies on libc and unsafe.
*/
use crate::syscall_info::SyscallInfoError::UnsupportedType;
use std::mem::MaybeUninit;
#[derive(thiserror::Error, Debug)]
pub enum SyscallInfoError {
#[error("Error code when calling ptrace: {error_code}")]
PTraceError { error_code: libc::c_long },
#[error("Allocated buffer for syscall info was too small, this should not happen: allocated {allocated} but expected {expected}")]
TooSmallBufferError { allocated: usize, expected: usize },
#[error("No info available, it means program was not stopped on a syscall entry/exit")]
NotInSyscall,
#[error("Unsupported info type returned: {op}")]
UnsupportedType { op: u8 },
}
#[derive(derive_more::Debug)]
#[allow(dead_code)]
pub enum SyscallInfo {
SysCallInfoEntry {
#[debug("{rip:#x}")]
rip: u64,
#[debug("{stack_pointer:#x}")]
stack_pointer: u64,
number: u64,
args: [u64; 6],
},
SysCallInfoExit {
#[debug("{rip:#x}")]
rip: u64,
#[debug("{stack_pointer:#x}")]
stack_pointer: u64,
return_val: i64,
is_error: u8,
},
}
pub fn syscall_info(pid: libc::pid_t) -> Result<SyscallInfo, SyscallInfoError> {
let mut syscall_info_raw = MaybeUninit::<libc::ptrace_syscall_info>::uninit();
let output_size = size_of_val(&syscall_info_raw);
let return_code = unsafe { libc::ptrace(libc::PTRACE_GET_SYSCALL_INFO, pid, output_size, syscall_info_raw.as_mut_ptr()) };
if return_code < 0 {
return Err(SyscallInfoError::PTraceError { error_code: return_code });
}
if return_code as usize > output_size { // Should not happen, but kernel is telling us that it wants to write more than we were expecting
return Err(SyscallInfoError::TooSmallBufferError { allocated: output_size, expected: return_code as usize });
}
let syscall_info_raw = unsafe { syscall_info_raw.assume_init() };
match syscall_info_raw.op {
libc::PTRACE_SYSCALL_INFO_NONE => Err(SyscallInfoError::NotInSyscall),
libc::PTRACE_SYSCALL_INFO_ENTRY => Ok(unsafe {
SyscallInfo::SysCallInfoEntry {
rip: syscall_info_raw.instruction_pointer,
stack_pointer: syscall_info_raw.stack_pointer,
number: syscall_info_raw.u.entry.nr,
args: syscall_info_raw.u.entry.args,
}
}),
libc::PTRACE_SYSCALL_INFO_EXIT => Ok(unsafe {
SyscallInfo::SysCallInfoExit {
rip: syscall_info_raw.instruction_pointer,
stack_pointer: syscall_info_raw.stack_pointer,
return_val: syscall_info_raw.u.exit.sval,
is_error: syscall_info_raw.u.exit.is_error,
}
}),
op => Err(UnsupportedType { op }),
}
}