libafl_qemu: add jmp instrumentation
This commit is contained in:
parent
b3416fe0c5
commit
b678f9f18b
@ -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");
|
||||||
|
@ -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)
|
||||||
|
}),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user