wraith/manipulation/remote/
handle.rs1#[cfg(all(not(feature = "std"), feature = "alloc"))]
4use alloc::{format, string::String, vec, vec::Vec};
5
6#[cfg(feature = "std")]
7use std::{format, string::String, vec, vec::Vec};
8
9use crate::error::{Result, WraithError};
10use crate::manipulation::syscall::{
11 get_syscall_table, nt_close, nt_success, DirectSyscall,
12};
13
14#[derive(Debug, Clone, Copy)]
16pub struct HandleDuplicateOptions {
17 pub desired_access: u32,
19 pub attributes: u32,
21 pub options: u32,
23}
24
25impl Default for HandleDuplicateOptions {
26 fn default() -> Self {
27 Self {
28 desired_access: 0,
29 attributes: 0,
30 options: DUPLICATE_SAME_ACCESS,
31 }
32 }
33}
34
35impl HandleDuplicateOptions {
36 pub fn same_access() -> Self {
38 Self::default()
39 }
40
41 pub fn close_source() -> Self {
43 Self {
44 desired_access: 0,
45 attributes: 0,
46 options: DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE,
47 }
48 }
49
50 pub fn with_access(access: u32) -> Self {
52 Self {
53 desired_access: access,
54 attributes: 0,
55 options: 0,
56 }
57 }
58}
59
60#[derive(Debug, Clone)]
62pub struct HandleInfo {
63 pub handle_value: usize,
64 pub object_type: u32,
65 pub granted_access: u32,
66 pub object_name: Option<String>,
67}
68
69pub struct StolenHandle {
71 handle: usize,
72 owns_handle: bool,
73}
74
75impl StolenHandle {
76 pub fn handle(&self) -> usize {
78 self.handle
79 }
80
81 pub fn leak(mut self) -> usize {
83 self.owns_handle = false;
84 self.handle
85 }
86
87 pub unsafe fn from_raw(handle: usize) -> Self {
92 Self {
93 handle,
94 owns_handle: true,
95 }
96 }
97
98 pub unsafe fn from_raw_borrowed(handle: usize) -> Self {
103 Self {
104 handle,
105 owns_handle: false,
106 }
107 }
108}
109
110impl Drop for StolenHandle {
111 fn drop(&mut self) {
112 if self.owns_handle && self.handle != 0 {
113 let _ = nt_close(self.handle);
114 }
115 }
116}
117
118pub fn duplicate_handle(
120 source_process: usize,
121 source_handle: usize,
122 target_process: usize,
123 options: HandleDuplicateOptions,
124) -> Result<StolenHandle> {
125 let table = get_syscall_table()?;
126 let syscall = DirectSyscall::from_table(table, "NtDuplicateObject")?;
127
128 let mut target_handle: usize = 0;
129
130 let status = unsafe {
132 syscall.call_many(&[
133 source_process,
134 source_handle,
135 target_process,
136 &mut target_handle as *mut usize as usize,
137 options.desired_access as usize,
138 options.attributes as usize,
139 options.options as usize,
140 ])
141 };
142
143 if nt_success(status) {
144 Ok(StolenHandle {
145 handle: target_handle,
146 owns_handle: true,
147 })
148 } else {
149 Err(WraithError::HandleDuplicateFailed {
150 reason: format!("NtDuplicateObject failed: {:#x}", status as u32),
151 })
152 }
153}
154
155pub fn steal_handle(
157 source_process: usize,
158 remote_handle: usize,
159 options: HandleDuplicateOptions,
160) -> Result<StolenHandle> {
161 let current_process: usize = usize::MAX; duplicate_handle(source_process, remote_handle, current_process, options)
163}
164
165pub fn enumerate_system_handles(
169 process_id_filter: Option<u32>,
170 object_type_filter: Option<u32>,
171) -> Result<Vec<SystemHandleEntry>> {
172 let table = get_syscall_table()?;
173 let syscall = DirectSyscall::from_table(table, "NtQuerySystemInformation")?;
174
175 const SYSTEM_HANDLE_INFORMATION: u32 = 16;
176 const SYSTEM_EXTENDED_HANDLE_INFORMATION: u32 = 64;
177
178 let mut buffer_size: usize = 1024 * 1024;
180 let mut buffer: Vec<u8>;
181 let mut return_length: u32 = 0;
182
183 loop {
184 buffer = vec![0u8; buffer_size];
185
186 let status = unsafe {
187 syscall.call4(
188 SYSTEM_EXTENDED_HANDLE_INFORMATION as usize,
189 buffer.as_mut_ptr() as usize,
190 buffer.len(),
191 &mut return_length as *mut u32 as usize,
192 )
193 };
194
195 if nt_success(status) {
196 break;
197 }
198
199 if status == 0xC0000004_u32 as i32 {
201 buffer_size = return_length as usize + 0x10000;
202 if buffer_size > 256 * 1024 * 1024 {
203 return Err(WraithError::HandleDuplicateFailed {
204 reason: "buffer too large".into(),
205 });
206 }
207 continue;
208 }
209
210 return Err(WraithError::HandleDuplicateFailed {
211 reason: format!("NtQuerySystemInformation failed: {:#x}", status as u32),
212 });
213 }
214
215 parse_handle_info(&buffer, process_id_filter, object_type_filter)
217}
218
219#[repr(C)]
220struct SystemHandleTableEntryInfoEx {
221 object: usize,
222 unique_process_id: usize,
223 handle_value: usize,
224 granted_access: u32,
225 creator_back_trace_index: u16,
226 object_type_index: u16,
227 handle_attributes: u32,
228 reserved: u32,
229}
230
231#[derive(Debug, Clone)]
233pub struct SystemHandleEntry {
234 pub process_id: u32,
235 pub handle_value: usize,
236 pub object_type: u16,
237 pub granted_access: u32,
238 pub object_address: usize,
239}
240
241fn parse_handle_info(
242 buffer: &[u8],
243 process_id_filter: Option<u32>,
244 object_type_filter: Option<u32>,
245) -> Result<Vec<SystemHandleEntry>> {
246 if buffer.len() < 16 {
247 return Ok(Vec::new());
248 }
249
250 let count = unsafe { *(buffer.as_ptr() as *const usize) };
252 if count == 0 || count > 10_000_000 {
253 return Ok(Vec::new());
254 }
255
256 let entry_size = core::mem::size_of::<SystemHandleTableEntryInfoEx>();
257 let entries_start = 2 * core::mem::size_of::<usize>(); let mut handles = Vec::new();
260
261 for i in 0..count {
262 let offset = entries_start + i * entry_size;
263 if offset + entry_size > buffer.len() {
264 break;
265 }
266
267 let entry = unsafe {
268 &*(buffer.as_ptr().add(offset) as *const SystemHandleTableEntryInfoEx)
269 };
270
271 if let Some(pid) = process_id_filter {
273 if entry.unique_process_id != pid as usize {
274 continue;
275 }
276 }
277
278 if let Some(obj_type) = object_type_filter {
279 if entry.object_type_index as u32 != obj_type {
280 continue;
281 }
282 }
283
284 handles.push(SystemHandleEntry {
285 process_id: entry.unique_process_id as u32,
286 handle_value: entry.handle_value,
287 object_type: entry.object_type_index,
288 granted_access: entry.granted_access,
289 object_address: entry.object,
290 });
291 }
292
293 Ok(handles)
294}
295
296pub fn find_handles_in_process(
298 target_pid: u32,
299 object_type: Option<u32>,
300) -> Result<Vec<SystemHandleEntry>> {
301 enumerate_system_handles(Some(target_pid), object_type)
302}
303
304pub fn steal_process_handle(
308 source_process_handle: usize,
309 remote_handle: usize,
310) -> Result<StolenHandle> {
311 steal_handle(source_process_handle, remote_handle, HandleDuplicateOptions::same_access())
312}
313
314pub mod object_types {
316 pub const PROCESS: u32 = 7;
317 pub const THREAD: u32 = 8;
318 pub const FILE: u32 = 31; pub const SECTION: u32 = 43; pub const KEY: u32 = 19; }
322
323const DUPLICATE_CLOSE_SOURCE: u32 = 0x00000001;
325const DUPLICATE_SAME_ACCESS: u32 = 0x00000002;
326
327#[cfg(test)]
328mod tests {
329 use super::*;
330
331 #[test]
332 fn test_handle_duplicate_options() {
333 let opts = HandleDuplicateOptions::same_access();
334 assert!(opts.options & DUPLICATE_SAME_ACCESS != 0);
335
336 let opts = HandleDuplicateOptions::close_source();
337 assert!(opts.options & DUPLICATE_CLOSE_SOURCE != 0);
338 assert!(opts.options & DUPLICATE_SAME_ACCESS != 0);
339 }
340
341 #[test]
342 fn test_enumerate_own_handles() {
343 let pid = std::process::id();
344 let result = find_handles_in_process(pid, None);
345 assert!(result.is_ok());
346
347 let handles = result.unwrap();
348 assert!(!handles.is_empty(), "should have handles");
350 }
351}