libafl_qemu: add jmp instrumentation

This commit is contained in:
Alwin Berger 2022-12-19 13:12:37 +01:00
parent b3416fe0c5
commit b678f9f18b
2 changed files with 323 additions and 0 deletions

View File

@ -395,6 +395,15 @@ extern "C" {
data: *const (), data: *const (),
); );
fn libafl_qemu_gdb_reply(buf: *const u8, len: usize); fn libafl_qemu_gdb_reply(buf: *const u8, len: usize);
// void libafl_add_jmp_hook(uint64_t (*gen)(target_ulong src, target_ulong dst, uint64_t data),
// void (*exec)(target_ulong src, target_ulong dst, uint64_t id, uint64_t data),
// uint64_t data);
fn libafl_add_jmp_hook(
gen: Option<extern "C" fn(GuestAddr, GuestAddr, u64) -> u64>,
exec: Option<extern "C" fn(GuestAddr, GuestAddr, u64, u64)>,
data: u64,
);
} }
#[cfg(emulation_mode = "usermode")] #[cfg(emulation_mode = "usermode")]
@ -1506,6 +1515,15 @@ impl Emulator {
} }
} }
pub fn add_jmp_hooks(
&self,
gen: Option<extern "C" fn(GuestAddr, GuestAddr, u64) -> u64>,
exec: Option<extern "C" fn(GuestAddr, GuestAddr, u64, u64)>,
data: u64,
) {
unsafe { libafl_add_jmp_hook(gen, exec, data) }
}
#[cfg(emulation_mode = "systemmode")] #[cfg(emulation_mode = "systemmode")]
pub fn save_snapshot(&self, name: &str, sync: bool) { pub fn save_snapshot(&self, name: &str, sync: bool) {
let s = CString::new(name).expect("Invalid snapshot name"); let s = CString::new(name).expect("Invalid snapshot name");

View File

@ -335,6 +335,278 @@ where
} }
} }
#[cfg(emulation_mode = "usermode")]
static mut SYSCALL_HOOKS: Vec<Hook> = vec![];
#[cfg(emulation_mode = "usermode")]
extern "C" fn syscall_hooks_wrapper<QT, S>(
sys_num: i32,
a0: u64,
a1: u64,
a2: u64,
a3: u64,
a4: u64,
a5: u64,
a6: u64,
a7: u64,
) -> SyscallHookResult
where
S: UsesInput,
QT: QemuHelperTuple<S>,
{
unsafe {
let hooks = get_qemu_hooks::<QT, S>();
let mut res = SyscallHookResult::new(None);
for hook in &SYSCALL_HOOKS {
match hook {
Hook::Function(ptr) => {
#[allow(clippy::type_complexity)]
let func: fn(
&mut QemuHooks<'_, QT, S>,
Option<&mut S>,
i32,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
) -> SyscallHookResult = transmute(*ptr);
let r = func(
hooks,
inprocess_get_state::<S>(),
sys_num,
a0,
a1,
a2,
a3,
a4,
a5,
a6,
a7,
);
if r.skip_syscall {
res.skip_syscall = true;
res.retval = r.retval;
}
}
Hook::Closure(ptr) => {
#[allow(clippy::type_complexity)]
let mut func: Box<
dyn FnMut(
&mut QemuHooks<'_, QT, S>,
Option<&mut S>,
i32,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
) -> SyscallHookResult,
> = transmute(*ptr);
let r = func(
hooks,
inprocess_get_state::<S>(),
sys_num,
a0,
a1,
a2,
a3,
a4,
a5,
a6,
a7,
);
// Forget the closure so that drop is not called on captured variables.
core::mem::forget(func);
if r.skip_syscall {
res.skip_syscall = true;
res.retval = r.retval;
}
}
_ => (),
}
}
res
}
}
#[cfg(emulation_mode = "usermode")]
static mut SYSCALL_POST_HOOKS: Vec<Hook> = vec![];
#[cfg(emulation_mode = "usermode")]
extern "C" fn syscall_after_hooks_wrapper<QT, S>(
result: u64,
sys_num: i32,
a0: u64,
a1: u64,
a2: u64,
a3: u64,
a4: u64,
a5: u64,
a6: u64,
a7: u64,
) -> u64
where
S: UsesInput,
QT: QemuHelperTuple<S>,
{
unsafe {
let hooks = get_qemu_hooks::<QT, S>();
let mut res = result;
for hook in &SYSCALL_POST_HOOKS {
match hook {
Hook::Function(ptr) => {
#[allow(clippy::type_complexity)]
let func: fn(
&mut QemuHooks<'_, QT, S>,
Option<&mut S>,
u64,
i32,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
) -> u64 = transmute(*ptr);
res = func(
hooks,
inprocess_get_state::<S>(),
res,
sys_num,
a0,
a1,
a2,
a3,
a4,
a5,
a6,
a7,
);
}
Hook::Closure(ptr) => {
#[allow(clippy::type_complexity)]
let mut func: Box<
dyn FnMut(
&mut QemuHooks<'_, QT, S>,
Option<&mut S>,
u64,
i32,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
) -> u64,
> = transmute(*ptr);
res = func(
hooks,
inprocess_get_state::<S>(),
res,
sys_num,
a0,
a1,
a2,
a3,
a4,
a5,
a6,
a7,
);
// Forget the closure so that drop is not called on captured variables.
core::mem::forget(func);
}
_ => (),
}
}
res
}
}
static mut JMP_HOOKS: Vec<(Hook, Hook)> = vec![];
extern "C" fn gen_jmp_hook_wrapper<QT, S>(src: GuestAddr, dst: GuestAddr, index: u64) -> u64
where
S: UsesInput,
QT: QemuHelperTuple<S>,
{
unsafe {
let hooks = get_qemu_hooks::<QT, S>();
let (gen, _) = &mut JMP_HOOKS[index as usize];
match gen {
Hook::Function(ptr) => {
let func: fn(
&mut QemuHooks<'_, QT, S>,
Option<&mut S>,
GuestAddr,
GuestAddr,
) -> Option<u64> = transmute(*ptr);
(func)(hooks, inprocess_get_state::<S>(), src, dst).map_or(SKIP_EXEC_HOOK, |id| id)
}
Hook::Closure(ptr) => {
let func: &mut Box<
dyn FnMut(
&mut QemuHooks<'_, QT, S>,
Option<&mut S>,
GuestAddr,
GuestAddr,
) -> Option<u64>,
> = transmute(ptr);
(func)(hooks, inprocess_get_state::<S>(), src, dst).map_or(SKIP_EXEC_HOOK, |id| id)
}
_ => 0,
}
}
}
extern "C" fn exec_jmp_hook_wrapper<QT, S>(src: GuestAddr, dst: GuestAddr, id: u64, index: u64)
where
S: UsesInput,
QT: QemuHelperTuple<S>,
{
unsafe {
let hooks = get_qemu_hooks::<QT, S>();
let (_, exec) = &mut JMP_HOOKS[index as usize];
match exec {
Hook::Function(ptr) => {
let func: fn(
&mut QemuHooks<'_, QT, S>,
Option<&mut S>,
GuestAddr,
GuestAddr,
u64,
) = transmute(*ptr);
(func)(hooks, inprocess_get_state::<S>(), src, dst, id);
}
Hook::Closure(ptr) => {
let func: &mut Box<
dyn FnMut(
&mut QemuHooks<'_, QT, S>,
Option<&mut S>,
GuestAddr,
GuestAddr,
u64,
),
> = transmute(ptr);
(func)(hooks, inprocess_get_state::<S>(), src, dst, id);
}
_ => (),
}
}
}
static mut HOOKS_IS_INITIALIZED: bool = false; static mut HOOKS_IS_INITIALIZED: bool = false;
static mut FIRST_EXEC: bool = true; static mut FIRST_EXEC: bool = true;
@ -1208,4 +1480,37 @@ where
CRASH_HOOKS.push(HookRepr::Closure(transmute(hook))); CRASH_HOOKS.push(HookRepr::Closure(transmute(hook)));
} }
} }
pub fn jmps(
&self,
generation_hook: Option<
fn(&mut Self, Option<&mut S>, src: GuestAddr, dest: GuestAddr) -> Option<u64>,
>,
execution_hook: Option<fn(&mut Self, Option<&mut S>, src: GuestAddr, dest: GuestAddr, id: u64)>,
) {
unsafe {
let index = JMP_HOOKS.len();
self.emulator.add_jmp_hooks(
if generation_hook.is_none() {
None
} else {
Some(gen_jmp_hook_wrapper::<QT, S>)
},
if execution_hook.is_none() {
None
} else {
Some(exec_jmp_hook_wrapper::<QT, S>)
},
index as u64,
);
JMP_HOOKS.push((
generation_hook.map_or(Hook::Empty, |hook| {
Hook::Function(hook as *const libc::c_void)
}),
execution_hook.map_or(Hook::Empty, |hook| {
Hook::Function(hook as *const libc::c_void)
}),
));
}
}
} }