proc_mem_rs/process/
mod.rs

1mod handle;
2mod tlhelp32;
3mod module;
4
5use std::{process::Command, os::windows::process::CommandExt, mem::size_of};
6use regex::bytes::Regex;
7use crate::ProcMemError;
8use handle::Handle;
9use tlhelp32::*;
10pub use module::{Module, Signature};
11
12use winapi::{um::{tlhelp32::{TH32CS_SNAPPROCESS, TH32CS_SNAPMODULE, TH32CS_SNAPMODULE32},
13                  winbase::CREATE_NO_WINDOW,
14                  memoryapi::{ReadProcessMemory, WriteProcessMemory, VirtualProtect},
15                  wow64apiset::IsWow64Process,
16},
17             shared::{minwindef::{FALSE, LPCVOID, LPVOID, BOOL, PBOOL}, basetsd::SIZE_T},
18};
19
20#[derive(Debug, Clone)]
21/// contains name, pid and handle of a process
22pub struct Process {
23    pub process_name: String,
24    /// unique identifier if the process
25    pub process_id: u32,
26    /// used when desired data is not inside a loaded module
27    pub process_base_address: usize,
28    /// either PROCESS_ALL_ACCESS or PROCESS_VM_READ | PROCESS_VM_WRITE
29    pub process_handle: Handle,
30    /// is x32 or x64
31    pub iswow64: bool,
32}
33
34impl Process {
35    /// returns the desired process with the provided pid
36    ///
37    /// ```rust
38    /// use proc_mem_rs::{Process, ProcMemError};
39    /// let process: Result<Process,ProcMemError> = Process::with_pid(12345);
40    /// ```
41    pub fn with_pid(pid: u32) -> Result<Self, ProcMemError> {
42        let h_snap = create_snapshot(TH32CS_SNAPPROCESS, 0)?;
43
44        let mut pe32 = new_pe32w();
45
46        if !process32first(&h_snap, &mut pe32) { return Err(ProcMemError::IterateSnapshotFailure); }
47
48        loop {
49            if pid.eq(&pe32.th32ProcessID) {
50                let process_name = String::from_utf16_lossy(&pe32.szExeFile).trim_end_matches('\u{0}').to_string();
51
52                let mut proc = Process {
53                    process_name: String::from(&process_name),
54                    process_id: pid,
55                    process_base_address: 0,
56                    process_handle: Handle::read_write(pid)?,
57                    iswow64: false,
58                };
59
60                proc.process_base_address = proc.module(&process_name)?.base_address();
61                proc.iswow64 = proc.iswow64();
62
63                return Ok(proc);
64            }
65
66            if !process32next(&h_snap, &mut pe32) { break; }
67        }
68        Err(ProcMemError::ProcessNotFound)
69    }
70
71
72    /// returns the desired process with the provided name
73    ///
74    /// ```rust
75    /// use proc_mem_rs::{Process, ProcMemError};
76    /// let process: Result<Process,ProcMemError> = Process::with_name("process.exe");
77    /// ```
78    pub fn with_name(name: &str) -> Result<Self, ProcMemError> {
79        let h_snap = create_snapshot(TH32CS_SNAPPROCESS, 0)?;
80
81        let mut pe32 = new_pe32w();
82
83        if !process32first(&h_snap, &mut pe32) { return Err(ProcMemError::IterateSnapshotFailure); }
84
85        loop {
86            let process_name = String::from_utf16_lossy(&pe32.szExeFile).trim_end_matches('\u{0}').to_string();
87            if process_name.eq(&name) {
88                let mut proc = Process {
89                    process_name: String::from(&process_name),
90                    process_id: pe32.th32ProcessID,
91                    process_base_address: 0,
92                    process_handle: Handle::read_write(pe32.th32ProcessID)?,
93                    iswow64: false,
94                };
95
96                proc.process_base_address = proc.module(&process_name)?.base_address();
97                proc.iswow64 = proc.iswow64();
98
99                return Ok(proc);
100            }
101
102            if !process32next(&h_snap, &mut pe32) { break; }
103        }
104        Err(ProcMemError::ProcessNotFound)
105    }
106
107
108    /// returns a Vec<Process> where all processes share the provided name
109    ///
110    /// ```rust
111    /// use proc_mem_rs::{Process, ProcMemError};
112    /// let processes: Result<Vec<Process>,ProcMemError> = Process::all_with_name("process.exe");
113    /// ```
114    pub fn all_with_name(name: &str) -> Result<Vec<Process>, ProcMemError> {
115        let mut results: Vec<Process> = Vec::new();
116
117        let h_snap = create_snapshot(TH32CS_SNAPPROCESS, 0)?;
118
119        let mut pe32 = new_pe32w();
120
121        if !process32first(&h_snap, &mut pe32) { return Err(ProcMemError::IterateSnapshotFailure); }
122
123        loop {
124            let process_name = String::from_utf16_lossy(&pe32.szExeFile).trim_end_matches('\u{0}').to_string();
125            if process_name.eq(&name) {
126                let mut proc = Process {
127                    process_name: String::from(&process_name),
128                    process_id: pe32.th32ProcessID,
129                    process_base_address: 0,
130                    process_handle: Handle::read_write(pe32.th32ProcessID)?,
131                    iswow64: false,
132                };
133
134                proc.process_base_address = proc.module(&process_name)?.base_address();
135                proc.iswow64 = proc.iswow64();
136
137                results.push(proc);
138            }
139
140            if !process32next(&h_snap, &mut pe32) { break; }
141        }
142
143        match results.is_empty() {
144            true => return Err(ProcMemError::ProcessNotFound),
145            false => return Ok(results)
146        }
147    }
148
149
150    /// returns an instance of module including its base address in memory
151    ///
152    /// ```rust
153    /// use proc_mem_rs::{Process, Module, ProcMemError};
154    /// let process = Process::with_name("process.exe")?;
155    /// let module: Result<Module,ProcMemError> = process.module("module.dll");
156    /// ```
157    pub fn module(&self, name: &str) -> Result<Module, ProcMemError> {
158        let h_snap = create_snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, *self.pid())?;
159
160        let mut me32 = new_me32w();
161
162        if !module32first(&h_snap, &mut me32) { return Err(ProcMemError::IterateSnapshotFailure); }
163
164        loop {
165            let module_name = String::from_utf16_lossy(&me32.szModule).trim_end_matches('\u{0}').to_string();
166
167            if module_name.eq(name) {
168                let module_path = String::from_utf16_lossy(&me32.szExePath).trim_end_matches('\u{0}').to_string();
169                return Ok(Module::new(
170                    module_name,
171                    module_path,
172                    *self.pid(),
173                    me32.modBaseAddr as usize,
174                    me32.modBaseSize as usize,
175                    &self,
176                ));
177            }
178
179            if !module32next(&h_snap, &mut me32) { break; }
180        }
181        Err(ProcMemError::ModuleNotFound)
182    }
183
184    /// Returns a vector of Module instances
185    ///
186    /// ```rust
187    /// use proc_mem_rs::{Module, Process, ProcMemError};
188    /// let process = Process::with_name("process.exe")?;
189    /// let modules: Result<Vec<Module>, ProcMemError> = process.list_modules();
190    /// ```
191    pub fn modules(&self) -> Result<Vec<Module>, ProcMemError> {
192        let h_snap = create_snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, *self.pid())?;
193
194        let mut me32 = new_me32w();
195
196        if !module32first(&h_snap, &mut me32) {
197            return Err(ProcMemError::IterateSnapshotFailure);
198        }
199
200        let mut modules = Vec::new();
201
202        loop {
203            let module_name = String::from_utf16_lossy(&me32.szModule).trim_end_matches('\u{0}').to_string();
204
205            let module_path = String::from_utf16_lossy(&me32.szExePath).trim_end_matches('\u{0}').to_string();
206
207            modules.push(Module::new(
208                module_name,
209                module_path,
210                *self.pid(),
211                me32.modBaseAddr as usize,
212                me32.modBaseSize as usize,
213                &self,
214            ));
215
216            if !module32next(&h_snap, &mut me32) { break; }
217        }
218
219        Ok(modules)
220    }
221
222    /// returns true if the process was terminated, otherwise will return false
223    ///
224    /// ```rust
225    /// use proc_mem_rs::{Process};
226    /// let process = Process::with_name("process.exe")?;
227    /// let did_terminate: bool = process.kill();
228    /// ```
229    pub fn kill(&self) -> bool {
230        let output = Command::new("taskkill.exe").arg("/PID").arg(&self.process_id.to_string()).arg("/F").creation_flags(CREATE_NO_WINDOW).output().expect("");
231
232        if output.status.success() {
233            println!("Process with PID {} was terminated", &self.process_id);
234            true
235        } else {
236            println!(
237                "Error killing process with PID {}: {}",
238                &self.process_id,
239                String::from_utf8_lossy(&output.stderr)
240            );
241            false
242        }
243    }
244
245
246    /// This function takes a type and the address to read.
247    /// On success the read value will be returned.
248    /// ```rust
249    /// use proc_mem_rs::{Process, Module, ProcMemError};
250    /// let chrome = Process::with_name("chrome.exe")?;
251    /// let module = chrome.module("kernel32.dll")?;
252    /// let read_value: Result<T, ProcMemError> = chrome.read_mem::<T>(module.base_address() + 0x1337);
253    /// ```
254    pub fn read_mem<T: Default>(&self, address: usize) -> Result<T, ProcMemError> {
255        let mut out: T = Default::default();
256
257        unsafe {
258            return if ReadProcessMemory(
259                *self.process_handle,
260                address as *const _,
261                &mut out as *mut T as *mut _,
262                std::mem::size_of::<T>(),
263                std::ptr::null_mut::<SIZE_T>(),
264            ) == FALSE {
265                println!("ReadProcessMemory failed. Error: {:?}", std::io::Error::last_os_error());
266                return Err(ProcMemError::ReadMemoryError);
267            } else { Ok(out) };
268        }
269    }
270
271    /// This function takes a type and a Vec of addresses/offsets,
272    /// the first entry being the base address to start from.
273    /// On success the read value will be returned.
274    /// ```rust
275    /// use proc_mem_rs::{Process, Module, ProcMemError};
276    /// let chrome = Process::with_name("chrome.exe")?;
277    /// let module = chrome.module("kernel32.dll")?;
278    /// let chain: Vec<usize> = vec![module.base_address(), 0xDEA964, 0x100];
279    /// let read_value: Result<T, ProcMemError> = chrome.read_mem_chain::<T>(chain);
280    /// ```
281    pub fn read_mem_chain<T: Default>(&self, mut chain: Vec<usize>) -> Result<T, ProcMemError> {
282        let mut address = chain.remove(0);
283
284        while chain.len() != 1 {
285            address += chain.remove(0);
286            address = if self.iswow64 {
287                self.read_mem::<u32>(address)? as usize
288            } else {
289                self.read_mem::<u64>(address)? as usize
290            }
291        }
292
293        let ret = self.read_mem::<T>(address + chain.remove(0))?;
294
295        return Ok(ret);
296    }
297
298
299    /// This function takes a type and a Vec of addresses/offsets,
300    /// the first entry being the base address to start from.
301    /// On success the address at the end of the chain will be returned.
302    /// ```rust
303    /// use proc_mem_rs::{Process, Module, ProcMemError};
304    /// let some_game = Process::with_name("some_game.exe")?;
305    /// let module = some_game.module("client.dll")?;
306    /// let chain: Vec<usize> = vec![module.base_address(), 0xDEA964, 0x100];
307    /// let desired_address: Result<usize, ProcMemError> = module.read_ptr_chain(chain);
308    /// ```
309    pub fn read_ptr_chain(&self, mut chain: Vec<usize>) -> Result<usize, ProcMemError> {
310        let mut address = chain.remove(0);
311
312
313        while chain.len() != 1 {
314            address += chain.remove(0);
315            address = if self.iswow64 {
316                self.read_mem::<u32>(address)? as usize
317            } else {
318                self.read_mem::<u64>(address)? as usize
319            }
320        }
321
322        return Ok(address + chain.remove(0));
323    }
324
325
326    /// This function takes a type and the address to write to.
327    /// The returned boolean will be true on success and false on failure
328    /// ```rust
329    /// use proc_mem_rs::{Process, Module};
330    /// let chrome = Process::with_name("chrome.exe")?;
331    /// let module = chrome.module("kernel32.dll")?;
332    /// let mut value_to_write: i32 = 1337;
333    /// let write_result: bool = chrome.write_mem(module.base_address() + 0x1337, value_to_write);
334    /// ```
335    pub fn write_mem<T: Default>(&self, address: usize, mut value: T) -> bool {
336        unsafe {
337            WriteProcessMemory(
338                *self.process_handle,
339                address as *mut _,
340                &mut value as *mut T as *mut _,
341                std::mem::size_of::<T>(),
342                std::ptr::null_mut::<SIZE_T>(),
343            ) != FALSE
344        }
345    }
346
347    /// With this function someone can write multiple bytes to a specified address.
348    /// The returned boolean will be true on success and false on failure
349    /// ```rust
350    /// use proc_mem_rs::{Process, Module};
351    /// let chrome = Process::with_name("chrome.exe")?;
352    /// let module = chrome.module("kernel32.dll")?;
353    /// let mut bytes_to_write: Vec<u8> = [ 0x48, 0xC7, 0xC0, 0x0A, 0x00, 0x00, 0x00 ].to_vec();
354    /// let write_result: bool = chrome.write_bytes(module.base_address() + 0x1337, bytes_to_write.as_mut_ptr(), bytes_to_write.len());
355    /// ```
356    pub fn write_bytes(&self, address: usize, buf: *mut u8, size: usize) -> bool {
357        unsafe {
358            WriteProcessMemory(
359                *self.process_handle,
360                address as *mut _,
361                buf as *mut _,
362                size as SIZE_T,
363                std::ptr::null_mut::<SIZE_T>(),
364            ) != FALSE
365        }
366    }
367
368    /// C style method to read memory
369    /// Third argument is the multiplicator of the Size of "T"
370    /// for example if someone would want to read multiple bytes
371    /// ```rust
372    /// use proc_mem_rs::{Process, Module};
373    /// let chrome = Process::with_name("chrome.exe")?;
374    /// let module = chrome.module("kernel32.dll")?;
375    /// let mut value_buffer: i32 = 0;
376    /// if !chrome.read_ptr(&mut value_buffer, module.base_address() + 0x1337) {
377    ///     println!("ReadMemory Failure");
378    /// } else {
379    ///     println!("ReadMemory Success");
380    /// }
381    /// ```
382    pub fn read_ptr<T: Copy>(&self, buf: *mut T, address: usize) -> bool {
383        unsafe {
384            ReadProcessMemory(
385                *self.process_handle,
386                address as LPCVOID,
387                buf as *mut T as LPVOID,
388                std::mem::size_of::<T>() as SIZE_T,
389                std::ptr::null_mut::<SIZE_T>(),
390            ) != FALSE
391        }
392    }
393
394    /// C style method to read multiple bytes from memory
395    /// ```rust
396    /// use proc_mem_rs::{Process, Module};
397    /// let chrome = Process::with_name("chrome.exe")?;
398    /// let module = chrome.module("kernel32.dll")?;
399    ///
400    /// let rsize = 10;
401    /// let mut bytes_buffer: Vec<u8> = vec![0u8;rsize];
402    /// if !chrome.read_bytes(module.base_address() + 0x1337, bytes_buffer.as_mut_ptr(), rsize) {
403    ///     println!("ReadMemory Failure");
404    /// } else {
405    ///     println!("ReadMemory Success");
406    /// }
407    /// ```
408    pub fn read_bytes(&self, address: usize, buf: *mut u8, size: usize) -> bool {
409        unsafe {
410            ReadProcessMemory(
411                *self.process_handle,
412                address as LPCVOID,
413                buf as LPVOID,
414                size as SIZE_T,
415                std::ptr::null_mut::<SIZE_T>(),
416            ) != FALSE
417        }
418    }
419
420    /// Returns a string slice of the process name
421    pub fn name(&self) -> &str {
422        &self.process_name
423    }
424    /// Returns the unique identifier aka. process id of the process
425    pub fn pid(&self) -> &u32 {
426        &self.process_id
427    }
428    // Determines whether the specified process is running under WOW64 or an Intel64 of x64 processor.
429    pub fn iswow64(&self) -> bool {
430        let mut tmp: BOOL = 0;
431        unsafe { IsWow64Process(*self.process_handle, &mut tmp as PBOOL) };
432        match tmp {
433            FALSE => false,
434            _ => true
435        }
436    }
437
438    /// Returns "TRUE" specified Memory Protection was changed successfully
439    pub fn protect_mem(&self, address: usize, size: usize, new_protect: u32, old_protect: *mut u32) -> bool {
440        let mut _result: BOOL = FALSE;
441        unsafe {
442            _result = VirtualProtect(address as LPVOID, size, new_protect, old_protect);
443        }
444        match _result {
445            FALSE => false,
446            _ => true
447        }
448    }
449
450    fn read_module(&self, address: usize, msize: usize) -> Result<Vec<u8>, ProcMemError> {
451        let mut out = vec![0u8; msize];
452        let out_ptr = out.as_mut_ptr();
453        unsafe {
454            if ReadProcessMemory(
455                *self.process_handle,
456                address as LPCVOID,
457                out_ptr as LPVOID,
458                size_of::<u8>() as SIZE_T * msize,
459                std::ptr::null_mut::<SIZE_T>(),
460            ) == FALSE {
461                Err(ProcMemError::ReadMemoryError)
462            } else {
463                Ok(out)
464            }
465        }
466    }
467
468    fn generate_regex(raw: &str) -> Option<Regex> {
469        let mut res = raw
470            .to_string()
471            .split_whitespace()
472            .map(|x| match &x {
473                &"?" => ".".to_string(),
474                x => format!("\\x{}", x),
475            })
476            .collect::<Vec<_>>()
477            .join("");
478        res.insert_str(0, "(?s-u)");
479        Regex::new(&res).ok()
480    }
481
482    pub fn find_pattern<T: Default>(&self, pattern: &str) -> Result<usize, ProcMemError> {
483        let size = ::std::mem::size_of::<T>();
484        let mut position: usize = 0;
485
486        while position < u32::MAX as usize {
487            // adjust the step as per requirements
488            let chunk: T = self.read_mem(position)
489                .unwrap_or_else(|_| T::default());
490            let data: &[u8] = unsafe {
491                std::slice::from_raw_parts(
492                    &chunk as *const T as *const u8,
493                    size,
494                )
495            };
496
497            if let Some(offset) = Self::generate_regex(pattern)
498                .and_then(|r| r.find(data))
499                .and_then(|m| Some(m.start())) {
500                return Ok(position + offset);
501            }
502
503            position += size;
504        }
505
506        Err(ProcMemError::SignatureNotFound)
507    }
508
509    pub fn find_signature<T: Default>(&self, sig: &Signature) -> Result<usize, ProcMemError> {
510        let mut addr = self.find_pattern::<T>(&sig.pattern)?;
511
512        for (_i,o) in sig.offsets.iter().enumerate() {
513            let pos = (addr as isize).wrapping_add(*o) as usize;
514            let raw: T = self.read_mem(pos)?;
515            let tmp = if self.iswow64 {
516                let data: u32 = unsafe {*((&raw as *const T as *const u32))};
517                data as usize
518            } else {
519                let data: u64 = unsafe {*((&raw as *const T as *const u64))};
520                data as usize
521            };
522            addr = tmp;
523        }
524
525        addr = (addr as isize).wrapping_add(sig.extra) as usize;
526
527        Ok(addr)
528    }
529}
530
531unsafe impl Send for Process {}
532unsafe impl Sync for Process {}