Description
Hi!
First of all - I'm eager to contribute the documentation, once I've understood, how the module should be used.
I was quite new to ptrace
and I'm still learning, so I might have missed something obvious, keep this in mind please :)
I had this small piece of code, which simply printed a message, whenever a syscall was entered or exited.
int main() {
pid_t child = fork();
if (child == 0) {
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
execl("/bin/ls", "ls", NULL);
} else {
for (;;) {
int status;
wait(&status);
if (WIFEXITED(status))
break;
long orig_rax = ptrace(PTRACE_PEEKUSER, child, 8 * ORIG_RAX, NULL);
printf("Syscall %ld\n", orig_rax);
ptrace(PTRACE_SYSCALL, child, NULL, NULL);
}
}
return 0;
}
This worked very well. Now I translated this into Rust.
extern crate colored;
extern crate spawn_ptrace;
extern crate nix;
use std::process::Command;
use nix::sys::ptrace::ptrace;
use nix::sys::ptrace::ptrace::*;
use nix::sys::wait::{wait, WaitStatus};
use std::ptr;
use nix::libc::c_void;
const RAX: i64 = 8 * 15;
fn main() {
use spawn_ptrace::CommandPtraceSpawn;
let child = Command::new("ls").arg("-l").spawn_ptrace().unwrap();
let pid = child.id() as i32;
loop {
match wait() {
Ok(WaitStatus::Exited(_, code)) => assert_eq!(code, 0),
Ok(WaitStatus::Stopped(_, sig)) => {
println!("Stopped with a signal: {:?}", sig);
}
Ok(s) => panic!("Unexpected stop reason: {:?}", s),
Err(e) => panic!("Unexpected waitpid error: {:?}", e),
}
let orig_rax = ptrace(PTRACE_PEEKUSER, pid, RAX as *mut c_void, ptr::null_mut()).unwrap();
println!("We've got syscall: {}", orig_rax);
ptrace(PTRACE_SYSCALL, pid, ptr::null_mut(), ptr::null_mut()).unwrap();
}
}
No good. Nothing displays, the process is stuck at the wait()
forever.
I took a look at the spawn-ptrace
crate, which doesn't call wait()
before, so next try:
extern crate colored;
extern crate spawn_ptrace;
extern crate nix;
use std::process::Command;
use nix::sys::ptrace::ptrace;
use nix::sys::ptrace::ptrace::*;
use nix::sys::wait::{wait, WaitStatus};
use std::ptr;
use nix::libc::c_void;
const RAX: i64 = 8 * 15;
fn main() {
use spawn_ptrace::CommandPtraceSpawn;
let child = Command::new("ls").arg("-l").spawn_ptrace().unwrap();
let pid = child.id() as i32;
loop {
let orig_rax = ptrace(PTRACE_PEEKUSER, pid, RAX as *mut c_void, ptr::null_mut()).unwrap();
println!("We've got syscall: {}", orig_rax);
ptrace(PTRACE_SYSCALL, pid, ptr::null_mut(), ptr::null_mut()).unwrap();
match wait() {
Ok(WaitStatus::Exited(_, code)) => assert_eq!(code, 0),
Ok(WaitStatus::Stopped(_, sig)) => {
println!("Stopped with a signal: {:?}", sig);
}
Ok(s) => panic!("Unexpected stop reason: {:?}", s),
Err(e) => panic!("Unexpected waitpid error: {:?}", e),
}
}
}
This displays all syscalls, compared to the C version, but panicks at the end, because the process is not in the stopped state.
We've got syscall: 3
Stopped with a signal: SIGTRAP
We've got syscall: 3
Stopped with a signal: SIGTRAP
We've got syscall: 231
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Sys(ESRCH)', src/libcore/result.rs:859
stack backtrace:
0: std::sys::imp::backtrace::tracing::imp::unwind_backtrace
1: std::sys_common::backtrace::_print
2: std::panicking::default_hook::{{closure}}
3: std::panicking::default_hook
4: std::panicking::rust_panic_with_hook
5: std::panicking::begin_panic
6: std::panicking::begin_panic_fmt
7: rust_begin_unwind
8: core::panicking::panic_fmt
9: core::result::unwrap_failed
at /build/rust/src/rustc-1.18.0-src/src/libcore/macros.rs:29
10: <core::result::Result<T, E>>::unwrap
at /build/rust/src/rustc-1.18.0-src/src/libcore/result.rs:737
11: ptrace_rust::main
at ./src/main.rs:21
12: __rust_maybe_catch_panic
13: std::rt::lang_start
14: main
15: __libc_start_main
16: _start
Could anyone explain, how the nix::sys::ptrace
module should be used? I'll be happy to contribute the documentation in return.