diff --git a/Cargo.toml b/Cargo.toml index 102cd21..bd76259 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,4 +12,7 @@ name = "ptrace" [dependencies] bitflags = "0.1" -posix-ipc = "0.0" +nix = "0.9" +num = "0.1.24" +enum_primitive = "0.0.2" +libc = "0.2" diff --git a/src/lib.rs b/src/lib.rs index 778f2f9..7fa2012 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,28 +1,31 @@ -#[allow(unstable)] +#![feature(iterator_step_by)] extern crate libc; -extern crate "posix-ipc" as ipc; +extern crate nix; #[macro_use] extern crate bitflags; +#[macro_use] extern crate enum_primitive; +extern crate num; -use std::os; use std::ptr; use std::default::Default; use std::vec::Vec; use std::mem; -use std::iter; -use std::num::FromPrimitive; -use std::cmp::min; +use std::io; + +use nix::sys::signal::Signal; + +use num::traits::FromPrimitive; pub type Address = u64; pub type Word = u64; -#[derive(Copy)] +#[derive(Copy, Clone)] pub enum Action { Allow, Kill } -#[derive(Debug, Copy)] +#[derive(Debug, Copy, Clone)] pub enum Request { TraceMe = 0, PeekText = 1, @@ -42,7 +45,8 @@ pub enum Request { Seize = 0x4206 } -#[derive(Copy, Debug, FromPrimitive)] +enum_from_primitive! { +#[derive(Copy, Clone, Debug)] pub enum Event { Fork = 1, VFork = 2, @@ -53,15 +57,16 @@ pub enum Event { Seccomp = 7, Stop = 128 } +} impl Event { pub fn from_wait_status(st: i32) -> Option { - let e: Option = FromPrimitive::from_i32(((st >> 8) & !5) >> 8); + let e: Option = Event::from_i32(((st >> 8) & !5) >> 8); return e; } } -#[derive(Copy, Default, Debug)] +#[derive(Copy, Clone, Default, Debug)] pub struct Registers { pub r15: Word, pub r14: Word, @@ -106,14 +111,14 @@ bitflags! { } } -pub fn setoptions(pid: libc::pid_t, opts: Options) -> Result { +pub fn setoptions(pid: libc::pid_t, opts: Options) -> Result { unsafe { raw (Request::SetOptions, pid, ptr::null_mut(), opts.bits as *mut libc::c_void) } } -pub fn getregs(pid: libc::pid_t) -> Result { +pub fn getregs(pid: libc::pid_t) -> Result { let mut buf: Registers = Default::default(); let buf_mut: *mut Registers = &mut buf; @@ -125,38 +130,40 @@ pub fn getregs(pid: libc::pid_t) -> Result { } } -pub fn setregs(pid: libc::pid_t, regs: &Registers) -> Result { +pub fn setregs(pid: libc::pid_t, regs: &Registers) -> Result { unsafe { let buf: *mut libc::c_void = mem::transmute(regs); raw (Request::SetRegs, pid, ptr::null_mut(), buf) } } -pub fn seize(pid: libc::pid_t) -> Result { +pub fn seize(pid: libc::pid_t) -> Result { unsafe { raw (Request::Seize, pid, ptr::null_mut(), ptr::null_mut()) } } -pub fn attach(pid: libc::pid_t) -> Result { +pub fn attach(pid: libc::pid_t) -> Result { unsafe { raw (Request::Attach, pid, ptr::null_mut(), ptr::null_mut()) } } -pub fn release(pid: libc::pid_t, signal: ipc::signals::Signal) -> Result { +pub fn release(pid: libc::pid_t, signal: Option) -> Result { + let sig = signal.map(|s| s as u32).unwrap_or(0); unsafe { - raw (Request::Detatch, pid, ptr::null_mut(), (signal as u32) as *mut libc::c_void) + raw (Request::Detatch, pid, ptr::null_mut(), sig as *mut libc::c_void) } } -pub fn cont(pid: libc::pid_t, signal: ipc::signals::Signal) -> Result { +pub fn cont(pid: libc::pid_t, signal: Option) -> Result { + let sig = signal.map(|s| s as u32).unwrap_or(0); unsafe { - raw (Request::Continue, pid, ptr::null_mut(), (signal as u32) as *mut libc::c_void) + raw (Request::Continue, pid, ptr::null_mut(), sig as *mut libc::c_void) } } -pub fn traceme() -> Result { +pub fn traceme() -> Result { unsafe { raw (Request::TraceMe, 0, ptr::null_mut(), ptr::null_mut()) } @@ -165,10 +172,10 @@ pub fn traceme() -> Result { unsafe fn raw(request: Request, pid: libc::pid_t, addr: *mut libc::c_void, - data: *mut libc::c_void) -> Result { + data: *mut libc::c_void) -> Result { let v = ptrace (request as libc::c_int, pid, addr, data); match v { - -1 => Result::Err(os::errno()), + -1 => Result::Err(io::Error::last_os_error().raw_os_error().unwrap()), _ => Result::Ok(v) } } @@ -180,29 +187,29 @@ extern { data: *mut libc::c_void) -> libc::c_long; } -#[derive(Copy, Debug)] +#[derive(Copy, Clone, Debug)] pub struct Syscall { pub args: [Word; 6], pub call: u64, pub pid: libc::pid_t, - pub returnVal: Word + pub return_val: Word } impl Syscall { - pub fn from_pid(pid: libc::pid_t) -> Result { + pub fn from_pid(pid: libc::pid_t) -> Result { match getregs (pid) { - Ok(regs) => + Ok(regs) => Ok(Syscall { pid: pid, call: regs.orig_rax, args: [regs.rdi, regs.rsi, regs.rdx, regs.rcx, regs.r8, regs.r9], - returnVal: 0 + return_val: 0 }), Err(e) => Err(e) } } - pub fn write(&self) -> Result { + pub fn write(&self) -> Result { match getregs(self.pid) { Ok(mut regs) => { regs.rdi = self.args[0]; @@ -212,7 +219,7 @@ impl Syscall { regs.r8 = self.args[4]; regs.r9 = self.args[5]; regs.orig_rax = self.call; - regs.rax = self.returnVal; + regs.rax = self.return_val; setregs(self.pid, ®s) }, Err(e) => Err(e) @@ -220,12 +227,12 @@ impl Syscall { } } -#[derive(Copy)] +#[derive(Copy, Clone)] pub struct Reader { pub pid: libc::pid_t } -#[derive(Copy)] +#[derive(Copy, Clone)] pub struct Writer { pub pid: libc::pid_t } @@ -237,7 +244,7 @@ impl Writer { } } - pub fn poke_data(&self, address: Address, data: Word) -> Result { + pub fn poke_data(&self, address: Address, data: Word) -> Result { match unsafe { raw (Request::PokeData, self.pid, address as *mut libc::c_void, data as *mut libc::c_void) } { @@ -246,12 +253,13 @@ impl Writer { } } + // TODO: incomplete pub fn write_object(&self, address: Address, data: &T) -> Result<(), usize> { let mut buf = Vec::with_capacity(mem::size_of::()); unsafe { let tptr: *const T = data; let p: *const u8 = mem::transmute(tptr); - for i in range(0, buf.capacity()) { + for i in 0..buf.capacity() { buf.push(*p.offset(i as isize)); } } @@ -259,15 +267,15 @@ impl Writer { Ok(()) } - pub fn write_data(&self, address: Address, buf: &Vec) -> Result<(), usize> { + pub fn write_data(&self, address: Address, buf: &Vec) -> Result<(), libc::c_int> { // The end of our range let max_addr = address + buf.len() as Address; // The last word we can completely overwrite let align_end = max_addr - (max_addr % mem::size_of::() as Address); - for write_addr in iter::range_step(address, align_end, mem::size_of::() as Address) { + for write_addr in (address..align_end).step_by(mem::size_of::()) { let mut d: Word = 0; let buf_idx = (write_addr - address) as usize; - for word_idx in iter::range(0, mem::size_of::()) { + for word_idx in 0..mem::size_of::() { d = set_byte(d, word_idx, buf[buf_idx + word_idx]); } match self.poke_data(write_addr, d) { @@ -283,7 +291,7 @@ impl Writer { Ok(v) => v, Err(e) => return Err(e) }; - for word_idx in iter::range(0, mem::size_of::()-2) { + for word_idx in 0..mem::size_of::()-2 { let buf_idx = buf_start + word_idx; d = set_byte(d, word_idx, buf[buf_idx]); } @@ -303,7 +311,7 @@ impl Reader { } } - pub fn peek_data(&self, address: Address) -> Result { + pub fn peek_data(&self, address: Address) -> Result { let l; unsafe { l = raw (Request::PeekData, self.pid, address as *mut libc::c_void, ptr::null_mut()) @@ -314,18 +322,18 @@ impl Reader { } } - pub fn read_string(&self, address: Address) -> Result, usize> { + pub fn read_string(&self, address: Address) -> Result, libc::c_int> { let mut end_of_str = false; let mut buf: Vec = Vec::with_capacity(1024); let max_addr = address + buf.capacity() as Address; let align_end = max_addr - (max_addr % mem::size_of::() as Address); - 'finish: for read_addr in iter::range_step(address, align_end, mem::size_of::() as Address) { + 'finish: for read_addr in (address..align_end).step_by(mem::size_of::()) { let d; match self.peek_data(read_addr) { Ok(v) => d = v, Err(e) => return Err(e) } - for word_idx in iter::range(0, mem::size_of::()) { + for word_idx in 0..mem::size_of::() { let chr = get_byte(d, word_idx); if chr == 0 { end_of_str = true; @@ -340,7 +348,7 @@ impl Reader { Ok(v) => d = v, Err(e) => return Err(e) } - for word_idx in range(0, mem::size_of::()) { + for word_idx in 0..mem::size_of::() { let chr = get_byte(d, word_idx); if chr == 0 { break; @@ -353,14 +361,14 @@ impl Reader { } fn get_byte(d: Word, byte_idx: usize) -> u8 { - assert!(byte_idx < mem::size_of::() * 8); + assert!(byte_idx < mem::size_of::()); ((d >> (byte_idx * 8)) & 0xff) as u8 } fn set_byte(d: Word, byte_idx: usize, value: u8) -> Word { - assert!(byte_idx < mem::size_of::() * 8); - let shift = mem::size_of::() * 8 * byte_idx; - let mask = (0xff << shift); + assert!(byte_idx < mem::size_of::()); + let shift = byte_idx * 8; + let mask = 0xff << shift; (d & !mask) | (((value as Word) << shift) & mask) } @@ -377,9 +385,20 @@ pub fn test_set_byte() { #[test] pub fn test_get_byte() { assert_eq!(get_byte(0, 0), 0); - assert_eq!(get_byte(0xffffffffffff, 0), 0xff); - assert_eq!(get_byte(0xffffffffffff, 8), 0xff); + assert_eq!(get_byte(!0, 0), 0xff); + assert_eq!(get_byte(!0, mem::size_of::()-1), 0xff); assert_eq!(get_byte(0xffffffffffaa, 0), 0xaa); assert_eq!(get_byte(0x0123456789ab, 1), 0x89); assert_eq!(get_byte(0x0123456789ab, 4), 0x23); } + +#[test] +#[should_panic] +pub fn test_set_byte_panic() { + set_byte(!0, mem::size_of::(), 0xff); +} +#[test] +#[should_panic] +pub fn test_get_byte_panic() { + get_byte(!0, mem::size_of::()); +} diff --git a/tests/read-and-write.rs b/tests/read-and-write.rs index 8e0b1fa..3603bf0 100644 --- a/tests/read-and-write.rs +++ b/tests/read-and-write.rs @@ -1,27 +1,27 @@ -#![feature(libc, std_misc)] extern crate libc; extern crate ptrace; -extern crate "posix-ipc" as ipc; use std::ffi::CString; -use std::os; +use std::io; use std::ptr; +use ptrace::Address; + #[test] fn test_attach_detach() { let pid = fork_and_halt(); assert!(match ptrace::attach(pid) { Ok(_) => true, _ => false }); unsafe { waitpid(pid, ptr::null_mut(), 0) }; - assert!(match ptrace::release(pid, ipc::signals::Signal::None) { Ok(_) => true, _ => false }); + assert!(match ptrace::release(pid, None) { Ok(_) => true, _ => false }); } #[test] fn test_read() { let (buf_addr, pid) = fork_with_buffer("foobar"); let reader = ptrace::Reader::new(pid); - match reader.peek_data(unsafe { buf_addr.offset(3) } as u64) { + match reader.peek_data(unsafe { buf_addr.offset(3) as Address }) { Ok(v) => assert_eq!((v & 0xff) as u8, 'b' as u8), - Err(_) => panic!("Error while reading: {:?}", os::last_os_error()) + Err(_) => panic!("Error while reading: {:?}", io::Error::last_os_error().raw_os_error().unwrap()) } } @@ -33,7 +33,7 @@ fn test_read_string() { Ok(v) => assert_eq!(v, vec!('f' as u8, 'o' as u8, 'o' as u8, 'b' as u8, 'a' as u8, 'r' as u8)), Err(_) => - panic!("Error while reading string: {:?}", os::last_os_error()) + panic!("Error while reading string: {:?}", io::Error::last_os_error().raw_os_error().unwrap()) } } @@ -50,7 +50,7 @@ fn test_write() { assert_eq!(v, foo_word); }, Err(_) => - panic!("Error while writing char: {:?}", os::last_os_error()) + panic!("Error while writing char: {:?}", io::Error::last_os_error().raw_os_error().unwrap()) } } @@ -68,7 +68,7 @@ fn test_write_small_buf() { assert_eq!(str::from_utf8(v.as_slice()), Ok("FOOBAR and then some")); }, Err(_) => - panic!("Error while writing buffer: {:?}", os::last_os_error()) + panic!("Error while writing buffer: {:?}", io::Error::last_os_error().raw_os_error().unwrap()) } } @@ -80,7 +80,7 @@ fn test_write_large_buf() { let (buf_addr, pid) = fork_with_buffer(s); let writer = ptrace::Writer::new(pid); let mut buf: Vec = Vec::new(); - buf.push_all("FRIDDLE FRITZ FROB BAZ BAR FOO".as_bytes()); + buf.extend("FRIDDLE FRITZ FROB BAZ BAR FOO".as_bytes()); match writer.write_data(buf_addr as u64, &buf) { Ok(_) => { let reader = ptrace::Reader::new(pid); @@ -88,12 +88,12 @@ fn test_write_large_buf() { assert_eq!(str::from_utf8(v.as_slice()), str::from_utf8(buf.as_slice())); }, Err(_) => - panic!("Error while writing buffer: {:?}", os::last_os_error()) + panic!("Error while writing buffer: {:?}", io::Error::last_os_error().raw_os_error().unwrap()) } } fn fork_with_buffer(buf: &str) -> (*const libc::c_char, libc::c_int) { - let buf = CString::from_slice(buf.as_bytes()); + let buf = CString::new(buf.as_bytes()).unwrap(); let buf_addr: *const libc::c_char = buf.as_ptr(); let pid = fork_and_halt(); ptrace::attach(pid).ok().expect("Could not attach to child");