benfred_read_process_memory/
lib.rs1#[macro_use]
27extern crate log;
28extern crate libc;
29
30use std::io;
31
32pub trait CopyAddress {
34 fn copy_address(&self, addr: usize, buf: &mut [u8]) -> io::Result<()>;
36}
37
38pub use platform::Pid;
40pub use platform::ProcessHandle;
62
63#[cfg(target_os="linux")]
64mod platform {
65 use libc::{pid_t, c_void, iovec, process_vm_readv};
66 use std::convert::TryFrom;
67 use std::io;
68 use std::fs;
69 use std::io::Seek;
70 use std::io::Read;
71 use std::process::Child;
72
73 use super::{CopyAddress};
74
75 pub type Pid = pid_t;
77 #[derive(Copy, Clone)]
79 pub struct ProcessHandle(Pid);
80
81 impl TryFrom<Pid> for ProcessHandle {
83 type Error = io::Error;
84
85 fn try_from(pid: Pid) -> io::Result<Self> {
86 Ok(Self(pid))
87 }
88 }
89
90 impl TryFrom<&Child> for ProcessHandle {
92 type Error = io::Error;
93
94 fn try_from(child: &Child) -> io::Result<Self> {
95 Self::try_from(child.id() as Pid)
96 }
97 }
98
99 impl CopyAddress for ProcessHandle {
100 fn copy_address(&self, addr: usize, buf: &mut [u8]) -> io::Result<()> {
101 let local_iov = iovec {
102 iov_base: buf.as_mut_ptr() as *mut c_void,
103 iov_len: buf.len(),
104 };
105 let remote_iov = iovec {
106 iov_base: addr as *mut c_void,
107 iov_len: buf.len(),
108 };
109 let result = unsafe { process_vm_readv(self.0, &local_iov, 1, &remote_iov, 1, 0) };
110 if result == -1 {
111 if let Some(libc::ENOSYS) = io::Error::last_os_error().raw_os_error() {
112 let mut procmem = fs::File::open(format!("/proc/{}/mem", self.0))?;
115 procmem.seek(io::SeekFrom::Start(addr as u64))?;
116 return procmem.read_exact(buf);
117 } else {
118 Err(io::Error::last_os_error())
119 }
120 } else {
121 Ok(())
122 }
123 }
124 }
125}
126
127#[cfg(target_os="macos")]
128mod platform {
129 extern crate mach;
130
131 use libc::{pid_t, c_int};
132 use self::mach::kern_return::{kern_return_t, KERN_SUCCESS};
133 use self::mach::port::{mach_port_t, mach_port_name_t, MACH_PORT_NULL};
134 use self::mach::vm_types::{mach_vm_address_t, mach_vm_size_t};
135
136 use std::convert::TryFrom;
137 use std::io;
138 use std::process::Child;
139
140 use super::{CopyAddress};
141
142 #[allow(non_camel_case_types)]
143 type vm_map_t = mach_port_t;
144 #[allow(non_camel_case_types)]
145 type vm_address_t = mach_vm_address_t;
146 #[allow(non_camel_case_types)]
147 type vm_size_t = mach_vm_size_t;
148
149 pub type Pid = pid_t;
151 #[derive(Copy, Clone)]
153 pub struct ProcessHandle(mach_port_name_t);
154
155 extern "C" {
156 fn vm_read_overwrite(target_task: vm_map_t,
157 address: vm_address_t,
158 size: vm_size_t,
159 data: vm_address_t,
160 out_size: *mut vm_size_t) -> kern_return_t;
161 }
162
163 fn task_for_pid(pid: Pid) -> io::Result<mach_port_name_t> {
166 let mut task: mach_port_name_t = MACH_PORT_NULL;
167
168 unsafe {
169 let result =
170 mach::traps::task_for_pid(mach::traps::mach_task_self(), pid as c_int, &mut task);
171 if result != KERN_SUCCESS {
172 return Err(io::Error::last_os_error());
173 }
174 }
175
176 Ok(task)
177 }
178
179 impl TryFrom<Pid> for ProcessHandle {
181 type Error = io::Error;
182
183 fn try_from(pid: Pid) -> io::Result<Self> {
184 Ok(Self(task_for_pid(pid)?))
185 }
186 }
187
188 impl TryFrom<mach_port_name_t> for ProcessHandle {
190 type Error = io::Error;
191
192 fn try_from(mach_port_name: mach_port_name_t) -> io::Result<Self> {
193 Ok(Self(mach_port_name))
194 }
195 }
196
197 impl TryFrom<&Child> for ProcessHandle {
208 type Error = io::Error;
209
210 fn try_from(child: &Child) -> io::Result<Self> {
211 Self::try_from(child.id() as Pid)
212 }
213 }
214
215 impl CopyAddress for ProcessHandle {
217 fn copy_address(&self, addr: usize, buf: &mut [u8]) -> io::Result<()> {
218 let mut read_len = buf.len() as vm_size_t;
219 let result = unsafe {
220 vm_read_overwrite(self.0,
221 addr as vm_address_t,
222 buf.len() as vm_size_t,
223 buf.as_mut_ptr() as vm_address_t,
224 &mut read_len)
225 };
226
227 if read_len != buf.len() as vm_size_t {
228 panic!("Mismatched read sizes for `vm_read` (expected {}, got {})",
229 buf.len(),
230 read_len)
231 }
232
233 if result != KERN_SUCCESS {
234 return Err(io::Error::last_os_error());
235 }
236 Ok(())
237 }
238 }
239}
240
241#[cfg(target_os="freebsd")]
242mod platform {
243 use libc::{pid_t, c_void, c_int};
244 use libc::{waitpid, WIFSTOPPED, PT_ATTACH, PT_DETACH, PT_IO, EBUSY};
245 use std::convert::TryFrom;
246 use std::{io, ptr};
247 use std::process::Child;
248
249 use super::{CopyAddress};
250
251 pub type Pid = pid_t;
253 #[derive(Copy, Clone)]
255 pub struct ProcessHandle(Pid);
256
257 #[repr(C)]
258 struct PtraceIoDesc {
259 piod_op: c_int,
260 piod_offs: *mut c_void,
261 piod_addr: *mut c_void,
262 piod_len: usize,
263 }
264
265 #[derive(PartialEq)]
271 enum PtraceLockState {
272 Release,
273 NoRelease,
274 }
275
276 extern "C" {
277 fn ptrace(request: c_int,
280 pid: pid_t,
281 io_desc: *const PtraceIoDesc,
282 data: c_int) -> c_int;
283 }
284
285 const PIOD_READ: c_int = 1;
288
289 impl TryFrom<Pid> for ProcessHandle {
291 type Error = io::Error;
292
293 fn try_from(pid: Pid) -> io::Result<Self> {
294 Ok(Self(pid))
295 }
296 }
297
298 impl TryFrom<&Child> for ProcessHandle {
300 type Error = io::Error;
301
302 fn try_from(child: &Child) -> io::Result<Self> {
303 Self::try_from(child.id() as Pid)
304 }
305 }
306
307 fn ptrace_attach(pid: Pid) -> io::Result<PtraceLockState> {
309 let attach_status = unsafe {
310 ptrace(PT_ATTACH, pid, ptr::null_mut(), 0)
311 };
312
313 let last_error = io::Error::last_os_error();
314
315 if let Some(error) = last_error.raw_os_error() {
316 if attach_status == -1 {
317 return match error {
318 EBUSY => Ok(PtraceLockState::NoRelease),
319 _ => Err(last_error),
320 }
321 }
322 }
323
324 let mut wait_status = 0;
325
326 let stopped = unsafe {
327 waitpid(pid, &mut wait_status as *mut _, 0);
328 WIFSTOPPED(wait_status)
329 };
330
331 if !stopped {
332 Err(io::Error::last_os_error())
333 } else {
334 Ok(PtraceLockState::Release)
335 }
336 }
337
338 fn ptrace_io(pid: Pid, addr: usize, buf: &mut [u8])
340 -> io::Result<()> {
341 let ptrace_io_desc = PtraceIoDesc {
342 piod_op: PIOD_READ,
343 piod_offs: addr as *mut c_void,
344 piod_addr: buf.as_mut_ptr() as *mut c_void,
345 piod_len: buf.len(),
346 };
347
348 let result = unsafe {
349 ptrace(PT_IO, pid, &ptrace_io_desc as *const _, 0)
350 };
351
352 if result == -1 {
353 Err(io::Error::last_os_error())
354 } else {
355 Ok(())
356 }
357 }
358
359
360 fn ptrace_detach(pid: Pid) -> io::Result<()> {
362 let detach_status = unsafe {
363 ptrace(PT_DETACH, pid, ptr::null_mut(), 0)
364 };
365
366 if detach_status == -1 {
367 Err(io::Error::last_os_error())
368 } else {
369 Ok(())
370 }
371 }
372
373 impl CopyAddress for ProcessHandle {
374 fn copy_address(&self, addr: usize, buf: &mut [u8]) -> io::Result<()> {
375 let should_detach = ptrace_attach(self.0)? == PtraceLockState::Release;
376
377 let result = ptrace_io(self.0, addr, buf);
378 if should_detach {
379 ptrace_detach(self.0)?
380 }
381 result
382 }
383 }
384}
385
386#[cfg(windows)]
387mod platform {
388 extern crate winapi;
389 extern crate kernel32;
390
391 use std::convert::TryFrom;
392 use std::io;
393 use std::mem;
394 use std::os::windows::io::{AsRawHandle, RawHandle};
395 use std::process::Child;
396 use std::ptr;
397
398 use super::{CopyAddress};
399
400 pub type Pid = winapi::DWORD;
402 #[derive(Copy, Clone, Eq, PartialEq, Hash)]
404 pub struct ProcessHandle(pub RawHandle);
405
406 impl TryFrom<Pid> for ProcessHandle {
408 type Error = io::Error;
409
410 fn try_from(pid: Pid) -> io::Result<Self> {
411 let handle = unsafe {
412 kernel32::OpenProcess(winapi::winnt::PROCESS_VM_READ, winapi::FALSE, pid)
413 };
414 if handle == (0 as RawHandle) {
415 Err(io::Error::last_os_error())
416 } else {
417 Ok(Self(handle))
418 }
419 }
420 }
421
422 impl TryFrom<&Child> for ProcessHandle {
424 type Error = io::Error;
425
426 fn try_from(child: &Child) -> io::Result<Self> {
427 Ok(Self(child.as_raw_handle()))
428 }
429 }
430
431 impl CopyAddress for ProcessHandle {
433 fn copy_address(&self, addr: usize, buf: &mut [u8]) -> io::Result<()> {
434 if buf.len() == 0 {
435 return Ok(());
436 }
437
438 if unsafe {
439 kernel32::ReadProcessMemory(self.0,
440 addr as winapi::LPVOID,
441 buf.as_mut_ptr() as winapi::LPVOID,
442 mem::size_of_val(buf) as winapi::SIZE_T,
443 ptr::null_mut())
444 } == winapi::FALSE {
445 Err(io::Error::last_os_error())
446 } else {
447 Ok(())
448 }
449 }
450 }
451}
452
453pub fn copy_address<T>(addr: usize, length: usize, source: &T) -> io::Result<Vec<u8>>
458 where T: CopyAddress
459{
460 debug!("copy_address: addr: {:x}", addr);
461
462 let mut copy = vec![0; length];
463
464 source.copy_address(addr, &mut copy)
465 .map_err(|e| {
466 warn!("copy_address failed for {:x}: {:?}", addr, e);
467 e
468 })
469 .and(Ok(copy))
470}
471
472#[cfg(test)]
473mod test {
474 use super::*;
475 use std::convert::TryFrom;
476 use std::env;
477 use std::io::{self, BufRead, BufReader};
478 use std::path::PathBuf;
479 use std::process::{Child, Command, Stdio};
480
481 fn test_process_path() -> Option<PathBuf> {
482 env::current_exe()
483 .ok()
484 .and_then(|p| {
485 p.parent().map(|p| {
486 p.with_file_name("test")
487 .with_extension(env::consts::EXE_EXTENSION)
488 })
489 })
490 }
491
492 fn spawn_with_handle(cmd: &mut Command) -> io::Result<(Child, ProcessHandle)> {
493 let child = cmd.spawn()?;
494 let handle = ProcessHandle::try_from(child.id() as Pid)?;
495 Ok((child, handle))
496 }
497
498 fn read_test_process(args: Option<&[&str]>) -> io::Result<Vec<u8>> {
499 let path = test_process_path().unwrap();
501 let mut cmd = Command::new(&path);
502 {
503 cmd.stdin(Stdio::piped())
504 .stdout(Stdio::piped());
505 }
506 if let Some(a) = args {
507 cmd.args(a);
508 }
509 let (mut child, handle) = spawn_with_handle(&mut cmd)?;
510 let reader = BufReader::new(child.stdout.take().unwrap());
513 let line = reader.lines().next().unwrap().unwrap();
514 let bits = line.split(' ').collect::<Vec<_>>();
515 let addr = usize::from_str_radix(&bits[0][2..], 16).unwrap();
516 let size = bits[1].parse::<usize>().unwrap();
517 let mem = copy_address(addr, size, &handle)?;
518 child.wait()?;
519 Ok(mem)
520 }
521
522 #[test]
523 fn test_read_small() {
524 let mem = read_test_process(None).unwrap();
525 assert_eq!(mem, (0..32u8).collect::<Vec<u8>>());
526 }
527
528 #[test]
529 fn test_read_large() {
530 const SIZE: usize = 5000;
532 let arg = format!("{}", SIZE);
533 let mem = read_test_process(Some(&[&arg])).unwrap();
534 let expected =
535 (0..SIZE).map(|v| (v % (u8::max_value() as usize + 1)) as u8).collect::<Vec<u8>>();
536 assert_eq!(mem, expected);
537 }
538}