1use super::table::{SyscallEntry, SyscallTable};
9use crate::error::{Result, WraithError};
10use core::arch::asm;
11
12#[cfg(target_arch = "x86_64")]
13const SYSCALL_BYTES: [u8; 2] = [0x0F, 0x05]; #[cfg(target_arch = "x86")]
16const SYSCALL_BYTES: [u8; 2] = [0x0F, 0x34]; pub struct IndirectSyscall {
23 ssn: u16,
24 syscall_addr: usize,
25}
26
27impl IndirectSyscall {
28 pub const unsafe fn new_unchecked(ssn: u16, syscall_addr: usize) -> Self {
33 Self { ssn, syscall_addr }
34 }
35
36 pub fn new(ssn: u16, syscall_addr: usize) -> Result<Self> {
38 if !Self::validate_syscall_address(syscall_addr) {
40 return Err(WraithError::SyscallEnumerationFailed {
41 reason: format!(
42 "address {:#x} does not contain valid syscall instruction",
43 syscall_addr
44 ),
45 });
46 }
47
48 Ok(Self { ssn, syscall_addr })
49 }
50
51 fn validate_syscall_address(addr: usize) -> bool {
53 if addr == 0 {
54 return false;
55 }
56
57 let bytes: [u8; 2] = unsafe { *(addr as *const [u8; 2]) };
61 bytes == SYSCALL_BYTES
62 }
63
64 pub fn from_entry(entry: &SyscallEntry) -> Result<Self> {
66 let syscall_addr = entry.syscall_address.ok_or_else(|| {
67 WraithError::SyscallEnumerationFailed {
68 reason: format!("no syscall address for {}", entry.name),
69 }
70 })?;
71
72 Self::new(entry.ssn, syscall_addr)
73 }
74
75 pub fn from_table(table: &SyscallTable, name: &str) -> Result<Self> {
77 let entry = table.get(name).ok_or_else(|| WraithError::SyscallNotFound {
78 name: name.to_string(),
79 })?;
80 Self::from_entry(entry)
81 }
82
83 pub const fn ssn(&self) -> u16 {
85 self.ssn
86 }
87
88 pub const fn syscall_address(&self) -> usize {
90 self.syscall_addr
91 }
92}
93
94#[cfg(target_arch = "x86_64")]
95impl IndirectSyscall {
96 #[inline(never)]
101 pub unsafe fn call0(&self) -> i32 {
102 let status: i32;
103 unsafe {
106 asm!(
107 "sub rsp, 0x28", "mov r10, rcx",
109 "mov eax, {ssn:e}",
110 "call {addr}",
111 "add rsp, 0x28",
112 ssn = in(reg) self.ssn as u32,
113 addr = in(reg) self.syscall_addr,
114 out("eax") status,
115 out("rcx") _,
116 out("r10") _,
117 out("r11") _,
118 );
119 }
120 status
121 }
122
123 #[inline(never)]
128 pub unsafe fn call1(&self, arg1: usize) -> i32 {
129 let status: i32;
130 unsafe {
132 asm!(
133 "sub rsp, 0x28",
134 "mov r10, rcx",
135 "mov eax, {ssn:e}",
136 "call {addr}",
137 "add rsp, 0x28",
138 ssn = in(reg) self.ssn as u32,
139 addr = in(reg) self.syscall_addr,
140 in("rcx") arg1,
141 out("eax") status,
142 out("r10") _,
143 out("r11") _,
144 );
145 }
146 status
147 }
148
149 #[inline(never)]
154 pub unsafe fn call2(&self, arg1: usize, arg2: usize) -> i32 {
155 let status: i32;
156 unsafe {
158 asm!(
159 "sub rsp, 0x28",
160 "mov r10, rcx",
161 "mov eax, {ssn:e}",
162 "call {addr}",
163 "add rsp, 0x28",
164 ssn = in(reg) self.ssn as u32,
165 addr = in(reg) self.syscall_addr,
166 in("rcx") arg1,
167 in("rdx") arg2,
168 out("eax") status,
169 out("r10") _,
170 out("r11") _,
171 );
172 }
173 status
174 }
175
176 #[inline(never)]
181 pub unsafe fn call3(&self, arg1: usize, arg2: usize, arg3: usize) -> i32 {
182 let status: i32;
183 unsafe {
185 asm!(
186 "sub rsp, 0x28",
187 "mov r10, rcx",
188 "mov eax, {ssn:e}",
189 "call {addr}",
190 "add rsp, 0x28",
191 ssn = in(reg) self.ssn as u32,
192 addr = in(reg) self.syscall_addr,
193 in("rcx") arg1,
194 in("rdx") arg2,
195 in("r8") arg3,
196 out("eax") status,
197 out("r10") _,
198 out("r11") _,
199 );
200 }
201 status
202 }
203
204 #[inline(never)]
209 pub unsafe fn call4(&self, arg1: usize, arg2: usize, arg3: usize, arg4: usize) -> i32 {
210 let status: i32;
211 unsafe {
213 asm!(
214 "sub rsp, 0x28",
215 "mov r10, rcx",
216 "mov eax, {ssn:e}",
217 "call {addr}",
218 "add rsp, 0x28",
219 ssn = in(reg) self.ssn as u32,
220 addr = in(reg) self.syscall_addr,
221 in("rcx") arg1,
222 in("rdx") arg2,
223 in("r8") arg3,
224 in("r9") arg4,
225 out("eax") status,
226 out("r10") _,
227 out("r11") _,
228 );
229 }
230 status
231 }
232
233 #[inline(never)]
238 pub unsafe fn call5(
239 &self,
240 arg1: usize,
241 arg2: usize,
242 arg3: usize,
243 arg4: usize,
244 arg5: usize,
245 ) -> i32 {
246 let status: i32;
247 unsafe {
251 asm!(
252 "sub rsp, 0x28",
253 "mov [rsp+0x20], {arg5}",
254 "mov r10, rcx",
255 "mov eax, {ssn:e}",
256 "call {addr}",
257 "add rsp, 0x28",
258 ssn = in(reg) self.ssn as u32,
259 addr = in(reg) self.syscall_addr,
260 arg5 = in(reg) arg5,
261 in("rcx") arg1,
262 in("rdx") arg2,
263 in("r8") arg3,
264 in("r9") arg4,
265 out("eax") status,
266 out("r10") _,
267 out("r11") _,
268 );
269 }
270 status
271 }
272
273 #[inline(never)]
278 pub unsafe fn call6(
279 &self,
280 arg1: usize,
281 arg2: usize,
282 arg3: usize,
283 arg4: usize,
284 arg5: usize,
285 arg6: usize,
286 ) -> i32 {
287 let status: i32;
288 unsafe {
292 asm!(
293 "sub rsp, 0x30",
294 "mov [rsp+0x20], {arg5}",
295 "mov [rsp+0x28], {arg6}",
296 "mov r10, rcx",
297 "mov eax, {ssn:e}",
298 "call {addr}",
299 "add rsp, 0x30",
300 ssn = in(reg) self.ssn as u32,
301 addr = in(reg) self.syscall_addr,
302 arg5 = in(reg) arg5,
303 arg6 = in(reg) arg6,
304 in("rcx") arg1,
305 in("rdx") arg2,
306 in("r8") arg3,
307 in("r9") arg4,
308 out("eax") status,
309 out("r10") _,
310 out("r11") _,
311 );
312 }
313 status
314 }
315
316 #[inline(never)]
321 pub unsafe fn call_many(&self, args: &[usize]) -> i32 {
322 match args.len() {
323 0 => unsafe { self.call0() },
324 1 => unsafe { self.call1(args[0]) },
325 2 => unsafe { self.call2(args[0], args[1]) },
326 3 => unsafe { self.call3(args[0], args[1], args[2]) },
327 4 => unsafe { self.call4(args[0], args[1], args[2], args[3]) },
328 5 => unsafe { self.call5(args[0], args[1], args[2], args[3], args[4]) },
329 6 => unsafe { self.call6(args[0], args[1], args[2], args[3], args[4], args[5]) },
330 _ => unsafe { self.call6(args[0], args[1], args[2], args[3], args[4], args[5]) },
331 }
332 }
333}
334
335#[cfg(target_arch = "x86")]
336impl IndirectSyscall {
337 #[inline(never)]
342 pub unsafe fn call(&self, args: &[usize]) -> i32 {
343 let status: i32;
344 let args_ptr = args.as_ptr();
345
346 unsafe {
348 asm!(
349 "mov eax, {ssn:e}",
350 "mov edx, {args}",
351 "call {addr}",
352 ssn = in(reg) self.ssn as u32,
353 args = in(reg) args_ptr,
354 addr = in(reg) self.syscall_addr,
355 out("eax") status,
356 options(nostack)
357 );
358 }
359 status
360 }
361
362 pub unsafe fn call0(&self) -> i32 {
363 unsafe { self.call(&[]) }
364 }
365
366 pub unsafe fn call1(&self, arg1: usize) -> i32 {
367 unsafe { self.call(&[arg1]) }
368 }
369
370 pub unsafe fn call2(&self, arg1: usize, arg2: usize) -> i32 {
371 unsafe { self.call(&[arg1, arg2]) }
372 }
373
374 pub unsafe fn call3(&self, arg1: usize, arg2: usize, arg3: usize) -> i32 {
375 unsafe { self.call(&[arg1, arg2, arg3]) }
376 }
377
378 pub unsafe fn call4(&self, arg1: usize, arg2: usize, arg3: usize, arg4: usize) -> i32 {
379 unsafe { self.call(&[arg1, arg2, arg3, arg4]) }
380 }
381
382 pub unsafe fn call5(
383 &self,
384 arg1: usize,
385 arg2: usize,
386 arg3: usize,
387 arg4: usize,
388 arg5: usize,
389 ) -> i32 {
390 unsafe { self.call(&[arg1, arg2, arg3, arg4, arg5]) }
391 }
392
393 pub unsafe fn call6(
394 &self,
395 arg1: usize,
396 arg2: usize,
397 arg3: usize,
398 arg4: usize,
399 arg5: usize,
400 arg6: usize,
401 ) -> i32 {
402 unsafe { self.call(&[arg1, arg2, arg3, arg4, arg5, arg6]) }
403 }
404
405 pub unsafe fn call_many(&self, args: &[usize]) -> i32 {
406 unsafe { self.call(args) }
407 }
408}
409
410#[cfg(test)]
411mod tests {
412 use super::*;
413
414 #[test]
415 fn test_indirect_syscall_ntclose() {
416 let table = SyscallTable::enumerate().expect("should enumerate");
417
418 if let Ok(syscall) = IndirectSyscall::from_table(&table, "NtClose") {
419 let status = unsafe { syscall.call1(0xDEADBEEF) };
421 assert_eq!(status, 0xC0000008_u32 as i32);
422 }
423 }
424
425 #[test]
426 fn test_syscall_address_in_ntdll() {
427 let table = SyscallTable::enumerate().expect("should enumerate");
428
429 if let Some(entry) = table.get("NtClose") {
430 if let Some(addr) = entry.syscall_address {
431 assert!(addr > entry.address, "syscall should be after function start");
433 assert!(
434 addr < entry.address + 32,
435 "syscall should be within stub"
436 );
437 }
438 }
439 }
440}