1use core::ffi::c_void;
6
7use crate::types::{
8 ImageRuntimeFunction, Registers, UnwindCode, UnwindInfo, UnwindOpCode::*,
9 UnwindOpCode, UNW_FLAG_CHAININFO, UNW_FLAG_EHANDLER,
10};
11
12pub unsafe fn rbp_offset(
16 module: *mut c_void,
17 runtime: &ImageRuntimeFunction,
18) -> Option<(u32, u32)> {
19 let unwind_info = (module as usize + runtime.UnwindData as usize) as *mut UnwindInfo;
20 let unwind_code = (unwind_info as *mut u8).add(4) as *mut UnwindCode;
21 let flag = (*unwind_info).VersionFlags.Flags();
22
23 let mut i = 0usize;
24 let mut total_stack = 0u32;
25 let mut rbp_pushed = false;
26 let mut stack_offset = 0u32;
27
28 while i < (*unwind_info).CountOfCodes as usize {
29 let unwind_code = unwind_code.add(i);
30 let op_info = (*unwind_code).Anonymous.OpInfo() as usize;
31 let unwind_op = (*unwind_code).Anonymous.UnwindOp();
32
33 match UnwindOpCode::try_from(unwind_op) {
34 Ok(UWOP_PUSH_NONVOL) => {
35 if Registers::Rsp == op_info {
36 return None;
37 }
38 if Registers::Rbp == op_info {
39 if rbp_pushed {
40 return None;
41 }
42 rbp_pushed = true;
43 stack_offset = total_stack;
44 }
45 total_stack += 8;
46 i += 1;
47 }
48 Ok(UWOP_ALLOC_LARGE) => {
49 if (*unwind_code).Anonymous.OpInfo() == 0 {
50 let frame_offset = ((*unwind_code.add(1)).FrameOffset as i32) * 8;
51 total_stack += frame_offset as u32;
52 i += 2;
53 } else {
54 let frame_offset = *(unwind_code.add(1) as *mut i32);
55 total_stack += frame_offset as u32;
56 i += 3;
57 }
58 }
59 Ok(UWOP_ALLOC_SMALL) => {
60 total_stack += ((op_info + 1) * 8) as u32;
61 i += 1;
62 }
63 Ok(UWOP_SAVE_NONVOL) => {
64 if Registers::Rsp == op_info {
65 return None;
66 }
67 if Registers::Rbp == op_info {
68 if rbp_pushed {
69 return None;
70 }
71 let offset = (*unwind_code.add(1)).FrameOffset * 8;
72 stack_offset = total_stack + offset as u32;
73 rbp_pushed = true;
74 }
75 i += 2;
76 }
77 Ok(UWOP_SAVE_NONVOL_BIG) => {
78 if Registers::Rsp == op_info {
79 return None;
80 }
81 if Registers::Rbp == op_info {
82 if rbp_pushed {
83 return None;
84 }
85 let offset = *(unwind_code.add(1) as *mut u32);
86 stack_offset = total_stack + offset;
87 rbp_pushed = true;
88 }
89 i += 3;
90 }
91 Ok(UWOP_SET_FPREG) => return None,
92 Ok(UWOP_SAVE_XMM128) => i += 2,
93 Ok(UWOP_SAVE_XMM128BIG) => i += 3,
94 Ok(UWOP_EPILOG) | Ok(UWOP_SPARE_CODE) => i += 1,
95 Ok(UWOP_PUSH_MACH_FRAME) => {
96 total_stack += if op_info == 0 { 0x40 } else { 0x48 };
97 i += 1;
98 }
99 _ => return None,
100 }
101 }
102
103 if (flag & UNW_FLAG_CHAININFO) != 0 {
104 let count = (*unwind_info).CountOfCodes as usize;
105 let index = if count & 1 == 1 { count + 1 } else { count };
106 let runtime = unwind_code.add(index) as *const ImageRuntimeFunction;
107 if let Some((_, child_total)) = rbp_offset(module, &*runtime) {
108 total_stack += child_total;
109 } else {
110 return None;
111 }
112 }
113
114 Some((stack_offset, total_stack))
115}
116
117pub unsafe fn stack_frame(
120 module: *mut c_void,
121 runtime: &ImageRuntimeFunction,
122) -> Option<(bool, u32)> {
123 let unwind_info = (module as usize + runtime.UnwindData as usize) as *mut UnwindInfo;
124 let unwind_code = (unwind_info as *mut u8).add(4) as *mut UnwindCode;
125 let flag = (*unwind_info).VersionFlags.Flags();
126
127 let mut i = 0usize;
128 let mut set_fpreg_hit = false;
129 let mut total_stack = 0i32;
130
131 while i < (*unwind_info).CountOfCodes as usize {
132 let unwind_code = unwind_code.add(i);
133 let op_info = (*unwind_code).Anonymous.OpInfo() as usize;
134 let unwind_op = (*unwind_code).Anonymous.UnwindOp();
135
136 match UnwindOpCode::try_from(unwind_op) {
137 Ok(UWOP_PUSH_NONVOL) => {
138 if Registers::Rsp == op_info && !set_fpreg_hit {
139 return None;
140 }
141 total_stack += 8;
142 i += 1;
143 }
144 Ok(UWOP_ALLOC_SMALL) => {
145 total_stack += ((op_info + 1) * 8) as i32;
146 i += 1;
147 }
148 Ok(UWOP_ALLOC_LARGE) => {
149 if (*unwind_code).Anonymous.OpInfo() == 0 {
150 let frame_offset = ((*unwind_code.add(1)).FrameOffset as i32) * 8;
151 total_stack += frame_offset;
152 i += 2;
153 } else {
154 let frame_offset = *(unwind_code.add(1) as *mut i32);
155 total_stack += frame_offset;
156 i += 3;
157 }
158 }
159 Ok(UWOP_SAVE_NONVOL) => {
160 if Registers::Rsp == op_info || Registers::Rbp == op_info {
161 return None;
162 }
163 i += 2;
164 }
165 Ok(UWOP_SAVE_NONVOL_BIG) => {
166 if Registers::Rsp == op_info || Registers::Rbp == op_info {
167 return None;
168 }
169 i += 3;
170 }
171 Ok(UWOP_SAVE_XMM128) => i += 2,
172 Ok(UWOP_SAVE_XMM128BIG) => i += 3,
173 Ok(UWOP_SET_FPREG) => {
174 if (flag & UNW_FLAG_EHANDLER) != 0 && (flag & UNW_FLAG_CHAININFO) != 0 {
175 return None;
176 }
177 if (*unwind_info).FrameInfo.FrameRegister() != Registers::Rbp as u8 {
178 return None;
179 }
180 set_fpreg_hit = true;
181 let offset = ((*unwind_info).FrameInfo.FrameOffset() as i32) << 4;
182 total_stack -= offset;
183 i += 1;
184 }
185 Ok(UWOP_EPILOG) | Ok(UWOP_SPARE_CODE) => i += 1,
186 Ok(UWOP_PUSH_MACH_FRAME) => {
187 total_stack += if op_info == 0 { 0x40 } else { 0x48 };
188 i += 1;
189 }
190 _ => return None,
191 }
192 }
193
194 if (flag & UNW_FLAG_CHAININFO) != 0 {
195 let count = (*unwind_info).CountOfCodes as usize;
196 let index = if count & 1 == 1 { count + 1 } else { count };
197 let runtime = unwind_code.add(index) as *const ImageRuntimeFunction;
198 if let Some((chained_fpreg_hit, chained_stack)) = stack_frame(module, &*runtime) {
199 total_stack += chained_stack as i32;
200 set_fpreg_hit |= chained_fpreg_hit;
201 } else {
202 return None;
203 }
204 }
205
206 Some((set_fpreg_hit, total_stack as u32))
207}
208
209pub unsafe fn ignoring_set_fpreg(
213 module: *mut c_void,
214 runtime: &ImageRuntimeFunction,
215) -> Option<u32> {
216 let unwind_info = (module as usize + runtime.UnwindData as usize) as *mut UnwindInfo;
217 let unwind_code = (unwind_info as *mut u8).add(4) as *mut UnwindCode;
218 let flag = (*unwind_info).VersionFlags.Flags();
219
220 let mut i = 0usize;
221 let mut total_stack = 0u32;
222
223 while i < (*unwind_info).CountOfCodes as usize {
224 let unwind_code = unwind_code.add(i);
225 let op_info = (*unwind_code).Anonymous.OpInfo() as usize;
226 let unwind_op = (*unwind_code).Anonymous.UnwindOp();
227
228 match UnwindOpCode::try_from(unwind_op) {
229 Ok(UWOP_PUSH_NONVOL) => {
230 if Registers::Rsp == op_info {
231 return None;
232 }
233 total_stack += 8;
234 i += 1;
235 }
236 Ok(UWOP_ALLOC_SMALL) => {
237 total_stack += ((op_info + 1) * 8) as u32;
238 i += 1;
239 }
240 Ok(UWOP_ALLOC_LARGE) => {
241 if (*unwind_code).Anonymous.OpInfo() == 0 {
242 let frame_offset = ((*unwind_code.add(1)).FrameOffset as i32) * 8;
243 total_stack += frame_offset as u32;
244 i += 2;
245 } else {
246 let frame_offset = *(unwind_code.add(1) as *mut i32);
247 total_stack += frame_offset as u32;
248 i += 3;
249 }
250 }
251 Ok(UWOP_SAVE_NONVOL) => {
252 if Registers::Rsp == op_info {
253 return None;
254 }
255 i += 2;
256 }
257 Ok(UWOP_SAVE_NONVOL_BIG) => {
258 if Registers::Rsp == op_info {
259 return None;
260 }
261 i += 3;
262 }
263 Ok(UWOP_SAVE_XMM128) => i += 2,
264 Ok(UWOP_SAVE_XMM128BIG) => i += 3,
265 Ok(UWOP_SET_FPREG) => i += 1,
266 Ok(UWOP_EPILOG) | Ok(UWOP_SPARE_CODE) => i += 1,
267 Ok(UWOP_PUSH_MACH_FRAME) => {
268 total_stack += if op_info == 0 { 0x40 } else { 0x48 };
269 i += 1;
270 }
271 _ => return None,
272 }
273 }
274
275 if (flag & UNW_FLAG_CHAININFO) != 0 {
276 let count = (*unwind_info).CountOfCodes as usize;
277 let index = if count & 1 == 1 { count + 1 } else { count };
278 let runtime = unwind_code.add(index) as *const ImageRuntimeFunction;
279 if let Some(chained_stack) = ignoring_set_fpreg(module, &*runtime) {
280 total_stack += chained_stack;
281 } else {
282 return None;
283 }
284 }
285
286 Some(total_stack)
287}