Skip to content

Commit d051d79

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

File tree

3 files changed

+332
-17
lines changed

3 files changed

+332
-17
lines changed

Diff for: CHANGELOG.md

+7-3
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

Diff for: src/sys/ptrace.rs

+219-13
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,20 @@ pub unsafe fn ptrace(request: Request, pid: Pid, addr: *mut c_void, data: *mut c
134134
}
135135
}
136136

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-
};
137+
unsafe fn ptrace_peek(
138+
request: Request,
139+
pid: Pid,
140+
addr: *mut c_void,
141+
data: *mut c_void
142+
) -> Result<c_long> {
143+
144+
Errno::clear();
145+
let ret = libc::ptrace(
146+
request as RequestType,
147+
libc::pid_t::from(pid),
148+
addr,
149+
data
150+
);
142151
match Errno::result(ret) {
143152
Ok(..) | Err(Error::Sys(Errno::UnknownErrno)) => Ok(ret),
144153
err @ Err(..) => err,
@@ -168,8 +177,6 @@ unsafe fn ptrace_other(request: Request, pid: Pid, addr: *mut c_void, data: *mut
168177

169178
/// Set options, as with `ptrace(PTRACE_SETOPTIONS,...)`.
170179
pub fn setoptions(pid: Pid, options: Options) -> Result<()> {
171-
use std::ptr;
172-
173180
let res = unsafe {
174181
libc::ptrace(Request::PTRACE_SETOPTIONS as RequestType,
175182
libc::pid_t::from(pid),
@@ -238,12 +245,7 @@ pub fn syscall(pid: Pid) -> Result<()> {
238245
/// Attaches to the process specified in pid, making it a tracee of the calling process.
239246
pub fn attach(pid: Pid) -> Result<()> {
240247
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
248+
ptrace_other(Request::PTRACE_ATTACH, pid, ptr::null_mut(), ptr::null_mut()).map(|_| ()) // ignore the useless return value
247249
}
248250
}
249251

@@ -275,3 +277,207 @@ pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
275277
}
276278
}
277279

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

0 commit comments

Comments
 (0)