Skip to main content

spoof/
types.rs

1#![allow(non_snake_case, non_camel_case_types)]
2
3use core::ffi::c_void;
4
5/// Indicates the presence of an exception handler in the function.
6pub const UNW_FLAG_EHANDLER: u8 = 0x1;
7
8/// Indicates chained unwind information is present.
9pub const UNW_FLAG_CHAININFO: u8 = 0x4;
10
11/// IMAGE_DIRECTORY_ENTRY_EXCEPTION — index into the optional header's
12/// DataDirectory[] where the .pdata RUNTIME_FUNCTION table lives.
13pub const IMAGE_DIRECTORY_ENTRY_EXCEPTION: usize = 3;
14
15/// IMAGE_RUNTIME_FUNCTION_ENTRY — one entry of the .pdata exception table.
16#[repr(C)]
17#[derive(Clone, Copy, Default)]
18pub struct ImageRuntimeFunction {
19    pub BeginAddress: u32,
20    pub EndAddress: u32,
21    pub UnwindData: u32,
22}
23
24/// IMAGE_DATA_DIRECTORY — VA + size pair used by the optional header.
25#[repr(C)]
26#[derive(Clone, Copy, Default)]
27pub struct ImageDataDirectory {
28    pub VirtualAddress: u32,
29    pub Size: u32,
30}
31
32/// Config struct read by the asm trampoline. Layout must match
33/// `src/asm/{msvc,gnu}/synthetic.asm` and `desync.asm` byte-for-byte.
34/// Field reordering will corrupt argument passing — do not touch.
35#[repr(C)]
36#[derive(Debug)]
37pub struct Config {
38    pub rtl_user_addr: *const c_void,
39    pub rtl_user_thread_size: u64,
40    pub base_thread_addr: *const c_void,
41    pub base_thread_size: u64,
42    pub first_frame_fp: *const c_void,
43    pub second_frame_fp: *const c_void,
44    pub jmp_rbx_gadget: *const c_void,
45    pub add_rsp_gadget: *const c_void,
46    pub first_frame_size: u64,
47    pub second_frame_size: u64,
48    pub jmp_rbx_frame_size: u64,
49    pub add_rsp_frame_size: u64,
50    pub rbp_stack_offset: u64,
51    pub spoof_function: *const c_void,
52    pub return_address: *const c_void,
53    pub is_syscall: u32,
54    pub ssn: u32,
55    pub number_args: u64,
56    pub arg01: *const c_void,
57    pub arg02: *const c_void,
58    pub arg03: *const c_void,
59    pub arg04: *const c_void,
60    pub arg05: *const c_void,
61    pub arg06: *const c_void,
62    pub arg07: *const c_void,
63    pub arg08: *const c_void,
64    pub arg09: *const c_void,
65    pub arg10: *const c_void,
66    pub arg11: *const c_void,
67}
68
69impl Default for Config {
70    fn default() -> Self {
71        unsafe { core::mem::zeroed() }
72    }
73}
74
75/// x86_64 general-purpose register encoding used by unwind codes.
76#[derive(Debug, Clone, Copy)]
77#[repr(u8)]
78#[allow(dead_code)]
79pub enum Registers {
80    Rax = 0,
81    Rcx,
82    Rdx,
83    Rbx,
84    Rsp,
85    Rbp,
86    Rsi,
87    Rdi,
88    R8,
89    R9,
90    R10,
91    R11,
92    R12,
93    R13,
94    R14,
95    R15,
96}
97
98impl PartialEq<usize> for Registers {
99    fn eq(&self, other: &usize) -> bool {
100        *self as usize == *other
101    }
102}
103
104/// One unwind code slot. The full union: either a packed 16-bit slot
105/// (CodeOffset/UnwindOp/OpInfo) or a raw FrameOffset for following slots.
106#[repr(C)]
107pub union UnwindCode {
108    pub FrameOffset: u16,
109    pub Anonymous: UnwindCode0,
110}
111
112bitfield::bitfield! {
113    #[repr(C)]
114    #[derive(Clone, Copy, Debug)]
115    pub struct UnwindCode0(u16);
116    pub u8, CodeOffset, SetCodeOffset: 7, 0;
117    pub u8, UnwindOp, SetUnwindOp: 11, 8;
118    pub u8, OpInfo, SetOpInfo: 15, 12;
119}
120
121/// Optional Exception/ChainInfo slot in UNWIND_INFO.
122#[repr(C)]
123pub union UnwindInfo0 {
124    pub ExceptionHandler: u32,
125    pub FunctionEntry: u32,
126}
127
128bitfield::bitfield! {
129    #[repr(C)]
130    #[derive(Clone, Copy, Debug)]
131    pub struct UnwindVersionFlags(u8);
132    pub u8, Version, SetVersion: 2, 0;
133    pub u8, Flags, SetFlags: 7, 3;
134}
135
136bitfield::bitfield! {
137    #[repr(C)]
138    #[derive(Clone, Copy, Debug)]
139    pub struct UnwindFrameInfo(u8);
140    pub u8, FrameRegister, SetFrameRegister: 3, 0;
141    pub u8, FrameOffset, SetFrameOffset: 7, 4;
142}
143
144/// UNWIND_INFO header at the front of every function's unwind record.
145#[repr(C)]
146pub struct UnwindInfo {
147    pub VersionFlags: UnwindVersionFlags,
148    pub SizeOfProlog: u8,
149    pub CountOfCodes: u8,
150    pub FrameInfo: UnwindFrameInfo,
151    pub UnwindCode: UnwindCode,
152    pub Anonymous: UnwindInfo0,
153    pub ExceptionData: u32,
154}
155
156/// x64 unwind operation codes (subset we care about for stack-size
157/// calculation). MS docs: learn.microsoft.com/cpp/build/exception-handling-x64
158#[repr(u8)]
159#[allow(dead_code)]
160pub enum UnwindOpCode {
161    UWOP_PUSH_NONVOL = 0,
162    UWOP_ALLOC_LARGE = 1,
163    UWOP_ALLOC_SMALL = 2,
164    UWOP_SET_FPREG = 3,
165    UWOP_SAVE_NONVOL = 4,
166    UWOP_SAVE_NONVOL_BIG = 5,
167    UWOP_EPILOG = 6,
168    UWOP_SPARE_CODE = 7,
169    UWOP_SAVE_XMM128 = 8,
170    UWOP_SAVE_XMM128BIG = 9,
171    UWOP_PUSH_MACH_FRAME = 10,
172}
173
174impl TryFrom<u8> for UnwindOpCode {
175    type Error = ();
176
177    fn try_from(value: u8) -> Result<Self, Self::Error> {
178        match value {
179            0..=10 => Ok(unsafe { core::mem::transmute::<u8, UnwindOpCode>(value) }),
180            _ => Err(()),
181        }
182    }
183}