1use super::table::{SyscallEntry, SyscallTable};
7use crate::error::{Result, WraithError};
8use core::arch::asm;
9
10pub struct DirectSyscall {
15 ssn: u16,
16}
17
18impl DirectSyscall {
19 pub const fn new(ssn: u16) -> Self {
21 Self { ssn }
22 }
23
24 pub fn from_entry(entry: &SyscallEntry) -> Self {
26 Self { ssn: entry.ssn }
27 }
28
29 pub fn from_table(table: &SyscallTable, name: &str) -> Result<Self> {
31 let entry = table.get(name).ok_or_else(|| WraithError::SyscallNotFound {
32 name: name.to_string(),
33 })?;
34 Ok(Self::from_entry(entry))
35 }
36
37 pub const fn ssn(&self) -> u16 {
39 self.ssn
40 }
41}
42
43#[cfg(target_arch = "x86_64")]
45impl DirectSyscall {
46 #[inline(never)]
51 pub unsafe fn call0(&self) -> i32 {
52 let status: i32;
53 unsafe {
55 asm!(
56 "mov r10, rcx",
57 "mov eax, {ssn:e}",
58 "syscall",
59 ssn = in(reg) self.ssn as u32,
60 out("eax") status,
61 out("rcx") _,
62 out("r10") _,
63 out("r11") _,
64 options(nostack)
65 );
66 }
67 status
68 }
69
70 #[inline(never)]
75 pub unsafe fn call1(&self, arg1: usize) -> i32 {
76 let status: i32;
77 unsafe {
79 asm!(
80 "mov r10, rcx",
81 "mov eax, {ssn:e}",
82 "syscall",
83 ssn = in(reg) self.ssn as u32,
84 in("rcx") arg1,
85 out("eax") status,
86 out("r10") _,
87 out("r11") _,
88 options(nostack)
89 );
90 }
91 status
92 }
93
94 #[inline(never)]
99 pub unsafe fn call2(&self, arg1: usize, arg2: usize) -> i32 {
100 let status: i32;
101 unsafe {
103 asm!(
104 "mov r10, rcx",
105 "mov eax, {ssn:e}",
106 "syscall",
107 ssn = in(reg) self.ssn as u32,
108 in("rcx") arg1,
109 in("rdx") arg2,
110 out("eax") status,
111 out("r10") _,
112 out("r11") _,
113 options(nostack)
114 );
115 }
116 status
117 }
118
119 #[inline(never)]
124 pub unsafe fn call3(&self, arg1: usize, arg2: usize, arg3: usize) -> i32 {
125 let status: i32;
126 unsafe {
128 asm!(
129 "mov r10, rcx",
130 "mov eax, {ssn:e}",
131 "syscall",
132 ssn = in(reg) self.ssn as u32,
133 in("rcx") arg1,
134 in("rdx") arg2,
135 in("r8") arg3,
136 out("eax") status,
137 out("r10") _,
138 out("r11") _,
139 options(nostack)
140 );
141 }
142 status
143 }
144
145 #[inline(never)]
150 pub unsafe fn call4(&self, arg1: usize, arg2: usize, arg3: usize, arg4: usize) -> i32 {
151 let status: i32;
152 unsafe {
154 asm!(
155 "mov r10, rcx",
156 "mov eax, {ssn:e}",
157 "syscall",
158 ssn = in(reg) self.ssn as u32,
159 in("rcx") arg1,
160 in("rdx") arg2,
161 in("r8") arg3,
162 in("r9") arg4,
163 out("eax") status,
164 out("r10") _,
165 out("r11") _,
166 options(nostack)
167 );
168 }
169 status
170 }
171
172 #[inline(never)]
177 pub unsafe fn call5(
178 &self,
179 arg1: usize,
180 arg2: usize,
181 arg3: usize,
182 arg4: usize,
183 arg5: usize,
184 ) -> i32 {
185 let status: i32;
186 unsafe {
189 asm!(
190 "sub rsp, 0x30", "mov [rsp+0x28], {arg5}",
192 "mov r10, rcx",
193 "mov eax, {ssn:e}",
194 "syscall",
195 "add rsp, 0x30",
196 ssn = in(reg) self.ssn as u32,
197 arg5 = in(reg) arg5,
198 in("rcx") arg1,
199 in("rdx") arg2,
200 in("r8") arg3,
201 in("r9") arg4,
202 out("eax") status,
203 out("r10") _,
204 out("r11") _,
205 );
206 }
207 status
208 }
209
210 #[inline(never)]
215 pub unsafe fn call6(
216 &self,
217 arg1: usize,
218 arg2: usize,
219 arg3: usize,
220 arg4: usize,
221 arg5: usize,
222 arg6: usize,
223 ) -> i32 {
224 let status: i32;
225 unsafe {
228 asm!(
229 "sub rsp, 0x38", "mov [rsp+0x28], {arg5}",
231 "mov [rsp+0x30], {arg6}",
232 "mov r10, rcx",
233 "mov eax, {ssn:e}",
234 "syscall",
235 "add rsp, 0x38",
236 ssn = in(reg) self.ssn as u32,
237 arg5 = in(reg) arg5,
238 arg6 = in(reg) arg6,
239 in("rcx") arg1,
240 in("rdx") arg2,
241 in("r8") arg3,
242 in("r9") arg4,
243 out("eax") status,
244 out("r10") _,
245 out("r11") _,
246 );
247 }
248 status
249 }
250
251 #[inline(never)]
258 pub unsafe fn call_many(&self, args: &[usize]) -> i32 {
259 match args.len() {
260 0 => unsafe { self.call0() },
261 1 => unsafe { self.call1(args[0]) },
262 2 => unsafe { self.call2(args[0], args[1]) },
263 3 => unsafe { self.call3(args[0], args[1], args[2]) },
264 4 => unsafe { self.call4(args[0], args[1], args[2], args[3]) },
265 5 => unsafe { self.call5(args[0], args[1], args[2], args[3], args[4]) },
266 6 => unsafe { self.call6(args[0], args[1], args[2], args[3], args[4], args[5]) },
267 _ => {
268 unsafe { self.call6(args[0], args[1], args[2], args[3], args[4], args[5]) }
271 }
272 }
273 }
274}
275
276#[cfg(target_arch = "x86")]
278impl DirectSyscall {
279 #[inline(never)]
284 pub unsafe fn call(&self, args: &[usize]) -> i32 {
285 let status: i32;
287 let args_ptr = args.as_ptr();
288
289 unsafe {
291 asm!(
292 "mov eax, {ssn:e}",
293 "mov edx, {args}",
294 "int 0x2e",
295 ssn = in(reg) self.ssn as u32,
296 args = in(reg) args_ptr,
297 out("eax") status,
298 options(nostack)
299 );
300 }
301 status
302 }
303
304 pub unsafe fn call0(&self) -> i32 {
305 unsafe { self.call(&[]) }
306 }
307
308 pub unsafe fn call1(&self, arg1: usize) -> i32 {
309 unsafe { self.call(&[arg1]) }
310 }
311
312 pub unsafe fn call2(&self, arg1: usize, arg2: usize) -> i32 {
313 unsafe { self.call(&[arg1, arg2]) }
314 }
315
316 pub unsafe fn call3(&self, arg1: usize, arg2: usize, arg3: usize) -> i32 {
317 unsafe { self.call(&[arg1, arg2, arg3]) }
318 }
319
320 pub unsafe fn call4(&self, arg1: usize, arg2: usize, arg3: usize, arg4: usize) -> i32 {
321 unsafe { self.call(&[arg1, arg2, arg3, arg4]) }
322 }
323
324 pub unsafe fn call5(
325 &self,
326 arg1: usize,
327 arg2: usize,
328 arg3: usize,
329 arg4: usize,
330 arg5: usize,
331 ) -> i32 {
332 unsafe { self.call(&[arg1, arg2, arg3, arg4, arg5]) }
333 }
334
335 pub unsafe fn call6(
336 &self,
337 arg1: usize,
338 arg2: usize,
339 arg3: usize,
340 arg4: usize,
341 arg5: usize,
342 arg6: usize,
343 ) -> i32 {
344 unsafe { self.call(&[arg1, arg2, arg3, arg4, arg5, arg6]) }
345 }
346
347 pub unsafe fn call_many(&self, args: &[usize]) -> i32 {
348 unsafe { self.call(args) }
349 }
350}
351
352#[cfg(test)]
353mod tests {
354 use super::*;
355
356 #[test]
357 fn test_direct_syscall_ntclose() {
358 let table = SyscallTable::enumerate().expect("should enumerate");
359 let syscall = DirectSyscall::from_table(&table, "NtClose").expect("should find NtClose");
360
361 let status = unsafe { syscall.call1(0xDEADBEEF) };
364
365 assert_eq!(
367 status, 0xC0000008_u32 as i32,
368 "should return STATUS_INVALID_HANDLE"
369 );
370 }
371}