* Add post_gen

* Adopt post_gen hooks in DrCovHelper

* Bump qemu-libafl-bridge revision

---------

Co-authored-by: Your Name <you@example.com>
Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
This commit is contained in:
WorksButNotTested 2023-05-24 15:03:59 +01:00 committed by GitHub
parent 258780370f
commit 53dd6c6be6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 115 additions and 148 deletions

View File

@ -8,7 +8,7 @@ use which::which;
const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge"; const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
const QEMU_DIRNAME: &str = "qemu-libafl-bridge"; const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
const QEMU_REVISION: &str = "0dc52ed6f3915f727aaec8648706760f278f0571"; const QEMU_REVISION: &str = "6ae8b5bfb0bc4ac7a0b2ec463cf1000363836c0c";
fn build_dep_check(tools: &[&str]) { fn build_dep_check(tools: &[&str]) {
for tool in tools { for tool in tools {

View File

@ -1,100 +0,0 @@
use capstone::prelude::*;
use crate::{Emulator, GuestAddr};
pub struct Instruction {
pub start_addr: GuestAddr,
pub mnemonic: String,
pub operands: String,
pub insn_len: usize,
}
/*
* Generating the basic block from it's starting address (pc)
* Basic block:
* - Starting at pc
* - Ending at the first branch/jump/interrupt/call instruction
* Output:
* - Vector of instructions
* - Start address
* - mnemonic string
* - operand string
* - instruction length
*/
pub fn pc2basicblock(
pc: GuestAddr,
emu: &Emulator,
mode: Option<capstone::Mode>,
) -> Result<Vec<Instruction>, String> {
#[allow(unused_mut)]
let mut code = {
#[cfg(emulation_mode = "usermode")]
unsafe {
std::slice::from_raw_parts(emu.g2h(pc), 512)
}
#[cfg(emulation_mode = "systemmode")]
&mut [0; 512]
};
#[cfg(emulation_mode = "systemmode")]
unsafe {
emu.read_mem(pc, code)
};
// TODO better fault handling
if code.iter().all(|&x| x == 0) {
return Err("Memory region is empty".to_string());
}
let mut iaddr = pc;
let mut block = Vec::<Instruction>::new();
let mut cs = crate::capstone().detail(true).build().unwrap();
if let Some(m) = mode {
cs.set_mode(m).unwrap();
}
'disasm: while let Ok(insns) = cs.disasm_count(code, iaddr.into(), 1) {
if insns.is_empty() {
break;
}
let insn = insns.first().unwrap();
let insn_detail: InsnDetail = cs.insn_detail(insn).unwrap();
block.push(Instruction {
start_addr: insn.address() as GuestAddr,
mnemonic: insn.mnemonic().unwrap().to_string(),
operands: insn.op_str().unwrap().to_string(),
insn_len: insn.len(),
});
for detail in insn_detail.groups() {
match u32::from(detail.0) {
capstone::InsnGroupType::CS_GRP_BRANCH_RELATIVE
| capstone::InsnGroupType::CS_GRP_CALL
| capstone::InsnGroupType::CS_GRP_INT
| capstone::InsnGroupType::CS_GRP_INVALID
| capstone::InsnGroupType::CS_GRP_IRET
| capstone::InsnGroupType::CS_GRP_JUMP
| capstone::InsnGroupType::CS_GRP_PRIVILEGE
| capstone::InsnGroupType::CS_GRP_RET => {
break 'disasm;
}
_ => {}
}
}
iaddr += insn.bytes().len() as GuestAddr;
#[cfg(emulation_mode = "usermode")]
unsafe {
code = std::slice::from_raw_parts(emu.g2h(iaddr), 512);
}
#[cfg(emulation_mode = "systemmode")]
unsafe {
emu.read_mem(iaddr, code);
}
// TODO better fault handling
if code.iter().all(|&x| x == 0) {
return Err("Memory region is empty".to_string());
}
}
Ok(block)
}

View File

@ -414,7 +414,7 @@ where
where where
QT: QemuHelperTuple<S>, QT: QemuHelperTuple<S>,
{ {
hooks.blocks(Some(Self::gen_blocks_calls::<QT, S>), None); hooks.blocks(Some(Self::gen_blocks_calls::<QT, S>), None, None);
} }
fn pre_exec(&mut self, emulator: &Emulator, input: &S::Input) { fn pre_exec(&mut self, emulator: &Emulator, input: &S::Input) {

View File

@ -9,8 +9,7 @@ use rangemap::RangeMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
blocks::pc2basicblock, emu::{GuestAddr, GuestUsize},
emu::GuestAddr,
helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
hooks::QemuHooks, hooks::QemuHooks,
Emulator, Emulator,
@ -18,6 +17,7 @@ use crate::{
static DRCOV_IDS: Mutex<Option<Vec<u64>>> = Mutex::new(None); static DRCOV_IDS: Mutex<Option<Vec<u64>>> = Mutex::new(None);
static DRCOV_MAP: Mutex<Option<HashMap<GuestAddr, u64>>> = Mutex::new(None); static DRCOV_MAP: Mutex<Option<HashMap<GuestAddr, u64>>> = Mutex::new(None);
static DRCOV_LENGTHS: Mutex<Option<HashMap<GuestAddr, GuestUsize>>> = Mutex::new(None);
#[derive(Debug, Default, Serialize, Deserialize)] #[derive(Debug, Default, Serialize, Deserialize)]
pub struct QemuDrCovMetadata { pub struct QemuDrCovMetadata {
@ -55,6 +55,7 @@ impl QemuDrCovHelper {
let _ = DRCOV_IDS.lock().unwrap().insert(vec![]); let _ = DRCOV_IDS.lock().unwrap().insert(vec![]);
} }
let _ = DRCOV_MAP.lock().unwrap().insert(HashMap::new()); let _ = DRCOV_MAP.lock().unwrap().insert(HashMap::new());
let _ = DRCOV_LENGTHS.lock().unwrap().insert(HashMap::new());
Self { Self {
filter, filter,
module_mapping, module_mapping,
@ -80,6 +81,7 @@ where
{ {
hooks.blocks( hooks.blocks(
Some(gen_unique_block_ids::<QT, S>), Some(gen_unique_block_ids::<QT, S>),
Some(gen_block_lengths::<QT, S>),
Some(exec_trace_block::<QT, S>), Some(exec_trace_block::<QT, S>),
); );
} }
@ -88,13 +90,15 @@ where
fn post_exec<OT>( fn post_exec<OT>(
&mut self, &mut self,
emulator: &Emulator, _emulator: &Emulator,
_input: &S::Input, _input: &S::Input,
_observers: &mut OT, _observers: &mut OT,
_exit_kind: &mut ExitKind, _exit_kind: &mut ExitKind,
) where ) where
OT: ObserversTuple<S>, OT: ObserversTuple<S>,
{ {
let lengths_opt = DRCOV_LENGTHS.lock().unwrap();
let lengths = lengths_opt.as_ref().unwrap();
if self.full_trace { if self.full_trace {
if DRCOV_IDS.lock().unwrap().as_ref().unwrap().len() > self.drcov_len { if DRCOV_IDS.lock().unwrap().as_ref().unwrap().len() > self.drcov_len {
let mut drcov_vec = Vec::<DrCovBasicBlock>::new(); let mut drcov_vec = Vec::<DrCovBasicBlock>::new();
@ -114,27 +118,16 @@ where
continue 'pcs_full; continue 'pcs_full;
} }
if *idm == *id { if *idm == *id {
#[cfg(cpu_target = "arm")] match lengths.get(pc) {
let mode = if pc & 1 == 1 { Some(block_length) => {
Some(capstone::arch::arm::ArchMode::Thumb.into())
} else {
Some(capstone::arch::arm::ArchMode::Arm.into())
};
#[cfg(not(cpu_target = "arm"))]
let mode = None;
match pc2basicblock(*pc, emulator, mode) {
Ok(block) => {
let mut block_len = 0;
for instr in &block {
block_len += instr.insn_len;
}
drcov_vec.push(DrCovBasicBlock::new( drcov_vec.push(DrCovBasicBlock::new(
*pc as usize, *pc as usize,
*pc as usize + block_len, *pc as usize + *block_length as usize,
)); ));
} }
Err(r) => log::info!("{r:#?}"), None => {
log::info!("Failed to find block length for: {pc:}");
}
} }
} }
} }
@ -162,26 +155,16 @@ where
if !module_found { if !module_found {
continue 'pcs; continue 'pcs;
} }
match lengths.get(pc) {
#[cfg(cpu_target = "arm")] Some(block_length) => {
let mode = if pc & 1 == 1 { drcov_vec.push(DrCovBasicBlock::new(
Some(capstone::arch::arm::ArchMode::Thumb.into()) *pc as usize,
} else { *pc as usize + *block_length as usize,
Some(capstone::arch::arm::ArchMode::Arm.into()) ));
};
#[cfg(not(cpu_target = "arm"))]
let mode = None;
match pc2basicblock(*pc, emulator, mode) {
Ok(block) => {
let mut block_len = 0;
for instr in &block {
block_len += instr.insn_len;
} }
drcov_vec None => {
.push(DrCovBasicBlock::new(*pc as usize, *pc as usize + block_len)); log::info!("Failed to find block length for: {pc:}");
} }
Err(r) => log::info!("{r:#?}"),
} }
} }
@ -249,6 +232,31 @@ where
} }
} }
pub fn gen_block_lengths<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
_state: Option<&mut S>,
pc: GuestAddr,
block_length: GuestUsize,
) where
S: HasMetadata,
S: UsesInput,
QT: QemuHelperTuple<S>,
{
let drcov_helper = hooks
.helpers()
.match_first_type::<QemuDrCovHelper>()
.unwrap();
if !drcov_helper.must_instrument(pc) {
return;
}
DRCOV_LENGTHS
.lock()
.unwrap()
.as_mut()
.unwrap()
.insert(pc, block_length);
}
pub fn exec_trace_block<QT, S>(hooks: &mut QemuHooks<'_, QT, S>, _state: Option<&mut S>, id: u64) pub fn exec_trace_block<QT, S>(hooks: &mut QemuHooks<'_, QT, S>, _state: Option<&mut S>, id: u64)
where where
S: HasMetadata, S: HasMetadata,

View File

@ -336,6 +336,7 @@ extern "C" {
// void libafl_add_block_hook(uint64_t (*gen)(target_ulong pc), void (*exec)(uint64_t id)); // void libafl_add_block_hook(uint64_t (*gen)(target_ulong pc), void (*exec)(uint64_t id));
fn libafl_add_block_hook( fn libafl_add_block_hook(
gen: Option<extern "C" fn(GuestAddr, u64) -> u64>, gen: Option<extern "C" fn(GuestAddr, u64) -> u64>,
post_gen: Option<extern "C" fn(GuestAddr, GuestUsize, u64)>,
exec: Option<extern "C" fn(u64, u64)>, exec: Option<extern "C" fn(u64, u64)>,
data: u64, data: u64,
); );
@ -1037,10 +1038,11 @@ impl Emulator {
pub fn add_block_hooks( pub fn add_block_hooks(
&self, &self,
gen: Option<extern "C" fn(GuestAddr, u64) -> u64>, gen: Option<extern "C" fn(GuestAddr, u64) -> u64>,
post_gen: Option<extern "C" fn(GuestAddr, GuestUsize, u64)>,
exec: Option<extern "C" fn(u64, u64)>, exec: Option<extern "C" fn(u64, u64)>,
data: u64, data: u64,
) { ) {
unsafe { libafl_add_block_hook(gen, exec, data) } unsafe { libafl_add_block_hook(gen, post_gen, exec, data) }
} }
pub fn add_read_hooks( pub fn add_read_hooks(

View File

@ -15,7 +15,7 @@ pub use crate::emu::SyscallHookResult;
use crate::{ use crate::{
emu::{Emulator, FatPtr, MemAccessInfo, SKIP_EXEC_HOOK}, emu::{Emulator, FatPtr, MemAccessInfo, SKIP_EXEC_HOOK},
helper::QemuHelperTuple, helper::QemuHelperTuple,
GuestAddr, GuestAddr, GuestUsize,
}; };
// all kinds of hooks // all kinds of hooks
@ -136,7 +136,7 @@ where
} }
} }
static mut BLOCK_HOOKS: Vec<(Hook, Hook)> = vec![]; static mut BLOCK_HOOKS: Vec<(Hook, Hook, Hook)> = vec![];
extern "C" fn gen_block_hook_wrapper<QT, S>(pc: GuestAddr, index: u64) -> u64 extern "C" fn gen_block_hook_wrapper<QT, S>(pc: GuestAddr, index: u64) -> u64
where where
@ -145,7 +145,7 @@ where
{ {
unsafe { unsafe {
let hooks = get_qemu_hooks::<QT, S>(); let hooks = get_qemu_hooks::<QT, S>();
let (gen, _) = &mut BLOCK_HOOKS[index as usize]; let (gen, _, _) = &mut BLOCK_HOOKS[index as usize];
match gen { match gen {
Hook::Function(ptr) => { Hook::Function(ptr) => {
let func: fn(&mut QemuHooks<'_, QT, S>, Option<&mut S>, GuestAddr) -> Option<u64> = let func: fn(&mut QemuHooks<'_, QT, S>, Option<&mut S>, GuestAddr) -> Option<u64> =
@ -163,6 +163,34 @@ where
} }
} }
extern "C" fn gen_post_block_hook_wrapper<QT, S>(
pc: GuestAddr,
block_length: GuestUsize,
index: u64,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
{
unsafe {
let hooks = get_qemu_hooks::<QT, S>();
let (_, post_gen, _) = &mut BLOCK_HOOKS[index as usize];
match post_gen {
Hook::Function(ptr) => {
let func: fn(&mut QemuHooks<'_, QT, S>, Option<&mut S>, GuestAddr, GuestUsize) =
transmute(*ptr);
(func)(hooks, inprocess_get_state::<S>(), pc, block_length);
}
Hook::Closure(ptr) => {
let func: &mut Box<
dyn FnMut(&mut QemuHooks<'_, QT, S>, Option<&mut S>, GuestAddr, GuestUsize),
> = transmute(ptr);
(func)(hooks, inprocess_get_state::<S>(), pc, block_length);
}
_ => (),
}
}
}
extern "C" fn exec_block_hook_wrapper<QT, S>(id: u64, index: u64) extern "C" fn exec_block_hook_wrapper<QT, S>(id: u64, index: u64)
where where
S: UsesInput, S: UsesInput,
@ -170,7 +198,7 @@ where
{ {
unsafe { unsafe {
let hooks = get_qemu_hooks::<QT, S>(); let hooks = get_qemu_hooks::<QT, S>();
let (_, exec) = &mut BLOCK_HOOKS[index as usize]; let (_, _, exec) = &mut BLOCK_HOOKS[index as usize];
match exec { match exec {
Hook::Function(ptr) => { Hook::Function(ptr) => {
let func: fn(&mut QemuHooks<'_, QT, S>, Option<&mut S>, u64) = transmute(*ptr); let func: fn(&mut QemuHooks<'_, QT, S>, Option<&mut S>, u64) = transmute(*ptr);
@ -859,6 +887,9 @@ where
pub fn blocks( pub fn blocks(
&self, &self,
generation_hook: Option<fn(&mut Self, Option<&mut S>, pc: GuestAddr) -> Option<u64>>, generation_hook: Option<fn(&mut Self, Option<&mut S>, pc: GuestAddr) -> Option<u64>>,
post_generation_hook: Option<
fn(&mut Self, Option<&mut S>, pc: GuestAddr, block_length: GuestUsize),
>,
execution_hook: Option<fn(&mut Self, Option<&mut S>, id: u64)>, execution_hook: Option<fn(&mut Self, Option<&mut S>, id: u64)>,
) { ) {
unsafe { unsafe {
@ -869,6 +900,11 @@ where
} else { } else {
Some(gen_block_hook_wrapper::<QT, S>) Some(gen_block_hook_wrapper::<QT, S>)
}, },
if post_generation_hook.is_none() {
None
} else {
Some(gen_post_block_hook_wrapper::<QT, S>)
},
if execution_hook.is_none() { if execution_hook.is_none() {
None None
} else { } else {
@ -880,6 +916,9 @@ where
generation_hook.map_or(Hook::Empty, |hook| { generation_hook.map_or(Hook::Empty, |hook| {
Hook::Function(hook as *const libc::c_void) Hook::Function(hook as *const libc::c_void)
}), }),
post_generation_hook.map_or(Hook::Empty, |hook| {
Hook::Function(hook as *const libc::c_void)
}),
execution_hook.map_or(Hook::Empty, |hook| { execution_hook.map_or(Hook::Empty, |hook| {
Hook::Function(hook as *const libc::c_void) Hook::Function(hook as *const libc::c_void)
}), }),
@ -892,6 +931,9 @@ where
generation_hook: Option< generation_hook: Option<
Box<dyn FnMut(&'a mut Self, Option<&'a mut S>, GuestAddr) -> Option<u64>>, Box<dyn FnMut(&'a mut Self, Option<&'a mut S>, GuestAddr) -> Option<u64>>,
>, >,
post_generation_hook: Option<
Box<dyn FnMut(&'a mut Self, Option<&'a mut S>, GuestAddr, GuestUsize)>,
>,
execution_hook: Option<Box<dyn FnMut(&'a mut Self, Option<&'a mut S>, u64)>>, execution_hook: Option<Box<dyn FnMut(&'a mut Self, Option<&'a mut S>, u64)>>,
) { ) {
let index = BLOCK_HOOKS.len(); let index = BLOCK_HOOKS.len();
@ -901,6 +943,11 @@ where
} else { } else {
Some(gen_block_hook_wrapper::<QT, S>) Some(gen_block_hook_wrapper::<QT, S>)
}, },
if post_generation_hook.is_none() {
None
} else {
Some(gen_post_block_hook_wrapper::<QT, S>)
},
if execution_hook.is_none() { if execution_hook.is_none() {
None None
} else { } else {
@ -910,6 +957,7 @@ where
); );
BLOCK_HOOKS.push(( BLOCK_HOOKS.push((
generation_hook.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))), generation_hook.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))),
post_generation_hook.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))),
execution_hook.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))), execution_hook.map_or(Hook::Empty, |hook| Hook::Closure(transmute(hook))),
)); ));
} }
@ -917,6 +965,9 @@ where
pub fn blocks_raw( pub fn blocks_raw(
&self, &self,
generation_hook: Option<fn(&mut Self, Option<&mut S>, pc: GuestAddr) -> Option<u64>>, generation_hook: Option<fn(&mut Self, Option<&mut S>, pc: GuestAddr) -> Option<u64>>,
post_generation_hook: Option<
fn(&mut Self, Option<&mut S>, pc: GuestAddr, block_length: GuestUsize),
>,
execution_hook: Option<extern "C" fn(id: u64, data: u64)>, execution_hook: Option<extern "C" fn(id: u64, data: u64)>,
) { ) {
unsafe { unsafe {
@ -927,6 +978,11 @@ where
} else { } else {
Some(gen_block_hook_wrapper::<QT, S>) Some(gen_block_hook_wrapper::<QT, S>)
}, },
if post_generation_hook.is_none() {
None
} else {
Some(gen_post_block_hook_wrapper::<QT, S>)
},
execution_hook, execution_hook,
index as u64, index as u64,
); );
@ -934,6 +990,9 @@ where
generation_hook.map_or(Hook::Empty, |hook| { generation_hook.map_or(Hook::Empty, |hook| {
Hook::Function(hook as *const libc::c_void) Hook::Function(hook as *const libc::c_void)
}), }),
post_generation_hook.map_or(Hook::Empty, |hook| {
Hook::Function(hook as *const libc::c_void)
}),
Hook::Empty, Hook::Empty,
)); ));
} }

View File

@ -74,8 +74,6 @@ pub mod asan;
#[cfg(emulation_mode = "usermode")] #[cfg(emulation_mode = "usermode")]
pub use asan::{init_with_asan, QemuAsanHelper}; pub use asan::{init_with_asan, QemuAsanHelper};
pub mod blocks;
pub mod calls; pub mod calls;
pub mod drcov; pub mod drcov;