Implemented custom wrapper to get syscall info
This commit is contained in:
parent
c968c0d09a
commit
b4e7e6864a
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
33
src/main.rs
33
src/main.rs
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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 }),
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue