use crate::module_reader::ModuleMemory;
use mach2::{
kern_return::KERN_SUCCESS,
task::task_info,
task_info::{TASK_DYLD_ALL_IMAGE_INFO_64, TASK_DYLD_INFO, task_dyld_info},
vm::mach_vm_read_overwrite,
};
use std::mem::{MaybeUninit, size_of};
pub type ProcessHandle = mach2::mach_types::task_t;
pub struct ProcessReader {
process: ProcessHandle,
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
struct AllImagesInfo {
pub version: u32,
info_array_count: u32,
info_array_addr: u64,
_notification: u64,
_process_detached_from_shared_region: bool,
lib_system_initialized: bool,
pub dyld_image_load_address: u64,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
struct ImageInfo {
pub load_address: u64,
pub file_path: u64,
pub file_mod_date: u64,
}
#[derive(Debug, thiserror::Error, serde::Serialize)]
#[error("Copy from process {child} failed (source {src}, length: {length})")]
pub struct CopyFromProcessError {
pub child: ProcessHandle,
pub src: usize,
pub length: usize,
pub kern_return: mach2::kern_return::kern_return_t,
}
#[derive(Debug, thiserror::Error)]
pub enum FindModuleError {
#[error("Failed to get task info")]
TaskInfoError,
#[error("The image is not a supported format")]
ImageFormatInvalid,
#[error("The module was not found")]
ModuleNotFound,
#[error("Failed to read data from the module")]
FailedToReadModule(#[from] CopyFromProcessError),
}
impl ProcessReader {
pub fn new(process: ProcessHandle) -> ProcessReader {
ProcessReader { process }
}
pub fn read(&self, src: usize, dst: &mut [u8]) -> Result<usize, CopyFromProcessError> {
let mut size: u64 = 0;
let res = unsafe {
mach_vm_read_overwrite(
self.process,
src as _,
dst.len() as _,
dst.as_mut_ptr() as _,
&mut size as _,
)
};
if res == KERN_SUCCESS {
Ok(size as usize)
} else {
Err(CopyFromProcessError {
child: self.process,
src,
length: dst.len(),
kern_return: res,
})
}
}
pub fn find_module(&self, module_name: &str) -> Result<ModuleMemory<'_>, FindModuleError> {
let dyld_info = self.task_info()?;
if (dyld_info.all_image_info_format as u32) != TASK_DYLD_ALL_IMAGE_INFO_64 {
return Err(FindModuleError::ImageFormatInvalid);
}
let all_image_info_size = dyld_info.all_image_info_size;
let all_image_info_addr = dyld_info.all_image_info_addr;
if (all_image_info_size as usize) < size_of::<AllImagesInfo>() {
return Err(FindModuleError::ImageFormatInvalid);
}
let all_images_info =
unsafe { self.copy_object::<AllImagesInfo>(all_image_info_addr as _) }?;
let images = unsafe {
self.copy_array::<ImageInfo>(
all_images_info.info_array_addr as _,
all_images_info.info_array_count as _,
)
}?;
images
.iter()
.find(|&image| {
let image_path = self.copy_nul_terminated_string(image.file_path as usize);
if let Ok(image_path) = image_path {
if let Some(image_name) = image_path.into_bytes().rsplit(|&b| b == b'/').next()
{
image_name.eq(module_name.as_bytes())
} else {
false
}
} else {
false
}
})
.map(|image| ModuleMemory::from_process(self, image.load_address as usize))
.ok_or(FindModuleError::ModuleNotFound)
}
fn task_info(&self) -> Result<task_dyld_info, FindModuleError> {
let mut info = MaybeUninit::<task_dyld_info>::uninit();
let mut count = (size_of::<task_dyld_info>() / size_of::<u32>()) as u32;
let res = unsafe {
task_info(
self.process,
TASK_DYLD_INFO,
info.as_mut_ptr().cast(),
&mut count,
)
};
if res == KERN_SUCCESS {
unsafe { Ok(info.assume_init()) }
} else {
Err(FindModuleError::TaskInfoError)
}
}
}