summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/process_reader/src/platform
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/crashreporter/process_reader/src/platform')
-rw-r--r--toolkit/crashreporter/process_reader/src/platform/linux.rs322
-rw-r--r--toolkit/crashreporter/process_reader/src/platform/macos.rs225
-rw-r--r--toolkit/crashreporter/process_reader/src/platform/windows.rs201
3 files changed, 748 insertions, 0 deletions
diff --git a/toolkit/crashreporter/process_reader/src/platform/linux.rs b/toolkit/crashreporter/process_reader/src/platform/linux.rs
new file mode 100644
index 0000000000..4a87e5480b
--- /dev/null
+++ b/toolkit/crashreporter/process_reader/src/platform/linux.rs
@@ -0,0 +1,322 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use std::{
+ cmp::min,
+ fs::File,
+ io::{BufRead, BufReader, Error},
+ mem::{size_of, MaybeUninit},
+ ptr::null_mut,
+ slice,
+};
+
+use crate::{
+ error::{ProcessReaderError, PtraceError, ReadError},
+ ProcessHandle, ProcessReader,
+};
+
+use goblin::elf::{
+ self,
+ program_header::{PF_R, PT_NOTE},
+ Elf, ProgramHeader,
+};
+use libc::{
+ c_int, c_long, c_void, pid_t, ptrace, waitpid, EINTR, PTRACE_ATTACH, PTRACE_DETACH,
+ PTRACE_PEEKDATA, __WALL,
+};
+
+impl ProcessReader {
+ pub fn new(process: ProcessHandle) -> Result<ProcessReader, ProcessReaderError> {
+ let pid: pid_t = process;
+
+ ptrace_attach(pid)?;
+
+ let mut status: i32 = 0;
+
+ loop {
+ let res = unsafe { waitpid(pid, &mut status as *mut _, __WALL) };
+ if res < 0 {
+ match get_errno() {
+ EINTR => continue,
+ _ => {
+ ptrace_detach(pid)?;
+ return Err(ProcessReaderError::WaitPidError);
+ }
+ }
+ } else {
+ break;
+ }
+ }
+
+ Ok(ProcessReader { process: pid })
+ }
+
+ pub fn find_module(&self, module_name: &str) -> Result<usize, ProcessReaderError> {
+ let maps_file = File::open(format!("/proc/{}/maps", self.process))?;
+
+ BufReader::new(maps_file)
+ .lines()
+ .flatten()
+ .map(|line| parse_proc_maps_line(&line))
+ .filter_map(Result::ok)
+ .find_map(|(name, address)| {
+ if name.is_some_and(|name| name.eq(module_name)) {
+ Some(address)
+ } else {
+ None
+ }
+ })
+ .ok_or(ProcessReaderError::ModuleNotFound)
+ }
+
+ pub fn find_program_note(
+ &self,
+ module_address: usize,
+ note_type: u32,
+ note_size: usize,
+ ) -> Result<usize, ProcessReaderError> {
+ let header_bytes = self.copy_array(module_address, size_of::<elf::Header>())?;
+ let elf_header = Elf::parse_header(&header_bytes)?;
+
+ let program_header_bytes = self.copy_array(
+ module_address + (elf_header.e_phoff as usize),
+ (elf_header.e_phnum as usize) * (elf_header.e_phentsize as usize),
+ )?;
+
+ let mut elf = Elf::lazy_parse(elf_header)?;
+ let context = goblin::container::Ctx {
+ container: elf.header.container()?,
+ le: elf.header.endianness()?,
+ };
+
+ elf.program_headers = ProgramHeader::parse(
+ &program_header_bytes,
+ 0,
+ elf_header.e_phnum as usize,
+ context,
+ )?;
+
+ self.find_note_in_headers(&elf, module_address, note_type, note_size)
+ }
+
+ fn find_note_in_headers(
+ &self,
+ elf: &Elf,
+ address: usize,
+ note_type: u32,
+ note_size: usize,
+ ) -> Result<usize, ProcessReaderError> {
+ for program_header in elf.program_headers.iter() {
+ // We're looking for a note in the program headers, it needs to be
+ // readable and it needs to be at least as large as the
+ // requested size.
+ if (program_header.p_type == PT_NOTE)
+ && ((program_header.p_flags & PF_R) != 0
+ && (program_header.p_memsz as usize >= note_size))
+ {
+ // Iterate over the notes
+ let notes_address = address + program_header.p_offset as usize;
+ let mut notes_offset = 0;
+ let notes_size = program_header.p_memsz as usize;
+ while notes_offset < notes_size {
+ let note_address = notes_address + notes_offset;
+ if let Ok(note) = self.copy_object::<goblin::elf::note::Nhdr32>(note_address) {
+ if note.n_type == note_type {
+ return Ok(note_address);
+ }
+
+ notes_offset += size_of::<goblin::elf::note::Nhdr32>()
+ + (note.n_descsz as usize)
+ + (note.n_namesz as usize);
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ Err(ProcessReaderError::NoteNotFound)
+ }
+
+ pub fn copy_object_shallow<T>(&self, src: usize) -> Result<MaybeUninit<T>, ReadError> {
+ let data = self.copy_array(src, size_of::<T>())?;
+ let mut object = MaybeUninit::<T>::uninit();
+ let uninitialized_object = uninit_as_bytes_mut(&mut object);
+
+ for (index, &value) in data.iter().enumerate() {
+ uninitialized_object[index].write(value);
+ }
+
+ Ok(object)
+ }
+
+ pub fn copy_object<T>(&self, src: usize) -> Result<T, ReadError> {
+ self.copy_object_shallow(src)
+ .map(|object| unsafe { object.assume_init() })
+ }
+
+ pub fn copy_array<T>(&self, src: usize, num: usize) -> Result<Vec<T>, ReadError> {
+ let mut array = Vec::<MaybeUninit<T>>::with_capacity(num);
+ let num_bytes = num * size_of::<T>();
+ let mut array_buffer = array.as_mut_ptr() as *mut u8;
+ let mut index = 0;
+
+ while index < num_bytes {
+ let word = ptrace_read(self.process, src + index)?;
+ let len = min(size_of::<c_long>(), num_bytes - index);
+ let word_as_bytes = word.to_ne_bytes();
+ for &byte in word_as_bytes.iter().take(len) {
+ unsafe {
+ array_buffer.write(byte);
+ array_buffer = array_buffer.add(1);
+ }
+ }
+
+ index += size_of::<c_long>();
+ }
+
+ unsafe {
+ array.set_len(num);
+ Ok(std::mem::transmute(array))
+ }
+ }
+}
+
+impl Drop for ProcessReader {
+ fn drop(&mut self) {
+ let _ignored = ptrace_detach(self.process);
+ }
+}
+
+fn parse_proc_maps_line(line: &str) -> Result<(Option<String>, usize), ProcessReaderError> {
+ let mut splits = line.trim().splitn(6, ' ');
+ let address_str = splits
+ .next()
+ .ok_or(ProcessReaderError::ProcMapsParseError)?;
+ let _perms_str = splits
+ .next()
+ .ok_or(ProcessReaderError::ProcMapsParseError)?;
+ let _offset_str = splits
+ .next()
+ .ok_or(ProcessReaderError::ProcMapsParseError)?;
+ let _dev_str = splits
+ .next()
+ .ok_or(ProcessReaderError::ProcMapsParseError)?;
+ let _inode_str = splits
+ .next()
+ .ok_or(ProcessReaderError::ProcMapsParseError)?;
+ let path_str = splits
+ .next()
+ .ok_or(ProcessReaderError::ProcMapsParseError)?;
+
+ let address = get_proc_maps_address(address_str)?;
+
+ // Note that we don't care if the mapped file has been deleted because
+ // we're reading everything from memory.
+ let name = path_str
+ .trim_end_matches(" (deleted)")
+ .rsplit('/')
+ .next()
+ .map(String::from);
+
+ Ok((name, address))
+}
+
+fn get_proc_maps_address(addresses: &str) -> Result<usize, ProcessReaderError> {
+ let begin = addresses
+ .split('-')
+ .next()
+ .ok_or(ProcessReaderError::ProcMapsParseError)?;
+ usize::from_str_radix(begin, 16).map_err(ProcessReaderError::from)
+}
+
+fn uninit_as_bytes_mut<T>(elem: &mut MaybeUninit<T>) -> &mut [MaybeUninit<u8>] {
+ // SAFETY: MaybeUninit<u8> is always valid, even for padding bytes
+ unsafe { slice::from_raw_parts_mut(elem.as_mut_ptr() as *mut MaybeUninit<u8>, size_of::<T>()) }
+}
+
+/***********************************************************************
+ ***** libc helpers *****
+ ***********************************************************************/
+
+fn get_errno() -> c_int {
+ #[cfg(target_os = "linux")]
+ unsafe {
+ *libc::__errno_location()
+ }
+ #[cfg(target_os = "android")]
+ unsafe {
+ *libc::__errno()
+ }
+}
+
+fn clear_errno() {
+ #[cfg(target_os = "linux")]
+ unsafe {
+ *libc::__errno_location() = 0;
+ }
+ #[cfg(target_os = "android")]
+ unsafe {
+ *libc::__errno() = 0;
+ }
+}
+
+#[derive(Clone, Copy)]
+enum PTraceOperation {
+ Attach,
+ Detach,
+ PeekData,
+}
+
+#[cfg(all(target_os = "linux", target_env = "gnu"))]
+type PTraceOperationNative = libc::c_uint;
+#[cfg(all(target_os = "linux", target_env = "musl"))]
+type PTraceOperationNative = libc::c_int;
+#[cfg(target_os = "android")]
+type PTraceOperationNative = c_int;
+
+impl From<PTraceOperation> for PTraceOperationNative {
+ fn from(val: PTraceOperation) -> Self {
+ match val {
+ PTraceOperation::Attach => PTRACE_ATTACH,
+ PTraceOperation::Detach => PTRACE_DETACH,
+ PTraceOperation::PeekData => PTRACE_PEEKDATA,
+ }
+ }
+}
+
+fn ptrace_attach(pid: pid_t) -> Result<(), PtraceError> {
+ ptrace_helper(pid, PTraceOperation::Attach, 0).map(|_r| ())
+}
+
+fn ptrace_detach(pid: pid_t) -> Result<(), PtraceError> {
+ ptrace_helper(pid, PTraceOperation::Detach, 0).map(|_r| ())
+}
+
+fn ptrace_read(pid: libc::pid_t, addr: usize) -> Result<c_long, PtraceError> {
+ ptrace_helper(pid, PTraceOperation::PeekData, addr)
+}
+
+fn ptrace_helper(pid: pid_t, op: PTraceOperation, addr: usize) -> Result<c_long, PtraceError> {
+ clear_errno();
+ let result = unsafe { ptrace(op.into(), pid, addr, null_mut::<c_void>()) };
+
+ if result == -1 {
+ let errno = get_errno();
+ if errno != 0 {
+ let error = match op {
+ PTraceOperation::Attach => PtraceError::TraceError(Error::from_raw_os_error(errno)),
+ PTraceOperation::Detach => PtraceError::TraceError(Error::from_raw_os_error(errno)),
+ PTraceOperation::PeekData => {
+ PtraceError::ReadError(Error::from_raw_os_error(errno))
+ }
+ };
+ Err(error)
+ } else {
+ Ok(result)
+ }
+ } else {
+ Ok(result)
+ }
+}
diff --git a/toolkit/crashreporter/process_reader/src/platform/macos.rs b/toolkit/crashreporter/process_reader/src/platform/macos.rs
new file mode 100644
index 0000000000..a0dfa2b8fd
--- /dev/null
+++ b/toolkit/crashreporter/process_reader/src/platform/macos.rs
@@ -0,0 +1,225 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use goblin::mach::{
+ header::{Header64, MH_DYLIB, MH_EXECUTE, MH_MAGIC_64},
+ load_command::{LoadCommandHeader, Section64, SegmentCommand64, LC_SEGMENT_64},
+};
+use mach2::{
+ kern_return::KERN_SUCCESS,
+ task::task_info,
+ task_info::{task_dyld_info, TASK_DYLD_ALL_IMAGE_INFO_64, TASK_DYLD_INFO},
+ vm::mach_vm_read_overwrite,
+};
+use std::mem::{size_of, MaybeUninit};
+
+use crate::{
+ error::{ProcessReaderError, ReadError},
+ ProcessHandle, ProcessReader,
+};
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug)]
+struct AllImagesInfo {
+ // VERSION 1
+ pub version: u32,
+ /// The number of [`ImageInfo`] structs at that following address
+ info_array_count: u32,
+ /// The address in the process where the array of [`ImageInfo`] structs is
+ info_array_addr: u64,
+ /// A function pointer, unused
+ _notification: u64,
+ /// Unused
+ _process_detached_from_shared_region: bool,
+ // VERSION 2
+ lib_system_initialized: bool,
+ // Note that crashpad adds a 32-bit int here to get proper alignment when
+ // building on 32-bit targets...but we explicitly don't care about 32-bit
+ // targets since Apple doesn't
+ pub dyld_image_load_address: u64,
+}
+
+/// `dyld_image_info` from <usr/include/mach-o/dyld_images.h>
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
+struct ImageInfo {
+ /// The address in the process where the image is loaded
+ pub load_address: u64,
+ /// The address in the process where the image's file path can be read
+ pub file_path: u64,
+ /// Timestamp for when the image's file was last modified
+ pub file_mod_date: u64,
+}
+
+const DATA_SEGMENT: &[u8; 16] = b"__DATA\0\0\0\0\0\0\0\0\0\0";
+
+impl ProcessReader {
+ pub fn new(process: ProcessHandle) -> Result<ProcessReader, ProcessReaderError> {
+ Ok(ProcessReader { process })
+ }
+
+ pub fn find_module(&self, module_name: &str) -> Result<usize, ProcessReaderError> {
+ let dyld_info = self.task_info()?;
+ if (dyld_info.all_image_info_format as u32) != TASK_DYLD_ALL_IMAGE_INFO_64 {
+ return Err(ProcessReaderError::ImageFormatError);
+ }
+
+ 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(ProcessReaderError::ImageFormatError);
+ }
+
+ let all_images_info = self.copy_object::<AllImagesInfo>(all_image_info_addr as _)?;
+
+ // Load the images
+ let images = 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_null_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| image.load_address as usize)
+ .ok_or(ProcessReaderError::ModuleNotFound)
+ }
+
+ pub fn find_section(
+ &self,
+ module_address: usize,
+ section_name: &[u8; 16],
+ ) -> Result<usize, ProcessReaderError> {
+ let header = self.copy_object::<Header64>(module_address)?;
+ let mut address = module_address + size_of::<Header64>();
+
+ if header.magic == MH_MAGIC_64
+ && (header.filetype == MH_EXECUTE || header.filetype == MH_DYLIB)
+ {
+ let end_of_commands = address + (header.sizeofcmds as usize);
+
+ while address < end_of_commands {
+ let command = self.copy_object::<LoadCommandHeader>(address)?;
+
+ if command.cmd == LC_SEGMENT_64 {
+ if let Ok(offset) = self.find_section_in_segment(address, section_name) {
+ return module_address
+ .checked_add(offset)
+ .ok_or(ProcessReaderError::InvalidAddress);
+ }
+ }
+
+ address += command.cmdsize as usize;
+ }
+ }
+
+ Err(ProcessReaderError::SectionNotFound)
+ }
+
+ fn find_section_in_segment(
+ &self,
+ segment_address: usize,
+ section_name: &[u8; 16],
+ ) -> Result<usize, ProcessReaderError> {
+ let segment = self.copy_object::<SegmentCommand64>(segment_address)?;
+
+ if segment.segname.eq(DATA_SEGMENT) {
+ let sections_addr = segment_address + size_of::<SegmentCommand64>();
+ let sections = self.copy_array::<Section64>(sections_addr, segment.nsects as usize)?;
+ for section in &sections {
+ if section.sectname.eq(section_name) {
+ return Ok(section.offset as usize);
+ }
+ }
+ }
+
+ Err(ProcessReaderError::SectionNotFound)
+ }
+
+ fn task_info(&self) -> Result<task_dyld_info, ProcessReaderError> {
+ let mut info = std::mem::MaybeUninit::<task_dyld_info>::uninit();
+ let mut count = (std::mem::size_of::<task_dyld_info>() / std::mem::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 {
+ // SAFETY: this will be initialized if the call succeeded
+ unsafe { Ok(info.assume_init()) }
+ } else {
+ Err(ProcessReaderError::TaskInfoError)
+ }
+ }
+
+ pub fn copy_object_shallow<T>(&self, src: usize) -> Result<MaybeUninit<T>, ReadError> {
+ let mut object = MaybeUninit::<T>::uninit();
+ let mut size: u64 = 0;
+ let res = unsafe {
+ mach_vm_read_overwrite(
+ self.process,
+ src as u64,
+ size_of::<T>() as u64,
+ object.as_mut_ptr() as _,
+ &mut size as _,
+ )
+ };
+
+ if res == KERN_SUCCESS {
+ Ok(object)
+ } else {
+ Err(ReadError::MachError)
+ }
+ }
+
+ pub fn copy_object<T>(&self, src: usize) -> Result<T, ReadError> {
+ let object = self.copy_object_shallow(src)?;
+ Ok(unsafe { object.assume_init() })
+ }
+
+ pub fn copy_array<T>(&self, src: usize, num: usize) -> Result<Vec<T>, ReadError> {
+ let mut array: Vec<MaybeUninit<T>> = Vec::with_capacity(num);
+ let mut size: u64 = 0;
+ let res = unsafe {
+ mach_vm_read_overwrite(
+ self.process,
+ src as u64,
+ (num * size_of::<T>()) as u64,
+ array.as_mut_ptr() as _,
+ &mut size as _,
+ )
+ };
+
+ if res == KERN_SUCCESS {
+ unsafe {
+ array.set_len(num);
+ Ok(std::mem::transmute(array))
+ }
+ } else {
+ Err(ReadError::MachError)
+ }
+ }
+}
+
+impl Drop for ProcessReader {
+ fn drop(&mut self) {}
+}
diff --git a/toolkit/crashreporter/process_reader/src/platform/windows.rs b/toolkit/crashreporter/process_reader/src/platform/windows.rs
new file mode 100644
index 0000000000..2d8c63444e
--- /dev/null
+++ b/toolkit/crashreporter/process_reader/src/platform/windows.rs
@@ -0,0 +1,201 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use std::{
+ convert::TryInto,
+ ffi::OsString,
+ mem::{size_of, MaybeUninit},
+ os::windows::ffi::OsStringExt,
+ ptr::null_mut,
+};
+
+use windows_sys::Win32::{
+ Foundation::{FALSE, HMODULE, MAX_PATH},
+ System::{
+ Diagnostics::Debug::ReadProcessMemory,
+ ProcessStatus::{
+ K32EnumProcessModules, K32GetModuleBaseNameW, K32GetModuleInformation, MODULEINFO,
+ },
+ },
+};
+
+use crate::{
+ error::{ProcessReaderError, ReadError},
+ ProcessHandle, ProcessReader,
+};
+
+impl ProcessReader {
+ pub fn new(process: ProcessHandle) -> Result<ProcessReader, ProcessReaderError> {
+ Ok(ProcessReader { process })
+ }
+
+ pub fn find_module(&self, module_name: &str) -> Result<usize, ProcessReaderError> {
+ let modules = self.get_module_list()?;
+
+ let module = modules.iter().find_map(|&module| {
+ let name = self.get_module_name(module);
+ // Crude way of mimicking Windows lower-case comparisons but
+ // sufficient for our use-cases.
+ if name.is_some_and(|name| name.eq_ignore_ascii_case(module_name)) {
+ self.get_module_info(module)
+ .map(|module| module.lpBaseOfDll as usize)
+ } else {
+ None
+ }
+ });
+
+ module.ok_or(ProcessReaderError::ModuleNotFound)
+ }
+
+ pub fn find_section(
+ &self,
+ module_address: usize,
+ section_name: &[u8; 8],
+ ) -> Result<usize, ProcessReaderError> {
+ // We read only the first page from the module, this should be more than
+ // enough to read the header and section list. In the future we might do
+ // this incrementally but for now goblin requires an array to parse
+ // so we can't do it just yet.
+ const PAGE_SIZE: usize = 4096;
+ let bytes = self.copy_array(module_address as _, PAGE_SIZE)?;
+ let header = goblin::pe::header::Header::parse(&bytes)?;
+
+ // Skip the PE header so we can parse the sections
+ let optional_header_offset = header.dos_header.pe_pointer as usize
+ + goblin::pe::header::SIZEOF_PE_MAGIC
+ + goblin::pe::header::SIZEOF_COFF_HEADER;
+ let offset =
+ &mut (optional_header_offset + header.coff_header.size_of_optional_header as usize);
+
+ let sections = header.coff_header.sections(&bytes, offset)?;
+
+ for section in sections {
+ if section.name.eq(section_name) {
+ let address = module_address.checked_add(section.virtual_address as usize);
+ return address.ok_or(ProcessReaderError::InvalidAddress);
+ }
+ }
+
+ Err(ProcessReaderError::SectionNotFound)
+ }
+
+ fn get_module_list(&self) -> Result<Vec<HMODULE>, ProcessReaderError> {
+ let mut module_num: usize = 100;
+ let mut required_buffer_size: u32 = 0;
+ let mut module_array = Vec::<HMODULE>::with_capacity(module_num);
+
+ loop {
+ let buffer_size: u32 = (module_num * size_of::<HMODULE>()).try_into()?;
+ let res = unsafe {
+ K32EnumProcessModules(
+ self.process,
+ module_array.as_mut_ptr() as *mut _,
+ buffer_size,
+ &mut required_buffer_size as *mut _,
+ )
+ };
+
+ module_num = required_buffer_size as usize / size_of::<HMODULE>();
+
+ if res == 0 {
+ if required_buffer_size > buffer_size {
+ module_array = Vec::<HMODULE>::with_capacity(module_num);
+ } else {
+ return Err(ProcessReaderError::EnumProcessModulesError);
+ }
+ } else {
+ break;
+ }
+ }
+
+ // SAFETY: module_array has been filled by K32EnumProcessModules()
+ unsafe {
+ module_array.set_len(module_num);
+ };
+
+ Ok(module_array)
+ }
+
+ fn get_module_name(&self, module: HMODULE) -> Option<String> {
+ let mut path: [u16; MAX_PATH as usize] = [0; MAX_PATH as usize];
+ let res = unsafe {
+ K32GetModuleBaseNameW(self.process, module, (&mut path).as_mut_ptr(), MAX_PATH)
+ };
+
+ if res == 0 {
+ None
+ } else {
+ let name = OsString::from_wide(&path[0..res as usize]);
+ let name = name.to_str()?;
+ Some(name.to_string())
+ }
+ }
+
+ fn get_module_info(&self, module: HMODULE) -> Option<MODULEINFO> {
+ let mut info: MaybeUninit<MODULEINFO> = MaybeUninit::uninit();
+ let res = unsafe {
+ K32GetModuleInformation(
+ self.process,
+ module,
+ info.as_mut_ptr(),
+ size_of::<MODULEINFO>() as u32,
+ )
+ };
+
+ if res == 0 {
+ None
+ } else {
+ let info = unsafe { info.assume_init() };
+ Some(info)
+ }
+ }
+
+ pub fn copy_object_shallow<T>(&self, src: usize) -> Result<MaybeUninit<T>, ReadError> {
+ let mut object = MaybeUninit::<T>::uninit();
+ let res = unsafe {
+ ReadProcessMemory(
+ self.process,
+ src as _,
+ object.as_mut_ptr() as _,
+ size_of::<T>(),
+ null_mut(),
+ )
+ };
+
+ if res != FALSE {
+ Ok(object)
+ } else {
+ Err(ReadError::ReadProcessMemoryError)
+ }
+ }
+
+ pub fn copy_object<T>(&self, src: usize) -> Result<T, ReadError> {
+ let object = self.copy_object_shallow(src)?;
+ Ok(unsafe { object.assume_init() })
+ }
+
+ pub fn copy_array<T>(&self, src: usize, num: usize) -> Result<Vec<T>, ReadError> {
+ let num_of_bytes = num * size_of::<T>();
+ let mut array: Vec<T> = Vec::with_capacity(num);
+ let res = unsafe {
+ ReadProcessMemory(
+ self.process,
+ src as _,
+ array.as_mut_ptr() as _,
+ num_of_bytes,
+ null_mut(),
+ )
+ };
+
+ if res != FALSE {
+ unsafe {
+ array.set_len(num);
+ }
+
+ Ok(array)
+ } else {
+ Err(ReadError::ReadProcessMemoryError)
+ }
+ }
+}