#![warn(missing_docs)]
#![doc = include_str!("../README.md")]
#[cfg(target_os = "linux")]
mod linux;
#[cfg(target_os = "linux")]
pub use linux::*;
#[cfg(target_os = "macos")]
mod macos;
#[cfg(target_os = "macos")]
pub use macos::*;
#[cfg(target_os = "windows")]
mod windows;
#[cfg(target_os = "windows")]
pub use windows::*;
pub mod error;
use std::{io::Cursor, ffi::CString};
use byteorder::{ReadBytesExt, NativeEndian};
use crate::error::{Error, Result};
#[derive(PartialEq, Eq, Debug)]
pub enum MemoryPrivacy {
Shared,
Private,
}
#[derive(Debug)]
pub struct MemoryPermissions {
pub readable: bool,
pub writable: bool,
pub executable: bool,
pub privacy: MemoryPrivacy,
}
#[derive(PartialEq, Eq, Debug)]
pub enum MemoryPath {
MappedFile(String),
Stack,
ThreadStack(usize),
Vdso,
Heap,
Vvar,
Vsyscall,
Unknown,
}
#[derive(Debug)]
pub struct MemoryMap {
pub base: usize,
pub ceiling: usize,
pub perms: MemoryPermissions,
pub pathname: MemoryPath,
}
impl MemoryMap {
pub fn size_of_mapping(&self) -> usize {
self.ceiling - self.base
}
}
#[cfg(any(doc, target_os = "windows"))]
#[derive(Debug)]
pub struct ProcessModule {
pub name: String,
pub base_address: usize,
}
pub struct Process {
pub pid: u32,
pub maps: Vec<MemoryMap>,
#[cfg(any(doc, target_os = "windows"))]
pub modules: Vec<ProcessModule>,
#[cfg(target_os = "windows")]
handle: windows_sys::Win32::Foundation::HANDLE,
}
#[cfg(any(doc, target_os = "windows"))]
unsafe impl Send for Process {}
#[cfg(any(doc, target_os = "windows"))]
unsafe impl Sync for Process {}
impl Process {
pub fn read_u8(&self, ptr: usize) -> Result<u8> {
Cursor::new(self.read_buf(ptr, 1)?).read_u8().map_err(Error::Io)
}
pub fn read_i8(&self, ptr: usize) -> Result<i8> {
Cursor::new(self.read_buf(ptr, 1)?).read_i8().map_err(Error::Io)
}
pub fn read_u16(&self, ptr: usize) -> Result<u16> {
Cursor::new(self.read_buf(ptr, 2)?).read_u16::<NativeEndian>().map_err(Error::Io)
}
pub fn read_i16(&self, ptr: usize) -> Result<i16> {
Cursor::new(self.read_buf(ptr, 2)?).read_i16::<NativeEndian>().map_err(Error::Io)
}
pub fn read_u32(&self, ptr: usize) -> Result<u32> {
Cursor::new(self.read_buf(ptr, 4)?).read_u32::<NativeEndian>().map_err(Error::Io)
}
pub fn read_i32(&self, ptr: usize) -> Result<i32> {
Cursor::new(self.read_buf(ptr, 4)?).read_i32::<NativeEndian>().map_err(Error::Io)
}
pub fn read_u64(&self, ptr: usize) -> Result<u64> {
Cursor::new(self.read_buf(ptr, 8)?).read_u64::<NativeEndian>().map_err(Error::Io)
}
pub fn read_i64(&self, ptr: usize) -> Result<u64> {
Cursor::new(self.read_buf(ptr, 8)?).read_u64::<NativeEndian>().map_err(Error::Io)
}
pub fn read_ptr(&self, ptr: usize) -> Result<usize> {
Ok(if cfg!(target_pointer_width = "64") {
Cursor::new(self.read_buf(ptr, 8)?).read_u64::<NativeEndian>().map_err(Error::Io)? as usize
} else {
Cursor::new(self.read_buf(ptr, 4)?).read_u32::<NativeEndian>().map_err(Error::Io)? as usize
})
}
pub fn read_cstring(&self, ptr: usize) -> Result<String> {
let mut buf: Vec<u8> = vec![];
let mut i: usize = 0;
loop {
let x = self.read_u8(ptr + i)?;
buf.push(x);
i += 1;
if x == 0 {
break Ok(CString::from_vec_with_nul(buf).map_err(Error::FromVecWithNul)?.to_str().map_err(Error::Utf8)?.to_string());
}
}
}
pub fn walk_ptr(&self, map: &MemoryMap, query: usize) -> Result<Vec<usize>> {
let mut vec = Vec::new();
let buf = self.read_buf(map.base, map.size_of_mapping())?;
let size = std::mem::size_of::<usize>();
let len = map.size_of_mapping() / size;
for i in 0..len {
let mut cur = Cursor::new(&buf[i * size .. i * size + std::mem::size_of::<usize>()]);
let x = if cfg!(target_pointer_width = "64") {
cur.read_u64::<NativeEndian>().map_err(Error::Io)? as usize
} else {
cur.read_u32::<NativeEndian>().map_err(Error::Io)? as usize
};
if x == query {
vec.push(map.base + i * size);
}
}
Ok(vec)
}
}