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
This commit is contained in:
Andrea Fioraldi 2022-06-16 11:09:07 +02:00 committed by GitHub
parent 93048f6270
commit 7147170240
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 1547 additions and 1525 deletions

View File

@ -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,

View File

@ -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,

View File

@ -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).

View File

@ -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")]

View File

@ -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);
}

View File

@ -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 }

View File

@ -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 {

View File

@ -59,3 +59,8 @@ impl IntoPy<PyObject> for Regs {
n.into_py(py)
}
}
/// Return an ARM64 ArchCapstoneBuilder
pub fn capstone() -> capstone::arch::arm64::ArchCapstoneBuilder {
capstone::Capstone::new().arm()
}

View File

@ -47,3 +47,8 @@ impl IntoPy<PyObject> for Regs {
n.into_py(py)
}
}
/// Return an ARM ArchCapstoneBuilder
pub fn capstone() -> capstone::arch::arm::ArchCapstoneBuilder {
capstone::Capstone::new().arm()
}

View File

@ -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<QT>(&self, hooks: &QemuHooks<'_, I, QT, S>)
where
QT: QemuHelperTuple<I, S>,
{
//hooks.read_generation(gen_readwrite_asan::<I, QT, S>);
hooks.read8_execution(trace_read8_asan::<I, QT, S>);
hooks.read4_execution(trace_read4_asan::<I, QT, S>);
hooks.read2_execution(trace_read2_asan::<I, QT, S>);
hooks.read1_execution(trace_read1_asan::<I, QT, S>);
hooks.read_n_execution(trace_read_n_asan::<I, QT, S>);
hooks.reads(
Some(gen_readwrite_asan::<I, QT, S>),
Some(trace_read1_asan::<I, QT, S>),
Some(trace_read2_asan::<I, QT, S>),
Some(trace_read4_asan::<I, QT, S>),
Some(trace_read8_asan::<I, QT, S>),
Some(trace_read_n_asan::<I, QT, S>),
);
//hooks.write_generation(gen_readwrite_asan::<I, QT, S>);
hooks.write8_execution(trace_write8_asan::<I, QT, S>);
hooks.write4_execution(trace_write4_asan::<I, QT, S>);
hooks.write2_execution(trace_write2_asan::<I, QT, S>);
hooks.write1_execution(trace_write1_asan::<I, QT, S>);
hooks.write_n_execution(trace_write_n_asan::<I, QT, S>);
hooks.writes(
Some(gen_readwrite_asan::<I, QT, S>),
Some(trace_write1_asan::<I, QT, S>),
Some(trace_write2_asan::<I, QT, S>),
Some(trace_write4_asan::<I, QT, S>),
Some(trace_write8_asan::<I, QT, S>),
Some(trace_write_n_asan::<I, QT, S>),
);
hooks.syscalls(qasan_fake_syscall::<I, QT, S>);
}
@ -439,11 +443,8 @@ where
}
}
/*
// TODO add pc to generation hooks
pub fn gen_readwrite_asan<I, QT, S>(
_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<I, S>,
{
let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
if h.must_instrument(pc) {
Some(pc)
} else {
None
}
}
*/
pub fn trace_read1_asan<I, QT, S>(
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, QT, S>(
I: Input,
QT: QemuHelperTuple<I, S>,
{
let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap();
h.read_1(emulator, addr);
let emulator = hooks.emulator().clone();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
h.read_1(&emulator, addr);
}
pub fn trace_read2_asan<I, QT, S>(
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, QT, S>(
I: Input,
QT: QemuHelperTuple<I, S>,
{
let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap();
h.read_2(emulator, addr);
let emulator = hooks.emulator().clone();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
h.read_2(&emulator, addr);
}
pub fn trace_read4_asan<I, QT, S>(
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, QT, S>(
I: Input,
QT: QemuHelperTuple<I, S>,
{
let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap();
h.read_4(emulator, addr);
let emulator = hooks.emulator().clone();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
h.read_4(&emulator, addr);
}
pub fn trace_read8_asan<I, QT, S>(
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, QT, S>(
I: Input,
QT: QemuHelperTuple<I, S>,
{
let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap();
h.read_8(emulator, addr);
let emulator = hooks.emulator().clone();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
h.read_8(&emulator, addr);
}
pub fn trace_read_n_asan<I, QT, S>(
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, QT, S>(
I: Input,
QT: QemuHelperTuple<I, S>,
{
let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap();
h.read_n(emulator, addr, size);
let emulator = hooks.emulator().clone();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
h.read_n(&emulator, addr, size);
}
pub fn trace_write1_asan<I, QT, S>(
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, QT, S>(
I: Input,
QT: QemuHelperTuple<I, S>,
{
let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap();
h.write_1(emulator, addr);
let emulator = hooks.emulator().clone();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
h.write_1(&emulator, addr);
}
pub fn trace_write2_asan<I, QT, S>(
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, QT, S>(
I: Input,
QT: QemuHelperTuple<I, S>,
{
let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap();
h.write_2(emulator, addr);
let emulator = hooks.emulator().clone();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
h.write_2(&emulator, addr);
}
pub fn trace_write4_asan<I, QT, S>(
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, QT, S>(
I: Input,
QT: QemuHelperTuple<I, S>,
{
let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap();
h.write_4(emulator, addr);
let emulator = hooks.emulator().clone();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
h.write_4(&emulator, addr);
}
pub fn trace_write8_asan<I, QT, S>(
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, QT, S>(
I: Input,
QT: QemuHelperTuple<I, S>,
{
let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap();
h.write_8(emulator, addr);
let emulator = hooks.emulator().clone();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
h.write_8(&emulator, addr);
}
pub fn trace_write_n_asan<I, QT, S>(
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, QT, S>(
I: Input,
QT: QemuHelperTuple<I, S>,
{
let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap();
h.read_n(emulator, addr, size);
let emulator = hooks.emulator().clone();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
h.read_n(&emulator, addr, size);
}
#[allow(clippy::too_many_arguments)]
pub fn qasan_fake_syscall<I, QT, S>(
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<I, S>,
{
if sys_num == QASAN_FAKESYS_NR {
let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap();
let emulator = hooks.emulator().clone();
let h = hooks.match_helper_mut::<QemuAsanHelper>().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);

182
libafl_qemu/src/calls.rs Normal file
View File

@ -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<GuestAddr>,
}
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<I, S> QemuHelper<I, S> for QemuCallTracerHelper
where
I: Input,
{
fn init_hooks<'a, QT>(&self, hooks: &QemuHooks<'a, I, QT, S>)
where
QT: QemuHelperTuple<I, S>,
{
hooks.blocks(Some(gen_blocks_calls::<I, QT, S>), None);
}
fn pre_exec(&mut self, _emulator: &Emulator, _input: &I) {
self.reset();
}
}
/*pub fn on_call<I, QT, S>(hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, pc: GuestAddr)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
}*/
pub fn on_ret<I, QT, S>(hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, _pc: GuestAddr)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
#[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::<QemuCallTracerHelper>()
{
while let Some(addr) = h.callstack.pop() {
if addr == ret_addr {
break;
}
}
}
}
pub fn gen_blocks_calls<I, QT, S>(
hooks: &mut QemuHooks<'_, I, QT, S>,
_state: Option<&mut S>,
pc: GuestAddr,
) -> Option<u64>
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let emu = hooks.emulator();
if let Some(h) = hooks.helpers().match_first_type::<QemuCallTracerHelper>() {
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::<QemuCallTracerHelper>()
{
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
}

View File

@ -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<QT>(&self, hooks: &QemuHooks<'_, I, QT, S>)
where
QT: QemuHelperTuple<I, S>,
{
hooks.cmp_generation(gen_unique_cmp_ids::<I, QT, S>);
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::<I, QT, S>),
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<QT>(&self, hooks: &QemuHooks<'_, I, QT, S>)
where
QT: QemuHelperTuple<I, S>,
{
hooks.cmp_generation(gen_hashed_cmp_ids::<I, QT, S>);
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::<I, QT, S>),
Some(trace_cmp1_cmplog),
Some(trace_cmp2_cmplog),
Some(trace_cmp4_cmplog),
Some(trace_cmp8_cmplog),
);
}
}
pub fn gen_unique_cmp_ids<I, QT, S>(
_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<I, S>,
{
if let Some(h) = helpers.match_first_type::<QemuCmpLogHelper>() {
if let Some(h) = hooks.match_helper_mut::<QemuCmpLogHelper>() {
if !h.must_instrument(pc) {
return None;
}
@ -147,8 +148,7 @@ where
}
pub fn gen_hashed_cmp_ids<I, QT, S>(
_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<I, S>,
{
if let Some(h) = helpers.match_first_type::<QemuCmpLogChildHelper>() {
if let Some(h) = hooks.match_helper_mut::<QemuCmpLogChildHelper>() {
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);
}

View File

@ -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<QT>(&self, hooks: &QemuHooks<'_, I, QT, S>)
where
QT: QemuHelperTuple<I, S>,
{
hooks.edge_generation(gen_unique_edge_ids::<I, QT, S>);
if self.use_hitcounts {
hooks.emulator().set_exec_edge_hook(trace_edge_hitcount);
hooks.edges_raw(
Some(gen_unique_edge_ids::<I, QT, S>),
Some(trace_edge_hitcount),
);
} else {
hooks.emulator().set_exec_edge_hook(trace_edge_single);
hooks.edges_raw(
Some(gen_unique_edge_ids::<I, QT, S>),
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<QT>(&self, hooks: &QemuHooks<'_, I, QT, S>)
where
QT: QemuHelperTuple<I, S>,
{
hooks.edge_generation(gen_hashed_edge_ids::<I, QT, S>);
if self.use_hitcounts {
hooks.emulator().set_exec_edge_hook(trace_edge_hitcount_ptr);
hooks.edges_raw(
Some(gen_hashed_edge_ids::<I, QT, S>),
Some(trace_edge_hitcount_ptr),
);
} else {
hooks.emulator().set_exec_edge_hook(trace_edge_single_ptr);
hooks.edges_raw(
Some(gen_hashed_edge_ids::<I, QT, S>),
Some(trace_edge_single_ptr),
);
}
}
}
@ -143,18 +153,17 @@ where
thread_local!(static PREV_LOC : UnsafeCell<u64> = UnsafeCell::new(0));
pub fn gen_unique_edge_ids<I, QT, S>(
_emulator: &Emulator,
helpers: &mut QT,
hooks: &mut QemuHooks<'_, I, QT, S>,
state: Option<&mut S>,
src: u64,
dest: u64,
src: GuestAddr,
dest: GuestAddr,
) -> Option<u64>
where
S: HasMetadata,
I: Input,
QT: QemuHelperTuple<I, S>,
{
if let Some(h) = helpers.match_first_type::<QemuEdgeCoverageHelper>() {
if let Some(h) = hooks.helpers().match_first_type::<QemuEdgeCoverageHelper>() {
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<I, QT, S>(
_emulator: &Emulator,
helpers: &mut QT,
hooks: &mut QemuHooks<'_, I, QT, S>,
_state: Option<&mut S>,
src: u64,
dest: u64,
src: GuestAddr,
dest: GuestAddr,
) -> Option<u64>
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
if let Some(h) = helpers.match_first_type::<QemuEdgeCoverageChildHelper>() {
if let Some(h) = hooks
.helpers()
.match_first_type::<QemuEdgeCoverageChildHelper>()
{
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<I, QT, S>(
_emulator: &Emulator,
_helpers: &mut QT,
_hooks: &mut QemuHooks<'_, I, QT, S>,
_state: Option<&mut S>,
pc: u64,
) -> Option<u64> {
Some(pc)
pc: GuestAddr,
) -> Option<u64>
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
Some(pc as u64)
}
pub fn gen_hashed_block_ids<I, QT, S>(
_emulator: &Emulator,
_helpers: &mut QT,
_hooks: &mut QemuHooks<'_, I, QT, S>,
_state: Option<&mut S>,
pc: u64,
) -> Option<u64> {
Some(hash_me(pc))
pc: GuestAddr,
) -> Option<u64>
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
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);

View File

@ -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<extern "C" fn(GuestAddr, GuestAddr, u64) -> u64>,
exec: Option<extern "C" fn(u64, u64)>,
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<extern "C" fn(GuestAddr, u64) -> u64>,
exec: Option<extern "C" fn(u64, u64)>,
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<extern "C" fn(GuestAddr, usize, u64) -> u64>,
exec1: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec2: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec4: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec8: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec_n: Option<extern "C" fn(u64, GuestAddr, usize, u64)>,
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<extern "C" fn(GuestAddr, usize, u64) -> u64>,
exec1: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec2: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec4: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec8: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec_n: Option<extern "C" fn(u64, GuestAddr, usize, u64)>,
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<extern "C" fn(GuestAddr, usize, u64) -> u64>,
exec1: Option<extern "C" fn(u64, u8, u8, u64)>,
exec2: Option<extern "C" fn(u64, u16, u16, u64)>,
exec4: Option<extern "C" fn(u64, u32, u32, u64)>,
exec8: Option<extern "C" fn(u64, u64, u64, u64)>,
data: u64,
);
static mut libafl_on_thread_hook: unsafe extern "C" fn(u32);
@ -323,10 +367,10 @@ static mut GDB_COMMANDS: Vec<FatPtr> = vec![];
extern "C" fn gdb_cmd(buf: *const u8, len: usize, data: *const ()) -> i32 {
unsafe {
let closure =
&mut *(data as *mut std::boxed::Box<dyn for<'r> std::ops::FnMut(&'r str) -> bool>);
let closure = &mut *(data as *mut Box<dyn for<'r> 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<extern "C" fn(GuestAddr, GuestAddr, u64) -> u64>,
exec: Option<extern "C" fn(u64, u64)>,
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<extern "C" fn(GuestAddr, u64) -> u64>,
exec: Option<extern "C" fn(u64, u64)>,
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<extern "C" fn(GuestAddr, usize, u64) -> u64>,
exec1: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec2: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec4: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec8: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec_n: Option<extern "C" fn(u64, GuestAddr, usize, u64)>,
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<extern "C" fn(GuestAddr, usize, u64) -> u64>,
exec1: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec2: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec4: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec8: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec_n: Option<extern "C" fn(u64, GuestAddr, usize, u64)>,
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<extern "C" fn(GuestAddr, usize, u64) -> u64>,
exec1: Option<extern "C" fn(u64, u8, u8, u64)>,
exec2: Option<extern "C" fn(u64, u16, u16, u64)>,
exec4: Option<extern "C" fn(u64, u32, u32, u64)>,
exec8: Option<extern "C" fn(u64, u64, u64, u64)>,
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<dyn FnMut(&str) -> bool>) {
pub fn add_gdb_cmd(&self, callback: Box<dyn FnMut(&Self, &str) -> 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)
}
}
}

View File

@ -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<I, S>,
QT: QemuHelperTuple<I, S>,
{
hooks: Pin<Box<QemuHooks<'a, I, QT, S>>>,
hooks: &'a mut QemuHooks<'a, I, QT, S>,
inner: InProcessExecutor<'a, H, I, OT, S>,
}
@ -53,7 +50,7 @@ where
QT: QemuHelperTuple<I, S>,
{
pub fn new<EM, OF, Z>(
hooks: Pin<Box<QemuHooks<'a, I, QT, S>>>,
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<Box<QemuHooks<'a, I, QT, S>>> {
&self.hooks
pub fn hooks(&self) -> &QemuHooks<'a, I, QT, S> {
self.hooks
}
pub fn hooks_mut(&mut self) -> &mut Pin<Box<QemuHooks<'a, I, QT, S>>> {
&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<ExitKind, Error> {
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<I, S>,
SP: ShMemProvider,
{
hooks: Pin<Box<QemuHooks<'a, I, QT, S>>>,
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<EM, OF, Z>(
hooks: Pin<Box<QemuHooks<'a, I, QT, S>>>,
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<Box<QemuHooks<'a, I, QT, S>>> {
&self.hooks
pub fn hooks(&self) -> &QemuHooks<'a, I, QT, S> {
self.hooks
}
pub fn hooks_mut(&mut self) -> &mut Pin<Box<QemuHooks<'a, I, QT, S>>> {
&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<ExitKind, Error> {
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
}
}

View File

@ -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<QT>(&self, _hooks: &QemuHooks<'_, I, QT, S>)
where
QT: QemuHelperTuple<I, S>,
{
@ -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<QT>(&self, hooks: &QemuHooks<'_, I, QT, S>)
where
QT: QemuHelperTuple<I, S>;
@ -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<QT>(&self, _hooks: &QemuHooks<'_, I, QT, S>)
where
QT: QemuHelperTuple<I, S>,
{
@ -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<QT>(&self, hooks: &QemuHooks<'_, I, QT, S>)
where
QT: QemuHelperTuple<I, S>,
{

File diff suppressed because it is too large Load Diff

View File

@ -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<PyObject> 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)
}

View File

@ -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};

View File

@ -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<QT>(&self, hooks: &QemuHooks<'_, I, QT, S>)
where
QT: QemuHelperTuple<I, S>,
{
hooks.write8_execution(trace_write8_snapshot::<I, QT, S>);
hooks.write4_execution(trace_write4_snapshot::<I, QT, S>);
hooks.write2_execution(trace_write2_snapshot::<I, QT, S>);
hooks.write1_execution(trace_write1_snapshot::<I, QT, S>);
hooks.write_n_execution(trace_write_n_snapshot::<I, QT, S>);
hooks.writes(
None,
Some(trace_write1_snapshot::<I, QT, S>),
Some(trace_write2_snapshot::<I, QT, S>),
Some(trace_write4_snapshot::<I, QT, S>),
Some(trace_write8_snapshot::<I, QT, S>),
Some(trace_write_n_snapshot::<I, QT, S>),
);
hooks.after_syscalls(trace_mmap_snapshot::<I, QT, S>);
}
@ -220,8 +222,7 @@ where
}
pub fn trace_write1_snapshot<I, QT, S>(
_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, QT, S>(
I: Input,
QT: QemuHelperTuple<I, S>,
{
let h = helpers
.match_first_type_mut::<QemuSnapshotHelper>()
.unwrap();
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
h.access(addr, 1);
}
pub fn trace_write2_snapshot<I, QT, S>(
_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, QT, S>(
I: Input,
QT: QemuHelperTuple<I, S>,
{
let h = helpers
.match_first_type_mut::<QemuSnapshotHelper>()
.unwrap();
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
h.access(addr, 2);
}
pub fn trace_write4_snapshot<I, QT, S>(
_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, QT, S>(
I: Input,
QT: QemuHelperTuple<I, S>,
{
let h = helpers
.match_first_type_mut::<QemuSnapshotHelper>()
.unwrap();
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
h.access(addr, 4);
}
pub fn trace_write8_snapshot<I, QT, S>(
_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, QT, S>(
I: Input,
QT: QemuHelperTuple<I, S>,
{
let h = helpers
.match_first_type_mut::<QemuSnapshotHelper>()
.unwrap();
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
h.access(addr, 8);
}
pub fn trace_write_n_snapshot<I, QT, S>(
_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, QT, S>(
I: Input,
QT: QemuHelperTuple<I, S>,
{
let h = helpers
.match_first_type_mut::<QemuSnapshotHelper>()
.unwrap();
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
h.access(addr, size);
}
#[allow(clippy::too_many_arguments)]
#[allow(non_upper_case_globals)]
pub fn trace_mmap_snapshot<I, QT, S>(
_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::<QemuSnapshotHelper>()
.unwrap();
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
h.access(a1 as GuestAddr, a2 as usize);
}
SYS_readlinkat => {
let h = helpers
.match_first_type_mut::<QemuSnapshotHelper>()
.unwrap();
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
h.access(a2 as GuestAddr, a3 as usize);
}
SYS_futex => {
let h = helpers
.match_first_type_mut::<QemuSnapshotHelper>()
.unwrap();
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().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::<QemuSnapshotHelper>()
.unwrap();
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().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::<QemuSnapshotHelper>()
.unwrap();
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().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::<QemuSnapshotHelper>()
.unwrap();
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
h.access(a1 as GuestAddr, 4096); // stat is not greater than a page
}
SYS_getrandom => {
let h = helpers
.match_first_type_mut::<QemuSnapshotHelper>()
.unwrap();
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().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::<QemuSnapshotHelper>()
.unwrap();
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().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::<QemuSnapshotHelper>()
.unwrap();
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().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::<QemuSnapshotHelper>()
.unwrap();
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().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::<QemuSnapshotHelper>()
.unwrap();
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().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::<QemuSnapshotHelper>()
.unwrap();
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
h.add_mapped(a0 as GuestAddr, a2 as usize, Some(prot));
}
}

View File

@ -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<PyObject> 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)
}

View File

@ -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,