diff --git a/src/target/linux.rs b/src/target/linux.rs index 3c6d5b7..88b3b2f 100644 --- a/src/target/linux.rs +++ b/src/target/linux.rs @@ -28,6 +28,12 @@ impl LinuxTarget { Ok(LinuxTarget { pid }) } + /// Attaches process as a debugee. + pub fn attach(pid: Pid) -> Result> { + unix::attach(pid)?; + Ok(LinuxTarget { pid }) + } + /// Uses this process as a debuggee. pub fn me() -> LinuxTarget { LinuxTarget { pid: getpid() } diff --git a/src/target/unix.rs b/src/target/unix.rs index 0f863e4..3ed4791 100644 --- a/src/target/unix.rs +++ b/src/target/unix.rs @@ -40,3 +40,10 @@ pub(crate) fn launch(path: &str) -> Result> { } } } + +/// Attach existing process as a debugee. +pub(crate) fn attach(pid: Pid) -> Result<(), Box> { + ptrace::attach(pid)?; + let _status = waitpid(pid, None); + Ok(()) +} diff --git a/tests/attach_readmem.rs b/tests/attach_readmem.rs new file mode 100644 index 0000000..3b9cf8f --- /dev/null +++ b/tests/attach_readmem.rs @@ -0,0 +1,58 @@ +//! This is a simple test to attach to already running debugee process + +use nix::unistd::{execv, fork, ForkResult}; +use std::ffi::CString; + +mod test_utils; + +#[cfg(target_os = "linux")] +use headcrab::{symbol::Dwarf, target::LinuxTarget}; + +static BIN_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/testees/longer_hello"); + +// Ignoring because most linux distributions have attaching to a running process disabled. +// To run the test it either requires root privilages or CAP_SYS_PTRACE capability. +#[ignore] +#[cfg(target_os = "linux")] +#[test] +fn attach_readmem() -> Result<(), Box> { + test_utils::ensure_testees(); + + let debuginfo = Dwarf::new(BIN_PATH)?; + + let str_addr = debuginfo + .get_var_address("STATICVAR") + .expect("Expected static var has not been found in the target binary"); + + match fork()? { + ForkResult::Parent { child, .. } => { + use std::{thread, time}; + thread::sleep(time::Duration::from_millis(50)); + + let target = LinuxTarget::attach(child)?; + + // Read pointer + let mut ptr_addr: usize = 0; + unsafe { + target.read().read(&mut ptr_addr, str_addr).apply()?; + } + + // Read current value + let mut rval = [0u8; 13]; + unsafe { + target.read().read(&mut rval, ptr_addr).apply()?; + } + + assert_eq!(&rval, b"Hello, world!"); + + Ok(()) + } + ForkResult::Child => { + let path = CString::new(BIN_PATH)?; + execv(&path, &[])?; + + // execv replaces the process image, so this place in code will not be reached. + unreachable!(); + } + } +} diff --git a/tests/testees/.gitignore b/tests/testees/.gitignore index e92569d..4beacfa 100644 --- a/tests/testees/.gitignore +++ b/tests/testees/.gitignore @@ -1 +1,2 @@ /hello +/longer_hello diff --git a/tests/testees/longer_hello.rs b/tests/testees/longer_hello.rs new file mode 100644 index 0000000..b1feb08 --- /dev/null +++ b/tests/testees/longer_hello.rs @@ -0,0 +1,9 @@ +static STATICVAR: &str = "Hello, world!\n"; + +pub fn main() { + + use std::{thread, time}; + thread::sleep(time::Duration::from_millis(100)); + + println!("{}", STATICVAR); +}