1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
use crate::{
console::Console,
emu::Emu,
exception::{self, HandlerKind},
exception_type::ExceptionType,
};
impl Emu {
pub fn veh(&self) -> u64 {
self.threads[self.current_thread_id].veh
}
pub fn set_veh(&mut self, value: u64) {
self.threads[self.current_thread_id].veh = value;
}
pub fn uef(&self) -> u64 {
self.threads[self.current_thread_id].uef
}
pub fn set_uef(&mut self, value: u64) {
self.threads[self.current_thread_id].uef = value;
}
pub fn eh_ctx(&self) -> u32 {
self.threads[self.current_thread_id].eh_ctx
}
pub fn set_eh_ctx(&mut self, value: u32) {
self.threads[self.current_thread_id].eh_ctx = value;
}
pub fn seh(&self) -> u64 {
self.threads[self.current_thread_id].seh
}
pub fn set_seh(&mut self, value: u64) {
self.threads[self.current_thread_id].seh = value;
}
/// Trigger an exception.
/// If it has to be handled initiate contex tand jump to the programmed error routine.
/// Support SEH, VEH and UEF
pub fn exception(&mut self, ex_type: ExceptionType) {
/*
If the handler return the search constant will jump to next handler in this order:
VEH -> SEH1 -> SEH2 -> ... -> SEHn -> UEF -> terminate process
unhandling:
VEH:
- the api RemoveVectoredExceptionHandler removes the handler
SEH:
- automatically when is triggered. SEH point to next SEH.
UEF:
- SetUnhandledExceptionFilter
Responses:
VEH:
- EXCEPTION_CONTINUE_EXECUTION (continue to eip/rip which could be modified)
- EXCEPTION_CONTINUE_SEARCH (jump to next handler SEH -> UEF -> end proces)
SEH:
- EXCEPTION_CONTINUE_EXECUTION (continue and not jump to except)
- EXCEPTION_CONTINUE_SEARCH (jump to next handler SEH -> UEF -> end process)
- EXCEPTION_EXECUTE_HANDLER (jump to except)
UEF:
- EXCEPTION_CONTINUE_EXECUTION (continue to eip/rip which could be modified)
- EXCEPTION_CONTINUE_SEARCH (end process)
64bits SEH:
- is not a stack chain
- search RUNTIME_FUNCTION entry in the .pdata table using BeginAddress ≤ RIP < EndAddress
- in that entry there is the RVA of UNWIND_INFO struct on the .xdata
- at .pdata, 12 bytes of runtime entries:
typedef struct _RUNTIME_FUNCTION {
DWORD BeginAddress;
DWORD EndAddress;
DWORD UnwindInfo; // RVA to UNWIND_INFO at .xdata
} RUNTIME_FUNCTION, *PRUNTIME_FUNCTION;
- unwind info in the .xdata:
typedef struct _UNWIND_INFO {
UBYTE Version : 3; // always 1
UBYTE Flags : 5; // 0, EHANDLER, UHANDLER, etc.
UBYTE SizeOfProlog;
UBYTE CountOfCodes; // Nº of UNWIND_CODE
UBYTE FrameRegister : 4; // (ie. RBP=5)
UBYTE FrameOffset : 4; // frame scale
UNWIND_CODE UnwindCode[]; // descriptors
// opcional:
// DWORD ExceptionHandler; // RVA to handler if Flags indicate it
// DWORD ExceptionData[]; // extra data for handler
} UNWIND_INFO, *PUNWIND_INFO;
*/
self.fault_count += 1;
let addr: u64;
let next: u64;
// hook
let handle_exception: bool = if let Some(mut hook_fn) = self.hooks.hook_on_exception.take() {
let rip = self.regs().rip;
let result = hook_fn(self, rip, ex_type);
self.hooks.hook_on_exception = Some(hook_fn);
result
} else {
true
};
// No handled exceptions
if self.seh() == 0 && self.veh() == 0 && self.uef() == 0 {
log::trace!(
"exception without any SEH handler nor vector configured. pos = {} rip = {:x}",
self.pos,
self.regs().rip
);
return;
}
// hook replaced handler
if !handle_exception {
log::trace!("cancelled exception handling from hook.");
return;
}
// VEH
if self.veh() > 0 {
addr = self.veh();
exception::enter_for_handler(self, ex_type, HandlerKind::Veh);
if self.cfg.is_64bits {
self.set_rip(addr, false);
} else {
self.set_eip(addr, false);
}
// SEH
} else if self.seh() > 0 {
if self.cfg.is_64bits {
// 64bits seh
unimplemented!("check .pdata if exists");
} else {
// 32bits seh
next = match self.maps.read_dword(self.seh()) {
Some(value) => value.into(),
None => {
log::trace!("exception wihout correct SEH");
return;
}
};
addr = match self.maps.read_dword(self.seh() + 4) {
Some(value) => value.into(),
None => {
log::trace!("exception without correct SEH.");
return;
}
};
}
let con = Console::new();
if self.running_script {
self.set_seh(next);
exception::enter_for_handler(self, ex_type, HandlerKind::Seh);
if self.cfg.is_64bits {
self.set_rip(addr, false);
} else {
self.set_eip(addr, false);
}
return;
}
con.print("jump the exception pointer (y/n)?");
let cmd = con.cmd();
if cmd == "y" {
self.set_seh(next);
exception::enter_for_handler(self, ex_type, HandlerKind::Seh);
if self.cfg.is_64bits {
self.set_rip(addr, false);
} else {
self.set_eip(addr, false);
}
}
} else if self.uef() > 0 {
// UEF
addr = self.uef();
exception::enter_for_handler(self, ex_type, HandlerKind::Uef);
if self.cfg.is_64bits {
self.set_rip(addr, false);
} else {
self.set_eip(addr, false);
}
} else {
unreachable!();
}
}
}