1use super::driver::DriverHandle;
4use super::ioctl;
5use super::{ClientError, ClientResult};
6
7use std::mem::MaybeUninit;
8use std::ptr;
9
10pub struct ProcessOps<'a> {
12 handle: &'a DriverHandle,
13 pid: u32,
14}
15
16impl<'a> ProcessOps<'a> {
17 pub(crate) fn new(handle: &'a DriverHandle, pid: u32) -> ClientResult<Self> {
19 Ok(Self { handle, pid })
20 }
21
22 pub fn pid(&self) -> u32 {
24 self.pid
25 }
26
27 pub fn read<T: Copy>(&self, address: u64) -> ClientResult<T> {
29 let request = ioctl::ReadMemoryRequest {
30 process_id: self.pid,
31 address,
32 size: std::mem::size_of::<T>() as u32,
33 };
34
35 let mut buffer = vec![0u8; std::mem::size_of::<T>()];
36
37 let bytes = self.handle.ioctl_raw(
38 ioctl::codes::READ_MEMORY.code(),
39 unsafe {
40 std::slice::from_raw_parts(
41 &request as *const _ as *const u8,
42 std::mem::size_of::<ioctl::ReadMemoryRequest>(),
43 )
44 },
45 &mut buffer,
46 )?;
47
48 if bytes as usize != std::mem::size_of::<T>() {
49 return Err(ClientError::InvalidResponse {
50 expected: std::mem::size_of::<T>(),
51 received: bytes as usize,
52 });
53 }
54
55 Ok(unsafe { ptr::read(buffer.as_ptr() as *const T) })
56 }
57
58 pub fn read_bytes(&self, address: u64, size: usize) -> ClientResult<Vec<u8>> {
60 let request = ioctl::ReadMemoryRequest {
61 process_id: self.pid,
62 address,
63 size: size as u32,
64 };
65
66 let mut buffer = vec![0u8; size];
67
68 let bytes = self.handle.ioctl_raw(
69 ioctl::codes::READ_MEMORY.code(),
70 unsafe {
71 std::slice::from_raw_parts(
72 &request as *const _ as *const u8,
73 std::mem::size_of::<ioctl::ReadMemoryRequest>(),
74 )
75 },
76 &mut buffer,
77 )?;
78
79 buffer.truncate(bytes as usize);
80 Ok(buffer)
81 }
82
83 pub fn write<T: Copy>(&self, address: u64, value: &T) -> ClientResult<()> {
85 let header_size = std::mem::size_of::<ioctl::WriteMemoryRequest>();
86 let data_size = std::mem::size_of::<T>();
87 let mut buffer = vec![0u8; header_size + data_size];
88
89 let request = ioctl::WriteMemoryRequest {
90 process_id: self.pid,
91 address,
92 size: data_size as u32,
93 };
94
95 unsafe {
96 ptr::copy_nonoverlapping(
97 &request as *const _ as *const u8,
98 buffer.as_mut_ptr(),
99 header_size,
100 );
101 ptr::copy_nonoverlapping(
102 value as *const T as *const u8,
103 buffer.as_mut_ptr().add(header_size),
104 data_size,
105 );
106 }
107
108 let _ = self.handle.ioctl_raw(ioctl::codes::WRITE_MEMORY.code(), &buffer, &mut [])?;
109 Ok(())
110 }
111
112 pub fn write_bytes(&self, address: u64, data: &[u8]) -> ClientResult<()> {
114 let header_size = std::mem::size_of::<ioctl::WriteMemoryRequest>();
115 let mut buffer = vec![0u8; header_size + data.len()];
116
117 let request = ioctl::WriteMemoryRequest {
118 process_id: self.pid,
119 address,
120 size: data.len() as u32,
121 };
122
123 unsafe {
124 ptr::copy_nonoverlapping(
125 &request as *const _ as *const u8,
126 buffer.as_mut_ptr(),
127 header_size,
128 );
129 ptr::copy_nonoverlapping(
130 data.as_ptr(),
131 buffer.as_mut_ptr().add(header_size),
132 data.len(),
133 );
134 }
135
136 let _ = self.handle.ioctl_raw(ioctl::codes::WRITE_MEMORY.code(), &buffer, &mut [])?;
137 Ok(())
138 }
139
140 pub fn get_module_base(&self, module_name: &str) -> ClientResult<u64> {
142 let name_bytes = module_name.as_bytes();
143 let header_size = std::mem::size_of::<ioctl::GetModuleBaseRequest>();
144 let total_size = header_size + name_bytes.len();
145
146 let mut input = vec![0u8; total_size];
147 let request = ioctl::GetModuleBaseRequest {
148 process_id: self.pid,
149 module_name_offset: header_size as u32,
150 module_name_length: name_bytes.len() as u32,
151 };
152
153 unsafe {
154 ptr::copy_nonoverlapping(
155 &request as *const _ as *const u8,
156 input.as_mut_ptr(),
157 header_size,
158 );
159 ptr::copy_nonoverlapping(
160 name_bytes.as_ptr(),
161 input.as_mut_ptr().add(header_size),
162 name_bytes.len(),
163 );
164 }
165
166 let mut response = MaybeUninit::<ioctl::GetModuleBaseResponse>::uninit();
167
168 let bytes = self.handle.ioctl(
169 ioctl::codes::GET_MODULE_BASE.code(),
170 Some(&input[..]),
171 Some(unsafe { response.assume_init_mut() }),
172 )?;
173
174 if bytes as usize != std::mem::size_of::<ioctl::GetModuleBaseResponse>() {
175 return Err(ClientError::InvalidResponse {
176 expected: std::mem::size_of::<ioctl::GetModuleBaseResponse>(),
177 received: bytes as usize,
178 });
179 }
180
181 Ok(unsafe { response.assume_init() }.base_address)
182 }
183
184 pub fn allocate(&self, size: u64, protection: u32) -> ClientResult<u64> {
186 let request = ioctl::AllocateMemoryRequest {
187 process_id: self.pid,
188 size,
189 protection,
190 preferred_address: 0,
191 };
192
193 let mut response = MaybeUninit::<ioctl::AllocateMemoryResponse>::uninit();
194
195 let bytes = self.handle.ioctl(
196 ioctl::codes::ALLOCATE_MEMORY.code(),
197 Some(&request),
198 Some(unsafe { response.assume_init_mut() }),
199 )?;
200
201 if bytes as usize != std::mem::size_of::<ioctl::AllocateMemoryResponse>() {
202 return Err(ClientError::InvalidResponse {
203 expected: std::mem::size_of::<ioctl::AllocateMemoryResponse>(),
204 received: bytes as usize,
205 });
206 }
207
208 Ok(unsafe { response.assume_init() }.allocated_address)
209 }
210
211 pub fn allocate_at(&self, address: u64, size: u64, protection: u32) -> ClientResult<u64> {
213 let request = ioctl::AllocateMemoryRequest {
214 process_id: self.pid,
215 size,
216 protection,
217 preferred_address: address,
218 };
219
220 let mut response = MaybeUninit::<ioctl::AllocateMemoryResponse>::uninit();
221
222 let bytes = self.handle.ioctl(
223 ioctl::codes::ALLOCATE_MEMORY.code(),
224 Some(&request),
225 Some(unsafe { response.assume_init_mut() }),
226 )?;
227
228 if bytes as usize != std::mem::size_of::<ioctl::AllocateMemoryResponse>() {
229 return Err(ClientError::InvalidResponse {
230 expected: std::mem::size_of::<ioctl::AllocateMemoryResponse>(),
231 received: bytes as usize,
232 });
233 }
234
235 Ok(unsafe { response.assume_init() }.allocated_address)
236 }
237
238 pub fn free(&self, address: u64) -> ClientResult<()> {
240 let request = ioctl::FreeMemoryRequest {
241 process_id: self.pid,
242 address,
243 };
244
245 let _ = self.handle.ioctl(
246 ioctl::codes::FREE_MEMORY.code(),
247 Some(&request),
248 None::<&mut ()>,
249 )?;
250 Ok(())
251 }
252
253 pub fn protect(&self, address: u64, size: u64, protection: u32) -> ClientResult<u32> {
255 let request = ioctl::ProtectMemoryRequest {
256 process_id: self.pid,
257 address,
258 size,
259 new_protection: protection,
260 };
261
262 let mut response = MaybeUninit::<ioctl::ProtectMemoryResponse>::uninit();
263
264 let bytes = self.handle.ioctl(
265 ioctl::codes::PROTECT_MEMORY.code(),
266 Some(&request),
267 Some(unsafe { response.assume_init_mut() }),
268 )?;
269
270 if bytes as usize != std::mem::size_of::<ioctl::ProtectMemoryResponse>() {
271 return Err(ClientError::InvalidResponse {
272 expected: std::mem::size_of::<ioctl::ProtectMemoryResponse>(),
273 received: bytes as usize,
274 });
275 }
276
277 Ok(unsafe { response.assume_init() }.old_protection)
278 }
279
280 pub fn read_string(&self, address: u64, max_len: usize) -> ClientResult<String> {
282 let bytes = self.read_bytes(address, max_len)?;
283 let len = bytes.iter().position(|&b| b == 0).unwrap_or(bytes.len());
284 String::from_utf8_lossy(&bytes[..len]).into_owned().pipe(Ok)
285 }
286
287 pub fn read_wstring(&self, address: u64, max_chars: usize) -> ClientResult<String> {
289 let bytes = self.read_bytes(address, max_chars * 2)?;
290 let chars: Vec<u16> = bytes
291 .chunks_exact(2)
292 .map(|c| u16::from_le_bytes([c[0], c[1]]))
293 .take_while(|&c| c != 0)
294 .collect();
295 String::from_utf16_lossy(&chars).pipe(Ok)
296 }
297}
298
299trait Pipe: Sized {
301 fn pipe<F, R>(self, f: F) -> R
302 where
303 F: FnOnce(Self) -> R,
304 {
305 f(self)
306 }
307}
308
309impl<T> Pipe for T {}