Skip to content

Commit 68c0b6d

Browse files
committed
Add high-level wrappers for peeking and poking with ptrace
1 parent 0fff824 commit 68c0b6d

File tree

3 files changed

+338
-18
lines changed

3 files changed

+338
-18
lines changed

CHANGELOG.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,19 @@ This project adheres to [Semantic Versioning](http://semver.org/).
2222
([#739](https://github.com/nix-rust/nix/pull/739))
2323
- Expose `signalfd` module on Android as well.
2424
([#739](https://github.com/nix-rust/nix/pull/739))
25-
- Added nix::sys::ptrace::detach.
25+
- Added nix::sys::ptrace::detach.
2626
([#749](https://github.com/nix-rust/nix/pull/749))
2727
- Added timestamp socket control message variant:
2828
`nix::sys::socket::ControlMessage::ScmTimestamp`
2929
([#663](https://github.com/nix-rust/nix/pull/663))
3030
- Added socket option variant that enables the timestamp socket
3131
control message: `nix::sys::socket::sockopt::ReceiveTimestamp`
3232
([#663](https://github.com/nix-rust/nix/pull/663))
33-
33+
- Added specialized wrappers: `sys::ptrace::{peek, poke}{user, data}`
34+
and macros: `syscall_arg`, `syscall_arg32` for register-to-argument
35+
mappings. Using the matching routines
36+
with `sys::ptrace::ptrace` is now deprecated.
37+
([#666](https://github.com/nix-rust/nix/pull/666))
3438
### Changed
3539
- Renamed existing `ptrace` wrappers to encourage namespacing ([#692](https://github.com/nix-rust/nix/pull/692))
3640
- Marked `sys::ptrace::ptrace` as `unsafe`.
@@ -55,7 +59,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
5559
([#731](https://github.com/nix-rust/nix/pull/731))
5660
- Marked `pty::ptsname` function as `unsafe`
5761
([#744](https://github.com/nix-rust/nix/pull/744))
58-
- Moved constants ptrace request, event and options to enums and updated ptrace functions and argument types accordingly.
62+
- Moved constants ptrace request, event and options to enums and updated ptrace functions and argument types accordingly.
5963
([#749](https://github.com/nix-rust/nix/pull/749))
6064
- `AioCb::Drop` will now panic if the `AioCb` is still in-progress ([#715](https://github.com/nix-rust/nix/pull/715))
6165

src/sys/ptrace.rs

Lines changed: 225 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
//! For detailed description of the ptrace requests, consult `man ptrace`.
1+
//! Interface for `ptrace`
2+
//!
3+
//! For detailed description of the ptrace requests, consult [`ptrace`(2)].
4+
//! [`ptrace`(2)]: http://man7.org/linux/man-pages/man2/ptrace.2.html
25
36
use std::{mem, ptr};
47
use {Errno, Error, Result};
@@ -134,11 +137,20 @@ pub unsafe fn ptrace(request: Request, pid: Pid, addr: *mut c_void, data: *mut c
134137
}
135138
}
136139

137-
unsafe fn ptrace_peek(request: Request, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
138-
let ret = {
139-
Errno::clear();
140-
libc::ptrace(request as RequestType, libc::pid_t::from(pid), addr, data)
141-
};
140+
unsafe fn ptrace_peek(
141+
request: Request,
142+
pid: Pid,
143+
addr: *mut c_void,
144+
data: *mut c_void
145+
) -> Result<c_long> {
146+
147+
Errno::clear();
148+
let ret = libc::ptrace(
149+
request as RequestType,
150+
libc::pid_t::from(pid),
151+
addr,
152+
data
153+
);
142154
match Errno::result(ret) {
143155
Ok(..) | Err(Error::Sys(Errno::UnknownErrno)) => Ok(ret),
144156
err @ Err(..) => err,
@@ -168,8 +180,6 @@ unsafe fn ptrace_other(request: Request, pid: Pid, addr: *mut c_void, data: *mut
168180

169181
/// Set options, as with `ptrace(PTRACE_SETOPTIONS,...)`.
170182
pub fn setoptions(pid: Pid, options: Options) -> Result<()> {
171-
use std::ptr;
172-
173183
let res = unsafe {
174184
libc::ptrace(Request::PTRACE_SETOPTIONS as RequestType,
175185
libc::pid_t::from(pid),
@@ -238,12 +248,7 @@ pub fn syscall(pid: Pid) -> Result<()> {
238248
/// Attaches to the process specified in pid, making it a tracee of the calling process.
239249
pub fn attach(pid: Pid) -> Result<()> {
240250
unsafe {
241-
ptrace_other(
242-
Request::PTRACE_ATTACH,
243-
pid,
244-
ptr::null_mut(),
245-
ptr::null_mut(),
246-
).map(|_| ()) // ignore the useless return value
251+
ptrace_other(Request::PTRACE_ATTACH, pid, ptr::null_mut(), ptr::null_mut()).map(|_| ())
247252
}
248253
}
249254

@@ -275,3 +280,209 @@ pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
275280
}
276281
}
277282

283+
/// Represents all possible ptrace-accessible registers on x86_64
284+
#[cfg(target_arch = "x86_64")]
285+
#[allow(non_camel_case_types)]
286+
#[derive(Debug, PartialEq)]
287+
pub enum Register {
288+
R15 = 8 * ::libc::R15 as isize,
289+
R14 = 8 * ::libc::R14 as isize,
290+
R13 = 8 * ::libc::R13 as isize,
291+
R12 = 8 * ::libc::R12 as isize,
292+
RBP = 8 * ::libc::RBP as isize,
293+
RBX = 8 * ::libc::RBX as isize,
294+
R11 = 8 * ::libc::R11 as isize,
295+
R10 = 8 * ::libc::R10 as isize,
296+
R9 = 8 * ::libc::R9 as isize,
297+
R8 = 8 * ::libc::R8 as isize,
298+
RAX = 8 * ::libc::RAX as isize,
299+
RCX = 8 * ::libc::RCX as isize,
300+
RDX = 8 * ::libc::RDX as isize,
301+
RSI = 8 * ::libc::RSI as isize,
302+
RDI = 8 * ::libc::RDI as isize,
303+
ORIG_RAX = 8 * ::libc::ORIG_RAX as isize,
304+
RIP = 8 * ::libc::RIP as isize,
305+
CS = 8 * ::libc::CS as isize,
306+
EFLAGS = 8 * ::libc::EFLAGS as isize,
307+
RSP = 8 * ::libc::RSP as isize,
308+
SS = 8 * ::libc::SS as isize,
309+
FS_BASE = 8 * ::libc::FS_BASE as isize,
310+
GS_BASE = 8 * ::libc::GS_BASE as isize,
311+
DS = 8 * ::libc::DS as isize,
312+
ES = 8 * ::libc::ES as isize,
313+
FS = 8 * ::libc::FS as isize,
314+
GS = 8 * ::libc::GS as isize,
315+
}
316+
317+
/// Represents all possible ptrace-accessible registers on x86
318+
#[cfg(target_arch = "x86")]
319+
#[allow(non_camel_case_types)]
320+
#[derive(Debug, PartialEq)]
321+
pub enum Register {
322+
EBX = 4 * ::libc::EBX as isize,
323+
ECX = 4 * ::libc::ECX as isize,
324+
EDX = 4 * ::libc::EDX as isize,
325+
ESI = 4 * ::libc::ESI as isize,
326+
EDI = 4 * ::libc::EDI as isize,
327+
EBP = 4 * ::libc::EBP as isize,
328+
EAX = 4 * ::libc::EAX as isize,
329+
DS = 4 * ::libc::DS as isize,
330+
ES = 4 * ::libc::ES as isize,
331+
FS = 4 * ::libc::FS as isize,
332+
GS = 4 * ::libc::GS as isize,
333+
ORIG_EAX = 4 * ::libc::ORIG_EAX as isize,
334+
EIP = 4 * ::libc::EIP as isize,
335+
CS = 4 * ::libc::CS as isize,
336+
EFL = 4 * ::libc::EFL as isize,
337+
UESP = 4 * ::libc::UESP as isize,
338+
SS = 4 * ::libc::SS as isize,
339+
}
340+
341+
/// Returns the register containing nth register argument.
342+
///
343+
/// 0th argument is considered to be the syscall number.
344+
/// Please note that these mappings are only valid for 64-bit programs.
345+
/// Use [`syscall_arg32`] for tracing 32-bit programs instead.
346+
///
347+
/// [`syscall_arg32`]: macro.syscall_arg32.html
348+
/// # Examples
349+
///
350+
/// ```
351+
/// # #[macro_use] extern crate nix;
352+
/// # fn main() {
353+
/// assert_eq!(syscall_arg!(1), nix::sys::ptrace::Register::RDI);
354+
/// # }
355+
#[cfg(target_arch = "x86_64")]
356+
#[macro_export]
357+
macro_rules! syscall_arg {
358+
(0) => ($crate::sys::ptrace::Register::ORIG_RAX);
359+
(1) => ($crate::sys::ptrace::Register::RDI);
360+
(2) => ($crate::sys::ptrace::Register::RSI);
361+
(3) => ($crate::sys::ptrace::Register::RDX);
362+
(4) => ($crate::sys::ptrace::Register::R10);
363+
(5) => ($crate::sys::ptrace::Register::R8);
364+
(6) => ($crate::sys::ptrace::Register::R9);
365+
}
366+
367+
/// Returns the register containing nth register argument for 32-bit programs
368+
///
369+
/// 0th argument is considered to be the syscall number.
370+
/// Please note that these mappings are only valid for 32-bit programs.
371+
/// Use [`syscall_arg`] for tracing 64-bit programs instead.
372+
///
373+
/// [`syscall_arg`]: macro.syscall_arg.html
374+
/// # Examples
375+
///
376+
/// ```
377+
/// # #[macro_use] extern crate nix;
378+
/// # fn main() {
379+
/// assert_eq!(syscall_arg32!(1), nix::sys::ptrace::Register::RBX);
380+
/// # }
381+
#[cfg(target_arch = "x86_64")]
382+
#[macro_export]
383+
macro_rules! syscall_arg32 {
384+
(0) => ($crate::sys::ptrace::Register::ORIG_RAX);
385+
(1) => ($crate::sys::ptrace::Register::RBX);
386+
(2) => ($crate::sys::ptrace::Register::RCX);
387+
(3) => ($crate::sys::ptrace::Register::RDX);
388+
(4) => ($crate::sys::ptrace::Register::RSI);
389+
(5) => ($crate::sys::ptrace::Register::RDI);
390+
(6) => ($crate::sys::ptrace::Register::RBP);
391+
}
392+
393+
/// Returns the register containing nth register argument.
394+
///
395+
/// 0th argument is considered to be the syscall number.
396+
///
397+
/// # Examples
398+
///
399+
/// ```
400+
/// # #[macro_use] extern crate nix;
401+
/// # fn main() {
402+
/// assert_eq!(syscall_arg!(1), nix::sys::ptrace::Register::RDI);
403+
/// # }
404+
#[cfg(target_arch = "x86")]
405+
#[macro_export]
406+
macro_rules! syscall_arg {
407+
(0) => ($crate::sys::ptrace::Register::ORIG_EAX);
408+
(1) => ($crate::sys::ptrace::Register::EBX);
409+
(2) => ($crate::sys::ptrace::Register::ECX);
410+
(3) => ($crate::sys::ptrace::Register::EDX);
411+
(4) => ($crate::sys::ptrace::Register::ESI);
412+
(5) => ($crate::sys::ptrace::Register::EDI);
413+
(6) => ($crate::sys::ptrace::Register::EBP);
414+
}
415+
416+
/// An integer type, whose size equals a machine word
417+
///
418+
/// `ptrace` always returns a machine word. This type provides an abstraction
419+
/// of the fact that on *nix systems, `c_long` is always a machine word,
420+
/// so as to prevent the library from leaking C implementation-dependent types.
421+
type Word = usize;
422+
423+
/// Peeks a user-accessible register, as with `ptrace(PTRACE_PEEKUSER, ...)`
424+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
425+
pub fn peekuser(pid: Pid, reg: Register) -> Result<Word> {
426+
let reg_arg = (reg as i32) as *mut c_void;
427+
unsafe {
428+
ptrace_peek(Request::PTRACE_PEEKUSER, pid, reg_arg, ptr::null_mut()).map(|r| r as Word)
429+
}
430+
}
431+
432+
/// Sets the value of a user-accessible register, as with `ptrace(PTRACE_POKEUSER, ...)`
433+
///
434+
/// # Safety
435+
/// When incorrectly used, may change the registers to bad values,
436+
/// causing e.g. memory being corrupted by a syscall, thus is marked unsafe
437+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
438+
pub unsafe fn pokeuser(pid: Pid, reg: Register, val: Word) -> Result<()> {
439+
let reg_arg = (reg as u64) as *mut c_void;
440+
ptrace_other(Request::PTRACE_POKEUSER, pid, reg_arg, val as *mut c_void).map(|_| ()) // ignore the useless return value
441+
}
442+
443+
/// Peeks the memory of a process, as with `ptrace(PTRACE_PEEKDATA, ...)`
444+
///
445+
/// A memory chunk of a size of a machine word is returned.
446+
/// # Safety
447+
/// This function allows for accessing arbitrary data in the traced process
448+
/// and may crash the inferior if used incorrectly and is thus marked `unsafe`.
449+
pub unsafe fn peekdata(pid: Pid, addr: usize) -> Result<Word> {
450+
ptrace_peek(
451+
Request::PTRACE_PEEKDATA,
452+
pid,
453+
addr as *mut c_void,
454+
ptr::null_mut(),
455+
).map(|r| r as Word)
456+
}
457+
458+
/// Modifies the memory of a process, as with `ptrace(PTRACE_POKEUSER, ...)`
459+
///
460+
/// A memory chunk of a size of a machine word is overwriten in the requested
461+
/// place in the memory of a process.
462+
///
463+
/// # Safety
464+
/// This function allows for accessing arbitrary data in the traced process
465+
/// and may crash the inferior or introduce race conditions if used
466+
/// incorrectly and is thus marked `unsafe`.
467+
pub unsafe fn pokedata(pid: Pid, addr: usize, val: Word) -> Result<()> {
468+
ptrace_other(
469+
Request::PTRACE_POKEDATA,
470+
pid,
471+
addr as *mut c_void,
472+
val as *mut c_void,
473+
).map(|_| ()) // ignore the useless return value
474+
}
475+
476+
#[cfg(test)]
477+
mod tests {
478+
use super::Word;
479+
use std::mem::size_of;
480+
use libc::c_long;
481+
482+
#[test]
483+
fn test_types() {
484+
// c_long is implementation defined, so make sure
485+
// its width matches
486+
assert_eq!(size_of::<Word>(), size_of::<c_long>());
487+
}
488+
}

0 commit comments

Comments
 (0)