From ade93e92a4ecb6af475362e3ce39f73a5cb43cdc Mon Sep 17 00:00:00 2001 From: Frank van der Stam Date: Thu, 23 May 2024 18:17:43 +0700 Subject: [PATCH] Add a bunch of documentation and examples. Play around with 'cargo doc' --- Cargo.toml | 5 +- examples/dsr.rs | 4 +- src/helpers.rs | 33 ++-- src/pointer.rs | 19 +- src/process/inject_dll.rs | 27 +++ src/process/mod.rs | 43 ++++- src/process/process_modules.rs | 16 ++ src/process/process_name.rs | 105 +++++++---- src/process/read_write.rs | 16 ++ src/process/refresh.rs | 157 +++++++++------- src/process/scanning.rs | 58 ++++++ src/read_write.rs | 319 ++++++++++++++++++++++++++++++--- 12 files changed, 655 insertions(+), 147 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 75e22fe..7a194e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ [package] name = "mem-rs" -version = "0.1.5" +version = "0.1.6" edition = "2021" readme = "README.md" homepage = "https://github.com/FrankvdStam/mem-rs" @@ -26,9 +26,6 @@ keywords = ["memory", "gamedev"] categories = ["memory-management", "games", "development-tools"] description = "pattern scanning and abstraction for pointers in memory of running processes" - -[dependencies] - [dependencies.windows] version = "0.56.0" features = [ diff --git a/examples/dsr.rs b/examples/dsr.rs index d38e90c..89f2c99 100644 --- a/examples/dsr.rs +++ b/examples/dsr.rs @@ -79,8 +79,8 @@ fn main() println!("{:?}", w32_str); println!("{:?}", vec_u16_to_u8(&w32_str)); - let alloated_str = String::from(str); - let collected: Vec = alloated_str.encode_utf16().collect(); + let allocated_str = String::from(str); + let collected: Vec = allocated_str.encode_utf16().collect(); println!("{:?}", collected); unsafe { println!("{:?}", collected.align_to::()); } diff --git a/src/helpers.rs b/src/helpers.rs index 17746d9..709e43c 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -17,21 +17,22 @@ use std::path::Path; use windows::core::{PCSTR, PCWSTR}; -pub fn scan(code: &[u8], pattern: &[Option]) -> Option +/// Naive linear search for a needle in a haystack with wildcards +pub fn scan(haystack: &[u8], needle: &[Option]) -> Option { - if code.len() == 0 + if haystack.len() == 0 { return None; } - for i in 0..code.len() - pattern.len() + for i in 0..haystack.len() - needle.len() { let mut found = true; - for j in 0..pattern.len() + for j in 0..needle.len() { - if let Some(byte) = pattern[j] + if let Some(byte) = needle[j] { - if byte != code[i + j] + if byte != haystack[i + j] { found = false; break; @@ -46,6 +47,9 @@ pub fn scan(code: &[u8], pattern: &[Option]) -> Option return None; } +/// Converts a string of hex characters into a byte pattern with wildcards. +/// ? is the character used for wildcards. +/// Hex characters don't have to be prefixed with 0x pub fn to_pattern(str: &str) -> Vec> { let mut vec = Vec::new(); @@ -63,31 +67,38 @@ pub fn to_pattern(str: &str) -> Vec> return vec; } +/// Retrieve only the filename portion from a filepath. +pub fn get_file_name_from_string(str: &String) -> String +{ + return String::from(Path::new(&str).file_name().unwrap().to_str().unwrap()); +} + +/// Win32 memes. Use with caution. pub fn vec_u16_to_u8(vec_u16: &Vec) -> Vec { return unsafe { vec_u16.align_to::().1.to_vec() }; } +/// Win32 memes. Use with caution. pub fn w32str_to_string(w32str: &Vec) -> String { return w32str.iter().map(|&v| (v & 0xFF) as u8).take_while(|&c| c != 0).map(|c| c as char).collect(); } -pub fn get_file_name_from_string(str: &String) -> String -{ - return String::from(Path::new(&str).file_name().unwrap().to_str().unwrap()); -} - +/// Win32 memes. Use with caution. pub fn get_w32str_from_str(str: &str) -> Vec { return str.encode_utf16().collect(); } + +/// Win32 memes. Use with caution. pub fn get_pcwstr_from_str(str: &str) -> PCWSTR { let vec: Vec = str.encode_utf16().collect(); return PCWSTR(vec.as_ptr()); } +/// Win32 memes. Use with caution. pub fn get_pcstr_from_str(str: &str) -> PCSTR { return PCSTR(str.as_ptr()); diff --git a/src/pointer.rs b/src/pointer.rs index 2c03d8b..1780c23 100644 --- a/src/pointer.rs +++ b/src/pointer.rs @@ -19,12 +19,28 @@ use std::rc::Rc; use crate::read_write::{BaseReadWrite, ReadWrite}; use crate::process_data::ProcessData; + +/// Represents a pointer path that is dynamically resolved each read/write operation. +/// This ensures that the pointer is always valid. Race conditions can occur and the pointer could encounter +/// a null pointer along the path. Should always be constructed via the Process struct. +/// +/// # Example +/// +/// ``` +/// use mem_rs::prelude::*; +/// +/// let mut process = Process::new("name_of_process.exe"); +/// process.refresh()?; +/// let pointer = process.create_pointer(0x1234, vec![0]); +/// let data = pointer.read_u8_rel(Some(0x1234)); +/// ``` pub struct Pointer { process_data: Rc>, is_64_bit: bool, base_address: usize, offsets: Vec, + /// Set this to true to print each memory address while resolving the pointer path. pub debug: bool, } @@ -45,7 +61,7 @@ impl Default for Pointer impl Pointer { - pub fn new(process_data: Rc>, is_64_bit: bool, base_address: usize, offsets: Vec) -> Self + pub(crate) fn new(process_data: Rc>, is_64_bit: bool, base_address: usize, offsets: Vec) -> Self { Pointer { @@ -57,6 +73,7 @@ impl Pointer } } + /// Get the base address of this pointer, without resolving offsets. pub fn get_base_address(&self) -> usize { return self.base_address; diff --git a/src/process/inject_dll.rs b/src/process/inject_dll.rs index 43e7180..85ec7fd 100644 --- a/src/process/inject_dll.rs +++ b/src/process/inject_dll.rs @@ -1,3 +1,19 @@ +// This file is part of the mem-rs distribution (https://github.com/FrankvdStam/mem-rs). +// Copyright (c) 2022 Frank van der Stam. +// https://github.com/FrankvdStam/mem-rs/blob/main/LICENSE +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + use std::ffi::c_void; use std::mem::size_of; use windows::Win32::System::LibraryLoader::{GetModuleHandleW, GetProcAddress}; @@ -9,6 +25,17 @@ use crate::prelude::*; impl Process { + /// Attempts to inject a dll into the attached process using LoadLibraryW + /// + /// # Examples + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh().expect("Failed to attach/refresh!"); + /// process.inject_dll(r#"C:\temp\native.dll"#).expect("Failed to inject!"); + /// ``` pub fn inject_dll(&self, dll_path: &str) -> Result<(), String> { let mut path_w32_str: Vec = dll_path.encode_utf16().collect(); diff --git a/src/process/mod.rs b/src/process/mod.rs index 395c248..2eefc74 100644 --- a/src/process/mod.rs +++ b/src/process/mod.rs @@ -30,7 +30,21 @@ mod process_name; const STILL_ACTIVE: u32 = 259; - +/// Wraps a native process and allows memory access/manipulation +/// +/// # Examples +/// +/// ``` +/// use mem_rs::prelude::*; +/// +/// let mut process = Process::new("name_of_process.exe"); +/// if process.refresh().is_ok() +/// { +/// process.write_memory_abs(0x1234, &u32::to_ne_bytes(10)); +/// let result = process.read_u32_rel(Some(0x1234)); +/// println!("Result: {}", result); +/// } +/// ``` pub struct Process { process_data: Rc> @@ -38,7 +52,15 @@ pub struct Process impl Process { - ///Create a new process where name is the name of the executable + /// Creates a new process based on the process name. + /// + /// # Examples + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// ``` pub fn new(name: &str) -> Self { Process @@ -57,6 +79,23 @@ impl Process } } + /// Returns if the process is "attached" and can be read/written from/to + /// + /// # Examples + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// //returns false + /// let not_attached = process.is_attached(); + /// + /// //refreshing the process will cause it to become attached + /// process.refresh().unwrap(); + /// + /// //if name_of_process.exe is running, will return true + /// let attached = process.is_attached(); + /// ``` pub fn is_attached(&self) -> bool {return self.process_data.borrow().attached;} } diff --git a/src/process/process_modules.rs b/src/process/process_modules.rs index 1b38eb1..47af350 100644 --- a/src/process/process_modules.rs +++ b/src/process/process_modules.rs @@ -1,3 +1,19 @@ +// This file is part of the mem-rs distribution (https://github.com/FrankvdStam/mem-rs). +// Copyright (c) 2022 Frank van der Stam. +// https://github.com/FrankvdStam/mem-rs/blob/main/LICENSE +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + use std::ffi::c_void; use std::mem::size_of; use windows::Win32::Foundation::{HANDLE, HINSTANCE, HMODULE, MAX_PATH}; diff --git a/src/process/process_name.rs b/src/process/process_name.rs index 2f38998..640949a 100644 --- a/src/process/process_name.rs +++ b/src/process/process_name.rs @@ -1,3 +1,19 @@ +// This file is part of the mem-rs distribution (https://github.com/FrankvdStam/mem-rs). +// Copyright (c) 2022 Frank van der Stam. +// https://github.com/FrankvdStam/mem-rs/blob/main/LICENSE +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + use std::mem::size_of; use windows::Win32::Foundation::{CloseHandle, HINSTANCE, MAX_PATH}; use windows::Win32::System::ProcessStatus::{K32EnumProcesses, K32GetModuleFileNameExW}; @@ -7,57 +23,76 @@ use crate::process::Process; impl Process { + /// Returns the current process name, in which this very code is running. + /// Does NOT return the name of the target attachment process. + /// + /// # Examples + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let name = Process::get_current_process_name()?; + /// ``` pub fn get_current_process_name() -> Result { unsafe + { + let handle = GetCurrentProcess(); + let mut mod_name = [0; MAX_PATH as usize]; + if K32GetModuleFileNameExW(handle, HINSTANCE(0), &mut mod_name) != 0 { - let handle = GetCurrentProcess(); - let mut mod_name = [0; MAX_PATH as usize]; - if K32GetModuleFileNameExW(handle, HINSTANCE(0), &mut mod_name) != 0 - { - let file_path = w32str_to_string(&mod_name.to_vec()); - let file_name = get_file_name_from_string(&file_path); - return Ok(file_name); - } - Err(()) + let file_path = w32str_to_string(&mod_name.to_vec()); + let file_name = get_file_name_from_string(&file_path); + return Ok(file_name); } + Err(()) + } } + /// Returns all the processes that are currently running + /// + /// # Examples + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let names = Process::get_running_process_names(); + /// ``` pub fn get_running_process_names() -> Vec { unsafe - { - let mut process_names = Vec::new(); - let mut process_ids = [0u32; 2048]; - let mut bytes_needed = 0u32; - let _ = K32EnumProcesses(process_ids.as_mut_ptr(), (process_ids.len() * size_of::()) as u32, &mut bytes_needed); - let count = bytes_needed as usize / std::mem::size_of::(); + { + let mut process_names = Vec::new(); + let mut process_ids = [0u32; 2048]; + let mut bytes_needed = 0u32; + let _ = K32EnumProcesses(process_ids.as_mut_ptr(), (process_ids.len() * size_of::()) as u32, &mut bytes_needed); + let count = bytes_needed as usize / std::mem::size_of::(); - for i in 0..count - { - let pid = process_ids[i]; + for i in 0..count + { + let pid = process_ids[i]; - let mut mod_name = [0; MAX_PATH as usize]; + let mut mod_name = [0; MAX_PATH as usize]; - if let Ok(handle) = OpenProcess( - PROCESS_QUERY_INFORMATION - | PROCESS_VM_READ - | PROCESS_VM_WRITE - | PROCESS_VM_OPERATION, - false, - pid, - ) + if let Ok(handle) = OpenProcess( + PROCESS_QUERY_INFORMATION + | PROCESS_VM_READ + | PROCESS_VM_WRITE + | PROCESS_VM_OPERATION, + false, + pid, + ) + { + if K32GetModuleFileNameExW(handle, HINSTANCE(0), &mut mod_name) != 0 { - if K32GetModuleFileNameExW(handle, HINSTANCE(0), &mut mod_name) != 0 - { - let file_path = w32str_to_string(&mod_name.to_vec()); - let file_name = get_file_name_from_string(&file_path); - process_names.push(file_name); - } - let _ = CloseHandle(handle); + let file_path = w32str_to_string(&mod_name.to_vec()); + let file_name = get_file_name_from_string(&file_path); + process_names.push(file_name); } + let _ = CloseHandle(handle); } - return process_names; } + return process_names; + } } } \ No newline at end of file diff --git a/src/process/read_write.rs b/src/process/read_write.rs index 92a756e..7a38cd9 100644 --- a/src/process/read_write.rs +++ b/src/process/read_write.rs @@ -1,3 +1,19 @@ +// This file is part of the mem-rs distribution (https://github.com/FrankvdStam/mem-rs). +// Copyright (c) 2022 Frank van der Stam. +// https://github.com/FrankvdStam/mem-rs/blob/main/LICENSE +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + use crate::prelude::{BaseReadWrite, Process, ReadWrite}; impl BaseReadWrite for Process diff --git a/src/process/refresh.rs b/src/process/refresh.rs index 36fa586..8c52c1a 100644 --- a/src/process/refresh.rs +++ b/src/process/refresh.rs @@ -1,3 +1,19 @@ +// This file is part of the mem-rs distribution (https://github.com/FrankvdStam/mem-rs). +// Copyright (c) 2022 Frank van der Stam. +// https://github.com/FrankvdStam/mem-rs/blob/main/LICENSE +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + use std::mem::size_of; use windows::Win32::Foundation::{BOOL, CloseHandle, HANDLE, HINSTANCE}; use windows::Win32::System::ProcessStatus::{K32EnumProcesses, K32GetModuleFileNameExW}; @@ -9,92 +25,103 @@ use crate::process_module::ProcessModule; impl Process { - ///Cling to a running process + /// Attempts to "attach" to a running process by name. + /// Returns an error when the process is not running or when it has exited. + /// Caches the main module so that pattern scans can be done against it. + /// + /// # Examples + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh().expect("Failed to attach/refresh!"); + /// ``` pub fn refresh(&mut self) -> Result<(), String> { unsafe + { + //Check if a previously attached process has exited + let mut lp_exit_code: u32 = 0; + if self.process_data.borrow().attached && (!GetExitCodeProcess(self.process_data.borrow().handle, &mut lp_exit_code).is_ok() || lp_exit_code != STILL_ACTIVE) { - //Check if a previously attached process has exited - let mut lp_exit_code: u32 = 0; - if self.process_data.borrow().attached && (!GetExitCodeProcess(self.process_data.borrow().handle, &mut lp_exit_code).is_ok() || lp_exit_code != STILL_ACTIVE) - { - let mut process_data = self.process_data.borrow_mut(); + let mut process_data = self.process_data.borrow_mut(); - process_data.attached = false; - process_data.id = 0; - process_data.handle = HANDLE::default(); - process_data.filename = String::new(); - process_data.path = String::new(); - process_data.main_module = ProcessModule::default(); - process_data.modules = Vec::new(); + process_data.attached = false; + process_data.id = 0; + process_data.handle = HANDLE::default(); + process_data.filename = String::new(); + process_data.path = String::new(); + process_data.main_module = ProcessModule::default(); + process_data.modules = Vec::new(); - return Err(String::from("Process exited")); - } + return Err(String::from("Process exited")); + } - if self.process_data.borrow().attached - { - return Ok(()); - } + if self.process_data.borrow().attached + { + return Ok(()); + } - //Look for a running process with the correct name and attach to it - let mut process_ids = [0u32; 2048]; - let mut out_size = 0; + //Look for a running process with the correct name and attach to it + let mut process_ids = [0u32; 2048]; + let mut out_size = 0; - if !K32EnumProcesses(process_ids.as_mut_ptr(), (process_ids.len() * size_of::()) as u32, &mut out_size).as_bool() - { - return Err(String::from("Failed to get running processes")); - } + if !K32EnumProcesses(process_ids.as_mut_ptr(), (process_ids.len() * size_of::()) as u32, &mut out_size).as_bool() + { + return Err(String::from("Failed to get running processes")); + } + + let count = out_size as usize / std::mem::size_of::(); + for i in 0..count + { + let pid = process_ids[i]; - let count = out_size as usize / std::mem::size_of::(); - for i in 0..count + match OpenProcess( + PROCESS_QUERY_INFORMATION + | PROCESS_VM_READ + | PROCESS_VM_WRITE + | PROCESS_VM_OPERATION, + BOOL(0), + pid, + ) { - let pid = process_ids[i]; - - match OpenProcess( - PROCESS_QUERY_INFORMATION - | PROCESS_VM_READ - | PROCESS_VM_WRITE - | PROCESS_VM_OPERATION, - BOOL(0), - pid, - ) + Ok(handle) => { - Ok(handle) => - { - let mut mod_name = [0; windows::Win32::Foundation::MAX_PATH as usize]; - - if K32GetModuleFileNameExW(handle, HINSTANCE(0), &mut mod_name) != 0 - { - let file_path = w32str_to_string(&mod_name.to_vec()); - let file_name = get_file_name_from_string(&file_path); + let mut mod_name = [0; windows::Win32::Foundation::MAX_PATH as usize]; - //println!("{}", filename); + if K32GetModuleFileNameExW(handle, HINSTANCE(0), &mut mod_name) != 0 + { + let file_path = w32str_to_string(&mod_name.to_vec()); + let file_name = get_file_name_from_string(&file_path); - if self.process_data.borrow().name.to_lowercase() == file_name.to_lowercase() - { - let mut modules = Process::get_process_modules(handle); + //println!("{}", filename); - let mut process_data = self.process_data.borrow_mut(); + if self.process_data.borrow().name.to_lowercase() == file_name.to_lowercase() + { + let mut modules = Process::get_process_modules(handle); - process_data.id = pid; - process_data.handle = handle; - process_data.filename = file_name; - process_data.path = file_path; - process_data.attached = true; - process_data.main_module = modules.remove(0); - process_data.main_module.dump_memory(handle); - process_data.modules = modules; + let mut process_data = self.process_data.borrow_mut(); - return Ok(()); - } - } + process_data.id = pid; + process_data.handle = handle; + process_data.filename = file_name; + process_data.path = file_path; + process_data.attached = true; + process_data.main_module = modules.remove(0); + process_data.main_module.dump_memory(handle); + process_data.modules = modules; - let _ = CloseHandle(handle); + return Ok(()); } - _ => {}, + } + + let _ = CloseHandle(handle); } + _ => {}, } - return Err(String::from("Process not running")); } + return Err(String::from("Process not running")); + } } } \ No newline at end of file diff --git a/src/process/scanning.rs b/src/process/scanning.rs index 069f396..1f7f3e8 100644 --- a/src/process/scanning.rs +++ b/src/process/scanning.rs @@ -1,9 +1,38 @@ +// This file is part of the mem-rs distribution (https://github.com/FrankvdStam/mem-rs). +// Copyright (c) 2022 Frank van der Stam. +// https://github.com/FrankvdStam/mem-rs/blob/main/LICENSE +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + use crate::helpers::{scan, to_pattern}; use crate::pointer::Pointer; use crate::prelude::*; impl Process { + /// Does an absolute scan (for x86 targets or for process code) where the target pointer is absolute + /// Takes a list of offsets to create pointer jumps down a bigger complex structure. + /// Pointers implement memory reading and writing. + /// + /// # Examples + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh()?; + /// let pointer = process.scan_abs("Error message", "56 8B F1 8B 46 1C 50 A1 ? ? ? ? 32 C9", 8, vec![0, 0, 0])?; + /// ``` pub fn scan_abs(&self, error_name: &str, pattern: &str, scan_offset: usize, pointer_offsets: Vec) -> Result { let byte_pattern = to_pattern(pattern); @@ -19,6 +48,20 @@ impl Process return Ok(Pointer::new(self.process_data.clone(), true, address, pointer_offsets)); } + /// Does a relative scan (for x64 targets) where the target pointer is located relative to instruction's + /// size and location. + /// Takes a list of offsets to create pointer jumps down a bigger complex structure. + /// Pointers implement memory reading and writing. + /// + /// # Examples + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh()?; + /// let pointer = process.scan_rel("Error message", "48 8b 05 ? ? ? ? 48 8b 50 10 48 89 54 24 60", 3, 7, vec![0])?; + /// ``` pub fn scan_rel(&self, error_name: &str, pattern: &str, scan_offset: usize, instruction_size: usize, pointer_offsets: Vec) -> Result { let byte_pattern = to_pattern(pattern); @@ -35,6 +78,21 @@ impl Process return Ok(Pointer::new(self.process_data.clone(), true, result, pointer_offsets)); } + /// Create a pointer without scanning from an absolute address and a list of offsets. + /// For special use cases where an address might be the result of some calculation. + /// + /// let network = vanilla.process.create_pointer(network_ptr as usize, vec![0xc, 0x6c978]) + /// + /// # Examples + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh()?; + /// let magic_address = 0x1234; + /// let pointer = process.create_pointer(magic_address, vec![0xc, 0x10]); + /// ``` pub fn create_pointer(&self, address: usize, pointer_offsets: Vec) -> Pointer { return Pointer::new(self.process_data.clone(), true, address, pointer_offsets); diff --git a/src/read_write.rs b/src/read_write.rs index e7f992d..52e13aa 100644 --- a/src/read_write.rs +++ b/src/read_write.rs @@ -20,19 +20,71 @@ use windows::Win32::System::Diagnostics::Debug::{ ReadProcessMemory, WriteProces pub trait BaseReadWrite { - ///Read memory relative to the object's location in memory + /// Read memory relative to the object's location in memory. Supports an optional offset. + /// + /// # Example + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh()?; + /// let pointer = process.create_pointer(0x1234, vec![0]); + /// + /// let mut buffer: [u8; 8] = [0; 8]; + /// let success = pointer.read_memory_rel(Some(0x1234), &mut buffer); + /// ``` fn read_memory_rel(&self, offset: Option, buffer: &mut [u8]) -> bool; - ///Write memory relative to the object's location in memory + /// Write memory relative to the object's location in memory. Supports an optional offset. + /// + /// # Example + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh()?; + /// let pointer = process.create_pointer(0x1234, vec![0]); + /// + /// let mut buffer: [u8; 4] = [0x1, 0x2, 0x3, 0x4]; + /// let success = pointer.write_memory_rel(Some(0x1234), &mut buffer); + /// ``` fn write_memory_rel(&self, offset: Option, buffer: &[u8]) -> bool; - ///Read memory at absolute address + /// Read memory from an absolute address + /// + /// # Example + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh()?; + /// let pointer = process.create_pointer(0x1234, vec![0]); + /// + /// let mut buffer: [u8; 8] = [0; 8]; + /// let success = pointer.read_memory_abs(0x1234, &mut buffer); + /// ``` fn read_memory_abs(&self, address: usize, buffer: &mut [u8]) -> bool; - ///Write memory at absolute address + /// Write memory to an absolute address + /// + /// # Example + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh()?; + /// let pointer = process.create_pointer(0x1234, vec![0]); + /// + /// let mut buffer: [u8; 4] = [0x1, 0x2, 0x3, 0x4]; + /// let success = pointer.write_memory_abs(0x1234, &mut buffer); + /// ``` fn write_memory_abs(&self, address: usize, buffer: &[u8]) -> bool; - ///Read memory into a buffer from a process handle + /// Read memory into a buffer from a process handle fn read_with_handle(&self, handle: HANDLE, address: usize, buffer: &mut [u8]) -> bool { let mut read_bytes = 0; @@ -43,7 +95,7 @@ pub trait BaseReadWrite return read_bytes == buffer.len(); } - ///Write from a buffer ino memory from a process handle + /// Write from a buffer ino memory from a process handle fn write_with_handle(&self, handle: HANDLE, address: usize, buffer: &[u8]) -> bool { let mut wrote_bytes = 0; @@ -57,10 +109,22 @@ pub trait BaseReadWrite pub trait ReadWrite: BaseReadWrite { - ///================================================================================================================================================================== - /// Reading + //================================================================================================================================================================== + //Reading - ///Read an i8 from the given address + /// Relatively read an i8 from an optional offset + /// + /// # Example + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh()?; + /// let pointer = process.create_pointer(0x1234, vec![0]); + /// + /// let data = pointer.read_i8_rel(Some(0x1234)); + /// ``` fn read_i8_rel(&self, address: Option) -> i8 { let mut buffer = [0; 1]; @@ -68,7 +132,19 @@ pub trait ReadWrite: BaseReadWrite return i8::from_ne_bytes(buffer); } - ///Read an i32 from the given address + /// Relatively read an i32 from an optional offset + /// + /// # Example + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh()?; + /// let pointer = process.create_pointer(0x1234, vec![0]); + /// + /// let data = pointer.read_i32_rel(Some(0x1234)); + /// ``` fn read_i32_rel(&self, address: Option) -> i32 { let mut buffer = [0; 4]; @@ -76,7 +152,19 @@ pub trait ReadWrite: BaseReadWrite return i32::from_ne_bytes(buffer); } - ///Read an i64 from the given address + /// Relatively read an i64 from an optional offset + /// + /// # Example + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh()?; + /// let pointer = process.create_pointer(0x1234, vec![0]); + /// + /// let data = pointer.read_i64_rel(Some(0x1234)); + /// ``` fn read_i64_rel(&self, address: Option) -> i64 { let mut buffer = [0; 8]; @@ -84,7 +172,19 @@ pub trait ReadWrite: BaseReadWrite return i64::from_ne_bytes(buffer); } - ///Read an u8 from the given address + /// Relatively read an u8 from an optional offset + /// + /// # Example + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh()?; + /// let pointer = process.create_pointer(0x1234, vec![0]); + /// + /// let data = pointer.read_u8_rel(Some(0x1234)); + /// ``` fn read_u8_rel(&self, address: Option) -> u8 { let mut buffer = [0; 1]; @@ -92,7 +192,19 @@ pub trait ReadWrite: BaseReadWrite return buffer[0]; } - ///Read an u32 from the given address + /// Relatively read an u32 from an optional offset + /// + /// # Example + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh()?; + /// let pointer = process.create_pointer(0x1234, vec![0]); + /// + /// let data = pointer.read_u32_rel(Some(0x1234)); + /// ``` fn read_u32_rel(&self, address: Option) -> u32 { let mut buffer = [0; 4]; @@ -100,7 +212,19 @@ pub trait ReadWrite: BaseReadWrite return u32::from_ne_bytes(buffer); } - ///Read an u64 from the given address + /// Relatively read an u64 from an optional offset + /// + /// # Example + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh()?; + /// let pointer = process.create_pointer(0x1234, vec![0]); + /// + /// let data = pointer.read_u64_rel(Some(0x1234)); + /// ``` fn read_u64_rel(&self, address: Option) -> u64 { let mut buffer = [0; 8]; @@ -108,7 +232,19 @@ pub trait ReadWrite: BaseReadWrite return u64::from_ne_bytes(buffer); } - ///Read an f32 from the given address + /// Relatively read an f32 from an optional offset + /// + /// # Example + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh()?; + /// let pointer = process.create_pointer(0x1234, vec![0]); + /// + /// let data = pointer.read_f32_rel(Some(0x1234)); + /// ``` fn read_f32_rel(&self, address: Option) -> f32 { let mut buffer = [0; 4]; @@ -116,7 +252,19 @@ pub trait ReadWrite: BaseReadWrite return f32::from_ne_bytes(buffer); } - ///Read an f64 from the given address + /// Relatively read an f64 from an optional offset + /// + /// # Example + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh()?; + /// let pointer = process.create_pointer(0x1234, vec![0]); + /// + /// let data = pointer.read_f64_rel(Some(0x1234)); + /// ``` fn read_f64_rel(&self, address: Option) -> f64 { let mut buffer = [0; 8]; @@ -124,7 +272,20 @@ pub trait ReadWrite: BaseReadWrite return f64::from_ne_bytes(buffer); } - ///Read a bool from the given address + /// Relatively read a bool from an optional offset. + /// A single byte is read from the resolved address, the value of the bool is true if this byte is non-zero. + /// + /// # Example + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh()?; + /// let pointer = process.create_pointer(0x1234, vec![0]); + /// + /// let data = pointer.read_f32_rel(Some(0x1234)); + /// ``` fn read_bool_rel(&self, address: Option) -> bool { let mut buffer = [0; 1]; @@ -132,59 +293,163 @@ pub trait ReadWrite: BaseReadWrite return buffer[0] != 0; } - ///================================================================================================================================================================== - /// Writing + //================================================================================================================================================================== + //Writing - ///Write an i8 to the given address + /// Relatively write an i8 to an optional offset + /// + /// # Example + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh()?; + /// let pointer = process.create_pointer(0x1234, vec![0]); + /// + /// let data: i8 = 10; + /// pointer.write_i8_rel(Some(0x1234), data); + /// ``` fn write_i8_rel(&self, address: Option, value: i8) { let buffer = value.to_ne_bytes(); self.write_memory_rel(address, &buffer); } - ///Write an i32 to the given address + /// Relatively write an i32 to an optional offset + /// + /// # Example + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh()?; + /// let pointer = process.create_pointer(0x1234, vec![0]); + /// + /// let data: i32 = 10; + /// pointer.write_i32_rel(Some(0x1234), data); + /// ``` fn write_i32_rel(&self, address: Option, value: i32) { let buffer = value.to_ne_bytes(); self.write_memory_rel(address, &buffer); } - ///Write an i64 to the given address + /// Relatively write an i64 to an optional offset + /// + /// # Example + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh()?; + /// let pointer = process.create_pointer(0x1234, vec![0]); + /// + /// let data: i64 = 10; + /// pointer.write_i64_rel(Some(0x1234), data); + /// ``` fn write_i64_rel(&self, address: Option, value: i64) { let buffer = value.to_ne_bytes(); self.write_memory_rel(address, &buffer); } - ///Write an u8 to the given address + /// Relatively write an u8 to an optional offset + /// + /// # Example + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh()?; + /// let pointer = process.create_pointer(0x1234, vec![0]); + /// + /// let data: u8 = 10; + /// pointer.write_u8_rel(Some(0x1234), data); + /// ``` fn write_u8_rel(&self, address: Option, value: u8) { let buffer = value.to_ne_bytes(); self.write_memory_rel(address, &buffer); } - ///Write an u32 to the given address + /// Relatively write an u32 to an optional offset + /// + /// # Example + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh()?; + /// let pointer = process.create_pointer(0x1234, vec![0]); + /// + /// let data: u32 = 10; + /// pointer.write_u32_rel(Some(0x1234), data); + /// ``` fn write_u32_rel(&self, address: Option, value: u32) { let buffer = value.to_ne_bytes(); self.write_memory_rel(address, &buffer); } - ///Write an u64 to the given address + /// Relatively write an u64 to an optional offset + /// + /// # Example + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh()?; + /// let pointer = process.create_pointer(0x1234, vec![0]); + /// + /// let data: u64 = 10; + /// pointer.write_u64_rel(Some(0x1234), data); + /// ``` fn write_u64_rel(&self, address: Option, value: u64) { let buffer = value.to_ne_bytes(); self.write_memory_rel(address, &buffer); } - ///Write an f32 to the given address + /// Relatively write an f32 to an optional offset + /// + /// # Example + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh()?; + /// let pointer = process.create_pointer(0x1234, vec![0]); + /// + /// let data: f32 = 10.0f32; + /// pointer.write_f32_rel(Some(0x1234), data); + /// ``` fn write_f32_rel(&self, address: Option, value: f32) { let buffer = value.to_ne_bytes(); self.write_memory_rel(address, &buffer); } - ///Write an f64 to the given address + /// Relatively write an f64 to an optional offset + /// + /// # Example + /// + /// ``` + /// use mem_rs::prelude::*; + /// + /// let mut process = Process::new("name_of_process.exe"); + /// process.refresh()?; + /// let pointer = process.create_pointer(0x1234, vec![0]); + /// + /// let data: f64 = 10.0f64; + /// pointer.write_f64_rel(Some(0x1234), data); + /// ``` fn write_f64_rel(&self, address: Option, value: f64) { let buffer = value.to_ne_bytes();