From b678f9f18b6d8983aec886713a4e11159559893f Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 19 Dec 2022 13:12:37 +0100 Subject: [PATCH] libafl_qemu: add jmp instrumentation --- libafl_qemu/src/emu.rs | 18 +++ libafl_qemu/src/hooks.rs | 305 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 323 insertions(+) diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index da3b11facc..445cd43a30 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -395,6 +395,15 @@ extern "C" { data: *const (), ); 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 u64>, + exec: Option, + data: u64, + ); } #[cfg(emulation_mode = "usermode")] @@ -1506,6 +1515,15 @@ impl Emulator { } } + pub fn add_jmp_hooks( + &self, + gen: Option u64>, + exec: Option, + data: u64, + ) { + unsafe { libafl_add_jmp_hook(gen, exec, data) } + } + #[cfg(emulation_mode = "systemmode")] pub fn save_snapshot(&self, name: &str, sync: bool) { let s = CString::new(name).expect("Invalid snapshot name"); diff --git a/libafl_qemu/src/hooks.rs b/libafl_qemu/src/hooks.rs index 12ee0ebd0f..b4181cabe4 100644 --- a/libafl_qemu/src/hooks.rs +++ b/libafl_qemu/src/hooks.rs @@ -335,6 +335,278 @@ where } } +#[cfg(emulation_mode = "usermode")] +static mut SYSCALL_HOOKS: Vec = vec![]; +#[cfg(emulation_mode = "usermode")] +extern "C" fn syscall_hooks_wrapper( + 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, +{ + unsafe { + let hooks = get_qemu_hooks::(); + 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::(), + 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::(), + 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 = vec![]; +#[cfg(emulation_mode = "usermode")] +extern "C" fn syscall_after_hooks_wrapper( + 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, +{ + unsafe { + let hooks = get_qemu_hooks::(); + 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::(), + 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::(), + 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(src: GuestAddr, dst: GuestAddr, index: u64) -> u64 +where + S: UsesInput, + QT: QemuHelperTuple, +{ + unsafe { + let hooks = get_qemu_hooks::(); + 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 = transmute(*ptr); + (func)(hooks, inprocess_get_state::(), 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, + > = transmute(ptr); + (func)(hooks, inprocess_get_state::(), src, dst).map_or(SKIP_EXEC_HOOK, |id| id) + } + _ => 0, + } + } +} + +extern "C" fn exec_jmp_hook_wrapper(src: GuestAddr, dst: GuestAddr, id: u64, index: u64) +where + S: UsesInput, + QT: QemuHelperTuple, +{ + unsafe { + let hooks = get_qemu_hooks::(); + 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::(), 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::(), src, dst, id); + } + _ => (), + } + } +} + static mut HOOKS_IS_INITIALIZED: bool = false; static mut FIRST_EXEC: bool = true; @@ -1208,4 +1480,37 @@ where 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, + >, + execution_hook: Option, 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::) + }, + if execution_hook.is_none() { + None + } else { + Some(exec_jmp_hook_wrapper::) + }, + 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) + }), + )); + } + } }