From 7147170240de408020f8430d34c390564dd4a8da Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Thu, 16 Jun 2022 11:09:07 +0200 Subject: [PATCH] New hooks for libafl_qemu (#673) * new block and edge hooks * Wrking new hooks * no Pin, just box * working call tracing * invalidate_block flag * working call stack tracking helper * callstack push * fixes * py * fixes * clippy * clippy * gdb api * kill introspection * fix * upd qemu * upd qemu --- fuzzers/fuzzbench_qemu/src/fuzzer.rs | 4 +- fuzzers/qemu_launcher/src/fuzzer.rs | 4 +- libafl/Cargo.toml | 2 +- libafl/src/lib.rs | 1 + libafl/src/monitors/tui/ui.rs | 81 +- libafl_qemu/Cargo.toml | 1 + libafl_qemu/build_linux.rs | 2 +- libafl_qemu/src/aarch64.rs | 5 + libafl_qemu/src/arm.rs | 5 + libafl_qemu/src/asan.rs | 142 +- libafl_qemu/src/calls.rs | 182 +++ libafl_qemu/src/cmplog.rs | 48 +- libafl_qemu/src/edges.rs | 93 +- libafl_qemu/src/emu.rs | 293 ++-- libafl_qemu/src/executor.rs | 61 +- libafl_qemu/src/helper.rs | 10 +- libafl_qemu/src/hooks.rs | 2001 ++++++++++++-------------- libafl_qemu/src/i386.rs | 9 + libafl_qemu/src/lib.rs | 8 +- libafl_qemu/src/snapshot.rs | 102 +- libafl_qemu/src/x86_64.rs | 10 + libafl_sugar/src/qemu.rs | 8 +- 22 files changed, 1547 insertions(+), 1525 deletions(-) create mode 100644 libafl_qemu/src/calls.rs diff --git a/fuzzers/fuzzbench_qemu/src/fuzzer.rs b/fuzzers/fuzzbench_qemu/src/fuzzer.rs index 0766ba2ec1..1dd5eae6e0 100644 --- a/fuzzers/fuzzbench_qemu/src/fuzzer.rs +++ b/fuzzers/fuzzbench_qemu/src/fuzzer.rs @@ -332,7 +332,7 @@ fn fuzz( ExitKind::Ok }; - let hooks = QemuHooks::new( + let mut hooks = QemuHooks::new( &emu, tuple_list!( QemuEdgeCoverageHelper::default(), @@ -343,7 +343,7 @@ fn fuzz( ); let executor = QemuExecutor::new( - hooks, + &mut hooks, &mut harness, tuple_list!(edges_observer, time_observer), &mut fuzzer, diff --git a/fuzzers/qemu_launcher/src/fuzzer.rs b/fuzzers/qemu_launcher/src/fuzzer.rs index ceb05dd755..60e19bf9cd 100644 --- a/fuzzers/qemu_launcher/src/fuzzer.rs +++ b/fuzzers/qemu_launcher/src/fuzzer.rs @@ -157,11 +157,11 @@ pub fn fuzz() { // A fuzzer with feedbacks and a corpus scheduler let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); - let hooks = QemuHooks::new(&emu, tuple_list!(QemuEdgeCoverageHelper::default(),)); + let mut hooks = QemuHooks::new(&emu, tuple_list!(QemuEdgeCoverageHelper::default())); // Create a QEMU in-process executor let executor = QemuExecutor::new( - hooks, + &mut hooks, &mut harness, tuple_list!(edges_observer, time_observer), &mut fuzzer, diff --git a/libafl/Cargo.toml b/libafl/Cargo.toml index 8e04c34142..fa4183ad9c 100644 --- a/libafl/Cargo.toml +++ b/libafl/Cargo.toml @@ -12,7 +12,7 @@ edition = "2021" categories = ["development-tools::testing", "emulators", "embedded", "os", "no-std"] [features] -default = ["std", "derive", "llmp_compression", "rand_trait", "fork", "introspection"] +default = ["std", "derive", "llmp_compression", "rand_trait", "fork"] std = ["serde_json", "serde_json/std", "hostname", "nix", "serde/std", "bincode", "wait-timeout", "regex", "byteorder", "once_cell", "uuid", "tui_monitor", "ctor", "backtrace", "num_cpus"] # print, env, launcher ... support derive = ["libafl_derive"] # provide derive(SerdeAny) macro. fork = [] # uses the fork() syscall to spawn children, instead of launching a new command, if supported by the OS (has no effect on Windows, no_std). diff --git a/libafl/src/lib.rs b/libafl/src/lib.rs index 3d1dfdd67a..c1afb5cc64 100644 --- a/libafl/src/lib.rs +++ b/libafl/src/lib.rs @@ -71,6 +71,7 @@ Welcome to `LibAFL` ) )] // Till they fix this buggy lint in clippy +#![allow(clippy::borrow_as_ptr)] #![allow(clippy::borrow_deref_ref)] #[cfg(feature = "std")] diff --git a/libafl/src/monitors/tui/ui.rs b/libafl/src/monitors/tui/ui.rs index 36e4bf47a4..6bc0daf580 100644 --- a/libafl/src/monitors/tui/ui.rs +++ b/libafl/src/monitors/tui/ui.rs @@ -132,12 +132,14 @@ impl TuiUI { let tabs = Tabs::new(titles) .block( Block::default() - .title(Span::styled( - "charts (`g` switch)", - Style::default() - .fg(Color::LightCyan) - .add_modifier(Modifier::BOLD), - )) + .title( + Span::styled( + "charts (`g` switch)", + Style::default() + .fg(Color::LightCyan) + .add_modifier(Modifier::BOLD), + ), + ) .borders(Borders::ALL), ) .highlight_style(Style::default().fg(Color::LightYellow)) @@ -275,12 +277,14 @@ impl TuiUI { let chart = Chart::new(datasets) .block( Block::default() - .title(Span::styled( - title, - Style::default() - .fg(Color::LightCyan) - .add_modifier(Modifier::BOLD), - )) + .title( + Span::styled( + title, + Style::default() + .fg(Color::LightCyan) + .add_modifier(Modifier::BOLD), + ), + ) .borders(Borders::ALL), ) .x_axis( @@ -357,12 +361,14 @@ impl TuiUI { let table = Table::new(items) .block( Block::default() - .title(Span::styled( - "generic", - Style::default() - .fg(Color::LightCyan) - .add_modifier(Modifier::BOLD), - )) + .title( + Span::styled( + "generic", + Style::default() + .fg(Color::LightCyan) + .add_modifier(Modifier::BOLD), + ), + ) .borders(Borders::ALL), ) .widths(&[Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]); @@ -468,18 +474,19 @@ impl TuiUI { }; } - let table = Table::new(items) - .block( - Block::default() - .title(Span::styled( - "introspection", - Style::default() - .fg(Color::LightCyan) - .add_modifier(Modifier::BOLD), - )) - .borders(Borders::ALL), - ) - .widths(&[Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]); + let table = + Table::new(items) + .block( + Block::default() + .title(Span::styled( + "introspection", + Style::default() + .fg(Color::LightCyan) + .add_modifier(Modifier::BOLD), + )) + .borders(Borders::ALL), + ) + .widths(&[Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]); f.render_widget(table, client_chunks[1]); } } @@ -496,12 +503,14 @@ impl TuiUI { .map(|msg| ListItem::new(Span::raw(msg))) .collect(); let logs = List::new(logs).block( - Block::default().borders(Borders::ALL).title(Span::styled( - "clients logs (`t` to show/hide)", - Style::default() - .fg(Color::LightCyan) - .add_modifier(Modifier::BOLD), - )), + Block::default().borders(Borders::ALL).title( + Span::styled( + "clients logs (`t` to show/hide)", + Style::default() + .fg(Color::LightCyan) + .add_modifier(Modifier::BOLD), + ), + ), ); f.render_widget(logs, area); } diff --git a/libafl_qemu/Cargo.toml b/libafl_qemu/Cargo.toml index 7773c35344..5599f05f37 100644 --- a/libafl_qemu/Cargo.toml +++ b/libafl_qemu/Cargo.toml @@ -38,6 +38,7 @@ strum_macros = "0.21" syscall-numbers = "2.0" bio = "0.39" thread_local = "1.1.3" +capstone = "0.10" #pyo3 = { version = "0.15", features = ["extension-module"], optional = true } pyo3 = { version = "0.15", optional = true } diff --git a/libafl_qemu/build_linux.rs b/libafl_qemu/build_linux.rs index 1747c78773..9f1f40dcb3 100644 --- a/libafl_qemu/build_linux.rs +++ b/libafl_qemu/build_linux.rs @@ -3,7 +3,7 @@ use which::which; const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge"; const QEMU_DIRNAME: &str = "qemu-libafl-bridge"; -const QEMU_REVISION: &str = "6a9a929222cbc8b10adfb048aa24f73486e0a886"; +const QEMU_REVISION: &str = "03e283c85800496b60fb757d68a7df2821fb7a90"; fn build_dep_check(tools: &[&str]) { for tool in tools { diff --git a/libafl_qemu/src/aarch64.rs b/libafl_qemu/src/aarch64.rs index 75cbbc8395..0963cdcc08 100644 --- a/libafl_qemu/src/aarch64.rs +++ b/libafl_qemu/src/aarch64.rs @@ -59,3 +59,8 @@ impl IntoPy for Regs { n.into_py(py) } } + +/// Return an ARM64 ArchCapstoneBuilder +pub fn capstone() -> capstone::arch::arm64::ArchCapstoneBuilder { + capstone::Capstone::new().arm() +} diff --git a/libafl_qemu/src/arm.rs b/libafl_qemu/src/arm.rs index 548c299bdd..5be3a9682f 100644 --- a/libafl_qemu/src/arm.rs +++ b/libafl_qemu/src/arm.rs @@ -47,3 +47,8 @@ impl IntoPy for Regs { n.into_py(py) } } + +/// Return an ARM ArchCapstoneBuilder +pub fn capstone() -> capstone::arch::arm::ArchCapstoneBuilder { + capstone::Capstone::new().arm() +} diff --git a/libafl_qemu/src/asan.rs b/libafl_qemu/src/asan.rs index dad45b4649..7c4b2f6e40 100644 --- a/libafl_qemu/src/asan.rs +++ b/libafl_qemu/src/asan.rs @@ -1,6 +1,6 @@ use libafl::{inputs::Input, state::HasMetadata}; use num_enum::{IntoPrimitive, TryFromPrimitive}; -use std::{env, fs, pin::Pin, ptr}; +use std::{env, fs, ptr}; use crate::{ emu::{Emulator, SyscallHookResult}, @@ -413,23 +413,27 @@ where { const HOOKS_DO_SIDE_EFFECTS: bool = false; - fn init_hooks<'a, QT>(&self, hooks: Pin<&QemuHooks<'a, I, QT, S>>) + fn init_hooks(&self, hooks: &QemuHooks<'_, I, QT, S>) where QT: QemuHelperTuple, { - //hooks.read_generation(gen_readwrite_asan::); - hooks.read8_execution(trace_read8_asan::); - hooks.read4_execution(trace_read4_asan::); - hooks.read2_execution(trace_read2_asan::); - hooks.read1_execution(trace_read1_asan::); - hooks.read_n_execution(trace_read_n_asan::); + hooks.reads( + Some(gen_readwrite_asan::), + Some(trace_read1_asan::), + Some(trace_read2_asan::), + Some(trace_read4_asan::), + Some(trace_read8_asan::), + Some(trace_read_n_asan::), + ); - //hooks.write_generation(gen_readwrite_asan::); - hooks.write8_execution(trace_write8_asan::); - hooks.write4_execution(trace_write4_asan::); - hooks.write2_execution(trace_write2_asan::); - hooks.write1_execution(trace_write1_asan::); - hooks.write_n_execution(trace_write_n_asan::); + hooks.writes( + Some(gen_readwrite_asan::), + Some(trace_write1_asan::), + Some(trace_write2_asan::), + Some(trace_write4_asan::), + Some(trace_write8_asan::), + Some(trace_write_n_asan::), + ); hooks.syscalls(qasan_fake_syscall::); } @@ -439,11 +443,8 @@ where } } -/* -// TODO add pc to generation hooks pub fn gen_readwrite_asan( - _emulator: &Emulator, - helpers: &mut QT, + hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, pc: u64, _size: usize, @@ -452,18 +453,16 @@ where I: Input, QT: QemuHelperTuple, { - let h = helpers.match_first_type_mut::().unwrap(); + let h = hooks.match_helper_mut::().unwrap(); if h.must_instrument(pc) { Some(pc) } else { None } } -*/ pub fn trace_read1_asan( - emulator: &Emulator, - helpers: &mut QT, + hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, _id: u64, addr: GuestAddr, @@ -471,13 +470,13 @@ pub fn trace_read1_asan( I: Input, QT: QemuHelperTuple, { - let h = helpers.match_first_type_mut::().unwrap(); - h.read_1(emulator, addr); + let emulator = hooks.emulator().clone(); + let h = hooks.match_helper_mut::().unwrap(); + h.read_1(&emulator, addr); } pub fn trace_read2_asan( - emulator: &Emulator, - helpers: &mut QT, + hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, _id: u64, addr: GuestAddr, @@ -485,13 +484,13 @@ pub fn trace_read2_asan( I: Input, QT: QemuHelperTuple, { - let h = helpers.match_first_type_mut::().unwrap(); - h.read_2(emulator, addr); + let emulator = hooks.emulator().clone(); + let h = hooks.match_helper_mut::().unwrap(); + h.read_2(&emulator, addr); } pub fn trace_read4_asan( - emulator: &Emulator, - helpers: &mut QT, + hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, _id: u64, addr: GuestAddr, @@ -499,13 +498,13 @@ pub fn trace_read4_asan( I: Input, QT: QemuHelperTuple, { - let h = helpers.match_first_type_mut::().unwrap(); - h.read_4(emulator, addr); + let emulator = hooks.emulator().clone(); + let h = hooks.match_helper_mut::().unwrap(); + h.read_4(&emulator, addr); } pub fn trace_read8_asan( - emulator: &Emulator, - helpers: &mut QT, + hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, _id: u64, addr: GuestAddr, @@ -513,13 +512,13 @@ pub fn trace_read8_asan( I: Input, QT: QemuHelperTuple, { - let h = helpers.match_first_type_mut::().unwrap(); - h.read_8(emulator, addr); + let emulator = hooks.emulator().clone(); + let h = hooks.match_helper_mut::().unwrap(); + h.read_8(&emulator, addr); } pub fn trace_read_n_asan( - emulator: &Emulator, - helpers: &mut QT, + hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, _id: u64, addr: GuestAddr, @@ -528,13 +527,13 @@ pub fn trace_read_n_asan( I: Input, QT: QemuHelperTuple, { - let h = helpers.match_first_type_mut::().unwrap(); - h.read_n(emulator, addr, size); + let emulator = hooks.emulator().clone(); + let h = hooks.match_helper_mut::().unwrap(); + h.read_n(&emulator, addr, size); } pub fn trace_write1_asan( - emulator: &Emulator, - helpers: &mut QT, + hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, _id: u64, addr: GuestAddr, @@ -542,13 +541,13 @@ pub fn trace_write1_asan( I: Input, QT: QemuHelperTuple, { - let h = helpers.match_first_type_mut::().unwrap(); - h.write_1(emulator, addr); + let emulator = hooks.emulator().clone(); + let h = hooks.match_helper_mut::().unwrap(); + h.write_1(&emulator, addr); } pub fn trace_write2_asan( - emulator: &Emulator, - helpers: &mut QT, + hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, _id: u64, addr: GuestAddr, @@ -556,13 +555,13 @@ pub fn trace_write2_asan( I: Input, QT: QemuHelperTuple, { - let h = helpers.match_first_type_mut::().unwrap(); - h.write_2(emulator, addr); + let emulator = hooks.emulator().clone(); + let h = hooks.match_helper_mut::().unwrap(); + h.write_2(&emulator, addr); } pub fn trace_write4_asan( - emulator: &Emulator, - helpers: &mut QT, + hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, _id: u64, addr: GuestAddr, @@ -570,13 +569,13 @@ pub fn trace_write4_asan( I: Input, QT: QemuHelperTuple, { - let h = helpers.match_first_type_mut::().unwrap(); - h.write_4(emulator, addr); + let emulator = hooks.emulator().clone(); + let h = hooks.match_helper_mut::().unwrap(); + h.write_4(&emulator, addr); } pub fn trace_write8_asan( - emulator: &Emulator, - helpers: &mut QT, + hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, _id: u64, addr: GuestAddr, @@ -584,13 +583,13 @@ pub fn trace_write8_asan( I: Input, QT: QemuHelperTuple, { - let h = helpers.match_first_type_mut::().unwrap(); - h.write_8(emulator, addr); + let emulator = hooks.emulator().clone(); + let h = hooks.match_helper_mut::().unwrap(); + h.write_8(&emulator, addr); } pub fn trace_write_n_asan( - emulator: &Emulator, - helpers: &mut QT, + hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, _id: u64, addr: GuestAddr, @@ -599,14 +598,14 @@ pub fn trace_write_n_asan( I: Input, QT: QemuHelperTuple, { - let h = helpers.match_first_type_mut::().unwrap(); - h.read_n(emulator, addr, size); + let emulator = hooks.emulator().clone(); + let h = hooks.match_helper_mut::().unwrap(); + h.read_n(&emulator, addr, size); } #[allow(clippy::too_many_arguments)] pub fn qasan_fake_syscall( - emulator: &Emulator, - helpers: &mut QT, + hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, sys_num: i32, a0: u64, @@ -623,39 +622,40 @@ where QT: QemuHelperTuple, { if sys_num == QASAN_FAKESYS_NR { - let h = helpers.match_first_type_mut::().unwrap(); + let emulator = hooks.emulator().clone(); + let h = hooks.match_helper_mut::().unwrap(); let mut r = 0; match QasanAction::try_from(a0).expect("Invalid QASan action number") { QasanAction::CheckLoad => { - h.read_n(emulator, a1 as GuestAddr, a2 as usize); + h.read_n(&emulator, a1 as GuestAddr, a2 as usize); } QasanAction::CheckStore => { - h.write_n(emulator, a1 as GuestAddr, a2 as usize); + h.write_n(&emulator, a1 as GuestAddr, a2 as usize); } QasanAction::Poison => { h.poison( - emulator, + &emulator, a1 as GuestAddr, a2 as usize, PoisonKind::try_from(a3 as u8).unwrap(), ); } QasanAction::UserPoison => { - h.poison(emulator, a1 as GuestAddr, a2 as usize, PoisonKind::User); + h.poison(&emulator, a1 as GuestAddr, a2 as usize, PoisonKind::User); } QasanAction::UnPoison => { - h.unpoison(emulator, a1 as GuestAddr, a2 as usize); + h.unpoison(&emulator, a1 as GuestAddr, a2 as usize); } QasanAction::IsPoison => { - if h.is_poisoned(emulator, a1 as GuestAddr, a2 as usize) { + if h.is_poisoned(&emulator, a1 as GuestAddr, a2 as usize) { r = 1; } } QasanAction::Alloc => { - h.alloc(emulator, a1, a2); + h.alloc(&emulator, a1, a2); } QasanAction::Dealloc => { - h.dealloc(emulator, a1); + h.dealloc(&emulator, a1); } QasanAction::Enable => { h.set_enabled(true); diff --git a/libafl_qemu/src/calls.rs b/libafl_qemu/src/calls.rs new file mode 100644 index 0000000000..56cf43cc70 --- /dev/null +++ b/libafl_qemu/src/calls.rs @@ -0,0 +1,182 @@ +use capstone::prelude::*; +use libafl::inputs::Input; + +use crate::{ + capstone, + helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, + hooks::QemuHooks, + Emulator, GuestAddr, Regs, +}; + +#[derive(Debug)] +pub struct QemuCallTracerHelper { + filter: QemuInstrumentationFilter, + cs: Capstone, + callstack: Vec, +} + +impl QemuCallTracerHelper { + #[must_use] + pub fn new(filter: QemuInstrumentationFilter) -> Self { + Self { + filter, + cs: capstone().detail(true).build().unwrap(), + callstack: vec![], + } + } + + #[must_use] + pub fn must_instrument(&self, addr: u64) -> bool { + self.filter.allowed(addr) + } + + #[must_use] + pub fn callstack(&self) -> &[GuestAddr] { + &self.callstack + } + + pub fn reset(&mut self) { + self.callstack.clear(); + } +} + +impl Default for QemuCallTracerHelper { + fn default() -> Self { + Self::new(QemuInstrumentationFilter::None) + } +} + +impl QemuHelper for QemuCallTracerHelper +where + I: Input, +{ + fn init_hooks<'a, QT>(&self, hooks: &QemuHooks<'a, I, QT, S>) + where + QT: QemuHelperTuple, + { + hooks.blocks(Some(gen_blocks_calls::), None); + } + + fn pre_exec(&mut self, _emulator: &Emulator, _input: &I) { + self.reset(); + } +} + +/*pub fn on_call(hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, pc: GuestAddr) +where + I: Input, + QT: QemuHelperTuple, +{ +}*/ + +pub fn on_ret(hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, _pc: GuestAddr) +where + I: Input, + QT: QemuHelperTuple, +{ + #[cfg(cpu_target = "x86_64")] + let ret_addr = { + let emu = hooks.emulator(); + let stack_ptr: GuestAddr = emu.read_reg(Regs::Rsp).unwrap(); + let mut ret_addr = [0; 8]; + unsafe { emu.read_mem(stack_ptr, &mut ret_addr) }; + GuestAddr::from_le_bytes(ret_addr) + }; + + #[cfg(cpu_target = "i386")] + let ret_addr = { + let emu = hooks.emulator(); + let stack_ptr: GuestAddr = emu.read_reg(Regs::Esp).unwrap(); + let mut ret_addr = [0; 4]; + unsafe { emu.read_mem(stack_ptr, &mut ret_addr) }; + GuestAddr::from_le_bytes(ret_addr) + }; + + #[cfg(any(cpu_target = "arm", cpu_target = "aarch64"))] + let ret_addr = { + let emu = hooks.emulator(); + let ret_addr: GuestAddr = emu.read_reg(Regs::Lr).unwrap(); + ret_addr + }; + + // eprintln!("RET @ 0x{:#x}", ret_addr); + + if let Some(h) = hooks + .helpers_mut() + .match_first_type_mut::() + { + while let Some(addr) = h.callstack.pop() { + if addr == ret_addr { + break; + } + } + } +} + +pub fn gen_blocks_calls( + hooks: &mut QemuHooks<'_, I, QT, S>, + _state: Option<&mut S>, + pc: GuestAddr, +) -> Option +where + I: Input, + QT: QemuHelperTuple, +{ + let emu = hooks.emulator(); + if let Some(h) = hooks.helpers().match_first_type::() { + if !h.must_instrument(pc) { + return None; + } + + let mut code = unsafe { std::slice::from_raw_parts(emu.g2h(pc), 512) }; + let mut iaddr = pc; + + 'disasm: while let Ok(insns) = h.cs.disasm_count(code, iaddr, 1) { + if insns.is_empty() { + break; + } + let insn = insns.first().unwrap(); + let insn_detail: InsnDetail = h.cs.insn_detail(insn).unwrap(); + for detail in insn_detail.groups() { + match u32::from(detail.0) { + capstone::InsnGroupType::CS_GRP_CALL => { + // hooks.instruction_closure(insn.address() as GuestAddr, on_call, false); + let call_len = insn.bytes().len() as GuestAddr; + let call_cb = move |hooks: &mut QemuHooks<'_, I, QT, S>, _, pc| { + // eprintln!("CALL @ 0x{:#x}", pc + call_len); + if let Some(h) = hooks + .helpers_mut() + .match_first_type_mut::() + { + h.callstack.push(pc + call_len); + } + }; + unsafe { + hooks.instruction_closure( + insn.address() as GuestAddr, + Box::new(call_cb), + false, + ); + } + } + capstone::InsnGroupType::CS_GRP_RET => { + hooks.instruction(insn.address() as GuestAddr, on_ret, false); + break 'disasm; + } + capstone::InsnGroupType::CS_GRP_INVALID + | capstone::InsnGroupType::CS_GRP_JUMP + | capstone::InsnGroupType::CS_GRP_IRET + | capstone::InsnGroupType::CS_GRP_PRIVILEGE => { + break 'disasm; + } + _ => {} + } + } + + iaddr += insn.bytes().len() as u64; + code = unsafe { std::slice::from_raw_parts(emu.g2h(iaddr), 512) }; + } + } + + None +} diff --git a/libafl_qemu/src/cmplog.rs b/libafl_qemu/src/cmplog.rs index 2304448f0c..70bd094029 100644 --- a/libafl_qemu/src/cmplog.rs +++ b/libafl_qemu/src/cmplog.rs @@ -1,4 +1,3 @@ -use core::pin::Pin; use hashbrown::HashMap; use libafl::{inputs::Input, state::HasMetadata}; pub use libafl_targets::{ @@ -8,7 +7,6 @@ pub use libafl_targets::{ use serde::{Deserialize, Serialize}; use crate::{ - emu::Emulator, helper::{hash_me, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, hooks::QemuHooks, }; @@ -59,15 +57,17 @@ where I: Input, S: HasMetadata, { - fn init_hooks<'a, QT>(&self, hooks: Pin<&QemuHooks<'a, I, QT, S>>) + fn init_hooks(&self, hooks: &QemuHooks<'_, I, QT, S>) where QT: QemuHelperTuple, { - hooks.cmp_generation(gen_unique_cmp_ids::); - hooks.emulator().set_exec_cmp8_hook(trace_cmp8_cmplog); - hooks.emulator().set_exec_cmp4_hook(trace_cmp4_cmplog); - hooks.emulator().set_exec_cmp2_hook(trace_cmp2_cmplog); - hooks.emulator().set_exec_cmp1_hook(trace_cmp1_cmplog); + hooks.cmps_raw( + Some(gen_unique_cmp_ids::), + Some(trace_cmp1_cmplog), + Some(trace_cmp2_cmplog), + Some(trace_cmp4_cmplog), + Some(trace_cmp8_cmplog), + ); } } @@ -101,21 +101,22 @@ where { const HOOKS_DO_SIDE_EFFECTS: bool = false; - fn init_hooks<'a, QT>(&self, hooks: Pin<&QemuHooks<'a, I, QT, S>>) + fn init_hooks(&self, hooks: &QemuHooks<'_, I, QT, S>) where QT: QemuHelperTuple, { - hooks.cmp_generation(gen_hashed_cmp_ids::); - hooks.emulator().set_exec_cmp8_hook(trace_cmp8_cmplog); - hooks.emulator().set_exec_cmp4_hook(trace_cmp4_cmplog); - hooks.emulator().set_exec_cmp2_hook(trace_cmp2_cmplog); - hooks.emulator().set_exec_cmp1_hook(trace_cmp1_cmplog); + hooks.cmps_raw( + Some(gen_hashed_cmp_ids::), + Some(trace_cmp1_cmplog), + Some(trace_cmp2_cmplog), + Some(trace_cmp4_cmplog), + Some(trace_cmp8_cmplog), + ); } } pub fn gen_unique_cmp_ids( - _emulator: &Emulator, - helpers: &mut QT, + hooks: &mut QemuHooks<'_, I, QT, S>, state: Option<&mut S>, pc: u64, _size: usize, @@ -125,7 +126,7 @@ where I: Input, QT: QemuHelperTuple, { - if let Some(h) = helpers.match_first_type::() { + if let Some(h) = hooks.match_helper_mut::() { if !h.must_instrument(pc) { return None; } @@ -147,8 +148,7 @@ where } pub fn gen_hashed_cmp_ids( - _emulator: &Emulator, - helpers: &mut QT, + hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, pc: u64, _size: usize, @@ -158,7 +158,7 @@ where I: Input, QT: QemuHelperTuple, { - if let Some(h) = helpers.match_first_type::() { + if let Some(h) = hooks.match_helper_mut::() { if !h.must_instrument(pc) { return None; } @@ -166,25 +166,25 @@ where Some(hash_me(pc) & (CMPLOG_MAP_W as u64 - 1)) } -pub extern "C" fn trace_cmp1_cmplog(id: u64, v0: u8, v1: u8) { +pub extern "C" fn trace_cmp1_cmplog(id: u64, v0: u8, v1: u8, _data: u64) { unsafe { __libafl_targets_cmplog_instructions(id as usize, 1, u64::from(v0), u64::from(v1)); } } -pub extern "C" fn trace_cmp2_cmplog(id: u64, v0: u16, v1: u16) { +pub extern "C" fn trace_cmp2_cmplog(id: u64, v0: u16, v1: u16, _data: u64) { unsafe { __libafl_targets_cmplog_instructions(id as usize, 2, u64::from(v0), u64::from(v1)); } } -pub extern "C" fn trace_cmp4_cmplog(id: u64, v0: u32, v1: u32) { +pub extern "C" fn trace_cmp4_cmplog(id: u64, v0: u32, v1: u32, _data: u64) { unsafe { __libafl_targets_cmplog_instructions(id as usize, 4, u64::from(v0), u64::from(v1)); } } -pub extern "C" fn trace_cmp8_cmplog(id: u64, v0: u64, v1: u64) { +pub extern "C" fn trace_cmp8_cmplog(id: u64, v0: u64, v1: u64, _data: u64) { unsafe { __libafl_targets_cmplog_instructions(id as usize, 8, v0, v1); } diff --git a/libafl_qemu/src/edges.rs b/libafl_qemu/src/edges.rs index 230dfd0efb..98a8832394 100644 --- a/libafl_qemu/src/edges.rs +++ b/libafl_qemu/src/edges.rs @@ -4,17 +4,17 @@ pub use libafl_targets::{ edges_max_num, EDGES_MAP, EDGES_MAP_PTR, EDGES_MAP_PTR_SIZE, EDGES_MAP_SIZE, MAX_EDGES_NUM, }; use serde::{Deserialize, Serialize}; -use std::{cell::UnsafeCell, cmp::max, pin::Pin}; +use std::{cell::UnsafeCell, cmp::max}; use crate::{ - emu::Emulator, + emu::GuestAddr, helper::{hash_me, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, hooks::QemuHooks, }; #[derive(Debug, Default, Serialize, Deserialize)] pub struct QemuEdgesMapMetadata { - pub map: HashMap<(u64, u64), u64>, + pub map: HashMap<(GuestAddr, GuestAddr), u64>, pub current_id: u64, } @@ -70,15 +70,20 @@ where I: Input, S: HasMetadata, { - fn init_hooks<'a, QT>(&self, hooks: Pin<&QemuHooks<'a, I, QT, S>>) + fn init_hooks(&self, hooks: &QemuHooks<'_, I, QT, S>) where QT: QemuHelperTuple, { - hooks.edge_generation(gen_unique_edge_ids::); if self.use_hitcounts { - hooks.emulator().set_exec_edge_hook(trace_edge_hitcount); + hooks.edges_raw( + Some(gen_unique_edge_ids::), + Some(trace_edge_hitcount), + ); } else { - hooks.emulator().set_exec_edge_hook(trace_edge_single); + hooks.edges_raw( + Some(gen_unique_edge_ids::), + Some(trace_edge_single), + ); } } } @@ -127,15 +132,20 @@ where { const HOOKS_DO_SIDE_EFFECTS: bool = false; - fn init_hooks<'a, QT>(&self, hooks: Pin<&QemuHooks<'a, I, QT, S>>) + fn init_hooks(&self, hooks: &QemuHooks<'_, I, QT, S>) where QT: QemuHelperTuple, { - hooks.edge_generation(gen_hashed_edge_ids::); if self.use_hitcounts { - hooks.emulator().set_exec_edge_hook(trace_edge_hitcount_ptr); + hooks.edges_raw( + Some(gen_hashed_edge_ids::), + Some(trace_edge_hitcount_ptr), + ); } else { - hooks.emulator().set_exec_edge_hook(trace_edge_single_ptr); + hooks.edges_raw( + Some(gen_hashed_edge_ids::), + Some(trace_edge_single_ptr), + ); } } } @@ -143,18 +153,17 @@ where thread_local!(static PREV_LOC : UnsafeCell = UnsafeCell::new(0)); pub fn gen_unique_edge_ids( - _emulator: &Emulator, - helpers: &mut QT, + hooks: &mut QemuHooks<'_, I, QT, S>, state: Option<&mut S>, - src: u64, - dest: u64, + src: GuestAddr, + dest: GuestAddr, ) -> Option where S: HasMetadata, I: Input, QT: QemuHelperTuple, { - if let Some(h) = helpers.match_first_type::() { + if let Some(h) = hooks.helpers().match_first_type::() { if !h.must_instrument(src) && !h.must_instrument(dest) { return None; } @@ -189,45 +198,47 @@ where } } -pub extern "C" fn trace_edge_hitcount(id: u64) { +pub extern "C" fn trace_edge_hitcount(id: u64, _data: u64) { unsafe { EDGES_MAP[id as usize] = EDGES_MAP[id as usize].wrapping_add(1); } } -pub extern "C" fn trace_edge_single(id: u64) { +pub extern "C" fn trace_edge_single(id: u64, _data: u64) { unsafe { EDGES_MAP[id as usize] = 1; } } pub fn gen_hashed_edge_ids( - _emulator: &Emulator, - helpers: &mut QT, + hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, - src: u64, - dest: u64, + src: GuestAddr, + dest: GuestAddr, ) -> Option where I: Input, QT: QemuHelperTuple, { - if let Some(h) = helpers.match_first_type::() { + if let Some(h) = hooks + .helpers() + .match_first_type::() + { if !h.must_instrument(src) && !h.must_instrument(dest) { return None; } } - Some((hash_me(src) ^ hash_me(dest)) & (unsafe { EDGES_MAP_PTR_SIZE } as u64 - 1)) + Some((hash_me(src as u64) ^ hash_me(dest as u64)) & (unsafe { EDGES_MAP_PTR_SIZE } as u64 - 1)) } -pub extern "C" fn trace_edge_hitcount_ptr(id: u64) { +pub extern "C" fn trace_edge_hitcount_ptr(id: u64, _data: u64) { unsafe { let ptr = EDGES_MAP_PTR.add(id as usize); *ptr = (*ptr).wrapping_add(1); } } -pub extern "C" fn trace_edge_single_ptr(id: u64) { +pub extern "C" fn trace_edge_single_ptr(id: u64, _data: u64) { unsafe { let ptr = EDGES_MAP_PTR.add(id as usize); *ptr = 1; @@ -235,24 +246,30 @@ pub extern "C" fn trace_edge_single_ptr(id: u64) { } pub fn gen_addr_block_ids( - _emulator: &Emulator, - _helpers: &mut QT, + _hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, - pc: u64, -) -> Option { - Some(pc) + pc: GuestAddr, +) -> Option +where + I: Input, + QT: QemuHelperTuple, +{ + Some(pc as u64) } pub fn gen_hashed_block_ids( - _emulator: &Emulator, - _helpers: &mut QT, + _hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, - pc: u64, -) -> Option { - Some(hash_me(pc)) + pc: GuestAddr, +) -> Option +where + I: Input, + QT: QemuHelperTuple, +{ + Some(hash_me(pc as u64)) } -pub extern "C" fn trace_block_transition_hitcount(id: u64) { +pub extern "C" fn trace_block_transition_hitcount(id: u64, _data: u64) { unsafe { PREV_LOC.with(|prev_loc| { let x = ((*prev_loc.get() ^ id) as usize) & (EDGES_MAP_PTR_SIZE - 1); @@ -263,7 +280,7 @@ pub extern "C" fn trace_block_transition_hitcount(id: u64) { } } -pub extern "C" fn trace_block_transition_single(id: u64) { +pub extern "C" fn trace_block_transition_single(id: u64, _data: u64) { unsafe { PREV_LOC.with(|prev_loc| { let x = ((*prev_loc.get() ^ id) as usize) & (EDGES_MAP_PTR_SIZE - 1); diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index 2756acd812..614200fef5 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -189,8 +189,14 @@ extern "C" { fn libafl_qemu_set_breakpoint(addr: u64) -> i32; fn libafl_qemu_remove_breakpoint(addr: u64) -> i32; fn libafl_flush_jit(); - fn libafl_qemu_set_hook(addr: u64, callback: extern "C" fn(u64), val: u64) -> i32; - fn libafl_qemu_remove_hook(addr: u64) -> i32; + fn libafl_qemu_set_hook( + addr: GuestAddr, + callback: extern "C" fn(GuestAddr, u64), + data: u64, + invalidate_block: i32, + ) -> usize; + // fn libafl_qemu_remove_hook(num: usize, invalidate_block: i32) -> i32; + fn libafl_qemu_remove_hooks_at(addr: GuestAddr, invalidate_block: i32) -> usize; fn libafl_qemu_run() -> i32; fn libafl_load_addr() -> u64; fn libafl_get_brk() -> u64; @@ -217,30 +223,68 @@ extern "C" { static guest_base: usize; static mut mmap_next_start: GuestAddr; - static mut libafl_exec_edge_hook: unsafe extern "C" fn(u64); - static mut libafl_gen_edge_hook: unsafe extern "C" fn(u64, u64) -> u64; - static mut libafl_exec_block_hook: unsafe extern "C" fn(u64); - static mut libafl_gen_block_hook: unsafe extern "C" fn(u64) -> u64; + // void libafl_add_edge_hook(uint64_t (*gen)(target_ulong src, target_ulong dst), void (*exec)(uint64_t id)); + fn libafl_add_edge_hook( + gen: Option u64>, + exec: Option, + data: u64, + ); - static mut libafl_exec_read_hook1: unsafe extern "C" fn(u64, u64); - static mut libafl_exec_read_hook2: unsafe extern "C" fn(u64, u64); - static mut libafl_exec_read_hook4: unsafe extern "C" fn(u64, u64); - static mut libafl_exec_read_hook8: unsafe extern "C" fn(u64, u64); - static mut libafl_exec_read_hookN: unsafe extern "C" fn(u64, u64, u32); - static mut libafl_gen_read_hook: unsafe extern "C" fn(u32) -> u64; + // void libafl_add_block_hook(uint64_t (*gen)(target_ulong pc), void (*exec)(uint64_t id)); + fn libafl_add_block_hook( + gen: Option u64>, + exec: Option, + data: u64, + ); - static mut libafl_exec_write_hook1: unsafe extern "C" fn(u64, u64); - static mut libafl_exec_write_hook2: unsafe extern "C" fn(u64, u64); - static mut libafl_exec_write_hook4: unsafe extern "C" fn(u64, u64); - static mut libafl_exec_write_hook8: unsafe extern "C" fn(u64, u64); - static mut libafl_exec_write_hookN: unsafe extern "C" fn(u64, u64, u32); - static mut libafl_gen_write_hook: unsafe extern "C" fn(u32) -> u64; + // void libafl_add_read_hook(uint64_t (*gen)(target_ulong pc, size_t size, uint64_t data), + // void (*exec1)(uint64_t id, target_ulong addr, uint64_t data), + // void (*exec2)(uint64_t id, target_ulong addr, uint64_t data), + // void (*exec4)(uint64_t id, target_ulong addr, uint64_t data), + // void (*exec8)(uint64_t id, target_ulong addr, uint64_t data), + // void (*exec_n)(uint64_t id, target_ulong addr, size_t size, uint64_t data), + // uint64_t data); + fn libafl_add_read_hook( + gen: Option u64>, + exec1: Option, + exec2: Option, + exec4: Option, + exec8: Option, + exec_n: Option, + data: u64, + ); - static mut libafl_exec_cmp_hook1: unsafe extern "C" fn(u64, u8, u8); - static mut libafl_exec_cmp_hook2: unsafe extern "C" fn(u64, u16, u16); - static mut libafl_exec_cmp_hook4: unsafe extern "C" fn(u64, u32, u32); - static mut libafl_exec_cmp_hook8: unsafe extern "C" fn(u64, u64, u64); - static mut libafl_gen_cmp_hook: unsafe extern "C" fn(u64, u32) -> u64; + // void libafl_add_write_hook(uint64_t (*gen)(target_ulong pc, size_t size, uint64_t data), + // void (*exec1)(uint64_t id, target_ulong addr, uint64_t data), + // void (*exec2)(uint64_t id, target_ulong addr, uint64_t data), + // void (*exec4)(uint64_t id, target_ulong addr, uint64_t data), + // void (*exec8)(uint64_t id, target_ulong addr, uint64_t data), + // void (*exec_n)(uint64_t id, target_ulong addr, size_t size, uint64_t data), + // uint64_t data); + fn libafl_add_write_hook( + gen: Option u64>, + exec1: Option, + exec2: Option, + exec4: Option, + exec8: Option, + exec_n: Option, + data: u64, + ); + + // void libafl_add_cmp_hook(uint64_t (*gen)(target_ulong pc, size_t size, uint64_t data), + // void (*exec1)(uint64_t id, uint8_t v0, uint8_t v1, uint64_t data), + // void (*exec2)(uint64_t id, uint16_t v0, uint16_t v1, uint64_t data), + // void (*exec4)(uint64_t id, uint32_t v0, uint32_t v1, uint64_t data), + // void (*exec8)(uint64_t id, uint64_t v0, uint64_t v1, uint64_t data), + // uint64_t data); + fn libafl_add_cmp_hook( + gen: Option u64>, + exec1: Option, + exec2: Option, + exec4: Option, + exec8: Option, + data: u64, + ); static mut libafl_on_thread_hook: unsafe extern "C" fn(u32); @@ -323,10 +367,10 @@ static mut GDB_COMMANDS: Vec = vec![]; extern "C" fn gdb_cmd(buf: *const u8, len: usize, data: *const ()) -> i32 { unsafe { - let closure = - &mut *(data as *mut std::boxed::Box std::ops::FnMut(&'r str) -> bool>); + let closure = &mut *(data as *mut Box FnMut(&Emulator, &'r str) -> bool>); let cmd = std::str::from_utf8_unchecked(std::slice::from_raw_parts(buf, len)); - if closure(cmd) { + let emu = Emulator::new_empty(); + if closure(&emu, cmd) { 1 } else { 0 @@ -336,7 +380,7 @@ extern "C" fn gdb_cmd(buf: *const u8, len: usize, data: *const ()) -> i32 { static mut EMULATOR_IS_INITIALIZED: bool = false; -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct Emulator { _private: (), } @@ -453,16 +497,19 @@ impl Emulator { } } - pub fn set_hook(&self, addr: GuestAddr, callback: extern "C" fn(u64), val: u64) { - unsafe { - libafl_qemu_set_hook(addr.into(), callback, val); - } + pub fn set_hook( + &self, + addr: GuestAddr, + callback: extern "C" fn(GuestAddr, u64), + data: u64, + invalidate_block: bool, + ) -> usize { + unsafe { libafl_qemu_set_hook(addr.into(), callback, data, i32::from(invalidate_block)) } } - pub fn remove_hook(&self, addr: GuestAddr) { - unsafe { - libafl_qemu_remove_hook(addr.into()); - } + #[must_use] + pub fn remove_hook(&self, addr: GuestAddr, invalidate_block: bool) -> usize { + unsafe { libafl_qemu_remove_hooks_at(addr.into(), i32::from(invalidate_block)) } } /// This function will run the emulator until the next breakpoint, or until finish. @@ -577,133 +624,60 @@ impl Emulator { } } - // TODO add has_X_hook() and panic when setting a hook for the second time - - pub fn set_exec_edge_hook(&self, hook: extern "C" fn(id: u64)) { - unsafe { - libafl_exec_edge_hook = hook; - } + pub fn add_edge_hooks( + &self, + gen: Option u64>, + exec: Option, + data: u64, + ) { + unsafe { libafl_add_edge_hook(gen, exec, data) } } - pub fn set_gen_edge_hook(&self, hook: extern "C" fn(src: u64, dest: u64) -> u64) { - unsafe { - libafl_gen_edge_hook = hook; - } + pub fn add_block_hooks( + &self, + gen: Option u64>, + exec: Option, + data: u64, + ) { + unsafe { libafl_add_block_hook(gen, exec, data) } } - pub fn set_exec_block_hook(&self, hook: extern "C" fn(pc: u64)) { - unsafe { - libafl_exec_block_hook = hook; - } + pub fn add_read_hooks( + &self, + gen: Option u64>, + exec1: Option, + exec2: Option, + exec4: Option, + exec8: Option, + exec_n: Option, + data: u64, + ) { + unsafe { libafl_add_read_hook(gen, exec1, exec2, exec4, exec8, exec_n, data) } } - pub fn set_gen_block_hook(&self, hook: extern "C" fn(pc: u64) -> u64) { - unsafe { - libafl_gen_block_hook = hook; - } + pub fn add_write_hooks( + &self, + gen: Option u64>, + exec1: Option, + exec2: Option, + exec4: Option, + exec8: Option, + exec_n: Option, + data: u64, + ) { + unsafe { libafl_add_write_hook(gen, exec1, exec2, exec4, exec8, exec_n, data) } } - pub fn set_exec_read1_hook(&self, hook: extern "C" fn(id: u64, addr: u64)) { - unsafe { - libafl_exec_read_hook1 = hook; - } - } - - pub fn set_exec_read2_hook(&self, hook: extern "C" fn(id: u64, addr: u64)) { - unsafe { - libafl_exec_read_hook2 = hook; - } - } - - pub fn set_exec_read4_hook(&self, hook: extern "C" fn(id: u64, addr: u64)) { - unsafe { - libafl_exec_read_hook4 = hook; - } - } - - pub fn set_exec_read8_hook(&self, hook: extern "C" fn(id: u64, addr: u64)) { - unsafe { - libafl_exec_read_hook8 = hook; - } - } - - pub fn set_exec_read_n_hook(&self, hook: extern "C" fn(id: u64, addr: u64, size: u32)) { - unsafe { - libafl_exec_read_hookN = hook; - } - } - - pub fn set_gen_read_hook(&self, hook: extern "C" fn(size: u32) -> u64) { - unsafe { - libafl_gen_read_hook = hook; - } - } - - pub fn set_exec_write1_hook(&self, hook: extern "C" fn(id: u64, addr: u64)) { - unsafe { - libafl_exec_write_hook1 = hook; - } - } - - pub fn set_exec_write2_hook(&self, hook: extern "C" fn(id: u64, addr: u64)) { - unsafe { - libafl_exec_write_hook2 = hook; - } - } - - pub fn set_exec_write4_hook(&self, hook: extern "C" fn(id: u64, addr: u64)) { - unsafe { - libafl_exec_write_hook4 = hook; - } - } - - pub fn set_exec_write8_hook(&self, hook: extern "C" fn(id: u64, addr: u64)) { - unsafe { - libafl_exec_write_hook8 = hook; - } - } - - pub fn set_exec_write_n_hook(&self, hook: extern "C" fn(id: u64, addr: u64, size: u32)) { - unsafe { - libafl_exec_write_hookN = hook; - } - } - - // TODO add pc arg - pub fn set_gen_write_hook(&self, hook: extern "C" fn(size: u32) -> u64) { - unsafe { - libafl_gen_write_hook = hook; - } - } - - pub fn set_exec_cmp1_hook(&self, hook: extern "C" fn(id: u64, v0: u8, v1: u8)) { - unsafe { - libafl_exec_cmp_hook1 = hook; - } - } - - pub fn set_exec_cmp2_hook(&self, hook: extern "C" fn(id: u64, v0: u16, v1: u16)) { - unsafe { - libafl_exec_cmp_hook2 = hook; - } - } - - pub fn set_exec_cmp4_hook(&self, hook: extern "C" fn(id: u64, v0: u32, v1: u32)) { - unsafe { - libafl_exec_cmp_hook4 = hook; - } - } - - pub fn set_exec_cmp8_hook(&self, hook: extern "C" fn(id: u64, v0: u64, v1: u64)) { - unsafe { - libafl_exec_cmp_hook8 = hook; - } - } - - pub fn set_gen_cmp_hook(&self, hook: extern "C" fn(pc: u64, size: u32) -> u64) { - unsafe { - libafl_gen_cmp_hook = hook; - } + pub fn add_cmp_hooks( + &self, + gen: Option u64>, + exec1: Option, + exec2: Option, + exec4: Option, + exec8: Option, + data: u64, + ) { + unsafe { libafl_add_cmp_hook(gen, exec1, exec2, exec4, exec8, data) } } pub fn set_on_thread_hook(&self, hook: extern "C" fn(tid: u32)) { @@ -730,7 +704,7 @@ impl Emulator { } } - pub fn add_gdb_cmd(&self, callback: Box bool>) { + pub fn add_gdb_cmd(&self, callback: Box bool>) { unsafe { GDB_COMMANDS.push(core::mem::transmute(callback)); libafl_qemu_add_gdb_cmd( @@ -791,7 +765,7 @@ pub mod pybind { ) } - extern "C" fn py_generic_hook_wrapper(idx: u64) { + extern "C" fn py_generic_hook_wrapper(_pc: GuestAddr, idx: u64) { let obj = unsafe { &PY_GENERIC_HOOKS[idx as usize].1 }; Python::with_gil(|py| { obj.call0(py).expect("Error in the hook"); @@ -918,15 +892,16 @@ pub mod pybind { unsafe { let idx = PY_GENERIC_HOOKS.len(); PY_GENERIC_HOOKS.push((addr, hook)); - self.emu.set_hook(addr, py_generic_hook_wrapper, idx as u64); + self.emu + .set_hook(addr, py_generic_hook_wrapper, idx as u64, true); } } - fn remove_hook(&self, addr: GuestAddr) { + fn remove_hook(&self, addr: GuestAddr) -> usize { unsafe { PY_GENERIC_HOOKS.retain(|(a, _)| *a != addr); } - self.emu.remove_hook(addr); + self.emu.remove_hook(addr, true) } } } diff --git a/libafl_qemu/src/executor.rs b/libafl_qemu/src/executor.rs index 3f18cd7fab..cf0472b468 100644 --- a/libafl_qemu/src/executor.rs +++ b/libafl_qemu/src/executor.rs @@ -1,8 +1,5 @@ //! A `QEMU`-based executor for binary-only instrumentation in `LibAFL` -use core::{ - fmt::{self, Debug, Formatter}, - pin::Pin, -}; +use core::fmt::{self, Debug, Formatter}; use libafl::{ bolts::shmem::ShMemProvider, @@ -26,7 +23,7 @@ where OT: ObserversTuple, QT: QemuHelperTuple, { - hooks: Pin>>, + hooks: &'a mut QemuHooks<'a, I, QT, S>, inner: InProcessExecutor<'a, H, I, OT, S>, } @@ -53,7 +50,7 @@ where QT: QemuHelperTuple, { pub fn new( - hooks: Pin>>, + hooks: &'a mut QemuHooks<'a, I, QT, S>, harness_fn: &'a mut H, observers: OT, fuzzer: &mut Z, @@ -80,12 +77,12 @@ where &mut self.inner } - pub fn hooks(&self) -> &Pin>> { - &self.hooks + pub fn hooks(&self) -> &QemuHooks<'a, I, QT, S> { + self.hooks } - pub fn hooks_mut(&mut self) -> &mut Pin>> { - &mut self.hooks + pub fn hooks_mut(&mut self) -> &mut QemuHooks<'a, I, QT, S> { + self.hooks } pub fn emulator(&self) -> &Emulator { @@ -108,21 +105,9 @@ where input: &I, ) -> Result { let emu = Emulator::new_empty(); - unsafe { - self.hooks - .as_mut() - .get_unchecked_mut() - .helpers_mut() - .pre_exec_all(&emu, input); - } + self.hooks.helpers_mut().pre_exec_all(&emu, input); let r = self.inner.run_target(fuzzer, state, mgr, input); - unsafe { - self.hooks - .as_mut() - .get_unchecked_mut() - .helpers_mut() - .post_exec_all(&emu, input); - } + self.hooks.helpers_mut().post_exec_all(&emu, input); r } } @@ -153,7 +138,7 @@ where QT: QemuHelperTuple, SP: ShMemProvider, { - hooks: Pin>>, + hooks: &'a mut QemuHooks<'a, I, QT, S>, inner: InProcessForkExecutor<'a, H, I, OT, S, SP>, } @@ -182,7 +167,7 @@ where SP: ShMemProvider, { pub fn new( - hooks: Pin>>, + hooks: &'a mut QemuHooks<'a, I, QT, S>, harness_fn: &'a mut H, observers: OT, fuzzer: &mut Z, @@ -219,12 +204,12 @@ where &mut self.inner } - pub fn hooks(&self) -> &Pin>> { - &self.hooks + pub fn hooks(&self) -> &QemuHooks<'a, I, QT, S> { + self.hooks } - pub fn hooks_mut(&mut self) -> &mut Pin>> { - &mut self.hooks + pub fn hooks_mut(&mut self) -> &mut QemuHooks<'a, I, QT, S> { + self.hooks } pub fn emulator(&self) -> &Emulator { @@ -249,21 +234,9 @@ where input: &I, ) -> Result { let emu = Emulator::new_empty(); - unsafe { - self.hooks - .as_mut() - .get_unchecked_mut() - .helpers_mut() - .pre_exec_all(&emu, input); - } + self.hooks.helpers_mut().pre_exec_all(&emu, input); let r = self.inner.run_target(fuzzer, state, mgr, input); - unsafe { - self.hooks - .as_mut() - .get_unchecked_mut() - .helpers_mut() - .post_exec_all(&emu, input); - } + self.hooks.helpers_mut().post_exec_all(&emu, input); r } } diff --git a/libafl_qemu/src/helper.rs b/libafl_qemu/src/helper.rs index cb71b473e2..6d743e477b 100644 --- a/libafl_qemu/src/helper.rs +++ b/libafl_qemu/src/helper.rs @@ -1,4 +1,4 @@ -use core::{fmt::Debug, ops::Range, pin::Pin}; +use core::{fmt::Debug, ops::Range}; use libafl::{bolts::tuples::MatchFirstType, inputs::Input}; use crate::{emu::Emulator, hooks::QemuHooks}; @@ -11,7 +11,7 @@ where { const HOOKS_DO_SIDE_EFFECTS: bool = true; - fn init_hooks<'a, QT>(&self, _hooks: Pin<&QemuHooks<'a, I, QT, S>>) + fn init_hooks(&self, _hooks: &QemuHooks<'_, I, QT, S>) where QT: QemuHelperTuple, { @@ -28,7 +28,7 @@ where { const HOOKS_DO_SIDE_EFFECTS: bool; - fn init_hooks_all<'a, QT>(&self, hooks: Pin<&QemuHooks<'a, I, QT, S>>) + fn init_hooks_all(&self, hooks: &QemuHooks<'_, I, QT, S>) where QT: QemuHelperTuple; @@ -43,7 +43,7 @@ where { const HOOKS_DO_SIDE_EFFECTS: bool = false; - fn init_hooks_all<'a, QT>(&self, _hooks: Pin<&QemuHooks<'a, I, QT, S>>) + fn init_hooks_all(&self, _hooks: &QemuHooks<'_, I, QT, S>) where QT: QemuHelperTuple, { @@ -62,7 +62,7 @@ where { const HOOKS_DO_SIDE_EFFECTS: bool = Head::HOOKS_DO_SIDE_EFFECTS || Tail::HOOKS_DO_SIDE_EFFECTS; - fn init_hooks_all<'a, QT>(&self, hooks: Pin<&QemuHooks<'a, I, QT, S>>) + fn init_hooks_all(&self, hooks: &QemuHooks<'_, I, QT, S>) where QT: QemuHelperTuple, { diff --git a/libafl_qemu/src/hooks.rs b/libafl_qemu/src/hooks.rs index b9d91ee344..908b0a60d1 100644 --- a/libafl_qemu/src/hooks.rs +++ b/libafl_qemu/src/hooks.rs @@ -4,9 +4,8 @@ use core::{ ffi::c_void, fmt::{self, Debug, Formatter}, - marker::{PhantomData, PhantomPinned}, + marker::PhantomData, mem::transmute, - pin::Pin, ptr::{self, addr_of}, }; @@ -15,7 +14,7 @@ use libafl::{executors::inprocess::inprocess_get_state, inputs::Input}; pub use crate::emu::SyscallHookResult; use crate::{ emu::{Emulator, FatPtr, SKIP_EXEC_HOOK}, - helper::{QemuHelper, QemuHelperTuple}, + helper::QemuHelperTuple, GuestAddr, }; @@ -28,6 +27,7 @@ enum Hook { Empty, } +/* // function signature for Read or Write hook functions with known length (1, 2, 4, 8) type FixedLenHookFn = fn(&Emulator, &mut QT, Option<&mut S>, u64, GuestAddr); type FixedLenHookCl = Box, u64, GuestAddr)>; @@ -36,773 +36,395 @@ type FixedLenHookCl = Box, u type DynamicLenHookFn = fn(&Emulator, &mut QT, Option<&mut S>, u64, GuestAddr, usize); type DynamicLenHookCl = Box, u64, GuestAddr, usize)>; +*/ -static mut QEMU_HELPERS_PTR: *const c_void = ptr::null(); -unsafe fn get_qemu_helpers<'a, QT>() -> &'a mut QT { - (QEMU_HELPERS_PTR as *mut QT) +static mut QEMU_HOOKS_PTR: *const c_void = ptr::null(); +unsafe fn get_qemu_hooks<'a, I, QT, S>() -> &'a mut QemuHooks<'a, I, QT, S> +where + I: Input, + QT: QemuHelperTuple, +{ + (QEMU_HOOKS_PTR as *mut QemuHooks<'a, I, QT, S>) .as_mut() .expect("A high-level hook is installed but QemuHooks is not initialized") } -static mut QEMU_HOOKS_PTR: *const c_void = ptr::null(); -unsafe fn get_qemu_hooks<'a, I, QT, S>() -> Pin<&'a mut QemuHooks<'a, I, QT, S>> -where - I: Input, - QT: QemuHelperTuple, -{ - Pin::new_unchecked( - (QEMU_HOOKS_PTR as *mut QemuHooks<'a, I, QT, S>) - .as_mut() - .expect("A high-level hook is installed but QemuHooks is not initialized"), - ) -} +static mut GENERIC_HOOKS: Vec = vec![]; -static mut GEN_EDGE_HOOK: Hook = Hook::Empty; -extern "C" fn gen_edge_hook_wrapper(src: u64, dst: u64) -> u64 +extern "C" fn generic_hook_wrapper(pc: GuestAddr, index: u64) where I: Input, QT: QemuHelperTuple, { unsafe { - let helpers = get_qemu_helpers::(); - let emulator = Emulator::new_empty(); - match &GEN_EDGE_HOOK { + let hooks = get_qemu_hooks::(); + let hook = &mut GENERIC_HOOKS[index as usize]; + match hook { Hook::Function(ptr) => { - let func: fn(&Emulator, &mut QT, Option<&mut S>, u64, u64) -> Option = + let func: fn(&mut QemuHooks<'_, I, QT, S>, Option<&mut S>, GuestAddr) = transmute(*ptr); - (func)(&emulator, helpers, inprocess_get_state::(), src, dst) - .map_or(SKIP_EXEC_HOOK, |id| id) + (func)(hooks, inprocess_get_state::(), pc); } Hook::Closure(ptr) => { - let mut func: Box< - dyn FnMut(&Emulator, &mut QT, Option<&mut S>, u64, u64) -> Option, - > = transmute(*ptr); - (func)(&emulator, helpers, inprocess_get_state::(), src, dst) - .map_or(SKIP_EXEC_HOOK, |id| id) + let func: &mut Box< + dyn FnMut(&mut QemuHooks<'_, I, QT, S>, Option<&mut S>, GuestAddr), + > = transmute(ptr); + (func)(hooks, inprocess_get_state::(), pc); } - _ => SKIP_EXEC_HOOK, + _ => (), } } } -static mut EDGE_HOOKS: Vec = vec![]; -extern "C" fn edge_hooks_wrapper(id: u64) +static mut EDGE_HOOKS: Vec<(Hook, Hook)> = vec![]; + +extern "C" fn gen_edge_hook_wrapper(src: GuestAddr, dst: GuestAddr, index: u64) -> u64 where I: Input, QT: QemuHelperTuple, { unsafe { - let helpers = get_qemu_helpers::(); - let emulator = Emulator::new_empty(); - for hook in &EDGE_HOOKS { - match hook { - Hook::Function(ptr) => { - let func: fn(&Emulator, &mut QT, Option<&mut S>, u64) = transmute(*ptr); - (func)(&emulator, helpers, inprocess_get_state::(), id); - } - Hook::Closure(ptr) => { - let mut func: Box, u64)> = - transmute(*ptr); - (func)(&emulator, helpers, inprocess_get_state::(), id); - - // Forget the closure so that drop is not called on captured variables. - core::mem::forget(func); - } - _ => (), - } - } - } -} - -static mut GEN_BLOCK_HOOK: Hook = Hook::Empty; -extern "C" fn gen_block_hook_wrapper(pc: u64) -> u64 -where - I: Input, - QT: QemuHelperTuple, -{ - unsafe { - let helpers = get_qemu_helpers::(); - let emulator = Emulator::new_empty(); - match &GEN_BLOCK_HOOK { + let hooks = get_qemu_hooks::(); + let (gen, _) = &mut EDGE_HOOKS[index as usize]; + match gen { Hook::Function(ptr) => { - let func: fn(&Emulator, &mut QT, Option<&mut S>, u64) -> Option = - transmute(*ptr); - (func)(&emulator, helpers, inprocess_get_state::(), pc) - .map_or(SKIP_EXEC_HOOK, |id| id) + let func: fn( + &mut QemuHooks<'_, I, 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 mut func: Box< - dyn FnMut(&Emulator, &mut QT, Option<&mut S>, u64) -> Option, - > = transmute(*ptr); - let ret = (func)(&emulator, helpers, inprocess_get_state::(), pc) - .map_or(SKIP_EXEC_HOOK, |id| id); - - // Forget the closure so that drop is not called on captured variables. - core::mem::forget(func); - - ret + let func: &mut Box< + dyn FnMut( + &mut QemuHooks<'_, I, QT, S>, + Option<&mut S>, + GuestAddr, + GuestAddr, + ) -> Option, + > = transmute(ptr); + (func)(hooks, inprocess_get_state::(), src, dst).map_or(SKIP_EXEC_HOOK, |id| id) } - _ => SKIP_EXEC_HOOK, + _ => 0, } } } -static mut BLOCK_HOOKS: Vec = vec![]; -extern "C" fn block_hooks_wrapper(id: u64) +extern "C" fn exec_edge_hook_wrapper(id: u64, index: u64) where I: Input, QT: QemuHelperTuple, { unsafe { - let helpers = get_qemu_helpers::(); - let emulator = Emulator::new_empty(); - for hook in &BLOCK_HOOKS { - match hook { - Hook::Function(ptr) => { - let func: fn(&Emulator, &mut QT, Option<&mut S>, u64) = transmute(*ptr); - (func)(&emulator, helpers, inprocess_get_state::(), id); - } - Hook::Closure(ptr) => { - let mut func: Box, u64)> = - transmute(*ptr); - (func)(&emulator, helpers, inprocess_get_state::(), id); - - // Forget the closure so that drop is not called on captured variables. - core::mem::forget(func); - } - _ => (), - } - } - } -} - -static mut GEN_READ_HOOK: Hook = Hook::Empty; -extern "C" fn gen_read_hook_wrapper(size: u32) -> u64 -where - I: Input, - QT: QemuHelperTuple, -{ - unsafe { - let helpers = get_qemu_helpers::(); - let emulator = Emulator::new_empty(); - match &GEN_READ_HOOK { + let hooks = get_qemu_hooks::(); + let (_, exec) = &mut EDGE_HOOKS[index as usize]; + match exec { Hook::Function(ptr) => { - let func: fn(&Emulator, &mut QT, Option<&mut S>, usize) -> Option = - transmute(*ptr); - (func)( - &emulator, - helpers, - inprocess_get_state::(), - size as usize, - ) - .map_or(SKIP_EXEC_HOOK, |id| id) + let func: fn(&mut QemuHooks<'_, I, QT, S>, Option<&mut S>, u64) = transmute(*ptr); + (func)(hooks, inprocess_get_state::(), id); } Hook::Closure(ptr) => { - let mut func: Box< - dyn FnMut(&Emulator, &mut QT, Option<&mut S>, usize) -> Option, - > = transmute(*ptr); - let ret = (func)( - &emulator, - helpers, - inprocess_get_state::(), - size as usize, - ) - .map_or(SKIP_EXEC_HOOK, |id| id); - - // Forget the closure so that drop is not called on captured variables. - core::mem::forget(func); - - ret + let func: &mut Box, Option<&mut S>, u64)> = + transmute(ptr); + (func)(hooks, inprocess_get_state::(), id); } - _ => SKIP_EXEC_HOOK, + _ => (), } } } -static mut GEN_WRITE_HOOK: Hook = Hook::Empty; -extern "C" fn gen_write_hook_wrapper(size: u32) -> u64 +static mut BLOCK_HOOKS: Vec<(Hook, Hook)> = vec![]; + +extern "C" fn gen_block_hook_wrapper(pc: GuestAddr, index: u64) -> u64 where I: Input, QT: QemuHelperTuple, { unsafe { - let helpers = get_qemu_helpers::(); - let emulator = Emulator::new_empty(); - match &GEN_WRITE_HOOK { + let hooks = get_qemu_hooks::(); + let (gen, _) = &mut BLOCK_HOOKS[index as usize]; + match gen { Hook::Function(ptr) => { - let func: fn(&Emulator, &mut QT, Option<&mut S>, usize) -> Option = - transmute(*ptr); - (func)( - &emulator, - helpers, - inprocess_get_state::(), - size as usize, - ) - .map_or(SKIP_EXEC_HOOK, |id| id) + let func: fn( + &mut QemuHooks<'_, I, QT, S>, + Option<&mut S>, + GuestAddr, + ) -> Option = transmute(*ptr); + (func)(hooks, inprocess_get_state::(), pc).map_or(SKIP_EXEC_HOOK, |id| id) } Hook::Closure(ptr) => { - let mut func: Box< - dyn FnMut(&Emulator, &mut QT, Option<&mut S>, usize) -> Option, - > = transmute(*ptr); - let ret = (func)( - &emulator, - helpers, - inprocess_get_state::(), - size as usize, - ) - .map_or(SKIP_EXEC_HOOK, |id| id); - - // Forget the closure so that drop is not called on captured variables. - core::mem::forget(func); - - ret + let func: &mut Box< + dyn FnMut( + &mut QemuHooks<'_, I, QT, S>, + Option<&mut S>, + GuestAddr, + ) -> Option, + > = transmute(ptr); + (func)(hooks, inprocess_get_state::(), pc).map_or(SKIP_EXEC_HOOK, |id| id) } - _ => SKIP_EXEC_HOOK, + _ => 0, } } } -static mut READ1_HOOKS: Vec = vec![]; -extern "C" fn read1_hooks_wrapper(id: u64, addr: u64) +extern "C" fn exec_block_hook_wrapper(id: u64, index: u64) where I: Input, QT: QemuHelperTuple, { unsafe { - let helpers = get_qemu_helpers::(); - let emulator = Emulator::new_empty(); - for hook in &READ1_HOOKS { - match hook { - Hook::Function(ptr) => { - let func: FixedLenHookFn = transmute(*ptr); - (func)( - &emulator, - helpers, - inprocess_get_state::(), - id, - addr as GuestAddr, - ); - } - Hook::Closure(ptr) => { - let mut func: FixedLenHookCl = transmute(*ptr); - (func)( - &emulator, - helpers, - inprocess_get_state::(), - id, - addr as GuestAddr, - ); - } - _ => (), - } - } - } -} - -static mut READ2_HOOKS: Vec = vec![]; -extern "C" fn read2_hooks_wrapper(id: u64, addr: u64) -where - I: Input, - QT: QemuHelperTuple, -{ - unsafe { - let helpers = get_qemu_helpers::(); - let emulator = Emulator::new_empty(); - for hook in &READ2_HOOKS { - match hook { - Hook::Function(ptr) => { - let func: FixedLenHookFn = transmute(*ptr); - (func)( - &emulator, - helpers, - inprocess_get_state::(), - id, - addr as GuestAddr, - ); - } - Hook::Closure(ptr) => { - let mut func: FixedLenHookCl = transmute(*ptr); - (func)( - &emulator, - helpers, - inprocess_get_state::(), - id, - addr as GuestAddr, - ); - } - _ => (), - } - } - } -} - -static mut READ4_HOOKS: Vec = vec![]; -extern "C" fn read4_hooks_wrapper(id: u64, addr: u64) -where - I: Input, - QT: QemuHelperTuple, -{ - unsafe { - let helpers = get_qemu_helpers::(); - let emulator = Emulator::new_empty(); - for hook in &READ4_HOOKS { - match hook { - Hook::Function(ptr) => { - let func: FixedLenHookFn = transmute(*ptr); - (func)( - &emulator, - helpers, - inprocess_get_state::(), - id, - addr as GuestAddr, - ); - } - Hook::Closure(ptr) => { - let mut func: FixedLenHookCl = transmute(*ptr); - (func)( - &emulator, - helpers, - inprocess_get_state::(), - id, - addr as GuestAddr, - ); - } - _ => (), - } - } - } -} - -static mut READ8_HOOKS: Vec = vec![]; -extern "C" fn read8_hooks_wrapper(id: u64, addr: u64) -where - I: Input, - QT: QemuHelperTuple, -{ - unsafe { - let helpers = get_qemu_helpers::(); - let emulator = Emulator::new_empty(); - for hook in &READ8_HOOKS { - match hook { - Hook::Function(ptr) => { - let func: FixedLenHookFn = transmute(*ptr); - (func)( - &emulator, - helpers, - inprocess_get_state::(), - id, - addr as GuestAddr, - ); - } - Hook::Closure(ptr) => { - let mut func: FixedLenHookCl = transmute(*ptr); - (func)( - &emulator, - helpers, - inprocess_get_state::(), - id, - addr as GuestAddr, - ); - } - _ => (), - } - } - } -} - -static mut READ_N_HOOKS: Vec = vec![]; -extern "C" fn read_n_hooks_wrapper(id: u64, addr: u64, size: u32) -where - I: Input, - QT: QemuHelperTuple, -{ - unsafe { - let helpers = get_qemu_helpers::(); - let emulator = Emulator::new_empty(); - for hook in &READ_N_HOOKS { - match hook { - Hook::Function(ptr) => { - let func: DynamicLenHookFn = transmute(*ptr); - (func)( - &emulator, - helpers, - inprocess_get_state::(), - id, - addr as GuestAddr, - size as usize, - ); - } - Hook::Closure(ptr) => { - let mut func: DynamicLenHookCl = transmute(*ptr); - (func)( - &emulator, - helpers, - inprocess_get_state::(), - id, - addr as GuestAddr, - size as usize, - ); - } - _ => (), - } - } - } -} - -static mut WRITE1_HOOKS: Vec = vec![]; -extern "C" fn write1_hooks_wrapper(id: u64, addr: u64) -where - I: Input, - QT: QemuHelperTuple, -{ - unsafe { - let helpers = get_qemu_helpers::(); - let emulator = Emulator::new_empty(); - for hook in &WRITE1_HOOKS { - match hook { - Hook::Function(ptr) => { - let func: FixedLenHookFn = transmute(*ptr); - (func)( - &emulator, - helpers, - inprocess_get_state::(), - id, - addr as GuestAddr, - ); - } - Hook::Closure(ptr) => { - let mut func: FixedLenHookCl = transmute(*ptr); - (func)( - &emulator, - helpers, - inprocess_get_state::(), - id, - addr as GuestAddr, - ); - } - _ => (), - } - } - } -} - -static mut WRITE2_HOOKS: Vec = vec![]; -extern "C" fn write2_hooks_wrapper(id: u64, addr: u64) -where - I: Input, - QT: QemuHelperTuple, -{ - unsafe { - let helpers = get_qemu_helpers::(); - let emulator = Emulator::new_empty(); - for hook in &WRITE2_HOOKS { - match hook { - Hook::Function(ptr) => { - let func: FixedLenHookFn = transmute(*ptr); - (func)( - &emulator, - helpers, - inprocess_get_state::(), - id, - addr as GuestAddr, - ); - } - Hook::Closure(ptr) => { - let mut func: FixedLenHookCl = transmute(*ptr); - (func)( - &emulator, - helpers, - inprocess_get_state::(), - id, - addr as GuestAddr, - ); - } - _ => (), - } - } - } -} - -static mut WRITE4_HOOKS: Vec = vec![]; -extern "C" fn write4_hooks_wrapper(id: u64, addr: u64) -where - I: Input, - QT: QemuHelperTuple, -{ - unsafe { - let helpers = get_qemu_helpers::(); - let emulator = Emulator::new_empty(); - for hook in &WRITE4_HOOKS { - match hook { - Hook::Function(ptr) => { - let func: FixedLenHookFn = transmute(*ptr); - (func)( - &emulator, - helpers, - inprocess_get_state::(), - id, - addr as GuestAddr, - ); - } - Hook::Closure(ptr) => { - let mut func: FixedLenHookCl = transmute(*ptr); - (func)( - &emulator, - helpers, - inprocess_get_state::(), - id, - addr as GuestAddr, - ); - } - _ => (), - } - } - } -} - -static mut WRITE8_HOOKS: Vec = vec![]; -extern "C" fn write8_hooks_wrapper(id: u64, addr: u64) -where - I: Input, - QT: QemuHelperTuple, -{ - unsafe { - let helpers = get_qemu_helpers::(); - let emulator = Emulator::new_empty(); - for hook in &WRITE8_HOOKS { - match hook { - Hook::Function(ptr) => { - let func: FixedLenHookFn = transmute(*ptr); - (func)( - &emulator, - helpers, - inprocess_get_state::(), - id, - addr as GuestAddr, - ); - } - Hook::Closure(ptr) => { - let mut func: FixedLenHookCl = transmute(*ptr); - (func)( - &emulator, - helpers, - inprocess_get_state::(), - id, - addr as GuestAddr, - ); - } - _ => (), - } - } - } -} - -static mut WRITE_N_HOOKS: Vec = vec![]; -extern "C" fn write_n_hooks_wrapper(id: u64, addr: u64, size: u32) -where - I: Input, - QT: QemuHelperTuple, -{ - unsafe { - let helpers = get_qemu_helpers::(); - let emulator = Emulator::new_empty(); - for hook in &WRITE1_HOOKS { - match hook { - Hook::Function(ptr) => { - let func: DynamicLenHookFn = transmute(*ptr); - (func)( - &emulator, - helpers, - inprocess_get_state::(), - id, - addr as GuestAddr, - size as usize, - ); - } - Hook::Closure(ptr) => { - let mut func: DynamicLenHookCl = transmute(*ptr); - (func)( - &emulator, - helpers, - inprocess_get_state::(), - id, - addr as GuestAddr, - size as usize, - ); - } - _ => (), - } - } - } -} - -static mut GEN_CMP_HOOK: Hook = Hook::Empty; -extern "C" fn gen_cmp_hook_wrapper(pc: u64, size: u32) -> u64 -where - I: Input, - QT: QemuHelperTuple, -{ - unsafe { - let helpers = get_qemu_helpers::(); - let emulator = Emulator::new_empty(); - match &GEN_CMP_HOOK { + let hooks = get_qemu_hooks::(); + let (_, exec) = &mut BLOCK_HOOKS[index as usize]; + match exec { Hook::Function(ptr) => { - let func: fn(&Emulator, &mut QT, Option<&mut S>, u64, usize) -> Option = - transmute(*ptr); - (func)( - &emulator, - helpers, - inprocess_get_state::(), - pc, - size as usize, - ) - .map_or(SKIP_EXEC_HOOK, |id| id) + let func: fn(&mut QemuHooks<'_, I, QT, S>, Option<&mut S>, u64) = transmute(*ptr); + (func)(hooks, inprocess_get_state::(), id); } Hook::Closure(ptr) => { - let mut func: Box< - dyn FnMut(&Emulator, &mut QT, Option<&mut S>, u64, usize) -> Option, - > = transmute(*ptr); - let ret = (func)( - &emulator, - helpers, - inprocess_get_state::(), - pc, - size as usize, - ) - .map_or(SKIP_EXEC_HOOK, |id| id); - - // Forget the closure so that drop is not called on captured variables. - core::mem::forget(func); - - ret + let func: &mut Box, Option<&mut S>, u64)> = + transmute(ptr); + (func)(hooks, inprocess_get_state::(), id); } - _ => SKIP_EXEC_HOOK, + _ => (), } } } -static mut CMP1_HOOKS: Vec = vec![]; -extern "C" fn cmp1_hooks_wrapper(id: u64, v0: u8, v1: u8) +static mut READ_HOOKS: Vec<(Hook, Hook, Hook, Hook, Hook, Hook)> = vec![]; +static mut WRITE_HOOKS: Vec<(Hook, Hook, Hook, Hook, Hook, Hook)> = vec![]; + +extern "C" fn gen_read_hook_wrapper(pc: GuestAddr, size: usize, index: u64) -> u64 where I: Input, QT: QemuHelperTuple, { unsafe { - let helpers = get_qemu_helpers::(); - let emulator = Emulator::new_empty(); - for hook in &CMP1_HOOKS { - match hook { - Hook::Function(ptr) => { - let func: fn(&Emulator, &mut QT, Option<&mut S>, u64, u8, u8) = transmute(*ptr); - (func)(&emulator, helpers, inprocess_get_state::(), id, v0, v1); - } - Hook::Closure(ptr) => { - let mut func: Box, u64, u8, u8)> = - transmute(*ptr); - (func)(&emulator, helpers, inprocess_get_state::(), id, v0, v1); - - // Forget the closure so that drop is not called on captured variables. - core::mem::forget(func); - } - _ => (), + let hooks = get_qemu_hooks::(); + let (gen, _, _, _, _, _) = &mut READ_HOOKS[index as usize]; + match gen { + Hook::Function(ptr) => { + let func: fn( + &mut QemuHooks<'_, I, QT, S>, + Option<&mut S>, + GuestAddr, + usize, + ) -> Option = transmute(*ptr); + (func)(hooks, inprocess_get_state::(), pc, size).map_or(SKIP_EXEC_HOOK, |id| id) } + Hook::Closure(ptr) => { + let func: &mut Box< + dyn FnMut( + &mut QemuHooks<'_, I, QT, S>, + Option<&mut S>, + GuestAddr, + usize, + ) -> Option, + > = transmute(ptr); + (func)(hooks, inprocess_get_state::(), pc, size).map_or(SKIP_EXEC_HOOK, |id| id) + } + _ => 0, } } } -static mut CMP2_HOOKS: Vec = vec![]; -extern "C" fn cmp2_hooks_wrapper(id: u64, v0: u16, v1: u16) +extern "C" fn gen_write_hook_wrapper(pc: GuestAddr, size: usize, index: u64) -> u64 where I: Input, QT: QemuHelperTuple, { unsafe { - let helpers = get_qemu_helpers::(); - let emulator = Emulator::new_empty(); - for hook in &CMP2_HOOKS { - match hook { - Hook::Function(ptr) => { - let func: fn(&Emulator, &mut QT, Option<&mut S>, u64, u16, u16) = - transmute(*ptr); - (func)(&emulator, helpers, inprocess_get_state::(), id, v0, v1); - } - Hook::Closure(ptr) => { - let mut func: Box< - dyn FnMut(&Emulator, &mut QT, Option<&mut S>, u64, u16, u16), - > = transmute(*ptr); - (func)(&emulator, helpers, inprocess_get_state::(), id, v0, v1); - - // Forget the closure so that drop is not called on captured variables. - core::mem::forget(func); - } - _ => (), + let hooks = get_qemu_hooks::(); + let (gen, _, _, _, _, _) = &mut WRITE_HOOKS[index as usize]; + match gen { + Hook::Function(ptr) => { + let func: fn( + &mut QemuHooks<'_, I, QT, S>, + Option<&mut S>, + GuestAddr, + usize, + ) -> Option = transmute(*ptr); + (func)(hooks, inprocess_get_state::(), pc, size).map_or(SKIP_EXEC_HOOK, |id| id) } + Hook::Closure(ptr) => { + let func: &mut Box< + dyn FnMut( + &mut QemuHooks<'_, I, QT, S>, + Option<&mut S>, + GuestAddr, + usize, + ) -> Option, + > = transmute(ptr); + (func)(hooks, inprocess_get_state::(), pc, size).map_or(SKIP_EXEC_HOOK, |id| id) + } + _ => 0, } } } -static mut CMP4_HOOKS: Vec = vec![]; -extern "C" fn cmp4_hooks_wrapper(id: u64, v0: u32, v1: u32) +macro_rules! define_rw_exec_hook { + ($name:ident, $field:tt, $global:ident) => { + extern "C" fn $name(id: u64, addr: GuestAddr, index: u64) + where + I: Input, + QT: QemuHelperTuple, + { + unsafe { + let hooks = get_qemu_hooks::(); + let exec = &mut $global[index as usize].$field; + match exec { + Hook::Function(ptr) => { + let func: fn(&mut QemuHooks<'_, I, QT, S>, Option<&mut S>, u64, GuestAddr) = + transmute(*ptr); + (func)(hooks, inprocess_get_state::(), id, addr); + } + Hook::Closure(ptr) => { + let func: &mut Box< + dyn FnMut(&mut QemuHooks<'_, I, QT, S>, Option<&mut S>, u64, GuestAddr), + > = transmute(ptr); + (func)(hooks, inprocess_get_state::(), id, addr); + } + _ => (), + } + } + } + }; +} + +macro_rules! define_rw_exec_hook_n { + ($name:ident, $field:tt, $global:ident) => { + extern "C" fn $name(id: u64, addr: GuestAddr, size: usize, index: u64) + where + I: Input, + QT: QemuHelperTuple, + { + unsafe { + let hooks = get_qemu_hooks::(); + let exec = &mut $global[index as usize].$field; + match exec { + Hook::Function(ptr) => { + let func: fn( + &mut QemuHooks<'_, I, QT, S>, + Option<&mut S>, + u64, + GuestAddr, + usize, + ) = transmute(*ptr); + (func)(hooks, inprocess_get_state::(), id, addr, size); + } + Hook::Closure(ptr) => { + let func: &mut Box< + dyn FnMut( + &mut QemuHooks<'_, I, QT, S>, + Option<&mut S>, + u64, + GuestAddr, + usize, + ), + > = transmute(ptr); + (func)(hooks, inprocess_get_state::(), id, addr, size); + } + _ => (), + } + } + } + }; +} + +define_rw_exec_hook!(exec_read1_hook_wrapper, 1, READ_HOOKS); +define_rw_exec_hook!(exec_read2_hook_wrapper, 2, READ_HOOKS); +define_rw_exec_hook!(exec_read4_hook_wrapper, 3, READ_HOOKS); +define_rw_exec_hook!(exec_read8_hook_wrapper, 4, READ_HOOKS); +define_rw_exec_hook_n!(exec_read_n_hook_wrapper, 5, READ_HOOKS); + +define_rw_exec_hook!(exec_write1_hook_wrapper, 1, WRITE_HOOKS); +define_rw_exec_hook!(exec_write2_hook_wrapper, 2, WRITE_HOOKS); +define_rw_exec_hook!(exec_write4_hook_wrapper, 3, WRITE_HOOKS); +define_rw_exec_hook!(exec_write8_hook_wrapper, 4, WRITE_HOOKS); +define_rw_exec_hook_n!(exec_write_n_hook_wrapper, 5, WRITE_HOOKS); + +static mut CMP_HOOKS: Vec<(Hook, Hook, Hook, Hook, Hook)> = vec![]; + +extern "C" fn gen_cmp_hook_wrapper(pc: GuestAddr, size: usize, index: u64) -> u64 where I: Input, QT: QemuHelperTuple, { unsafe { - let helpers = get_qemu_helpers::(); - let emulator = Emulator::new_empty(); - for hook in &CMP4_HOOKS { - match hook { - Hook::Function(ptr) => { - let func: fn(&Emulator, &mut QT, Option<&mut S>, u64, u32, u32) = - transmute(*ptr); - (func)(&emulator, helpers, inprocess_get_state::(), id, v0, v1); - } - Hook::Closure(ptr) => { - let mut func: Box< - dyn FnMut(&Emulator, &mut QT, Option<&mut S>, u64, u32, u32), - > = transmute(*ptr); - (func)(&emulator, helpers, inprocess_get_state::(), id, v0, v1); - - // Forget the closure so that drop is not called on captured variables. - core::mem::forget(func); - } - _ => (), + let hooks = get_qemu_hooks::(); + let (gen, _, _, _, _) = &mut CMP_HOOKS[index as usize]; + match gen { + Hook::Function(ptr) => { + let func: fn( + &mut QemuHooks<'_, I, QT, S>, + Option<&mut S>, + GuestAddr, + usize, + ) -> Option = transmute(*ptr); + (func)(hooks, inprocess_get_state::(), pc, size).map_or(SKIP_EXEC_HOOK, |id| id) } + Hook::Closure(ptr) => { + let func: &mut Box< + dyn FnMut( + &mut QemuHooks<'_, I, QT, S>, + Option<&mut S>, + GuestAddr, + usize, + ) -> Option, + > = transmute(ptr); + (func)(hooks, inprocess_get_state::(), pc, size).map_or(SKIP_EXEC_HOOK, |id| id) + } + _ => 0, } } } -static mut CMP8_HOOKS: Vec = vec![]; -extern "C" fn cmp8_hooks_wrapper(id: u64, v0: u64, v1: u64) -where - I: Input, - QT: QemuHelperTuple, -{ - unsafe { - let helpers = get_qemu_helpers::(); - let emulator = Emulator::new_empty(); - for hook in &CMP8_HOOKS { - match hook { - Hook::Function(ptr) => { - let func: fn(&Emulator, &mut QT, Option<&mut S>, u64, u64, u64) = - transmute(*ptr); - (func)(&emulator, helpers, inprocess_get_state::(), id, v0, v1); +macro_rules! define_cmp_exec_hook { + ($name:ident, $field:tt, $itype:ty) => { + extern "C" fn $name(id: u64, v0: $itype, v1: $itype, index: u64) + where + I: Input, + QT: QemuHelperTuple, + { + unsafe { + let hooks = get_qemu_hooks::(); + let exec = &mut CMP_HOOKS[index as usize].$field; + match exec { + Hook::Function(ptr) => { + let func: fn( + &mut QemuHooks<'_, I, QT, S>, + Option<&mut S>, + u64, + $itype, + $itype, + ) = transmute(*ptr); + (func)(hooks, inprocess_get_state::(), id, v0, v1); + } + Hook::Closure(ptr) => { + let func: &mut Box< + dyn FnMut( + &mut QemuHooks<'_, I, QT, S>, + Option<&mut S>, + u64, + $itype, + $itype, + ), + > = transmute(ptr); + (func)(hooks, inprocess_get_state::(), id, v0, v1); + } + _ => (), } - Hook::Closure(ptr) => { - let mut func: Box< - dyn FnMut(&Emulator, &mut QT, Option<&mut S>, u64, u64, u64), - > = transmute(*ptr); - (func)(&emulator, helpers, inprocess_get_state::(), id, v0, v1); - - // Forget the closure so that drop is not called on captured variables. - core::mem::forget(func); - } - _ => (), } } - } + }; } +define_cmp_exec_hook!(exec_cmp1_hook_wrapper, 1, u8); +define_cmp_exec_hook!(exec_cmp2_hook_wrapper, 2, u16); +define_cmp_exec_hook!(exec_cmp4_hook_wrapper, 3, u32); +define_cmp_exec_hook!(exec_cmp8_hook_wrapper, 4, u64); + static mut ON_THREAD_HOOKS: Vec = vec![]; extern "C" fn on_thread_hooks_wrapper(tid: u32) where @@ -810,43 +432,27 @@ where QT: QemuHelperTuple, { unsafe { - let emu = Emulator::new_empty(); for hook in &mut ON_THREAD_HOOKS { let hooks = get_qemu_hooks::(); match hook { Hook::Function(ptr) => { - let func: fn( - &Emulator, - Pin<&mut QemuHooks<'_, I, QT, S>>, - Option<&mut S>, - u32, - ) = transmute(*ptr); - (func)(&emu, hooks, inprocess_get_state::(), tid); + let func: fn(&mut QemuHooks<'_, I, QT, S>, Option<&mut S>, u32) = + transmute(*ptr); + (func)(hooks, inprocess_get_state::(), tid); } Hook::Closure(ptr) => { let mut func: Box< - dyn FnMut( - &Emulator, - Pin<&mut QemuHooks<'_, I, QT, S>>, - Option<&mut S>, - u32, - ), + dyn FnMut(&mut QemuHooks<'_, I, QT, S>, Option<&mut S>, u32), > = transmute(*ptr); - (func)(&emu, hooks, inprocess_get_state::(), tid); + (func)(hooks, inprocess_get_state::(), tid); // Forget the closure so that drop is not called on captured variables. core::mem::forget(func); } Hook::Once(ptr) => { - let func: Box< - dyn FnOnce( - &Emulator, - Pin<&mut QemuHooks<'_, I, QT, S>>, - Option<&mut S>, - u32, - ), - > = transmute(*ptr); - (func)(&emu, hooks, inprocess_get_state::(), tid); + let func: Box, Option<&mut S>, u32)> = + transmute(*ptr); + (func)(hooks, inprocess_get_state::(), tid); *hook = Hook::Empty; } Hook::Empty => (), @@ -872,16 +478,14 @@ where QT: QemuHelperTuple, { unsafe { - let helpers = get_qemu_helpers::(); - let emulator = Emulator::new_empty(); + 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( - &Emulator, - &mut QT, + &mut QemuHooks<'_, I, QT, S>, Option<&mut S>, i32, u64, @@ -894,8 +498,7 @@ where u64, ) -> SyscallHookResult = transmute(*ptr); let r = (func)( - &emulator, - helpers, + hooks, inprocess_get_state::(), sys_num, a0, @@ -916,8 +519,7 @@ where #[allow(clippy::type_complexity)] let mut func: Box< dyn FnMut( - &Emulator, - &mut QT, + &mut QemuHooks<'_, I, QT, S>, Option<&mut S>, i32, u64, @@ -931,8 +533,7 @@ where ) -> SyscallHookResult, > = transmute(*ptr); let r = (func)( - &emulator, - helpers, + hooks, inprocess_get_state::(), sys_num, a0, @@ -978,16 +579,14 @@ where QT: QemuHelperTuple, { unsafe { - let helpers = get_qemu_helpers::(); - let emulator = Emulator::new_empty(); + 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( - &Emulator, - &mut QT, + &mut QemuHooks<'_, I, QT, S>, Option<&mut S>, u64, i32, @@ -1001,8 +600,7 @@ where u64, ) -> u64 = transmute(*ptr); res = (func)( - &emulator, - helpers, + hooks, inprocess_get_state::(), res, sys_num, @@ -1020,8 +618,7 @@ where #[allow(clippy::type_complexity)] let mut func: Box< dyn FnMut( - &Emulator, - &mut QT, + &mut QemuHooks<'_, I, QT, S>, Option<&mut S>, u64, i32, @@ -1036,8 +633,7 @@ where ) -> u64, > = transmute(*ptr); res = (func)( - &emulator, - helpers, + hooks, inprocess_get_state::(), res, sys_num, @@ -1071,7 +667,6 @@ where helpers: QT, emulator: &'a Emulator, phantom: PhantomData<(I, S)>, - _pin: PhantomPinned, } impl<'a, I, QT, S> Debug for QemuHooks<'a, I, QT, S> @@ -1092,7 +687,7 @@ where QT: QemuHelperTuple, I: Input, { - pub fn new(emulator: &'a Emulator, helpers: QT) -> Pin> { + pub fn new(emulator: &'a Emulator, helpers: QT) -> Box { unsafe { assert!( !HOOKS_IS_INITIALIZED, @@ -1102,39 +697,32 @@ where } // re-translate blocks with hooks emulator.flush_jit(); - let slf = Box::pin(Self { + let slf = Box::new(Self { emulator, helpers, phantom: PhantomData, - _pin: PhantomPinned, }); - slf.helpers.init_hooks_all(slf.as_ref()); + slf.helpers.init_hooks_all(&slf); unsafe { - QEMU_HELPERS_PTR = addr_of!(slf.helpers) as *const c_void; QEMU_HOOKS_PTR = addr_of!(*slf) as *const c_void; } slf } #[must_use] - pub fn match_helper<'b, T>(self: &'b Pin<&mut Self>) -> Option<&'b T> + pub fn match_helper(&self) -> Option<&T> where - T: QemuHelper, + T: 'static, { self.helpers.match_first_type::() } #[must_use] - pub fn match_helper_mut<'b, T>(self: &'b mut Pin<&mut Self>) -> Option<&'b mut T> + pub fn match_helper_mut(&mut self) -> Option<&mut T> where - T: QemuHelper, + T: 'static, { - unsafe { - self.as_mut() - .get_unchecked_mut() - .helpers - .match_first_type_mut::() - } + self.helpers.match_first_type_mut::() } pub fn emulator(&self) -> &Emulator { @@ -1149,403 +737,689 @@ where &mut self.helpers } - pub fn edge_generation( + pub fn instruction( &self, - hook: fn(&Emulator, &mut QT, Option<&mut S>, src: u64, dest: u64) -> Option, + addr: GuestAddr, + hook: fn(&mut Self, Option<&mut S>, GuestAddr), + invalidate_block: bool, ) { unsafe { - GEN_EDGE_HOOK = Hook::Function(hook as *const libc::c_void); + let index = GENERIC_HOOKS.len(); + self.emulator.set_hook( + addr, + generic_hook_wrapper::, + index as u64, + invalidate_block, + ); + GENERIC_HOOKS.push(Hook::Function(hook as *const libc::c_void)); } - self.emulator - .set_gen_edge_hook(gen_edge_hook_wrapper::); } - pub fn edge_generation_closure( + pub unsafe fn instruction_closure( &self, - hook: Box, u64, u64) -> Option>, + addr: GuestAddr, + hook: Box, GuestAddr)>, + invalidate_block: bool, + ) { + let index = GENERIC_HOOKS.len(); + self.emulator.set_hook( + addr, + generic_hook_wrapper::, + index as u64, + invalidate_block, + ); + GENERIC_HOOKS.push(Hook::Closure(transmute(hook))); + } + + pub fn edges( + &self, + generation_hook: Option< + fn(&mut Self, Option<&mut S>, src: GuestAddr, dest: GuestAddr) -> Option, + >, + execution_hook: Option, id: u64)>, ) { unsafe { - GEN_EDGE_HOOK = Hook::Closure(transmute(hook)); + let index = EDGE_HOOKS.len(); + self.emulator.add_edge_hooks( + if generation_hook.is_none() { + None + } else { + Some(gen_edge_hook_wrapper::) + }, + if execution_hook.is_none() { + None + } else { + Some(exec_edge_hook_wrapper::) + }, + index as u64, + ); + EDGE_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) + }), + )); } - self.emulator - .set_gen_edge_hook(gen_edge_hook_wrapper::); } - pub fn edge_execution(&self, hook: fn(&Emulator, &mut QT, Option<&mut S>, id: u64)) { - unsafe { - EDGE_HOOKS.push(Hook::Function(hook as *const libc::c_void)); - } - self.emulator - .set_exec_edge_hook(edge_hooks_wrapper::); - } - - pub fn edge_execution_closure( + pub unsafe fn edges_closures( &self, - hook: Box, u64)>, + generation_hook: Option< + Box, GuestAddr, GuestAddr) -> Option>, + >, + execution_hook: Option, u64)>>, + ) { + let index = EDGE_HOOKS.len(); + self.emulator.add_edge_hooks( + if generation_hook.is_none() { + None + } else { + Some(gen_edge_hook_wrapper::) + }, + if execution_hook.is_none() { + None + } else { + Some(exec_edge_hook_wrapper::) + }, + index as u64, + ); + EDGE_HOOKS.push(( + generation_hook.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))), + execution_hook.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))), + )); + } + + pub fn edges_raw( + &self, + generation_hook: Option< + fn(&mut Self, Option<&mut S>, src: GuestAddr, dest: GuestAddr) -> Option, + >, + execution_hook: Option, ) { unsafe { - EDGE_HOOKS.push(Hook::Closure(transmute(hook))); + let index = EDGE_HOOKS.len(); + self.emulator.add_edge_hooks( + if generation_hook.is_none() { + None + } else { + Some(gen_edge_hook_wrapper::) + }, + execution_hook, + index as u64, + ); + EDGE_HOOKS.push(( + generation_hook.map_or(Hook::Empty, |hook| { + Hook::Function(hook as *const libc::c_void) + }), + Hook::Empty, + )); } - self.emulator - .set_exec_edge_hook(edge_hooks_wrapper::); } - pub fn block_generation( + pub fn blocks( &self, - hook: fn(&Emulator, &mut QT, Option<&mut S>, pc: u64) -> Option, + generation_hook: Option, pc: GuestAddr) -> Option>, + execution_hook: Option, id: u64)>, ) { unsafe { - GEN_BLOCK_HOOK = Hook::Function(hook as *const libc::c_void); + let index = BLOCK_HOOKS.len(); + self.emulator.add_block_hooks( + if generation_hook.is_none() { + None + } else { + Some(gen_block_hook_wrapper::) + }, + if execution_hook.is_none() { + None + } else { + Some(exec_block_hook_wrapper::) + }, + index as u64, + ); + BLOCK_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) + }), + )); } - self.emulator - .set_gen_block_hook(gen_block_hook_wrapper::); } - pub fn block_generation_closure( + pub unsafe fn blocks_closures( &self, - hook: Box, u64) -> Option>, + generation_hook: Option< + Box, GuestAddr) -> Option>, + >, + execution_hook: Option, u64)>>, + ) { + let index = BLOCK_HOOKS.len(); + self.emulator.add_block_hooks( + if generation_hook.is_none() { + None + } else { + Some(gen_block_hook_wrapper::) + }, + if execution_hook.is_none() { + None + } else { + Some(exec_block_hook_wrapper::) + }, + index as u64, + ); + BLOCK_HOOKS.push(( + generation_hook.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))), + execution_hook.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))), + )); + } + + pub fn blocks_raw( + &self, + generation_hook: Option, pc: GuestAddr) -> Option>, + execution_hook: Option, ) { unsafe { - GEN_BLOCK_HOOK = Hook::Closure(transmute(hook)); + let index = BLOCK_HOOKS.len(); + self.emulator.add_block_hooks( + if generation_hook.is_none() { + None + } else { + Some(gen_block_hook_wrapper::) + }, + execution_hook, + index as u64, + ); + BLOCK_HOOKS.push(( + generation_hook.map_or(Hook::Empty, |hook| { + Hook::Function(hook as *const libc::c_void) + }), + Hook::Empty, + )); } - self.emulator - .set_gen_block_hook(gen_block_hook_wrapper::); } - pub fn block_execution(&self, hook: fn(&Emulator, &mut QT, Option<&mut S>, id: u64)) { - unsafe { - BLOCK_HOOKS.push(Hook::Function(hook as *const libc::c_void)); - } - self.emulator - .set_exec_block_hook(block_hooks_wrapper::); - } - - pub fn block_execution_closure( + pub fn reads( &self, - hook: Box, u64)>, + generation_hook: Option< + fn(&mut Self, Option<&mut S>, pc: GuestAddr, size: usize) -> Option, + >, + execution_hook1: Option, id: u64, addr: GuestAddr)>, + execution_hook2: Option, id: u64, addr: GuestAddr)>, + execution_hook4: Option, id: u64, addr: GuestAddr)>, + execution_hook8: Option, id: u64, addr: GuestAddr)>, + execution_hook_n: Option< + fn(&mut Self, Option<&mut S>, id: u64, addr: GuestAddr, size: usize), + >, ) { unsafe { - BLOCK_HOOKS.push(Hook::Closure(transmute(hook))); + let index = READ_HOOKS.len(); + self.emulator.add_read_hooks( + if generation_hook.is_none() { + None + } else { + Some(gen_read_hook_wrapper::) + }, + if execution_hook1.is_none() { + None + } else { + Some(exec_read1_hook_wrapper::) + }, + if execution_hook2.is_none() { + None + } else { + Some(exec_read2_hook_wrapper::) + }, + if execution_hook4.is_none() { + None + } else { + Some(exec_read4_hook_wrapper::) + }, + if execution_hook8.is_none() { + None + } else { + Some(exec_read8_hook_wrapper::) + }, + if execution_hook_n.is_none() { + None + } else { + Some(exec_read_n_hook_wrapper::) + }, + index as u64, + ); + READ_HOOKS.push(( + generation_hook.map_or(Hook::Empty, |hook| { + Hook::Function(hook as *const libc::c_void) + }), + execution_hook1.map_or(Hook::Empty, |hook| { + Hook::Function(hook as *const libc::c_void) + }), + execution_hook2.map_or(Hook::Empty, |hook| { + Hook::Function(hook as *const libc::c_void) + }), + execution_hook4.map_or(Hook::Empty, |hook| { + Hook::Function(hook as *const libc::c_void) + }), + execution_hook8.map_or(Hook::Empty, |hook| { + Hook::Function(hook as *const libc::c_void) + }), + execution_hook_n.map_or(Hook::Empty, |hook| { + Hook::Function(hook as *const libc::c_void) + }), + )); } - self.emulator - .set_exec_block_hook(block_hooks_wrapper::); } - pub fn read_generation( + pub unsafe fn reads_closures( &self, - hook: fn(&Emulator, &mut QT, Option<&mut S>, size: usize) -> Option, + generation_hook: Option< + Box, GuestAddr, usize) -> Option>, + >, + execution_hook1: Option, u64, GuestAddr)>>, + execution_hook2: Option, u64, GuestAddr)>>, + execution_hook4: Option, u64, GuestAddr)>>, + execution_hook8: Option, u64, GuestAddr)>>, + execution_hook_n: Option< + Box, u64, GuestAddr, usize)>, + >, + ) { + let index = READ_HOOKS.len(); + self.emulator.add_read_hooks( + if generation_hook.is_none() { + None + } else { + Some(gen_read_hook_wrapper::) + }, + if execution_hook1.is_none() { + None + } else { + Some(exec_read1_hook_wrapper::) + }, + if execution_hook2.is_none() { + None + } else { + Some(exec_read2_hook_wrapper::) + }, + if execution_hook4.is_none() { + None + } else { + Some(exec_read4_hook_wrapper::) + }, + if execution_hook8.is_none() { + None + } else { + Some(exec_read8_hook_wrapper::) + }, + if execution_hook_n.is_none() { + None + } else { + Some(exec_read_n_hook_wrapper::) + }, + index as u64, + ); + READ_HOOKS.push(( + generation_hook.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))), + execution_hook1.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))), + execution_hook2.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))), + execution_hook4.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))), + execution_hook8.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))), + execution_hook_n.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))), + )); + } + + pub fn reads_raw( + &self, + generation_hook: Option< + fn(&mut Self, Option<&mut S>, pc: GuestAddr, size: usize) -> Option, + >, + execution_hook1: Option, + execution_hook2: Option, + execution_hook4: Option, + execution_hook8: Option, + execution_hook_n: Option, ) { unsafe { - GEN_READ_HOOK = Hook::Function(hook as *const libc::c_void); + let index = READ_HOOKS.len(); + self.emulator.add_read_hooks( + if generation_hook.is_none() { + None + } else { + Some(gen_read_hook_wrapper::) + }, + execution_hook1, + execution_hook2, + execution_hook4, + execution_hook8, + execution_hook_n, + index as u64, + ); + READ_HOOKS.push(( + generation_hook.map_or(Hook::Empty, |hook| { + Hook::Function(hook as *const libc::c_void) + }), + Hook::Empty, + Hook::Empty, + Hook::Empty, + Hook::Empty, + Hook::Empty, + )); } - self.emulator - .set_gen_read_hook(gen_read_hook_wrapper::); } - pub fn read_generation_closure( + pub fn writes( &self, - hook: Box, usize) -> Option>, + generation_hook: Option< + fn(&mut Self, Option<&mut S>, pc: GuestAddr, size: usize) -> Option, + >, + execution_hook1: Option, id: u64, addr: GuestAddr)>, + execution_hook2: Option, id: u64, addr: GuestAddr)>, + execution_hook4: Option, id: u64, addr: GuestAddr)>, + execution_hook8: Option, id: u64, addr: GuestAddr)>, + execution_hook_n: Option< + fn(&mut Self, Option<&mut S>, id: u64, addr: GuestAddr, size: usize), + >, ) { unsafe { - GEN_READ_HOOK = Hook::Closure(transmute(hook)); + let index = WRITE_HOOKS.len(); + self.emulator.add_write_hooks( + if generation_hook.is_none() { + None + } else { + Some(gen_write_hook_wrapper::) + }, + if execution_hook1.is_none() { + None + } else { + Some(exec_write1_hook_wrapper::) + }, + if execution_hook2.is_none() { + None + } else { + Some(exec_write2_hook_wrapper::) + }, + if execution_hook4.is_none() { + None + } else { + Some(exec_write4_hook_wrapper::) + }, + if execution_hook8.is_none() { + None + } else { + Some(exec_write8_hook_wrapper::) + }, + if execution_hook_n.is_none() { + None + } else { + Some(exec_write_n_hook_wrapper::) + }, + index as u64, + ); + WRITE_HOOKS.push(( + generation_hook.map_or(Hook::Empty, |hook| { + Hook::Function(hook as *const libc::c_void) + }), + execution_hook1.map_or(Hook::Empty, |hook| { + Hook::Function(hook as *const libc::c_void) + }), + execution_hook2.map_or(Hook::Empty, |hook| { + Hook::Function(hook as *const libc::c_void) + }), + execution_hook4.map_or(Hook::Empty, |hook| { + Hook::Function(hook as *const libc::c_void) + }), + execution_hook8.map_or(Hook::Empty, |hook| { + Hook::Function(hook as *const libc::c_void) + }), + execution_hook_n.map_or(Hook::Empty, |hook| { + Hook::Function(hook as *const libc::c_void) + }), + )); } - self.emulator - .set_gen_read_hook(gen_read_hook_wrapper::); } - pub fn read1_execution(&self, hook: FixedLenHookFn) { - unsafe { - READ1_HOOKS.push(Hook::Function(hook as *const libc::c_void)); - } - self.emulator - .set_exec_read1_hook(read1_hooks_wrapper::); - } - - pub fn read1_execution_closure(&self, hook: FixedLenHookCl) { - unsafe { - READ1_HOOKS.push(Hook::Closure(transmute(hook))); - } - self.emulator - .set_exec_read1_hook(read1_hooks_wrapper::); - } - - pub fn read2_execution(&self, hook: FixedLenHookFn) { - unsafe { - READ2_HOOKS.push(Hook::Function(hook as *const libc::c_void)); - } - self.emulator - .set_exec_read2_hook(read2_hooks_wrapper::); - } - - pub fn read2_execution_closure(&self, hook: FixedLenHookCl) { - unsafe { - READ2_HOOKS.push(Hook::Closure(transmute(hook))); - } - self.emulator - .set_exec_read2_hook(read2_hooks_wrapper::); - } - - pub fn read4_execution(&self, hook: FixedLenHookFn) { - unsafe { - READ4_HOOKS.push(Hook::Function(hook as *const libc::c_void)); - } - self.emulator - .set_exec_read4_hook(read4_hooks_wrapper::); - } - - pub fn read4_execution_closure(&self, hook: FixedLenHookCl) { - unsafe { - READ4_HOOKS.push(Hook::Closure(transmute(hook))); - } - self.emulator - .set_exec_read4_hook(read4_hooks_wrapper::); - } - - pub fn read8_execution(&self, hook: FixedLenHookFn) { - unsafe { - READ8_HOOKS.push(Hook::Function(hook as *const libc::c_void)); - } - self.emulator - .set_exec_read8_hook(read8_hooks_wrapper::); - } - - pub fn read8_execution_closure(&self, hook: FixedLenHookCl) { - unsafe { - READ8_HOOKS.push(Hook::Closure(transmute(hook))); - } - self.emulator - .set_exec_read8_hook(read8_hooks_wrapper::); - } - - pub fn read_n_execution(&self, hook: DynamicLenHookFn) { - unsafe { - READ_N_HOOKS.push(Hook::Function(hook as *const libc::c_void)); - } - self.emulator - .set_exec_read_n_hook(read_n_hooks_wrapper::); - } - - pub fn read_n_execution_closure(&self, hook: DynamicLenHookCl) { - unsafe { - READ_N_HOOKS.push(Hook::Closure(transmute(hook))); - } - self.emulator - .set_exec_read_n_hook(read_n_hooks_wrapper::); - } - - pub fn write_generation( + pub unsafe fn writes_closures( &self, - hook: fn(&Emulator, &mut QT, Option<&mut S>, size: usize) -> Option, + generation_hook: Option< + Box, GuestAddr, usize) -> Option>, + >, + execution_hook1: Option, u64, GuestAddr)>>, + execution_hook2: Option, u64, GuestAddr)>>, + execution_hook4: Option, u64, GuestAddr)>>, + execution_hook8: Option, u64, GuestAddr)>>, + execution_hook_n: Option< + Box, u64, GuestAddr, usize)>, + >, + ) { + let index = WRITE_HOOKS.len(); + self.emulator.add_write_hooks( + if generation_hook.is_none() { + None + } else { + Some(gen_write_hook_wrapper::) + }, + if execution_hook1.is_none() { + None + } else { + Some(exec_write1_hook_wrapper::) + }, + if execution_hook2.is_none() { + None + } else { + Some(exec_write2_hook_wrapper::) + }, + if execution_hook4.is_none() { + None + } else { + Some(exec_write4_hook_wrapper::) + }, + if execution_hook8.is_none() { + None + } else { + Some(exec_write8_hook_wrapper::) + }, + if execution_hook_n.is_none() { + None + } else { + Some(exec_write_n_hook_wrapper::) + }, + index as u64, + ); + WRITE_HOOKS.push(( + generation_hook.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))), + execution_hook1.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))), + execution_hook2.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))), + execution_hook4.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))), + execution_hook8.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))), + execution_hook_n.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))), + )); + } + + pub fn writes_raw( + &self, + generation_hook: Option< + fn(&mut Self, Option<&mut S>, pc: GuestAddr, size: usize) -> Option, + >, + execution_hook1: Option, + execution_hook2: Option, + execution_hook4: Option, + execution_hook8: Option, + execution_hook_n: Option, ) { unsafe { - GEN_WRITE_HOOK = Hook::Function(hook as *const libc::c_void); + let index = WRITE_HOOKS.len(); + self.emulator.add_write_hooks( + if generation_hook.is_none() { + None + } else { + Some(gen_write_hook_wrapper::) + }, + execution_hook1, + execution_hook2, + execution_hook4, + execution_hook8, + execution_hook_n, + index as u64, + ); + WRITE_HOOKS.push(( + generation_hook.map_or(Hook::Empty, |hook| { + Hook::Function(hook as *const libc::c_void) + }), + Hook::Empty, + Hook::Empty, + Hook::Empty, + Hook::Empty, + Hook::Empty, + )); } - self.emulator - .set_gen_write_hook(gen_write_hook_wrapper::); } - pub fn write_generation_closure( + pub fn cmps( &self, - hook: Box, usize) -> Option>, + generation_hook: Option< + fn(&mut Self, Option<&mut S>, pc: GuestAddr, size: usize) -> Option, + >, + execution_hook1: Option, id: u64, v0: u8, v1: u8)>, + execution_hook2: Option, id: u64, v0: u16, v1: u16)>, + execution_hook4: Option, id: u64, v0: u32, v1: u32)>, + execution_hook8: Option, id: u64, v0: u64, v1: u64)>, ) { unsafe { - GEN_WRITE_HOOK = Hook::Closure(transmute(hook)); + let index = CMP_HOOKS.len(); + self.emulator.add_cmp_hooks( + if generation_hook.is_none() { + None + } else { + Some(gen_cmp_hook_wrapper::) + }, + if execution_hook1.is_none() { + None + } else { + Some(exec_cmp1_hook_wrapper::) + }, + if execution_hook2.is_none() { + None + } else { + Some(exec_cmp2_hook_wrapper::) + }, + if execution_hook4.is_none() { + None + } else { + Some(exec_cmp4_hook_wrapper::) + }, + if execution_hook8.is_none() { + None + } else { + Some(exec_cmp8_hook_wrapper::) + }, + index as u64, + ); + CMP_HOOKS.push(( + generation_hook.map_or(Hook::Empty, |hook| { + Hook::Function(hook as *const libc::c_void) + }), + execution_hook1.map_or(Hook::Empty, |hook| { + Hook::Function(hook as *const libc::c_void) + }), + execution_hook2.map_or(Hook::Empty, |hook| { + Hook::Function(hook as *const libc::c_void) + }), + execution_hook4.map_or(Hook::Empty, |hook| { + Hook::Function(hook as *const libc::c_void) + }), + execution_hook8.map_or(Hook::Empty, |hook| { + Hook::Function(hook as *const libc::c_void) + }), + )); } - self.emulator - .set_gen_write_hook(gen_write_hook_wrapper::); } - pub fn write1_execution(&self, hook: FixedLenHookFn) { - unsafe { - WRITE1_HOOKS.push(Hook::Function(hook as *const libc::c_void)); - } - self.emulator - .set_exec_write1_hook(write1_hooks_wrapper::); - } - - pub fn write1_execution_closure(&self, hook: FixedLenHookCl) { - unsafe { - WRITE1_HOOKS.push(Hook::Closure(transmute(hook))); - } - self.emulator - .set_exec_write1_hook(write1_hooks_wrapper::); - } - - pub fn write2_execution(&self, hook: FixedLenHookFn) { - unsafe { - WRITE2_HOOKS.push(Hook::Function(hook as *const libc::c_void)); - } - self.emulator - .set_exec_write2_hook(write2_hooks_wrapper::); - } - - pub fn write2_execution_closure(&self, hook: FixedLenHookCl) { - unsafe { - WRITE2_HOOKS.push(Hook::Closure(transmute(hook))); - } - self.emulator - .set_exec_write2_hook(write2_hooks_wrapper::); - } - - pub fn write4_execution(&self, hook: FixedLenHookFn) { - unsafe { - WRITE4_HOOKS.push(Hook::Function(hook as *const libc::c_void)); - } - self.emulator - .set_exec_write4_hook(write4_hooks_wrapper::); - } - - pub fn write4_execution_closure(&self, hook: FixedLenHookCl) { - unsafe { - WRITE4_HOOKS.push(Hook::Closure(transmute(hook))); - } - self.emulator - .set_exec_write4_hook(write4_hooks_wrapper::); - } - - pub fn write8_execution(&self, hook: FixedLenHookFn) { - unsafe { - WRITE8_HOOKS.push(Hook::Function(hook as *const libc::c_void)); - } - self.emulator - .set_exec_write8_hook(write8_hooks_wrapper::); - } - - pub fn write8_execution_closure(&self, hook: FixedLenHookCl) { - unsafe { - WRITE8_HOOKS.push(Hook::Closure(transmute(hook))); - } - self.emulator - .set_exec_write8_hook(write8_hooks_wrapper::); - } - - pub fn write_n_execution(&self, hook: DynamicLenHookFn) { - unsafe { - WRITE_N_HOOKS.push(Hook::Function(hook as *const libc::c_void)); - } - self.emulator - .set_exec_write_n_hook(write_n_hooks_wrapper::); - } - - pub fn write_n_execution_closure(&self, hook: DynamicLenHookCl) { - unsafe { - WRITE_N_HOOKS.push(Hook::Closure(transmute(hook))); - } - self.emulator - .set_exec_write_n_hook(write_n_hooks_wrapper::); - } - - pub fn cmp_generation( + pub unsafe fn cmps_closures( &self, - hook: fn(&Emulator, &mut QT, Option<&mut S>, pc: u64, size: usize) -> Option, + generation_hook: Option< + Box, GuestAddr, usize) -> Option>, + >, + execution_hook1: Option, u64, u8, u8)>>, + execution_hook2: Option, u64, u16, u16)>>, + execution_hook4: Option, u64, u32, u32)>>, + execution_hook8: Option, u64, u64, u64)>>, + ) { + let index = CMP_HOOKS.len(); + self.emulator.add_cmp_hooks( + if generation_hook.is_none() { + None + } else { + Some(gen_cmp_hook_wrapper::) + }, + if execution_hook1.is_none() { + None + } else { + Some(exec_cmp1_hook_wrapper::) + }, + if execution_hook2.is_none() { + None + } else { + Some(exec_cmp2_hook_wrapper::) + }, + if execution_hook4.is_none() { + None + } else { + Some(exec_cmp4_hook_wrapper::) + }, + if execution_hook8.is_none() { + None + } else { + Some(exec_cmp8_hook_wrapper::) + }, + index as u64, + ); + CMP_HOOKS.push(( + generation_hook.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))), + execution_hook1.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))), + execution_hook2.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))), + execution_hook4.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))), + execution_hook8.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))), + )); + } + + pub fn cmps_raw( + &self, + generation_hook: Option< + fn(&mut Self, Option<&mut S>, pc: GuestAddr, size: usize) -> Option, + >, + execution_hook1: Option, + execution_hook2: Option, + execution_hook4: Option, + execution_hook8: Option, ) { unsafe { - GEN_CMP_HOOK = Hook::Function(hook as *const libc::c_void); + let index = CMP_HOOKS.len(); + self.emulator.add_cmp_hooks( + if generation_hook.is_none() { + None + } else { + Some(gen_cmp_hook_wrapper::) + }, + execution_hook1, + execution_hook2, + execution_hook4, + execution_hook8, + index as u64, + ); + CMP_HOOKS.push(( + generation_hook.map_or(Hook::Empty, |hook| { + Hook::Function(hook as *const libc::c_void) + }), + Hook::Empty, + Hook::Empty, + Hook::Empty, + Hook::Empty, + )); } - self.emulator - .set_gen_cmp_hook(gen_cmp_hook_wrapper::); } - pub fn cmp_generation_closure( - &self, - hook: Box, u64, usize) -> Option>, - ) { - unsafe { - GEN_CMP_HOOK = Hook::Closure(transmute(hook)); - } - self.emulator - .set_gen_cmp_hook(gen_cmp_hook_wrapper::); - } - - pub fn cmp1_execution( - &self, - hook: fn(&Emulator, &mut QT, Option<&mut S>, id: u64, v0: u8, v1: u8), - ) { - unsafe { - CMP1_HOOKS.push(Hook::Function(hook as *const libc::c_void)); - } - self.emulator - .set_exec_cmp1_hook(cmp1_hooks_wrapper::); - } - - pub fn cmp1_execution_closure( - &self, - hook: Box, u64, u8, u8)>, - ) { - unsafe { - CMP1_HOOKS.push(Hook::Closure(transmute(hook))); - } - self.emulator - .set_exec_cmp1_hook(cmp1_hooks_wrapper::); - } - - pub fn cmp2_execution( - &self, - hook: fn(&Emulator, &mut QT, Option<&mut S>, id: u64, v0: u16, v1: u16), - ) { - unsafe { - CMP2_HOOKS.push(Hook::Function(hook as *const libc::c_void)); - } - self.emulator - .set_exec_cmp2_hook(cmp2_hooks_wrapper::); - } - - pub fn cmp2_execution_closure( - &self, - hook: Box, u64, u16, u16)>, - ) { - unsafe { - CMP2_HOOKS.push(Hook::Closure(transmute(hook))); - } - self.emulator - .set_exec_cmp2_hook(cmp2_hooks_wrapper::); - } - - pub fn cmp4_execution( - &self, - hook: fn(&Emulator, &mut QT, Option<&mut S>, id: u64, v0: u32, v1: u32), - ) { - unsafe { - CMP4_HOOKS.push(Hook::Function(hook as *const libc::c_void)); - } - self.emulator - .set_exec_cmp4_hook(cmp4_hooks_wrapper::); - } - - pub fn cmp4_execution_closure( - &self, - hook: Box, u64, u32, u32)>, - ) { - unsafe { - CMP4_HOOKS.push(Hook::Closure(transmute(hook))); - } - self.emulator - .set_exec_cmp4_hook(cmp4_hooks_wrapper::); - } - - pub fn cmp8_execution( - &self, - hook: fn(&Emulator, &mut QT, Option<&mut S>, id: u64, v0: u64, v1: u64), - ) { - unsafe { - CMP8_HOOKS.push(Hook::Function(hook as *const libc::c_void)); - } - self.emulator - .set_exec_cmp8_hook(cmp8_hooks_wrapper::); - } - - pub fn cmp8_execution_closure( - &self, - hook: Box, u64, u64, u64)>, - ) { - unsafe { - CMP8_HOOKS.push(Hook::Closure(transmute(hook))); - } - self.emulator - .set_exec_cmp8_hook(cmp8_hooks_wrapper::); - } - - pub fn thread_creation(&self, hook: fn(&Emulator, Pin<&mut Self>, Option<&mut S>, tid: u32)) { + pub fn thread_creation(&self, hook: fn(&mut Self, Option<&mut S>, tid: u32)) { unsafe { ON_THREAD_HOOKS.push(Hook::Function(hook as *const libc::c_void)); } @@ -1555,7 +1429,7 @@ where pub fn thread_creation_closure( &self, - hook: Box, Option<&mut S>, u32) + 'a>, + hook: Box, u32) + 'a>, ) { unsafe { ON_THREAD_HOOKS.push(Hook::Closure(transmute(hook))); @@ -1564,10 +1438,7 @@ where .set_on_thread_hook(on_thread_hooks_wrapper::); } - pub fn thread_creation_once( - &self, - hook: Box, Option<&mut S>, u32) + 'a>, - ) { + pub fn thread_creation_once(&self, hook: Box, u32) + 'a>) { unsafe { ON_THREAD_HOOKS.push(Hook::Once(transmute(hook))); } @@ -1579,8 +1450,7 @@ where pub fn syscalls( &self, hook: fn( - &Emulator, - &mut QT, + &mut Self, Option<&mut S>, sys_num: i32, u64, @@ -1605,8 +1475,7 @@ where &self, hook: Box< dyn FnMut( - &Emulator, - &mut QT, + &mut Self, Option<&mut S>, i32, u64, @@ -1631,8 +1500,7 @@ where pub fn after_syscalls( &self, hook: fn( - &Emulator, - &mut QT, + &mut Self, Option<&mut S>, result: u64, sys_num: i32, @@ -1658,8 +1526,7 @@ where &self, hook: Box< dyn FnMut( - &Emulator, - &mut QT, + &mut Self, Option<&mut S>, u64, i32, diff --git a/libafl_qemu/src/i386.rs b/libafl_qemu/src/i386.rs index 477ea22d37..bdfcc95b53 100644 --- a/libafl_qemu/src/i386.rs +++ b/libafl_qemu/src/i386.rs @@ -4,6 +4,8 @@ pub use strum_macros::EnumIter; #[cfg(feature = "python")] use pyo3::prelude::*; +use capstone::arch::BuildsCapstone; + pub use syscall_numbers::x86::*; #[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)] @@ -35,3 +37,10 @@ impl IntoPy for Regs { n.into_py(py) } } + +/// Return an X86 ArchCapstoneBuilder +pub fn capstone() -> capstone::arch::x86::ArchCapstoneBuilder { + capstone::Capstone::new() + .x86() + .mode(capstone::arch::x86::ArchMode::Mode32) +} diff --git a/libafl_qemu/src/lib.rs b/libafl_qemu/src/lib.rs index a28a0071d8..d812deecfe 100644 --- a/libafl_qemu/src/lib.rs +++ b/libafl_qemu/src/lib.rs @@ -7,8 +7,12 @@ allow(clippy::useless_conversion) )] #![allow(clippy::needless_pass_by_value)] +#![allow(clippy::transmute_ptr_to_ptr)] +#![allow(clippy::too_many_arguments)] // Till they fix this buggy lint in clippy -#![allow(clippy::borrow_deref_ref)] +#![allow(clippy::borrow_as_ptr)] +// Allow only ATM, it will be evetually removed +#![allow(clippy::missing_safety_doc)] use std::env; @@ -50,6 +54,8 @@ pub use snapshot::QemuSnapshotHelper; pub mod asan; pub use asan::{init_with_asan, QemuAsanHelper}; +pub mod calls; + pub mod executor; pub use executor::{QemuExecutor, QemuForkExecutor}; diff --git a/libafl_qemu/src/snapshot.rs b/libafl_qemu/src/snapshot.rs index 5ee988ce4b..fc9ba747aa 100644 --- a/libafl_qemu/src/snapshot.rs +++ b/libafl_qemu/src/snapshot.rs @@ -3,7 +3,6 @@ use libafl::{inputs::Input, state::HasMetadata}; use std::{ cell::UnsafeCell, collections::{HashMap, HashSet}, - pin::Pin, sync::Mutex, }; use thread_local::ThreadLocal; @@ -197,15 +196,18 @@ where I: Input, S: HasMetadata, { - fn init_hooks<'a, QT>(&self, hooks: Pin<&QemuHooks<'a, I, QT, S>>) + fn init_hooks(&self, hooks: &QemuHooks<'_, I, QT, S>) where QT: QemuHelperTuple, { - hooks.write8_execution(trace_write8_snapshot::); - hooks.write4_execution(trace_write4_snapshot::); - hooks.write2_execution(trace_write2_snapshot::); - hooks.write1_execution(trace_write1_snapshot::); - hooks.write_n_execution(trace_write_n_snapshot::); + hooks.writes( + None, + Some(trace_write1_snapshot::), + Some(trace_write2_snapshot::), + Some(trace_write4_snapshot::), + Some(trace_write8_snapshot::), + Some(trace_write_n_snapshot::), + ); hooks.after_syscalls(trace_mmap_snapshot::); } @@ -220,8 +222,7 @@ where } pub fn trace_write1_snapshot( - _emulator: &Emulator, - helpers: &mut QT, + hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, _id: u64, addr: GuestAddr, @@ -229,15 +230,12 @@ pub fn trace_write1_snapshot( I: Input, QT: QemuHelperTuple, { - let h = helpers - .match_first_type_mut::() - .unwrap(); + let h = hooks.match_helper_mut::().unwrap(); h.access(addr, 1); } pub fn trace_write2_snapshot( - _emulator: &Emulator, - helpers: &mut QT, + hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, _id: u64, addr: GuestAddr, @@ -245,15 +243,12 @@ pub fn trace_write2_snapshot( I: Input, QT: QemuHelperTuple, { - let h = helpers - .match_first_type_mut::() - .unwrap(); + let h = hooks.match_helper_mut::().unwrap(); h.access(addr, 2); } pub fn trace_write4_snapshot( - _emulator: &Emulator, - helpers: &mut QT, + hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, _id: u64, addr: GuestAddr, @@ -261,15 +256,12 @@ pub fn trace_write4_snapshot( I: Input, QT: QemuHelperTuple, { - let h = helpers - .match_first_type_mut::() - .unwrap(); + let h = hooks.match_helper_mut::().unwrap(); h.access(addr, 4); } pub fn trace_write8_snapshot( - _emulator: &Emulator, - helpers: &mut QT, + hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, _id: u64, addr: GuestAddr, @@ -277,15 +269,12 @@ pub fn trace_write8_snapshot( I: Input, QT: QemuHelperTuple, { - let h = helpers - .match_first_type_mut::() - .unwrap(); + let h = hooks.match_helper_mut::().unwrap(); h.access(addr, 8); } pub fn trace_write_n_snapshot( - _emulator: &Emulator, - helpers: &mut QT, + hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, _id: u64, addr: GuestAddr, @@ -294,17 +283,14 @@ pub fn trace_write_n_snapshot( I: Input, QT: QemuHelperTuple, { - let h = helpers - .match_first_type_mut::() - .unwrap(); + let h = hooks.match_helper_mut::().unwrap(); h.access(addr, size); } #[allow(clippy::too_many_arguments)] #[allow(non_upper_case_globals)] pub fn trace_mmap_snapshot( - _emulator: &Emulator, - helpers: &mut QT, + hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, result: u64, sys_num: i32, @@ -324,51 +310,37 @@ where // NOT A COMPLETE LIST OF MEMORY EFFECTS match i64::from(sys_num) { SYS_read | SYS_pread64 => { - let h = helpers - .match_first_type_mut::() - .unwrap(); + let h = hooks.match_helper_mut::().unwrap(); h.access(a1 as GuestAddr, a2 as usize); } SYS_readlinkat => { - let h = helpers - .match_first_type_mut::() - .unwrap(); + let h = hooks.match_helper_mut::().unwrap(); h.access(a2 as GuestAddr, a3 as usize); } SYS_futex => { - let h = helpers - .match_first_type_mut::() - .unwrap(); + let h = hooks.match_helper_mut::().unwrap(); h.access(a0 as GuestAddr, a3 as usize); } #[cfg(not(cpu_target = "arm"))] SYS_newfstatat => { if a2 != 0 { - let h = helpers - .match_first_type_mut::() - .unwrap(); + let h = hooks.match_helper_mut::().unwrap(); h.access(a2 as GuestAddr, 4096); // stat is not greater than a page } } #[cfg(cpu_target = "arm")] SYS_fstatat64 => { if a2 != 0 { - let h = helpers - .match_first_type_mut::() - .unwrap(); + let h = hooks.match_helper_mut::().unwrap(); h.access(a2 as GuestAddr, 4096); // stat is not greater than a page } } SYS_statfs | SYS_fstatfs | SYS_fstat => { - let h = helpers - .match_first_type_mut::() - .unwrap(); + let h = hooks.match_helper_mut::().unwrap(); h.access(a1 as GuestAddr, 4096); // stat is not greater than a page } SYS_getrandom => { - let h = helpers - .match_first_type_mut::() - .unwrap(); + let h = hooks.match_helper_mut::().unwrap(); h.access(a0 as GuestAddr, a1 as usize); } // mmap syscalls @@ -382,9 +354,7 @@ where #[cfg(cpu_target = "arm")] if i64::from(sys_num) == SYS_mmap2 { if let Ok(prot) = MmapPerms::try_from(a2 as i32) { - let h = helpers - .match_first_type_mut::() - .unwrap(); + let h = hooks.match_helper_mut::().unwrap(); h.add_mapped(result as GuestAddr, a1 as usize, Some(prot)); } } else if i64::from(sys_num) == SYS_mremap { @@ -394,9 +364,7 @@ where h.add_mapped(result as GuestAddr, a2 as usize, None); } else if i64::from(sys_num) == SYS_mprotect { if let Ok(prot) = MmapPerms::try_from(a2 as i32) { - let h = helpers - .match_first_type_mut::() - .unwrap(); + let h = hooks.match_helper_mut::().unwrap(); h.add_mapped(a0 as GuestAddr, a2 as usize, Some(prot)); } } @@ -404,21 +372,15 @@ where #[cfg(not(cpu_target = "arm"))] if i64::from(sys_num) == SYS_mmap { if let Ok(prot) = MmapPerms::try_from(a2 as i32) { - let h = helpers - .match_first_type_mut::() - .unwrap(); + let h = hooks.match_helper_mut::().unwrap(); h.add_mapped(result as GuestAddr, a1 as usize, Some(prot)); } } else if i64::from(sys_num) == SYS_mremap { - let h = helpers - .match_first_type_mut::() - .unwrap(); + let h = hooks.match_helper_mut::().unwrap(); h.add_mapped(result as GuestAddr, a2 as usize, None); } else if i64::from(sys_num) == SYS_mprotect { if let Ok(prot) = MmapPerms::try_from(a2 as i32) { - let h = helpers - .match_first_type_mut::() - .unwrap(); + let h = hooks.match_helper_mut::().unwrap(); h.add_mapped(a0 as GuestAddr, a2 as usize, Some(prot)); } } diff --git a/libafl_qemu/src/x86_64.rs b/libafl_qemu/src/x86_64.rs index 5d5fc0f5f1..b92f214780 100644 --- a/libafl_qemu/src/x86_64.rs +++ b/libafl_qemu/src/x86_64.rs @@ -4,6 +4,8 @@ pub use strum_macros::EnumIter; #[cfg(feature = "python")] use pyo3::prelude::*; +use capstone::arch::BuildsCapstone; + pub use syscall_numbers::x86_64::*; #[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)] @@ -43,3 +45,11 @@ impl IntoPy for Regs { n.into_py(py) } } + +/// Return an X86 `ArchCapstoneBuilder` +#[must_use] +pub fn capstone() -> capstone::arch::x86::ArchCapstoneBuilder { + capstone::Capstone::new() + .x86() + .mode(capstone::arch::x86::ArchMode::Mode64) +} diff --git a/libafl_sugar/src/qemu.rs b/libafl_sugar/src/qemu.rs index baa8055c0c..c2e3e705cf 100644 --- a/libafl_sugar/src/qemu.rs +++ b/libafl_sugar/src/qemu.rs @@ -207,7 +207,7 @@ where }; if self.use_cmplog.unwrap_or(false) { - let hooks = QemuHooks::new( + let mut hooks = QemuHooks::new( emulator, tuple_list!( QemuEdgeCoverageHelper::default(), @@ -216,7 +216,7 @@ where ); let executor = QemuExecutor::new( - hooks, + &mut hooks, &mut harness, tuple_list!(edges_observer, time_observer), &mut fuzzer, @@ -316,11 +316,11 @@ where } } } else { - let hooks = + let mut hooks = QemuHooks::new(emulator, tuple_list!(QemuEdgeCoverageHelper::default())); let executor = QemuExecutor::new( - hooks, + &mut hooks, &mut harness, tuple_list!(edges_observer, time_observer), &mut fuzzer,