Refactor libafl_qemu creating the Emulator struct and post syscall hooks (#430)

* working without asan.rs

* working asan

* update fuzzers

* mremap in snapshot

* sugar

* python

* fix python

* clippy

* fmt

* fuck you loader
This commit is contained in:
Andrea Fioraldi 2021-12-23 09:10:13 +01:00 committed by GitHub
parent d697554810
commit 6274ad4594
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 1057 additions and 779 deletions

View File

@ -5,7 +5,7 @@ edition = "2021"
[dependencies] [dependencies]
pyo3 = { version = "0.15", features = ["extension-module"] } pyo3 = { version = "0.15", features = ["extension-module"] }
libafl_qemu = { path = "../../libafl_qemu", version = "0.7", features = ["python"], features = ["x86_64"] } libafl_qemu = { path = "../../libafl_qemu", version = "0.7", features = ["python"] }
libafl_sugar = { path = "../../libafl_sugar", version = "0.7", features = ["python"] } libafl_sugar = { path = "../../libafl_sugar", version = "0.7", features = ["python"] }
[build-dependencies] [build-dependencies]

View File

@ -52,7 +52,7 @@ use libafl_qemu::{
edges, edges,
edges::QemuEdgeCoverageHelper, edges::QemuEdgeCoverageHelper,
elf::EasyElf, elf::EasyElf,
emu, emu::Emulator,
filter_qemu_args, filter_qemu_args,
//snapshot::QemuSnapshotHelper, //snapshot::QemuSnapshotHelper,
MmapPerms, MmapPerms,
@ -66,10 +66,6 @@ pub fn main() {
// Needed only on no_std // Needed only on no_std
//RegistryBuilder::register::<Tokens>(); //RegistryBuilder::register::<Tokens>();
let args: Vec<String> = env::args().collect();
let env: Vec<(String, String)> = env::vars().collect();
emu::init(&args, &env);
let res = match App::new("libafl_qemu_fuzzbench") let res = match App::new("libafl_qemu_fuzzbench")
.version("0.4.0") .version("0.4.0")
.author("AFLplusplus team") .author("AFLplusplus team")
@ -170,34 +166,37 @@ fn fuzz(
logfile: PathBuf, logfile: PathBuf,
timeout: Duration, timeout: Duration,
) -> Result<(), Error> { ) -> Result<(), Error> {
env::remove_var("LD_LIBRARY_PATH");
let args: Vec<String> = env::args().collect();
let env: Vec<(String, String)> = env::vars().collect();
let emu = Emulator::new(&args, &env);
let mut elf_buffer = Vec::new(); let mut elf_buffer = Vec::new();
let elf = EasyElf::from_file(emu::binary_path(), &mut elf_buffer)?; let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer)?;
let test_one_input_ptr = elf let test_one_input_ptr = elf
.resolve_symbol("LLVMFuzzerTestOneInput", emu::load_addr()) .resolve_symbol("LLVMFuzzerTestOneInput", emu.load_addr())
.expect("Symbol LLVMFuzzerTestOneInput not found"); .expect("Symbol LLVMFuzzerTestOneInput not found");
println!("LLVMFuzzerTestOneInput @ {:#x}", test_one_input_ptr); println!("LLVMFuzzerTestOneInput @ {:#x}", test_one_input_ptr);
emu::set_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput emu.set_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
emu::run(); unsafe { emu.run() };
println!( println!("Break at {:#x}", emu.read_reg::<_, u64>(Regs::Rip).unwrap());
"Break at {:#x}",
emu::read_reg::<_, u64>(Regs::Rip).unwrap()
);
let stack_ptr: u64 = emu::read_reg(Regs::Rsp).unwrap(); let stack_ptr: u64 = emu.read_reg(Regs::Rsp).unwrap();
let mut ret_addr = [0u64]; let mut ret_addr = [0u64];
emu::read_mem(stack_ptr, &mut ret_addr); unsafe { emu.read_mem(stack_ptr, &mut ret_addr) };
let ret_addr = ret_addr[0]; let ret_addr = ret_addr[0];
println!("Stack pointer = {:#x}", stack_ptr); println!("Stack pointer = {:#x}", stack_ptr);
println!("Return address = {:#x}", ret_addr); println!("Return address = {:#x}", ret_addr);
emu::remove_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput emu.remove_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
emu::set_breakpoint(ret_addr); // LLVMFuzzerTestOneInput ret addr emu.set_breakpoint(ret_addr); // LLVMFuzzerTestOneInput ret addr
let input_addr = emu::map_private(0, 4096, MmapPerms::ReadWrite).unwrap(); let input_addr = emu.map_private(0, 4096, MmapPerms::ReadWrite).unwrap();
println!("Placing input at {:#x}", input_addr); println!("Placing input at {:#x}", input_addr);
let log = RefCell::new( let log = RefCell::new(
@ -309,20 +308,23 @@ fn fuzz(
len = 4096; len = 4096;
} }
emu::write_mem(input_addr, buf); unsafe {
emu.write_mem(input_addr, buf);
emu::write_reg(Regs::Rdi, input_addr).unwrap(); emu.write_reg(Regs::Rdi, input_addr).unwrap();
emu::write_reg(Regs::Rsi, len).unwrap(); emu.write_reg(Regs::Rsi, len).unwrap();
emu::write_reg(Regs::Rip, test_one_input_ptr).unwrap(); emu.write_reg(Regs::Rip, test_one_input_ptr).unwrap();
emu::write_reg(Regs::Rsp, stack_ptr).unwrap(); emu.write_reg(Regs::Rsp, stack_ptr).unwrap();
emu::run(); emu.run();
}
ExitKind::Ok ExitKind::Ok
}; };
let executor = QemuExecutor::new( let executor = QemuExecutor::new(
&mut harness, &mut harness,
&emu,
tuple_list!( tuple_list!(
QemuEdgeCoverageHelper::new(), QemuEdgeCoverageHelper::new(),
QemuCmpLogHelper::new(), QemuCmpLogHelper::new(),

View File

@ -6,38 +6,38 @@ import lief
MAX_SIZE = 0x100 MAX_SIZE = 0x100
BINARY_PATH = './a.out' BINARY_PATH = './a.out'
qemu.init(['qemu-x86_64', BINARY_PATH], []) emu = qemu.Emulator(['qemu-x86_64', BINARY_PATH], [])
elf = lief.parse(BINARY_PATH) elf = lief.parse(BINARY_PATH)
test_one_input = elf.get_function_address("LLVMFuzzerTestOneInput") test_one_input = elf.get_function_address("LLVMFuzzerTestOneInput")
if elf.is_pie: if elf.is_pie:
test_one_input += qemu.load_addr() test_one_input += emu.load_addr()
print('LLVMFuzzerTestOneInput @ 0x%x' % test_one_input) print('LLVMFuzzerTestOneInput @ 0x%x' % test_one_input)
qemu.set_breakpoint(test_one_input) emu.set_breakpoint(test_one_input)
qemu.run() emu.run()
sp = qemu.read_reg(qemu.amd64.Rsp) sp = emu.read_reg(qemu.regs.Rsp)
print('SP = 0x%x' % sp) print('SP = 0x%x' % sp)
retaddr = int.from_bytes(qemu.read_mem(sp, 8), 'little') retaddr = int.from_bytes(emu.read_mem(sp, 8), 'little')
print('RET = 0x%x' % retaddr) print('RET = 0x%x' % retaddr)
inp = qemu.map_private(0, MAX_SIZE, qemu.mmap.ReadWrite) inp = emu.map_private(0, MAX_SIZE, qemu.mmap.ReadWrite)
assert(inp > 0) assert(inp > 0)
qemu.remove_breakpoint(test_one_input) emu.remove_breakpoint(test_one_input)
qemu.set_breakpoint(retaddr) emu.set_breakpoint(retaddr)
def harness(b): def harness(b):
if len(b) > MAX_SIZE: if len(b) > MAX_SIZE:
b = b[:MAX_SIZE] b = b[:MAX_SIZE]
qemu.write_mem(inp, b) emu.write_mem(inp, b)
qemu.write_reg(qemu.amd64.Rsi, len(b)) emu.write_reg(qemu.regs.Rsi, len(b))
qemu.write_reg(qemu.amd64.Rdi, inp) emu.write_reg(qemu.regs.Rdi, inp)
qemu.write_reg(qemu.amd64.Rsp, sp) emu.write_reg(qemu.regs.Rsp, sp)
qemu.write_reg(qemu.amd64.Rip, test_one_input) emu.write_reg(qemu.regs.Rip, test_one_input)
qemu.run() emu.run()
fuzz = sugar.QemuBytesCoverageSugar(['./in'], './out', 3456, [0,1,2,3]) fuzz = sugar.QemuBytesCoverageSugar(['./in'], './out', 3456, [0,1,2,3])
fuzz.run(harness) fuzz.run(emu, harness)

Binary file not shown.

View File

@ -30,7 +30,18 @@ use libafl::{
Error, Error,
}; };
use libafl_qemu::{ use libafl_qemu::{
edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu, MmapPerms, QemuExecutor, Regs, //asan::QemuAsanHelper,
cmplog,
cmplog::{CmpLogObserver, QemuCmpLogHelper},
edges,
edges::QemuEdgeCoverageHelper,
elf::EasyElf,
emu::Emulator,
filter_qemu_args,
//snapshot::QemuSnapshotHelper,
MmapPerms,
QemuExecutor,
Regs,
}; };
pub fn fuzz() { pub fn fuzz() {
@ -42,39 +53,37 @@ pub fn fuzz() {
let objective_dir = PathBuf::from("./crashes"); let objective_dir = PathBuf::from("./crashes");
// Initialize QEMU // Initialize QEMU
env::remove_var("LD_LIBRARY_PATH");
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
let env: Vec<(String, String)> = env::vars().collect(); let env: Vec<(String, String)> = env::vars().collect();
emu::init(&args, &env); let emu = Emulator::new(&args, &env);
let mut elf_buffer = Vec::new(); let mut elf_buffer = Vec::new();
let elf = EasyElf::from_file(emu::binary_path(), &mut elf_buffer).unwrap(); let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer).unwrap();
let test_one_input_ptr = elf let test_one_input_ptr = elf
.resolve_symbol("LLVMFuzzerTestOneInput", emu::load_addr()) .resolve_symbol("LLVMFuzzerTestOneInput", emu.load_addr())
.expect("Symbol LLVMFuzzerTestOneInput not found"); .expect("Symbol LLVMFuzzerTestOneInput not found");
println!("LLVMFuzzerTestOneInput @ {:#x}", test_one_input_ptr); println!("LLVMFuzzerTestOneInput @ {:#x}", test_one_input_ptr);
emu::set_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput emu.set_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
emu::run(); unsafe { emu.run() };
println!( println!("Break at {:#x}", emu.read_reg::<_, u64>(Regs::Rip).unwrap());
"Break at {:#x}",
emu::read_reg::<_, u64>(Regs::Rip).unwrap()
);
// Get the return address // Get the return address
let stack_ptr: u64 = emu::read_reg(Regs::Rsp).unwrap(); let stack_ptr: u64 = emu.read_reg(Regs::Rsp).unwrap();
let mut ret_addr = [0u64]; let mut ret_addr = [0u64];
emu::read_mem(stack_ptr, &mut ret_addr); unsafe { emu.read_mem(stack_ptr, &mut ret_addr) };
let ret_addr = ret_addr[0]; let ret_addr = ret_addr[0];
println!("Stack pointer = {:#x}", stack_ptr); println!("Stack pointer = {:#x}", stack_ptr);
println!("Return address = {:#x}", ret_addr); println!("Return address = {:#x}", ret_addr);
emu::remove_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput emu.remove_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
emu::set_breakpoint(ret_addr); // LLVMFuzzerTestOneInput ret addr emu.set_breakpoint(ret_addr); // LLVMFuzzerTestOneInput ret addr
let input_addr = emu::map_private(0, 4096, MmapPerms::ReadWrite).unwrap(); let input_addr = emu.map_private(0, 4096, MmapPerms::ReadWrite).unwrap();
println!("Placing input at {:#x}", input_addr); println!("Placing input at {:#x}", input_addr);
// The wrapped harness function, calling out to the LLVM-style harness // The wrapped harness function, calling out to the LLVM-style harness
@ -87,14 +96,16 @@ pub fn fuzz() {
len = 4096; len = 4096;
} }
emu::write_mem(input_addr, buf); unsafe {
emu.write_mem(input_addr, buf);
emu::write_reg(Regs::Rdi, input_addr).unwrap(); emu.write_reg(Regs::Rdi, input_addr).unwrap();
emu::write_reg(Regs::Rsi, len).unwrap(); emu.write_reg(Regs::Rsi, len).unwrap();
emu::write_reg(Regs::Rip, test_one_input_ptr).unwrap(); emu.write_reg(Regs::Rip, test_one_input_ptr).unwrap();
emu::write_reg(Regs::Rsp, stack_ptr).unwrap(); emu.write_reg(Regs::Rsp, stack_ptr).unwrap();
emu::run(); emu.run();
}
ExitKind::Ok ExitKind::Ok
}; };
@ -149,6 +160,7 @@ pub fn fuzz() {
// Create a QEMU in-process executor // Create a QEMU in-process executor
let executor = QemuExecutor::new( let executor = QemuExecutor::new(
&mut harness, &mut harness,
&emu,
// The QEMU helpers define common hooks like coverage tracking hooks // The QEMU helpers define common hooks like coverage tracking hooks
tuple_list!(QemuEdgeCoverageHelper::new()), tuple_list!(QemuEdgeCoverageHelper::new()),
tuple_list!(edges_observer, time_observer), tuple_list!(edges_observer, time_observer),

View File

@ -55,7 +55,7 @@ bincode = {version = "1.3", optional = true }
static_assertions = "1.1.0" static_assertions = "1.1.0"
ctor = "0.1.20" ctor = "0.1.20"
num_enum = { version = "0.5.1", default-features = false } num_enum = { version = "0.5.1", default-features = false }
typed-builder = "0.9.0" # Implement the builder pattern at compiletime typed-builder = "0.9.1" # Implement the builder pattern at compiletime
ahash = { version = "0.7", default-features=false, features=["compile-time-rng"] } # The hash function already used in hashbrown ahash = { version = "0.7", default-features=false, features=["compile-time-rng"] } # The hash function already used in hashbrown
intervaltree = { version = "0.2.7", default-features = false, features = ["serde"] } intervaltree = { version = "0.2.7", default-features = false, features = ["serde"] }

View File

@ -3,7 +3,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 = "652155bab71357380420f90a944e463ba4bcd372"; const QEMU_REVISION: &str = "fa2b9c4a25f548f15b3d1b1afcfdb75cc7165f9a";
fn build_dep_check(tools: &[&str]) { fn build_dep_check(tools: &[&str]) {
for tool in tools { for tool in tools {
@ -36,34 +36,25 @@ fn main() {
return; return;
} }
// Make sure we have at least and at most one architecutre feature set // Make sure we have at most one architecutre feature set
// Else, we default to `x86_64` - having a default makes CI easier :) // Else, we default to `x86_64` - having a default makes CI easier :)
assert_unique_feature!("arm", "aarch64", "i386", "i86_64"); assert_unique_feature!("arm", "aarch64", "i386", "i86_64");
#[cfg(not(any(
feature = "arm",
feature = "aarch64",
feature = "i386",
feature = "x86_64"
)))]
println!(
"cargo:warning=No architecture feature enabled for libafl_qemu, supported: arm, aarch64, i386, x86_64 - defaulting to x86_64"
);
let cpu_target = if cfg!(feature = "clippy") { let cpu_target = if cfg!(feature = "x86_64") {
// assume x86_64 for clippy "x86_64".to_string()
"x86_64"
} else if cfg!(feature = "arm") { } else if cfg!(feature = "arm") {
"arm" "arm".to_string()
} else if cfg!(feature = "aarch64") { } else if cfg!(feature = "aarch64") {
"aarch64" "aarch64".to_string()
} else if cfg!(feature = "i386") { } else if cfg!(feature = "i386") {
"368" "i368".to_string()
} else { } else {
// if cfg!(feature = "x86_64") { env::var("CPU_TARGET").unwrap_or_else(|_| {
"x86_64" println!(
/*} else { "cargo:warning=No architecture feature enabled or CPU_TARGET env specified for libafl_qemu, supported: arm, aarch64, i386, x86_64 - defaulting to x86_64"
panic!("No architecture feture enabled for libafl_qemu"); );
*/ "x86_64".to_string()
})
}; };
let jobs = env::var("CARGO_BUILD_JOBS"); let jobs = env::var("CARGO_BUILD_JOBS");
@ -112,8 +103,6 @@ fn main() {
Command::new("git") Command::new("git")
.current_dir(&out_dir_path) .current_dir(&out_dir_path)
.arg("clone") .arg("clone")
.arg("--depth")
.arg("1")
.arg(QEMU_URL) .arg(QEMU_URL)
.status() .status()
.unwrap(); .unwrap();
@ -231,6 +220,7 @@ fn main() {
for dir in &[ for dir in &[
build_dir.join("libcommon.fa.p"), build_dir.join("libcommon.fa.p"),
build_dir.join(&format!("libqemu-{}-linux-user.fa.p", cpu_target)), build_dir.join(&format!("libqemu-{}-linux-user.fa.p", cpu_target)),
build_dir.join("libcommon-user.fa.p"),
//build_dir.join("libqemuutil.a.p"), //build_dir.join("libqemuutil.a.p"),
//build_dir.join("libqom.fa.p"), //build_dir.join("libqom.fa.p"),
//build_dir.join("libhwcore.fa.p"), //build_dir.join("libhwcore.fa.p"),

View File

@ -3,8 +3,7 @@ use num_enum::{IntoPrimitive, TryFromPrimitive};
use std::{env, fs, ptr}; use std::{env, fs, ptr};
use crate::{ use crate::{
emu, emu::{Emulator, SyscallHookResult},
emu::SyscallHookResult,
executor::QemuExecutor, executor::QemuExecutor,
helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
Regs, Regs,
@ -114,7 +113,7 @@ unsafe extern "C" fn asan_giovese_populate_context(ctx: *mut CallContext, _pc: u
static mut ASAN_INITED: bool = false; static mut ASAN_INITED: bool = false;
pub fn init_with_asan(args: &mut Vec<String>, env: &mut [(String, String)]) -> i32 { pub fn init_with_asan(args: &mut Vec<String>, env: &mut [(String, String)]) -> Emulator {
assert!(!args.is_empty()); assert!(!args.is_empty());
let current = env::current_exe().unwrap(); let current = env::current_exe().unwrap();
let asan_lib = fs::canonicalize(&current) let asan_lib = fs::canonicalize(&current)
@ -160,7 +159,7 @@ pub fn init_with_asan(args: &mut Vec<String>, env: &mut [(String, String)]) -> i
asan_giovese_init(); asan_giovese_init();
ASAN_INITED = true; ASAN_INITED = true;
} }
emu::init(args, env) Emulator::new(args, env)
} }
// TODO intrumentation filter // TODO intrumentation filter
@ -172,7 +171,7 @@ pub struct QemuAsanHelper {
impl QemuAsanHelper { impl QemuAsanHelper {
#[must_use] #[must_use]
pub fn new() -> Self { pub fn new() -> Self {
assert!(unsafe { ASAN_INITED }, "The ASan runtime is not initialized, use init_with_asan(...) instead of just init(...)"); assert!(unsafe { ASAN_INITED }, "The ASan runtime is not initialized, use init_with_asan(...) instead of just Emulator::new(...)");
Self { Self {
enabled: true, enabled: true,
filter: QemuInstrumentationFilter::None, filter: QemuInstrumentationFilter::None,
@ -202,7 +201,7 @@ impl QemuAsanHelper {
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn alloc(&mut self, start: u64, end: u64) { pub fn alloc(&mut self, _emulator: &Emulator, start: u64, end: u64) {
unsafe { unsafe {
let ctx: *const CallContext = let ctx: *const CallContext =
libc::calloc(core::mem::size_of::<CallContext>(), 1) as *const _; libc::calloc(core::mem::size_of::<CallContext>(), 1) as *const _;
@ -211,188 +210,188 @@ impl QemuAsanHelper {
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn dealloc(&mut self, addr: u64) { pub fn dealloc(&mut self, emulator: &Emulator, addr: u64) {
unsafe { unsafe {
let ckinfo = asan_giovese_alloc_search(addr); let ckinfo = asan_giovese_alloc_search(addr);
if let Some(ck) = ckinfo.as_mut() { if let Some(ck) = ckinfo.as_mut() {
if ck.start != addr { if ck.start != addr {
// Free not the start of the chunk // Free not the start of the chunk
asan_giovese_badfree(addr, emu::read_reg(Regs::Pc).unwrap_or(u64::MAX)); asan_giovese_badfree(addr, emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX));
} }
let ctx: *const CallContext = let ctx: *const CallContext =
libc::calloc(core::mem::size_of::<CallContext>(), 1) as *const _; libc::calloc(core::mem::size_of::<CallContext>(), 1) as *const _;
ck.free_ctx = ctx; ck.free_ctx = ctx;
} else { } else {
// Free of wild ptr // Free of wild ptr
asan_giovese_badfree(addr, emu::read_reg(Regs::Pc).unwrap_or(u64::MAX)); asan_giovese_badfree(addr, emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX));
} }
} }
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
#[must_use] #[must_use]
pub fn is_poisoned(&self, addr: u64, size: usize) -> bool { pub fn is_poisoned(&self, emulator: &Emulator, addr: u64, size: usize) -> bool {
unsafe { asan_giovese_loadN(emu::g2h(addr), size) != 0 } unsafe { asan_giovese_loadN(emulator.g2h(addr), size) != 0 }
} }
pub fn read_1(&mut self, addr: u64) { pub fn read_1(&mut self, emulator: &Emulator, addr: u64) {
unsafe { unsafe {
if self.enabled() && asan_giovese_load1(emu::g2h(addr)) != 0 { if self.enabled() && asan_giovese_load1(emulator.g2h(addr)) != 0 {
asan_giovese_report_and_crash( asan_giovese_report_and_crash(
0, 0,
addr, addr,
1, 1,
emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX),
0, 0,
emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), emulator.read_reg(Regs::Sp).unwrap_or(u64::MAX),
); );
} }
} }
} }
pub fn read_2(&mut self, addr: u64) { pub fn read_2(&mut self, emulator: &Emulator, addr: u64) {
unsafe { unsafe {
if self.enabled() && asan_giovese_load2(emu::g2h(addr)) != 0 { if self.enabled() && asan_giovese_load2(emulator.g2h(addr)) != 0 {
asan_giovese_report_and_crash( asan_giovese_report_and_crash(
0, 0,
addr, addr,
2, 2,
emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX),
0, 0,
emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), emulator.read_reg(Regs::Sp).unwrap_or(u64::MAX),
); );
} }
} }
} }
pub fn read_4(&mut self, addr: u64) { pub fn read_4(&mut self, emulator: &Emulator, addr: u64) {
unsafe { unsafe {
if self.enabled() && asan_giovese_load4(emu::g2h(addr)) != 0 { if self.enabled() && asan_giovese_load4(emulator.g2h(addr)) != 0 {
asan_giovese_report_and_crash( asan_giovese_report_and_crash(
0, 0,
addr, addr,
4, 4,
emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX),
0, 0,
emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), emulator.read_reg(Regs::Sp).unwrap_or(u64::MAX),
); );
} }
} }
} }
pub fn read_8(&mut self, addr: u64) { pub fn read_8(&mut self, emulator: &Emulator, addr: u64) {
unsafe { unsafe {
if self.enabled() && asan_giovese_load8(emu::g2h(addr)) != 0 { if self.enabled() && asan_giovese_load8(emulator.g2h(addr)) != 0 {
asan_giovese_report_and_crash( asan_giovese_report_and_crash(
0, 0,
addr, addr,
8, 8,
emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX),
0, 0,
emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), emulator.read_reg(Regs::Sp).unwrap_or(u64::MAX),
); );
} }
} }
} }
pub fn read_n(&mut self, addr: u64, size: usize) { pub fn read_n(&mut self, emulator: &Emulator, addr: u64, size: usize) {
unsafe { unsafe {
if self.enabled() && asan_giovese_loadN(emu::g2h(addr), size) != 0 { if self.enabled() && asan_giovese_loadN(emulator.g2h(addr), size) != 0 {
asan_giovese_report_and_crash( asan_giovese_report_and_crash(
0, 0,
addr, addr,
size, size,
emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX),
0, 0,
emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), emulator.read_reg(Regs::Sp).unwrap_or(u64::MAX),
); );
} }
} }
} }
pub fn write_1(&mut self, addr: u64) { pub fn write_1(&mut self, emulator: &Emulator, addr: u64) {
unsafe { unsafe {
if self.enabled() && asan_giovese_store1(emu::g2h(addr)) != 0 { if self.enabled() && asan_giovese_store1(emulator.g2h(addr)) != 0 {
asan_giovese_report_and_crash( asan_giovese_report_and_crash(
1, 1,
addr, addr,
1, 1,
emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX),
0, 0,
emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), emulator.read_reg(Regs::Sp).unwrap_or(u64::MAX),
); );
} }
} }
} }
pub fn write_2(&mut self, addr: u64) { pub fn write_2(&mut self, emulator: &Emulator, addr: u64) {
unsafe { unsafe {
if self.enabled() && asan_giovese_store2(emu::g2h(addr)) != 0 { if self.enabled() && asan_giovese_store2(emulator.g2h(addr)) != 0 {
asan_giovese_report_and_crash( asan_giovese_report_and_crash(
1, 1,
addr, addr,
2, 2,
emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX),
0, 0,
emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), emulator.read_reg(Regs::Sp).unwrap_or(u64::MAX),
); );
} }
} }
} }
pub fn write_4(&mut self, addr: u64) { pub fn write_4(&mut self, emulator: &Emulator, addr: u64) {
unsafe { unsafe {
if self.enabled() && asan_giovese_store4(emu::g2h(addr)) != 0 { if self.enabled() && asan_giovese_store4(emulator.g2h(addr)) != 0 {
asan_giovese_report_and_crash( asan_giovese_report_and_crash(
1, 1,
addr, addr,
4, 4,
emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX),
0, 0,
emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), emulator.read_reg(Regs::Sp).unwrap_or(u64::MAX),
); );
} }
} }
} }
pub fn write_8(&mut self, addr: u64) { pub fn write_8(&mut self, emulator: &Emulator, addr: u64) {
unsafe { unsafe {
if self.enabled() && asan_giovese_store8(emu::g2h(addr)) != 0 { if self.enabled() && asan_giovese_store8(emulator.g2h(addr)) != 0 {
asan_giovese_report_and_crash( asan_giovese_report_and_crash(
1, 1,
addr, addr,
8, 8,
emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX),
0, 0,
emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), emulator.read_reg(Regs::Sp).unwrap_or(u64::MAX),
); );
} }
} }
} }
pub fn write_n(&mut self, addr: u64, size: usize) { pub fn write_n(&mut self, emulator: &Emulator, addr: u64, size: usize) {
unsafe { unsafe {
if self.enabled() && asan_giovese_storeN(emu::g2h(addr), size) != 0 { if self.enabled() && asan_giovese_storeN(emulator.g2h(addr), size) != 0 {
asan_giovese_report_and_crash( asan_giovese_report_and_crash(
1, 1,
addr, addr,
size, size,
emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX),
0, 0,
emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), emulator.read_reg(Regs::Sp).unwrap_or(u64::MAX),
); );
} }
} }
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn poison(&mut self, addr: u64, size: usize, poison: PoisonKind) { pub fn poison(&mut self, emulator: &Emulator, addr: u64, size: usize, poison: PoisonKind) {
unsafe { asan_giovese_poison_region(emu::g2h(addr), size, poison.into()) }; unsafe { asan_giovese_poison_region(emulator.g2h(addr), size, poison.into()) };
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn unpoison(&mut self, addr: u64, size: usize) { pub fn unpoison(&mut self, emulator: &Emulator, addr: u64, size: usize) {
unsafe { asan_giovese_unpoison_region(emu::g2h(addr), size) }; unsafe { asan_giovese_unpoison_region(emulator.g2h(addr), size) };
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
@ -435,13 +434,14 @@ where
executor.hook_syscalls(qasan_fake_syscall::<I, QT, S>); executor.hook_syscalls(qasan_fake_syscall::<I, QT, S>);
} }
fn post_exec(&mut self, _input: &I) { fn post_exec(&mut self, _emulator: &Emulator, _input: &I) {
self.reset(); self.reset();
} }
} }
// TODO add pc to generation hooks // TODO add pc to generation hooks
pub fn gen_readwrite_asan<I, QT, S>( pub fn gen_readwrite_asan<I, QT, S>(
_emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: &mut S,
pc: u64, pc: u64,
@ -459,43 +459,64 @@ where
} }
} }
pub fn trace_read1_asan<I, QT, S>(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64) pub fn trace_read1_asan<I, QT, S>(
where emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
addr: u64,
) where
I: Input, I: Input,
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap(); let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap();
h.read_1(addr); h.read_1(emulator, addr);
} }
pub fn trace_read2_asan<I, QT, S>(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64) pub fn trace_read2_asan<I, QT, S>(
where emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
addr: u64,
) where
I: Input, I: Input,
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap(); let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap();
h.read_2(addr); h.read_2(emulator, addr);
} }
pub fn trace_read4_asan<I, QT, S>(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64) pub fn trace_read4_asan<I, QT, S>(
where emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
addr: u64,
) where
I: Input, I: Input,
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap(); let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap();
h.read_4(addr); h.read_4(emulator, addr);
} }
pub fn trace_read8_asan<I, QT, S>(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64) pub fn trace_read8_asan<I, QT, S>(
where emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
addr: u64,
) where
I: Input, I: Input,
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap(); let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap();
h.read_8(addr); h.read_8(emulator, addr);
} }
pub fn trace_read_n_asan<I, QT, S>( pub fn trace_read_n_asan<I, QT, S>(
emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: &mut S,
_id: u64, _id: u64,
@ -506,46 +527,67 @@ pub fn trace_read_n_asan<I, QT, S>(
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap(); let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap();
h.read_n(addr, size); h.read_n(emulator, addr, size);
} }
pub fn trace_write1_asan<I, QT, S>(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64) pub fn trace_write1_asan<I, QT, S>(
where emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
addr: u64,
) where
I: Input, I: Input,
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap(); let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap();
h.write_1(addr); h.write_1(emulator, addr);
} }
pub fn trace_write2_asan<I, QT, S>(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64) pub fn trace_write2_asan<I, QT, S>(
where emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
addr: u64,
) where
I: Input, I: Input,
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap(); let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap();
h.write_2(addr); h.write_2(emulator, addr);
} }
pub fn trace_write4_asan<I, QT, S>(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64) pub fn trace_write4_asan<I, QT, S>(
where emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
addr: u64,
) where
I: Input, I: Input,
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap(); let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap();
h.write_4(addr); h.write_4(emulator, addr);
} }
pub fn trace_write8_asan<I, QT, S>(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64) pub fn trace_write8_asan<I, QT, S>(
where emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
addr: u64,
) where
I: Input, I: Input,
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap(); let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap();
h.write_8(addr); h.write_8(emulator, addr);
} }
pub fn trace_write_n_asan<I, QT, S>( pub fn trace_write_n_asan<I, QT, S>(
emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: &mut S,
_id: u64, _id: u64,
@ -556,11 +598,12 @@ pub fn trace_write_n_asan<I, QT, S>(
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap(); let h = helpers.match_first_type_mut::<QemuAsanHelper>().unwrap();
h.read_n(addr, size); h.read_n(emulator, addr, size);
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn qasan_fake_syscall<I, QT, S>( pub fn qasan_fake_syscall<I, QT, S>(
emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: &mut S,
sys_num: i32, sys_num: i32,
@ -582,30 +625,35 @@ where
let mut r = 0; let mut r = 0;
match QasanAction::try_from(a0).expect("Invalid QASan action number") { match QasanAction::try_from(a0).expect("Invalid QASan action number") {
QasanAction::CheckLoad => { QasanAction::CheckLoad => {
h.read_n(a1, a2 as usize); h.read_n(emulator, a1, a2 as usize);
} }
QasanAction::CheckStore => { QasanAction::CheckStore => {
h.write_n(a1, a2 as usize); h.write_n(emulator, a1, a2 as usize);
} }
QasanAction::Poison => { QasanAction::Poison => {
h.poison(a1, a2 as usize, PoisonKind::try_from(a3 as u8).unwrap()); h.poison(
emulator,
a1,
a2 as usize,
PoisonKind::try_from(a3 as u8).unwrap(),
);
} }
QasanAction::UserPoison => { QasanAction::UserPoison => {
h.poison(a1, a2 as usize, PoisonKind::User); h.poison(emulator, a1, a2 as usize, PoisonKind::User);
} }
QasanAction::UnPoison => { QasanAction::UnPoison => {
h.unpoison(a1, a2 as usize); h.unpoison(emulator, a1, a2 as usize);
} }
QasanAction::IsPoison => { QasanAction::IsPoison => {
if h.is_poisoned(a1, a2 as usize) { if h.is_poisoned(emulator, a1, a2 as usize) {
r = 1; r = 1;
} }
} }
QasanAction::Alloc => { QasanAction::Alloc => {
h.alloc(a1, a2); h.alloc(emulator, a1, a2);
} }
QasanAction::Dealloc => { QasanAction::Dealloc => {
h.dealloc(a1); h.dealloc(emulator, a1);
} }
QasanAction::Enable => { QasanAction::Enable => {
h.set_enabled(true); h.set_enabled(true);

View File

@ -6,7 +6,7 @@ pub use libafl_targets::{
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
emu, emu::Emulator,
executor::QemuExecutor, executor::QemuExecutor,
helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
}; };
@ -70,14 +70,15 @@ where
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
executor.hook_cmp_generation(gen_unique_cmp_ids::<I, QT, S>); executor.hook_cmp_generation(gen_unique_cmp_ids::<I, QT, S>);
emu::set_exec_cmp8_hook(trace_cmp8_cmplog); executor.emulator().set_exec_cmp8_hook(trace_cmp8_cmplog);
emu::set_exec_cmp4_hook(trace_cmp4_cmplog); executor.emulator().set_exec_cmp4_hook(trace_cmp4_cmplog);
emu::set_exec_cmp2_hook(trace_cmp2_cmplog); executor.emulator().set_exec_cmp2_hook(trace_cmp2_cmplog);
emu::set_exec_cmp1_hook(trace_cmp1_cmplog); executor.emulator().set_exec_cmp1_hook(trace_cmp1_cmplog);
} }
} }
pub fn gen_unique_cmp_ids<I, QT, S>( pub fn gen_unique_cmp_ids<I, QT, S>(
_emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
state: &mut S, state: &mut S,
pc: u64, pc: u64,

View File

@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
use std::{cell::UnsafeCell, cmp::max}; use std::{cell::UnsafeCell, cmp::max};
use crate::{ use crate::{
emu, emu::Emulator,
executor::QemuExecutor, executor::QemuExecutor,
helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
}; };
@ -69,7 +69,7 @@ where
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
executor.hook_edge_generation(gen_unique_edge_ids::<I, QT, S>); executor.hook_edge_generation(gen_unique_edge_ids::<I, QT, S>);
emu::set_exec_edge_hook(trace_edge_hitcount); executor.emulator().set_exec_edge_hook(trace_edge_hitcount);
} }
} }
@ -83,6 +83,7 @@ fn hash_me(mut x: u64) -> u64 {
} }
pub fn gen_unique_edge_ids<I, QT, S>( pub fn gen_unique_edge_ids<I, QT, S>(
_emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
state: &mut S, state: &mut S,
src: u64, src: u64,
@ -128,6 +129,7 @@ where
} }
pub fn gen_hashed_edge_ids<I, QT, S>( pub fn gen_hashed_edge_ids<I, QT, S>(
_emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: &mut S,
src: u64, src: u64,
@ -157,11 +159,21 @@ pub extern "C" fn trace_edge_single(id: u64) {
} }
} }
pub fn gen_addr_block_ids<I, QT, S>(_helpers: &mut QT, _state: &mut S, pc: u64) -> Option<u64> { pub fn gen_addr_block_ids<I, QT, S>(
_emulator: &Emulator,
_helpers: &mut QT,
_state: &mut S,
pc: u64,
) -> Option<u64> {
Some(pc) Some(pc)
} }
pub fn gen_hashed_block_ids<I, QT, S>(_helpers: &mut QT, _state: &mut S, pc: u64) -> Option<u64> { pub fn gen_hashed_block_ids<I, QT, S>(
_emulator: &Emulator,
_helpers: &mut QT,
_state: &mut S,
pc: u64,
) -> Option<u64> {
Some(hash_me(pc)) Some(hash_me(pc))
} }

View File

@ -226,31 +226,10 @@ extern "C" {
static mut libafl_exec_cmp_hook8: unsafe extern "C" fn(u64, u64, u64); 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; static mut libafl_gen_cmp_hook: unsafe extern "C" fn(u64, u32) -> u64;
static mut libafl_syscall_hook: static mut libafl_pre_syscall_hook:
unsafe extern "C" fn(i32, u64, u64, u64, u64, u64, u64, u64, u64) -> SyscallHookResult; unsafe extern "C" fn(i32, u64, u64, u64, u64, u64, u64, u64, u64) -> SyscallHookResult;
} static mut libafl_post_syscall_hook:
unsafe extern "C" fn(u64, i32, u64, u64, u64, u64, u64, u64, u64, u64) -> u64;
#[allow(clippy::must_use_candidate, clippy::similar_names)]
pub fn init(args: &[String], env: &[(String, String)]) -> i32 {
assert!(!args.is_empty());
let args: Vec<String> = args.iter().map(|x| x.clone() + "\0").collect();
let argv: Vec<*const u8> = args.iter().map(|x| x.as_bytes().as_ptr()).collect();
assert!(argv.len() < i32::MAX as usize);
let env_strs: Vec<String> = env
.iter()
.map(|(k, v)| format!("{}={}\0", &k, &v))
.collect();
let mut envp: Vec<*const u8> = env_strs.iter().map(|x| x.as_bytes().as_ptr()).collect();
envp.push(null());
#[allow(clippy::cast_possible_wrap)]
let argc = argv.len() as i32;
unsafe {
qemu_user_init(
argc,
argv.as_ptr() as *const *const u8,
envp.as_ptr() as *const *const u8,
)
}
} }
#[cfg_attr(feature = "python", pyclass(unsendable))] #[cfg_attr(feature = "python", pyclass(unsendable))]
@ -259,9 +238,10 @@ pub struct GuestMaps {
c_iter: *const c_void, c_iter: *const c_void,
} }
impl Default for GuestMaps { // Consider a private new only for Emulator
impl GuestMaps {
#[must_use] #[must_use]
fn default() -> Self { pub(crate) fn new() -> Self {
unsafe { unsafe {
let maps = read_self_maps(); let maps = read_self_maps();
Self { Self {
@ -272,24 +252,6 @@ impl Default for GuestMaps {
} }
} }
#[cfg(feature = "python")]
#[pymethods]
impl GuestMaps {
#[new]
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
#[cfg(not(feature = "python"))]
impl GuestMaps {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
impl Iterator for GuestMaps { impl Iterator for GuestMaps {
type Item = MapInfo; type Item = MapInfo;
@ -329,280 +291,494 @@ impl Drop for GuestMaps {
} }
} }
pub fn write_mem<T>(addr: u64, buf: &[T]) { static mut EMULATOR_IS_INITIALIZED: bool = false;
let host_addr = g2h(addr);
unsafe { pub struct Emulator {
_private: (),
}
#[allow(clippy::unused_self)]
impl Emulator {
#[allow(clippy::must_use_candidate, clippy::similar_names)]
pub fn new(args: &[String], env: &[(String, String)]) -> Emulator {
unsafe {
assert!(!EMULATOR_IS_INITIALIZED);
}
assert!(!args.is_empty());
let args: Vec<String> = args.iter().map(|x| x.clone() + "\0").collect();
let argv: Vec<*const u8> = args.iter().map(|x| x.as_bytes().as_ptr()).collect();
assert!(argv.len() < i32::MAX as usize);
let env_strs: Vec<String> = env
.iter()
.map(|(k, v)| format!("{}={}\0", &k, &v))
.collect();
let mut envp: Vec<*const u8> = env_strs.iter().map(|x| x.as_bytes().as_ptr()).collect();
envp.push(null());
#[allow(clippy::cast_possible_wrap)]
let argc = argv.len() as i32;
unsafe {
qemu_user_init(
argc,
argv.as_ptr() as *const *const u8,
envp.as_ptr() as *const *const u8,
);
EMULATOR_IS_INITIALIZED = true;
}
Emulator { _private: () }
}
#[must_use]
pub(crate) fn new_empty() -> Emulator {
Emulator { _private: () }
}
#[must_use]
pub fn mappings(&self) -> GuestMaps {
GuestMaps::new()
}
pub unsafe fn write_mem<T>(&self, addr: u64, buf: &[T]) {
let host_addr = self.g2h(addr);
copy_nonoverlapping( copy_nonoverlapping(
buf.as_ptr() as *const _ as *const u8, buf.as_ptr() as *const _ as *const u8,
host_addr, host_addr,
buf.len() * size_of::<T>(), buf.len() * size_of::<T>(),
); );
} }
}
pub fn read_mem<T>(addr: u64, buf: &mut [T]) { pub unsafe fn read_mem<T>(&self, addr: u64, buf: &mut [T]) {
let host_addr = g2h(addr); let host_addr = self.g2h(addr);
unsafe {
copy_nonoverlapping( copy_nonoverlapping(
host_addr as *const u8, host_addr as *const u8,
buf.as_mut_ptr() as *mut _ as *mut u8, buf.as_mut_ptr() as *mut _ as *mut u8,
buf.len() * size_of::<T>(), buf.len() * size_of::<T>(),
); );
} }
}
#[must_use] #[must_use]
pub fn num_regs() -> i32 { pub fn num_regs(&self) -> i32 {
unsafe { libafl_qemu_num_regs() } unsafe { libafl_qemu_num_regs() }
}
pub fn write_reg<R, T>(reg: R, val: T) -> Result<(), String>
where
T: Num + PartialOrd + Copy,
R: Into<i32>,
{
let reg = reg.into();
let success = unsafe { libafl_qemu_write_reg(reg, &val as *const _ as *const u8) };
if success == 0 {
Err(format!("Failed to write to register {}", reg))
} else {
Ok(())
} }
}
pub fn read_reg<R, T>(reg: R) -> Result<T, String> pub fn write_reg<R, T>(&self, reg: R, val: T) -> Result<(), String>
where where
T: Num + PartialOrd + Copy, T: Num + PartialOrd + Copy,
R: Into<i32>, R: Into<i32>,
{ {
let reg = reg.into(); let reg = reg.into();
let mut val = T::zero(); let success = unsafe { libafl_qemu_write_reg(reg, &val as *const _ as *const u8) };
let success = unsafe { libafl_qemu_read_reg(reg, &mut val as *mut _ as *mut u8) }; if success == 0 {
if success == 0 { Err(format!("Failed to write to register {}", reg))
Err(format!("Failed to read register {}", reg)) } else {
} else { Ok(())
Ok(val) }
} }
}
pub fn set_breakpoint(addr: u64) { pub fn read_reg<R, T>(&self, reg: R) -> Result<T, String>
unsafe { where
libafl_qemu_set_breakpoint(addr); T: Num + PartialOrd + Copy,
R: Into<i32>,
{
let reg = reg.into();
let mut val = T::zero();
let success = unsafe { libafl_qemu_read_reg(reg, &mut val as *mut _ as *mut u8) };
if success == 0 {
Err(format!("Failed to read register {}", reg))
} else {
Ok(val)
}
} }
}
pub fn remove_breakpoint(addr: u64) { pub fn set_breakpoint(&self, addr: u64) {
unsafe { unsafe {
libafl_qemu_remove_breakpoint(addr); libafl_qemu_set_breakpoint(addr);
}
} }
}
pub fn set_hook(addr: u64, callback: extern "C" fn(u64), val: u64) { pub fn remove_breakpoint(&self, addr: u64) {
unsafe { unsafe {
libafl_qemu_set_hook(addr, callback, val); libafl_qemu_remove_breakpoint(addr);
}
} }
}
pub fn remove_hook(addr: u64) { pub fn set_hook(&self, addr: u64, callback: extern "C" fn(u64), val: u64) {
unsafe { unsafe {
libafl_qemu_remove_hook(addr); libafl_qemu_set_hook(addr, callback, val);
}
} }
}
pub fn run() { pub fn remove_hook(&self, addr: u64) {
unsafe { unsafe {
libafl_qemu_remove_hook(addr);
}
}
pub unsafe fn run(&self) {
libafl_qemu_run(); libafl_qemu_run();
} }
#[must_use]
pub fn g2h<T>(&self, addr: u64) -> *mut T {
unsafe { transmute(addr + guest_base as u64) }
}
#[must_use]
pub fn h2g<T>(&self, addr: *const T) -> u64 {
unsafe { (addr as usize - guest_base) as u64 }
}
#[must_use]
pub fn binary_path<'a>(&self) -> &'a str {
unsafe { from_utf8_unchecked(from_raw_parts(exec_path, strlen(exec_path))) }
}
#[must_use]
pub fn load_addr(&self) -> u64 {
unsafe { libafl_load_addr() }
}
#[must_use]
pub fn get_brk(&self) -> u64 {
unsafe { libafl_get_brk() }
}
pub fn set_brk(&self, brk: u64) {
unsafe { libafl_set_brk(brk) };
}
pub fn map_private(&self, addr: u64, size: usize, perms: MmapPerms) -> Result<u64, String> {
let res = unsafe {
target_mmap(
addr,
size as u64,
perms.into(),
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
-1,
0,
)
};
if res == 0 {
Err(format!("Failed to map {}", addr))
} else {
Ok(res)
}
}
pub fn unmap(&self, addr: u64, size: usize) -> Result<(), String> {
if unsafe { target_munmap(addr, size as u64) } == 0 {
Ok(())
} else {
Err(format!("Failed to unmap {}", addr))
}
}
// 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 set_gen_edge_hook(&self, hook: extern "C" fn(src: u64, dest: u64) -> u64) {
unsafe {
libafl_gen_edge_hook = hook;
}
}
pub fn set_exec_block_hook(&self, hook: extern "C" fn(pc: u64)) {
unsafe {
libafl_exec_block_hook = hook;
}
}
pub fn set_gen_block_hook(&self, hook: extern "C" fn(pc: u64) -> u64) {
unsafe {
libafl_gen_block_hook = hook;
}
}
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 set_pre_syscall_hook(
&self,
hook: extern "C" fn(i32, u64, u64, u64, u64, u64, u64, u64, u64) -> SyscallHookResult,
) {
unsafe {
libafl_pre_syscall_hook = hook;
}
}
pub fn set_post_syscall_hook(
&self,
hook: extern "C" fn(u64, i32, u64, u64, u64, u64, u64, u64, u64, u64) -> u64,
) {
unsafe {
libafl_post_syscall_hook = hook;
}
}
} }
#[must_use] #[cfg(feature = "python")]
pub fn g2h<T>(addr: u64) -> *mut T { pub mod pybind {
unsafe { transmute(addr + guest_base as u64) } use super::{MmapPerms, SyscallHookResult};
} use core::mem::transmute;
use pyo3::exceptions::PyValueError;
use pyo3::{prelude::*, types::PyInt};
use std::convert::TryFrom;
#[must_use] static mut PY_SYSCALL_HOOK: Option<PyObject> = None;
pub fn h2g<T>(addr: *const T) -> u64 { static mut PY_GENERIC_HOOKS: Vec<(u64, PyObject)> = vec![];
unsafe { (addr as usize - guest_base) as u64 }
}
#[must_use] extern "C" fn py_syscall_hook_wrapper(
pub fn binary_path<'a>() -> &'a str { sys_num: i32,
unsafe { from_utf8_unchecked(from_raw_parts(exec_path, strlen(exec_path))) } a0: u64,
} a1: u64,
a2: u64,
#[must_use] a3: u64,
pub fn load_addr() -> u64 { a4: u64,
unsafe { libafl_load_addr() } a5: u64,
} a6: u64,
a7: u64,
#[must_use] ) -> SyscallHookResult {
pub fn get_brk() -> u64 { unsafe { PY_SYSCALL_HOOK.as_ref() }.map_or_else(
unsafe { libafl_get_brk() } || SyscallHookResult::new(None),
} |obj| {
let args = (sys_num, a0, a1, a2, a3, a4, a5, a6, a7);
pub fn set_brk(brk: u64) { Python::with_gil(|py| {
unsafe { libafl_set_brk(brk) }; let ret = obj.call1(py, args).expect("Error in the syscall hook");
} let any = ret.as_ref(py);
if any.is_none() {
pub fn map_private(addr: u64, size: usize, perms: MmapPerms) -> Result<u64, String> { SyscallHookResult::new(None)
let res = unsafe { } else {
target_mmap( let a: Result<&PyInt, _> = any.cast_as();
addr, if let Ok(i) = a {
size as u64, SyscallHookResult::new(Some(
perms.into(), i.extract().expect("Invalid syscall hook return value"),
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, ))
-1, } else {
0, SyscallHookResult::extract(any)
.expect("The syscall hook must return a SyscallHookResult")
}
}
})
},
) )
}; }
if res == 0 {
Err(format!("Failed to map {}", addr)) extern "C" fn py_generic_hook_wrapper(idx: u64) {
} else { let obj = unsafe { &PY_GENERIC_HOOKS[idx as usize].1 };
Ok(res) Python::with_gil(|py| {
} obj.call0(py).expect("Error in the hook");
} });
}
pub fn unmap(addr: u64, size: usize) -> Result<(), String> {
if unsafe { target_munmap(addr, size as u64) } == 0 { #[pyclass(unsendable)]
Ok(()) pub struct Emulator {
} else { pub emu: super::Emulator,
Err(format!("Failed to unmap {}", addr)) }
}
} #[pymethods]
impl Emulator {
// TODO add has_X_hook() and panic when setting a hook for the second time #[allow(clippy::needless_pass_by_value)]
#[new]
pub fn set_exec_edge_hook(hook: extern "C" fn(id: u64)) { fn new(args: Vec<String>, env: Vec<(String, String)>) -> Emulator {
unsafe { Emulator {
libafl_exec_edge_hook = hook; emu: super::Emulator::new(&args, &env),
} }
} }
pub fn set_gen_edge_hook(hook: extern "C" fn(src: u64, dest: u64) -> u64) { fn write_mem(&self, addr: u64, buf: &[u8]) {
unsafe { unsafe {
libafl_gen_edge_hook = hook; self.emu.write_mem(addr, buf);
} }
} }
pub fn set_exec_block_hook(hook: extern "C" fn(pc: u64)) { fn read_mem(&self, addr: u64, size: usize) -> Vec<u8> {
unsafe { let mut buf = vec![0; size];
libafl_exec_block_hook = hook; unsafe {
} self.emu.read_mem(addr, &mut buf);
} }
buf
pub fn set_gen_block_hook(hook: extern "C" fn(pc: u64) -> u64) { }
unsafe {
libafl_gen_block_hook = hook; fn num_regs(&self) -> i32 {
} self.emu.num_regs()
} }
pub fn set_exec_read1_hook(hook: extern "C" fn(id: u64, addr: u64)) { fn write_reg(&self, reg: i32, val: u64) -> PyResult<()> {
unsafe { self.emu.write_reg(reg, val).map_err(PyValueError::new_err)
libafl_exec_read_hook1 = hook; }
}
} fn read_reg(&self, reg: i32) -> PyResult<u64> {
self.emu.read_reg(reg).map_err(PyValueError::new_err)
pub fn set_exec_read2_hook(hook: extern "C" fn(id: u64, addr: u64)) { }
unsafe {
libafl_exec_read_hook2 = hook; fn set_breakpoint(&self, addr: u64) {
} self.emu.set_breakpoint(addr);
} }
pub fn set_exec_read4_hook(hook: extern "C" fn(id: u64, addr: u64)) { fn remove_breakpoint(&self, addr: u64) {
unsafe { self.emu.remove_breakpoint(addr);
libafl_exec_read_hook4 = hook; }
}
} fn run(&self) {
unsafe {
pub fn set_exec_read8_hook(hook: extern "C" fn(id: u64, addr: u64)) { self.emu.run();
unsafe { }
libafl_exec_read_hook8 = hook; }
}
} fn g2h(&self, addr: u64) -> u64 {
self.emu.g2h::<*const u8>(addr) as u64
pub fn set_exec_read_n_hook(hook: extern "C" fn(id: u64, addr: u64, size: u32)) { }
unsafe {
libafl_exec_read_hookN = hook; fn h2g(&self, addr: u64) -> u64 {
} self.emu.h2g(unsafe { transmute::<_, *const u8>(addr) })
} }
pub fn set_gen_read_hook(hook: extern "C" fn(size: u32) -> u64) { fn binary_path(&self) -> String {
unsafe { self.emu.binary_path().to_owned()
libafl_gen_read_hook = hook; }
}
} fn load_addr(&self) -> u64 {
self.emu.load_addr()
pub fn set_exec_write1_hook(hook: extern "C" fn(id: u64, addr: u64)) { }
unsafe {
libafl_exec_write_hook1 = hook; fn map_private(&self, addr: u64, size: usize, perms: i32) -> PyResult<u64> {
} if let Ok(p) = MmapPerms::try_from(perms) {
} self.emu
.map_private(addr, size, p)
pub fn set_exec_write2_hook(hook: extern "C" fn(id: u64, addr: u64)) { .map_err(PyValueError::new_err)
unsafe { } else {
libafl_exec_write_hook2 = hook; Err(PyValueError::new_err("Invalid perms"))
} }
} }
pub fn set_exec_write4_hook(hook: extern "C" fn(id: u64, addr: u64)) { fn unmap(&self, addr: u64, size: usize) -> PyResult<()> {
unsafe { self.emu.unmap(addr, size).map_err(PyValueError::new_err)
libafl_exec_write_hook4 = hook; }
}
} fn set_syscall_hook(&self, hook: PyObject) {
unsafe {
pub fn set_exec_write8_hook(hook: extern "C" fn(id: u64, addr: u64)) { PY_SYSCALL_HOOK = Some(hook);
unsafe { }
libafl_exec_write_hook8 = hook; self.emu.set_pre_syscall_hook(py_syscall_hook_wrapper);
} }
}
fn set_hook(&self, addr: u64, hook: PyObject) {
pub fn set_exec_write_n_hook(hook: extern "C" fn(id: u64, addr: u64, size: u32)) { unsafe {
unsafe { let idx = PY_GENERIC_HOOKS.len();
libafl_exec_write_hookN = hook; PY_GENERIC_HOOKS.push((addr, hook));
} self.emu.set_hook(addr, py_generic_hook_wrapper, idx as u64);
} }
}
// TODO add pc arg
pub fn set_gen_write_hook(hook: extern "C" fn(size: u32) -> u64) { fn remove_hook(&self, addr: u64) {
unsafe { unsafe {
libafl_gen_write_hook = hook; PY_GENERIC_HOOKS.retain(|(a, _)| *a != addr);
} }
} self.emu.remove_hook(addr);
}
pub fn set_exec_cmp1_hook(hook: extern "C" fn(id: u64, v0: u8, v1: u8)) {
unsafe {
libafl_exec_cmp_hook1 = hook;
}
}
pub fn set_exec_cmp2_hook(hook: extern "C" fn(id: u64, v0: u16, v1: u16)) {
unsafe {
libafl_exec_cmp_hook2 = hook;
}
}
pub fn set_exec_cmp4_hook(hook: extern "C" fn(id: u64, v0: u32, v1: u32)) {
unsafe {
libafl_exec_cmp_hook4 = hook;
}
}
pub fn set_exec_cmp8_hook(hook: extern "C" fn(id: u64, v0: u64, v1: u64)) {
unsafe {
libafl_exec_cmp_hook8 = hook;
}
}
pub fn set_gen_cmp_hook(hook: extern "C" fn(pc: u64, size: u32) -> u64) {
unsafe {
libafl_gen_cmp_hook = hook;
}
}
pub fn set_syscall_hook(
hook: extern "C" fn(i32, u64, u64, u64, u64, u64, u64, u64, u64) -> SyscallHookResult,
) {
unsafe {
libafl_syscall_hook = hook;
} }
} }

View File

@ -15,7 +15,10 @@ use libafl::{
}; };
pub use crate::emu::SyscallHookResult; pub use crate::emu::SyscallHookResult;
use crate::{emu, emu::SKIP_EXEC_HOOK, helper::QemuHelperTuple}; use crate::{
emu::{Emulator, SKIP_EXEC_HOOK},
helper::QemuHelperTuple,
};
static mut QEMU_HELPERS_PTR: *const c_void = ptr::null(); static mut QEMU_HELPERS_PTR: *const c_void = ptr::null();
@ -28,8 +31,10 @@ where
unsafe { unsafe {
let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap(); let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap();
let state = inprocess_get_state::<S>().unwrap(); let state = inprocess_get_state::<S>().unwrap();
let func: fn(&mut QT, &mut S, u64, u64) -> Option<u64> = transmute(GEN_EDGE_HOOK_PTR); let emulator = Emulator::new_empty();
(func)(helpers, state, src, dst).map_or(SKIP_EXEC_HOOK, |id| id) let func: fn(&Emulator, &mut QT, &mut S, u64, u64) -> Option<u64> =
transmute(GEN_EDGE_HOOK_PTR);
(func)(&emulator, helpers, state, src, dst).map_or(SKIP_EXEC_HOOK, |id| id)
} }
} }
@ -41,9 +46,10 @@ where
{ {
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap(); let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &EDGE_HOOKS } { for hook in unsafe { &EDGE_HOOKS } {
let func: fn(&mut QT, &mut S, u64) = unsafe { transmute(*hook) }; let func: fn(&Emulator, &mut QT, &mut S, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id); (func)(&emulator, helpers, state, id);
} }
} }
@ -56,8 +62,9 @@ where
unsafe { unsafe {
let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap(); let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap();
let state = inprocess_get_state::<S>().unwrap(); let state = inprocess_get_state::<S>().unwrap();
let func: fn(&mut QT, &mut S, u64) -> Option<u64> = transmute(GEN_EDGE_HOOK_PTR); let emulator = Emulator::new_empty();
(func)(helpers, state, pc).map_or(SKIP_EXEC_HOOK, |id| id) let func: fn(&Emulator, &mut QT, &mut S, u64) -> Option<u64> = transmute(GEN_EDGE_HOOK_PTR);
(func)(&emulator, helpers, state, pc).map_or(SKIP_EXEC_HOOK, |id| id)
} }
} }
@ -69,9 +76,10 @@ where
{ {
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap(); let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &BLOCK_HOOKS } { for hook in unsafe { &BLOCK_HOOKS } {
let func: fn(&mut QT, &mut S, u64) = unsafe { transmute(*hook) }; let func: fn(&Emulator, &mut QT, &mut S, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id); (func)(&emulator, helpers, state, id);
} }
} }
@ -84,8 +92,10 @@ where
unsafe { unsafe {
let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap(); let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap();
let state = inprocess_get_state::<S>().unwrap(); let state = inprocess_get_state::<S>().unwrap();
let func: fn(&mut QT, &mut S, usize) -> Option<u64> = transmute(GEN_READ_HOOK_PTR); let emulator = Emulator::new_empty();
(func)(helpers, state, size as usize).map_or(SKIP_EXEC_HOOK, |id| id) let func: fn(&Emulator, &mut QT, &mut S, usize) -> Option<u64> =
transmute(GEN_READ_HOOK_PTR);
(func)(&emulator, helpers, state, size as usize).map_or(SKIP_EXEC_HOOK, |id| id)
} }
} }
@ -98,8 +108,10 @@ where
unsafe { unsafe {
let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap(); let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap();
let state = inprocess_get_state::<S>().unwrap(); let state = inprocess_get_state::<S>().unwrap();
let func: fn(&mut QT, &mut S, usize) -> Option<u64> = transmute(GEN_WRITE_HOOK_PTR); let emulator = Emulator::new_empty();
(func)(helpers, state, size as usize).map_or(SKIP_EXEC_HOOK, |id| id) let func: fn(&Emulator, &mut QT, &mut S, usize) -> Option<u64> =
transmute(GEN_WRITE_HOOK_PTR);
(func)(&emulator, helpers, state, size as usize).map_or(SKIP_EXEC_HOOK, |id| id)
} }
} }
@ -111,9 +123,10 @@ where
{ {
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap(); let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &READ1_HOOKS } { for hook in unsafe { &READ1_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr); (func)(&emulator, helpers, state, id, addr);
} }
} }
@ -125,9 +138,10 @@ where
{ {
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap(); let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &READ2_HOOKS } { for hook in unsafe { &READ2_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr); (func)(&emulator, helpers, state, id, addr);
} }
} }
@ -139,9 +153,10 @@ where
{ {
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap(); let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &READ4_HOOKS } { for hook in unsafe { &READ4_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr); (func)(&emulator, helpers, state, id, addr);
} }
} }
@ -153,9 +168,10 @@ where
{ {
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap(); let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &READ8_HOOKS } { for hook in unsafe { &READ8_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr); (func)(&emulator, helpers, state, id, addr);
} }
} }
@ -167,9 +183,10 @@ where
{ {
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap(); let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &READ_N_HOOKS } { for hook in unsafe { &READ_N_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64, usize) = unsafe { transmute(*hook) }; let func: fn(&Emulator, &mut QT, &mut S, u64, u64, usize) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr, size as usize); (func)(&emulator, helpers, state, id, addr, size as usize);
} }
} }
@ -181,9 +198,10 @@ where
{ {
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap(); let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &WRITE1_HOOKS } { for hook in unsafe { &WRITE1_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr); (func)(&emulator, helpers, state, id, addr);
} }
} }
@ -195,9 +213,10 @@ where
{ {
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap(); let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &WRITE2_HOOKS } { for hook in unsafe { &WRITE2_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr); (func)(&emulator, helpers, state, id, addr);
} }
} }
@ -209,9 +228,10 @@ where
{ {
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap(); let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &WRITE4_HOOKS } { for hook in unsafe { &WRITE4_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr); (func)(&emulator, helpers, state, id, addr);
} }
} }
@ -223,9 +243,10 @@ where
{ {
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap(); let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &WRITE8_HOOKS } { for hook in unsafe { &WRITE8_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr); (func)(&emulator, helpers, state, id, addr);
} }
} }
@ -237,9 +258,10 @@ where
{ {
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap(); let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &WRITE_N_HOOKS } { for hook in unsafe { &WRITE_N_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64, usize) = unsafe { transmute(*hook) }; let func: fn(&Emulator, &mut QT, &mut S, u64, u64, usize) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr, size as usize); (func)(&emulator, helpers, state, id, addr, size as usize);
} }
} }
@ -252,8 +274,10 @@ where
unsafe { unsafe {
let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap(); let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap();
let state = inprocess_get_state::<S>().unwrap(); let state = inprocess_get_state::<S>().unwrap();
let func: fn(&mut QT, &mut S, u64, usize) -> Option<u64> = transmute(GEN_CMP_HOOK_PTR); let emulator = Emulator::new_empty();
(func)(helpers, state, pc, size as usize).map_or(SKIP_EXEC_HOOK, |id| id) let func: fn(&Emulator, &mut QT, &mut S, u64, usize) -> Option<u64> =
transmute(GEN_CMP_HOOK_PTR);
(func)(&emulator, helpers, state, pc, size as usize).map_or(SKIP_EXEC_HOOK, |id| id)
} }
} }
@ -265,9 +289,10 @@ where
{ {
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap(); let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &CMP1_HOOKS } { for hook in unsafe { &CMP1_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u8, u8) = unsafe { transmute(*hook) }; let func: fn(&Emulator, &mut QT, &mut S, u64, u8, u8) = unsafe { transmute(*hook) };
(func)(helpers, state, id, v0, v1); (func)(&emulator, helpers, state, id, v0, v1);
} }
} }
@ -279,9 +304,10 @@ where
{ {
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap(); let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &CMP2_HOOKS } { for hook in unsafe { &CMP2_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u16, u16) = unsafe { transmute(*hook) }; let func: fn(&Emulator, &mut QT, &mut S, u64, u16, u16) = unsafe { transmute(*hook) };
(func)(helpers, state, id, v0, v1); (func)(&emulator, helpers, state, id, v0, v1);
} }
} }
@ -293,9 +319,10 @@ where
{ {
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap(); let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &CMP4_HOOKS } { for hook in unsafe { &CMP4_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u32, u32) = unsafe { transmute(*hook) }; let func: fn(&Emulator, &mut QT, &mut S, u64, u32, u32) = unsafe { transmute(*hook) };
(func)(helpers, state, id, v0, v1); (func)(&emulator, helpers, state, id, v0, v1);
} }
} }
@ -307,9 +334,10 @@ where
{ {
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap(); let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &CMP8_HOOKS } { for hook in unsafe { &CMP8_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64, u64) = unsafe { transmute(*hook) }; let func: fn(&Emulator, &mut QT, &mut S, u64, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, v0, v1); (func)(&emulator, helpers, state, id, v0, v1);
} }
} }
@ -331,10 +359,12 @@ where
{ {
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap(); let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
let mut res = SyscallHookResult::new(None); let mut res = SyscallHookResult::new(None);
for hook in unsafe { &SYSCALL_HOOKS } { for hook in unsafe { &SYSCALL_HOOKS } {
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
let func: fn( let func: fn(
&Emulator,
&mut QT, &mut QT,
&mut S, &mut S,
i32, i32,
@ -347,7 +377,9 @@ where
u64, u64,
u64, u64,
) -> SyscallHookResult = unsafe { transmute(*hook) }; ) -> SyscallHookResult = unsafe { transmute(*hook) };
let r = (func)(helpers, state, sys_num, a0, a1, a2, a3, a4, a5, a6, a7); let r = (func)(
&emulator, helpers, state, sys_num, a0, a1, a2, a3, a4, a5, a6, a7,
);
if r.skip_syscall { if r.skip_syscall {
res.skip_syscall = true; res.skip_syscall = true;
res.retval = r.retval; res.retval = r.retval;
@ -356,6 +388,51 @@ where
res res
} }
static mut SYSCALL_POST_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn syscall_after_hooks_wrapper<I, QT, S>(
result: u64,
sys_num: i32,
a0: u64,
a1: u64,
a2: u64,
a3: u64,
a4: u64,
a5: u64,
a6: u64,
a7: u64,
) -> u64
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
let mut res = result;
for hook in unsafe { &SYSCALL_POST_HOOKS } {
#[allow(clippy::type_complexity)]
let func: fn(
&Emulator,
&mut QT,
&mut S,
u64,
i32,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
) -> u64 = unsafe { transmute(*hook) };
res = (func)(
&emulator, helpers, state, res, sys_num, a0, a1, a2, a3, a4, a5, a6, a7,
);
}
res
}
pub struct QemuExecutor<'a, H, I, OT, QT, S> pub struct QemuExecutor<'a, H, I, OT, QT, S>
where where
H: FnMut(&I) -> ExitKind, H: FnMut(&I) -> ExitKind,
@ -364,6 +441,7 @@ where
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
helpers: QT, helpers: QT,
emulator: &'a Emulator,
inner: InProcessExecutor<'a, H, I, OT, S>, inner: InProcessExecutor<'a, H, I, OT, S>,
} }
@ -376,6 +454,7 @@ where
{ {
pub fn new<EM, OC, OF, Z>( pub fn new<EM, OC, OF, Z>(
harness_fn: &'a mut H, harness_fn: &'a mut H,
emulator: &'a Emulator,
helpers: QT, helpers: QT,
observers: OT, observers: OT,
fuzzer: &mut Z, fuzzer: &mut Z,
@ -391,6 +470,7 @@ where
{ {
let slf = Self { let slf = Self {
helpers, helpers,
emulator,
inner: InProcessExecutor::new(harness_fn, observers, fuzzer, state, event_mgr)?, inner: InProcessExecutor::new(harness_fn, observers, fuzzer, state, event_mgr)?,
}; };
slf.helpers.init_all(&slf); slf.helpers.init_all(&slf);
@ -405,184 +485,230 @@ where
&mut self.inner &mut self.inner
} }
pub fn emulator(&self) -> &Emulator {
self.emulator
}
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_edge_generation( pub fn hook_edge_generation(
&self, &self,
hook: fn(&mut QT, &mut S, src: u64, dest: u64) -> Option<u64>, hook: fn(&Emulator, &mut QT, &mut S, src: u64, dest: u64) -> Option<u64>,
) { ) {
unsafe { unsafe {
GEN_EDGE_HOOK_PTR = hook as *const _; GEN_EDGE_HOOK_PTR = hook as *const _;
} }
emu::set_gen_edge_hook(gen_edge_hook_wrapper::<I, QT, S>); self.emulator
.set_gen_edge_hook(gen_edge_hook_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_edge_execution(&self, hook: fn(&mut QT, &mut S, id: u64)) { pub fn hook_edge_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64)) {
unsafe { unsafe {
EDGE_HOOKS.push(hook as *const _); EDGE_HOOKS.push(hook as *const _);
} }
emu::set_exec_edge_hook(edge_hooks_wrapper::<I, QT, S>); self.emulator
.set_exec_edge_hook(edge_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_block_generation(&self, hook: fn(&mut QT, &mut S, pc: u64) -> Option<u64>) { pub fn hook_block_generation(
&self,
hook: fn(&Emulator, &mut QT, &mut S, pc: u64) -> Option<u64>,
) {
unsafe { unsafe {
GEN_BLOCK_HOOK_PTR = hook as *const _; GEN_BLOCK_HOOK_PTR = hook as *const _;
} }
emu::set_gen_block_hook(gen_block_hook_wrapper::<I, QT, S>); self.emulator
.set_gen_block_hook(gen_block_hook_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_block_execution(&self, hook: fn(&mut QT, &mut S, id: u64)) { pub fn hook_block_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64)) {
unsafe { unsafe {
BLOCK_HOOKS.push(hook as *const _); BLOCK_HOOKS.push(hook as *const _);
} }
emu::set_exec_block_hook(block_hooks_wrapper::<I, QT, S>); self.emulator
.set_exec_block_hook(block_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_read_generation(&self, hook: fn(&mut QT, &mut S, size: usize) -> Option<u64>) { pub fn hook_read_generation(
&self,
hook: fn(&Emulator, &mut QT, &mut S, size: usize) -> Option<u64>,
) {
unsafe { unsafe {
GEN_READ_HOOK_PTR = hook as *const _; GEN_READ_HOOK_PTR = hook as *const _;
} }
emu::set_gen_read_hook(gen_read_hook_wrapper::<I, QT, S>); self.emulator
.set_gen_read_hook(gen_read_hook_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_read1_execution(&self, hook: fn(&mut QT, &mut S, id: u64, addr: u64)) { pub fn hook_read1_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64)) {
unsafe { unsafe {
READ1_HOOKS.push(hook as *const _); READ1_HOOKS.push(hook as *const _);
} }
emu::set_exec_read1_hook(read1_hooks_wrapper::<I, QT, S>); self.emulator
.set_exec_read1_hook(read1_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_read2_execution(&self, hook: fn(&mut QT, &mut S, id: u64, addr: u64)) { pub fn hook_read2_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64)) {
unsafe { unsafe {
READ2_HOOKS.push(hook as *const _); READ2_HOOKS.push(hook as *const _);
} }
emu::set_exec_read2_hook(read2_hooks_wrapper::<I, QT, S>); self.emulator
.set_exec_read2_hook(read2_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_read4_execution(&self, hook: fn(&mut QT, &mut S, id: u64, addr: u64)) { pub fn hook_read4_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64)) {
unsafe { unsafe {
READ4_HOOKS.push(hook as *const _); READ4_HOOKS.push(hook as *const _);
} }
emu::set_exec_read4_hook(read4_hooks_wrapper::<I, QT, S>); self.emulator
.set_exec_read4_hook(read4_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_read8_execution(&self, hook: fn(&mut QT, &mut S, id: u64, addr: u64)) { pub fn hook_read8_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64)) {
unsafe { unsafe {
READ8_HOOKS.push(hook as *const _); READ8_HOOKS.push(hook as *const _);
} }
emu::set_exec_read8_hook(read8_hooks_wrapper::<I, QT, S>); self.emulator
.set_exec_read8_hook(read8_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_read_n_execution( pub fn hook_read_n_execution(
&self, &self,
hook: fn(&mut QT, &mut S, id: u64, addr: u64, size: usize), hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64, size: usize),
) { ) {
unsafe { unsafe {
READ_N_HOOKS.push(hook as *const _); READ_N_HOOKS.push(hook as *const _);
} }
emu::set_exec_read_n_hook(read_n_hooks_wrapper::<I, QT, S>); self.emulator
.set_exec_read_n_hook(read_n_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_write_generation(&self, hook: fn(&mut QT, &mut S, size: usize) -> Option<u64>) { pub fn hook_write_generation(
&self,
hook: fn(&Emulator, &mut QT, &mut S, size: usize) -> Option<u64>,
) {
unsafe { unsafe {
GEN_WRITE_HOOK_PTR = hook as *const _; GEN_WRITE_HOOK_PTR = hook as *const _;
} }
emu::set_gen_write_hook(gen_write_hook_wrapper::<I, QT, S>); self.emulator
.set_gen_write_hook(gen_write_hook_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_write1_execution(&self, hook: fn(&mut QT, &mut S, id: u64, addr: u64)) { pub fn hook_write1_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64)) {
unsafe { unsafe {
WRITE1_HOOKS.push(hook as *const _); WRITE1_HOOKS.push(hook as *const _);
} }
emu::set_exec_write1_hook(write1_hooks_wrapper::<I, QT, S>); self.emulator
.set_exec_write1_hook(write1_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_write2_execution(&self, hook: fn(&mut QT, &mut S, id: u64, addr: u64)) { pub fn hook_write2_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64)) {
unsafe { unsafe {
WRITE2_HOOKS.push(hook as *const _); WRITE2_HOOKS.push(hook as *const _);
} }
emu::set_exec_write2_hook(write2_hooks_wrapper::<I, QT, S>); self.emulator
.set_exec_write2_hook(write2_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_write4_execution(&self, hook: fn(&mut QT, &mut S, id: u64, addr: u64)) { pub fn hook_write4_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64)) {
unsafe { unsafe {
WRITE4_HOOKS.push(hook as *const _); WRITE4_HOOKS.push(hook as *const _);
} }
emu::set_exec_write4_hook(write4_hooks_wrapper::<I, QT, S>); self.emulator
.set_exec_write4_hook(write4_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_write8_execution(&self, hook: fn(&mut QT, &mut S, id: u64, addr: u64)) { pub fn hook_write8_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64)) {
unsafe { unsafe {
WRITE8_HOOKS.push(hook as *const _); WRITE8_HOOKS.push(hook as *const _);
} }
emu::set_exec_write8_hook(write8_hooks_wrapper::<I, QT, S>); self.emulator
.set_exec_write8_hook(write8_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_write_n_execution( pub fn hook_write_n_execution(
&self, &self,
hook: fn(&mut QT, &mut S, id: u64, addr: u64, size: usize), hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64, size: usize),
) { ) {
unsafe { unsafe {
WRITE_N_HOOKS.push(hook as *const _); WRITE_N_HOOKS.push(hook as *const _);
} }
emu::set_exec_write_n_hook(write_n_hooks_wrapper::<I, QT, S>); self.emulator
.set_exec_write_n_hook(write_n_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_cmp_generation( pub fn hook_cmp_generation(
&self, &self,
hook: fn(&mut QT, &mut S, pc: u64, size: usize) -> Option<u64>, hook: fn(&Emulator, &mut QT, &mut S, pc: u64, size: usize) -> Option<u64>,
) { ) {
unsafe { unsafe {
GEN_CMP_HOOK_PTR = hook as *const _; GEN_CMP_HOOK_PTR = hook as *const _;
} }
emu::set_gen_cmp_hook(gen_cmp_hook_wrapper::<I, QT, S>); self.emulator
.set_gen_cmp_hook(gen_cmp_hook_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_cmp1_execution(&self, hook: fn(&mut QT, &mut S, id: u64, v0: u8, v1: u8)) { pub fn hook_cmp1_execution(
&self,
hook: fn(&Emulator, &mut QT, &mut S, id: u64, v0: u8, v1: u8),
) {
unsafe { unsafe {
CMP1_HOOKS.push(hook as *const _); CMP1_HOOKS.push(hook as *const _);
} }
emu::set_exec_cmp1_hook(cmp1_hooks_wrapper::<I, QT, S>); self.emulator
.set_exec_cmp1_hook(cmp1_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_cmp2_execution(&self, hook: fn(&mut QT, &mut S, id: u64, v0: u16, v1: u16)) { pub fn hook_cmp2_execution(
&self,
hook: fn(&Emulator, &mut QT, &mut S, id: u64, v0: u16, v1: u16),
) {
unsafe { unsafe {
CMP2_HOOKS.push(hook as *const _); CMP2_HOOKS.push(hook as *const _);
} }
emu::set_exec_cmp2_hook(cmp2_hooks_wrapper::<I, QT, S>); self.emulator
.set_exec_cmp2_hook(cmp2_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_cmp4_execution(&self, hook: fn(&mut QT, &mut S, id: u64, v0: u32, v1: u32)) { pub fn hook_cmp4_execution(
&self,
hook: fn(&Emulator, &mut QT, &mut S, id: u64, v0: u32, v1: u32),
) {
unsafe { unsafe {
CMP4_HOOKS.push(hook as *const _); CMP4_HOOKS.push(hook as *const _);
} }
emu::set_exec_cmp4_hook(cmp4_hooks_wrapper::<I, QT, S>); self.emulator
.set_exec_cmp4_hook(cmp4_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn hook_cmp8_execution(&self, hook: fn(&mut QT, &mut S, id: u64, v0: u64, v1: u64)) { pub fn hook_cmp8_execution(
&self,
hook: fn(&Emulator, &mut QT, &mut S, id: u64, v0: u64, v1: u64),
) {
unsafe { unsafe {
CMP8_HOOKS.push(hook as *const _); CMP8_HOOKS.push(hook as *const _);
} }
emu::set_exec_cmp8_hook(cmp8_hooks_wrapper::<I, QT, S>); self.emulator
.set_exec_cmp8_hook(cmp8_hooks_wrapper::<I, QT, S>);
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
@ -590,6 +716,7 @@ where
pub fn hook_syscalls( pub fn hook_syscalls(
&self, &self,
hook: fn( hook: fn(
&Emulator,
&mut QT, &mut QT,
&mut S, &mut S,
sys_num: i32, sys_num: i32,
@ -606,7 +733,35 @@ where
unsafe { unsafe {
SYSCALL_HOOKS.push(hook as *const _); SYSCALL_HOOKS.push(hook as *const _);
} }
emu::set_syscall_hook(syscall_hooks_wrapper::<I, QT, S>); self.emulator
.set_pre_syscall_hook(syscall_hooks_wrapper::<I, QT, S>);
}
#[allow(clippy::unused_self)]
#[allow(clippy::type_complexity)]
pub fn hook_after_syscalls(
&self,
hook: fn(
&Emulator,
&mut QT,
&mut S,
result: u64,
sys_num: i32,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
) -> u64,
) {
unsafe {
SYSCALL_POST_HOOKS.push(hook as *const _);
}
self.emulator
.set_post_syscall_hook(syscall_after_hooks_wrapper::<I, QT, S>);
} }
} }
@ -625,9 +780,9 @@ where
input: &I, input: &I,
) -> Result<ExitKind, Error> { ) -> Result<ExitKind, Error> {
unsafe { QEMU_HELPERS_PTR = &self.helpers as *const _ as *const c_void }; unsafe { QEMU_HELPERS_PTR = &self.helpers as *const _ as *const c_void };
self.helpers.pre_exec_all(input); self.helpers.pre_exec_all(self.emulator, input);
let r = self.inner.run_target(fuzzer, state, mgr, input); let r = self.inner.run_target(fuzzer, state, mgr, input);
self.helpers.post_exec_all(input); self.helpers.post_exec_all(self.emulator, input);
unsafe { QEMU_HELPERS_PTR = ptr::null() }; unsafe { QEMU_HELPERS_PTR = ptr::null() };
r r
} }

View File

@ -3,7 +3,7 @@ use libafl::{
}; };
use std::ops::Range; use std::ops::Range;
use crate::executor::QemuExecutor; use crate::{emu::Emulator, executor::QemuExecutor};
// TODO remove 'static when specialization will be stable // TODO remove 'static when specialization will be stable
pub trait QemuHelper<I, S>: 'static pub trait QemuHelper<I, S>: 'static
@ -18,9 +18,9 @@ where
{ {
} }
fn pre_exec(&mut self, _input: &I) {} fn pre_exec(&mut self, _emulator: &Emulator, _input: &I) {}
fn post_exec(&mut self, _input: &I) {} fn post_exec(&mut self, _emulator: &Emulator, _input: &I) {}
} }
pub trait QemuHelperTuple<I, S>: MatchFirstType pub trait QemuHelperTuple<I, S>: MatchFirstType
@ -33,9 +33,9 @@ where
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>; QT: QemuHelperTuple<I, S>;
fn pre_exec_all(&mut self, input: &I); fn pre_exec_all(&mut self, _emulator: &Emulator, input: &I);
fn post_exec_all(&mut self, input: &I); fn post_exec_all(&mut self, _emulator: &Emulator, input: &I);
} }
impl<I, S> QemuHelperTuple<I, S> for () impl<I, S> QemuHelperTuple<I, S> for ()
@ -50,9 +50,9 @@ where
{ {
} }
fn pre_exec_all(&mut self, _input: &I) {} fn pre_exec_all(&mut self, _emulator: &Emulator, _input: &I) {}
fn post_exec_all(&mut self, _input: &I) {} fn post_exec_all(&mut self, _emulator: &Emulator, _input: &I) {}
} }
impl<Head, Tail, I, S> QemuHelperTuple<I, S> for (Head, Tail) impl<Head, Tail, I, S> QemuHelperTuple<I, S> for (Head, Tail)
@ -71,14 +71,14 @@ where
self.1.init_all(executor); self.1.init_all(executor);
} }
fn pre_exec_all(&mut self, input: &I) { fn pre_exec_all(&mut self, emulator: &Emulator, input: &I) {
self.0.pre_exec(input); self.0.pre_exec(emulator, input);
self.1.pre_exec_all(input); self.1.pre_exec_all(emulator, input);
} }
fn post_exec_all(&mut self, input: &I) { fn post_exec_all(&mut self, emulator: &Emulator, input: &I) {
self.0.post_exec(input); self.0.post_exec(emulator, input);
self.1.post_exec_all(input); self.1.post_exec_all(emulator, input);
} }
} }

View File

@ -1,40 +1,23 @@
use std::env; use std::env;
#[cfg(feature = "aarch64")] #[cfg(cpu_target = "aarch64")]
pub mod aarch64; pub mod aarch64;
#[cfg(all(feature = "aarch64", not(feature = "clippy")))] #[cfg(all(cpu_target = "aarch64", not(feature = "clippy")))]
pub use aarch64::*; pub use aarch64::*;
#[cfg(feature = "arm")] #[cfg(cpu_target = "arm")]
pub mod arm; pub mod arm;
#[cfg(all(feature = "arm", not(feature = "clippy")))] #[cfg(all(cpu_target = "arm", not(feature = "clippy")))]
pub use arm::*; pub use arm::*;
#[cfg(feature = "i386")] #[cfg(cpu_target = "i386")]
pub mod i386; pub mod i386;
#[cfg(all(feature = "i386", not(feature = "clippy")))] #[cfg(all(cpu_target = "i386", not(feature = "clippy")))]
pub use i386::*; pub use i386::*;
// We default to x86_64, having a default makes CI easier :) #[cfg(cpu_target = "x86_64")]
#[cfg(any(
feature = "x86_64",
not(any(
feature = "arm",
feature = "aarch64",
feature = "i386",
feature = "x86_64"
))
))]
pub mod x86_64; pub mod x86_64;
#[cfg(any( #[cfg(cpu_target = "x86_64")]
feature = "x86_64",
not(any(
feature = "arm",
feature = "aarch64",
feature = "i386",
feature = "x86_64"
))
))]
pub use x86_64::*; pub use x86_64::*;
pub mod elf; pub mod elf;
@ -89,159 +72,15 @@ pub fn filter_qemu_args() -> Vec<String> {
} }
#[cfg(all(target_os = "linux", feature = "python"))] #[cfg(all(target_os = "linux", feature = "python"))]
use pyo3::{prelude::*, types::PyInt}; use pyo3::prelude::*;
#[cfg(all(target_os = "linux", feature = "python"))]
static mut PY_SYSCALL_HOOK: Option<PyObject> = None;
#[cfg(all(target_os = "linux", feature = "python"))]
static mut PY_GENERIC_HOOKS: Vec<(u64, PyObject)> = vec![];
#[cfg(all(target_os = "linux", feature = "python"))] #[cfg(all(target_os = "linux", feature = "python"))]
#[pymodule] #[pymodule]
#[pyo3(name = "libafl_qemu")] #[pyo3(name = "libafl_qemu")]
#[allow(clippy::items_after_statements, clippy::too_many_lines)] #[allow(clippy::items_after_statements, clippy::too_many_lines)]
pub fn python_module(py: Python, m: &PyModule) -> PyResult<()> { pub fn python_module(py: Python, m: &PyModule) -> PyResult<()> {
use core::mem::transmute;
use pyo3::exceptions::PyValueError;
use std::convert::TryFrom;
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
#[pyfn(m)]
#[allow(clippy::needless_pass_by_value)]
fn init(args: Vec<String>, env: Vec<(String, String)>) -> i32 {
emu::init(&args, &env)
}
#[pyfn(m)]
#[allow(clippy::needless_pass_by_value)]
fn write_mem(addr: u64, buf: &[u8]) {
emu::write_mem(addr, buf);
}
#[pyfn(m)]
fn read_mem(addr: u64, size: usize) -> Vec<u8> {
let mut buf = vec![0; size];
emu::read_mem(addr, &mut buf);
buf
}
#[pyfn(m)]
fn num_regs() -> i32 {
emu::num_regs()
}
#[pyfn(m)]
fn write_reg(reg: i32, val: u64) -> PyResult<()> {
emu::write_reg(reg, val).map_err(PyValueError::new_err)
}
#[pyfn(m)]
fn read_reg(reg: i32) -> PyResult<u64> {
emu::read_reg(reg).map_err(PyValueError::new_err)
}
#[pyfn(m)]
fn set_breakpoint(addr: u64) {
emu::set_breakpoint(addr);
}
#[pyfn(m)]
fn remove_breakpoint(addr: u64) {
emu::remove_breakpoint(addr);
}
#[pyfn(m)]
fn run() {
emu::run();
}
#[pyfn(m)]
fn g2h(addr: u64) -> u64 {
emu::g2h::<*const u8>(addr) as u64
}
#[pyfn(m)]
fn h2g(addr: u64) -> u64 {
emu::h2g(unsafe { transmute::<_, *const u8>(addr) })
}
#[pyfn(m)]
fn binary_path() -> String {
emu::binary_path().to_owned()
}
#[pyfn(m)]
fn load_addr() -> u64 {
emu::load_addr()
}
#[pyfn(m)]
fn map_private(addr: u64, size: usize, perms: i32) -> PyResult<u64> {
if let Ok(p) = MmapPerms::try_from(perms) {
emu::map_private(addr, size, p).map_err(PyValueError::new_err)
} else {
Err(PyValueError::new_err("Invalid perms"))
}
}
#[pyfn(m)]
fn unmap(addr: u64, size: usize) -> PyResult<()> {
emu::unmap(addr, size).map_err(PyValueError::new_err)
}
extern "C" fn py_syscall_hook_wrapper(
sys_num: i32,
a0: u64,
a1: u64,
a2: u64,
a3: u64,
a4: u64,
a5: u64,
a6: u64,
a7: u64,
) -> SyscallHookResult {
unsafe { PY_SYSCALL_HOOK.as_ref() }.map_or_else(
|| SyscallHookResult::new(None),
|obj| {
let args = (sys_num, a0, a1, a2, a3, a4, a5, a6, a7);
Python::with_gil(|py| {
let ret = obj.call1(py, args).expect("Error in the syscall hook");
let any = ret.as_ref(py);
if any.is_none() {
SyscallHookResult::new(None)
} else {
let a: Result<&PyInt, _> = any.cast_as();
if let Ok(i) = a {
SyscallHookResult::new(Some(
i.extract().expect("Invalid syscall hook return value"),
))
} else {
SyscallHookResult::extract(any)
.expect("The syscall hook must return a SyscallHookResult")
}
}
})
},
)
}
#[pyfn(m)]
fn set_syscall_hook(hook: PyObject) {
unsafe {
PY_SYSCALL_HOOK = Some(hook);
}
emu::set_syscall_hook(py_syscall_hook_wrapper);
}
extern "C" fn py_generic_hook_wrapper(idx: u64) {
let obj = unsafe { &PY_GENERIC_HOOKS[idx as usize].1 };
Python::with_gil(|py| {
obj.call0(py).expect("Error in the hook");
});
}
#[pyfn(m)]
fn set_hook(addr: u64, hook: PyObject) {
unsafe {
let idx = PY_GENERIC_HOOKS.len();
PY_GENERIC_HOOKS.push((addr, hook));
emu::set_hook(addr, py_generic_hook_wrapper, idx as u64);
}
}
#[pyfn(m)]
fn remove_hook(addr: u64) {
unsafe {
PY_GENERIC_HOOKS.retain(|(a, _)| *a != addr);
}
emu::remove_hook(addr);
}
let regsm = PyModule::new(py, "regs")?; let regsm = PyModule::new(py, "regs")?;
for r in Regs::iter() { for r in Regs::iter() {
let v: i32 = r.into(); let v: i32 = r.into();
@ -259,6 +98,7 @@ pub fn python_module(py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<emu::MapInfo>()?; m.add_class::<emu::MapInfo>()?;
m.add_class::<emu::GuestMaps>()?; m.add_class::<emu::GuestMaps>()?;
m.add_class::<emu::SyscallHookResult>()?; m.add_class::<emu::SyscallHookResult>()?;
m.add_class::<emu::pybind::Emulator>()?;
Ok(()) Ok(())
} }

View File

@ -2,11 +2,10 @@ use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, stat
use std::collections::HashMap; use std::collections::HashMap;
use crate::{ use crate::{
emu, emu::Emulator,
emu::{GuestMaps, SyscallHookResult},
executor::QemuExecutor, executor::QemuExecutor,
helper::{QemuHelper, QemuHelperTuple}, helper::{QemuHelper, QemuHelperTuple},
SYS_mmap, SYS_mmap, SYS_mremap,
}; };
pub const SNAPSHOT_PAGE_SIZE: usize = 4096; pub const SNAPSHOT_PAGE_SIZE: usize = 4096;
@ -42,10 +41,10 @@ impl QemuSnapshotHelper {
} }
} }
pub fn snapshot(&mut self) { pub fn snapshot(&mut self, emulator: &Emulator) {
self.brk = emu::get_brk(); self.brk = emulator.get_brk();
self.pages.clear(); self.pages.clear();
for map in GuestMaps::new() { for map in emulator.mappings() {
// TODO track all the pages OR track mproctect // TODO track all the pages OR track mproctect
if !map.flags().is_w() { if !map.flags().is_w() {
continue; continue;
@ -57,7 +56,7 @@ impl QemuSnapshotHelper {
dirty: false, dirty: false,
data: [0; SNAPSHOT_PAGE_SIZE], data: [0; SNAPSHOT_PAGE_SIZE],
}; };
emu::read_mem(addr, &mut info.data); unsafe { emulator.read_mem(addr, &mut info.data) };
self.pages.insert(addr, info); self.pages.insert(addr, info);
addr += SNAPSHOT_PAGE_SIZE as u64; addr += SNAPSHOT_PAGE_SIZE as u64;
} }
@ -94,26 +93,26 @@ impl QemuSnapshotHelper {
} }
} }
pub fn reset(&mut self) { pub fn reset(&mut self, emulator: &Emulator) {
self.access_cache = [u64::MAX; 4]; self.access_cache = [u64::MAX; 4];
self.access_cache_idx = 0; self.access_cache_idx = 0;
while let Some(page) = self.dirty.pop() { while let Some(page) = self.dirty.pop() {
if let Some(info) = self.pages.get_mut(&page) { if let Some(info) = self.pages.get_mut(&page) {
emu::write_mem(page, &info.data); unsafe { emulator.write_mem(page, &info.data) };
info.dirty = false; info.dirty = false;
} }
} }
emu::set_brk(self.brk); emulator.set_brk(self.brk);
self.reset_maps(); self.reset_maps(emulator);
} }
pub fn add_mapped(&mut self, start: u64, size: usize) { pub fn add_mapped(&mut self, start: u64, size: usize) {
self.new_maps.push((start, size)); self.new_maps.push((start, size));
} }
pub fn reset_maps(&mut self) { pub fn reset_maps(&mut self, emulator: &Emulator) {
for (addr, size) in &self.new_maps { for (addr, size) in &self.new_maps {
drop(emu::unmap(*addr, *size)); drop(emulator.unmap(*addr, *size));
} }
self.new_maps.clear(); self.new_maps.clear();
} }
@ -142,20 +141,25 @@ where
executor.hook_write1_execution(trace_write1_snapshot::<I, QT, S>); executor.hook_write1_execution(trace_write1_snapshot::<I, QT, S>);
executor.hook_write_n_execution(trace_write_n_snapshot::<I, QT, S>); executor.hook_write_n_execution(trace_write_n_snapshot::<I, QT, S>);
executor.hook_syscalls(trace_mmap_snapshot::<I, QT, S>); executor.hook_after_syscalls(trace_mmap_snapshot::<I, QT, S>);
} }
fn pre_exec(&mut self, _input: &I) { fn pre_exec(&mut self, emulator: &Emulator, _input: &I) {
if self.empty { if self.empty {
self.snapshot(); self.snapshot(emulator);
} else { } else {
self.reset(); self.reset(emulator);
} }
} }
} }
pub fn trace_write1_snapshot<I, QT, S>(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64) pub fn trace_write1_snapshot<I, QT, S>(
where _emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
addr: u64,
) where
I: Input, I: Input,
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
@ -165,8 +169,13 @@ where
h.access(addr, 1); h.access(addr, 1);
} }
pub fn trace_write2_snapshot<I, QT, S>(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64) pub fn trace_write2_snapshot<I, QT, S>(
where _emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
addr: u64,
) where
I: Input, I: Input,
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
@ -176,8 +185,13 @@ where
h.access(addr, 2); h.access(addr, 2);
} }
pub fn trace_write4_snapshot<I, QT, S>(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64) pub fn trace_write4_snapshot<I, QT, S>(
where _emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
addr: u64,
) where
I: Input, I: Input,
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
@ -187,8 +201,13 @@ where
h.access(addr, 4); h.access(addr, 4);
} }
pub fn trace_write8_snapshot<I, QT, S>(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64) pub fn trace_write8_snapshot<I, QT, S>(
where _emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
addr: u64,
) where
I: Input, I: Input,
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
@ -199,6 +218,7 @@ where
} }
pub fn trace_write_n_snapshot<I, QT, S>( pub fn trace_write_n_snapshot<I, QT, S>(
_emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: &mut S,
_id: u64, _id: u64,
@ -216,27 +236,39 @@ pub fn trace_write_n_snapshot<I, QT, S>(
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn trace_mmap_snapshot<I, QT, S>( pub fn trace_mmap_snapshot<I, QT, S>(
_emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: &mut S,
result: u64,
sys_num: i32, sys_num: i32,
a0: u64, a0: u64,
a1: u64, a1: u64,
_a2: u64, a2: u64,
_a3: u64, _a3: u64,
_a4: u64, _a4: u64,
_a5: u64, _a5: u64,
_a6: u64, _a6: u64,
_a7: u64, _a7: u64,
) -> SyscallHookResult ) -> u64
where where
I: Input, I: Input,
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
if result == u64::MAX
/* -1 */
{
return result;
}
if i64::from(sys_num) == SYS_mmap { if i64::from(sys_num) == SYS_mmap {
let h = helpers let h = helpers
.match_first_type_mut::<QemuSnapshotHelper>() .match_first_type_mut::<QemuSnapshotHelper>()
.unwrap(); .unwrap();
h.add_mapped(a0, a1 as usize); h.add_mapped(result, a1 as usize);
} else if i64::from(sys_num) == SYS_mremap {
let h = helpers
.match_first_type_mut::<QemuSnapshotHelper>()
.unwrap();
h.add_mapped(a0, a2 as usize);
} }
SyscallHookResult::new(None) result
} }

View File

@ -15,14 +15,21 @@ build = "build.rs"
python = ["pyo3", "libafl_qemu/python", "pyo3-build-config"] python = ["pyo3", "libafl_qemu/python", "pyo3-build-config"]
default = [] default = []
# for libafl_qemu
# The following architecture features are mutually exclusive.
x86_64 = ["libafl_qemu/x86_64"] # build qemu for x86_64 (default)
i386 = ["libafl_qemu/i386"] # build qemu for i386
arm = ["libafl_qemu/arm"] # build qemu for arm
aarch64 = ["libafl_qemu/aarch64"] # build qemu for aarch64
[build-dependencies] [build-dependencies]
pyo3-build-config = { version = "0.14.5", optional = true } pyo3-build-config = { version = "0.14.5", optional = true }
[dependencies] [dependencies]
libafl = { path = "../libafl", version = "0.7.0" } libafl = { path = "../libafl", version = "0.7.0" }
libafl_targets = { path = "../libafl_targets", version = "0.7.0" } libafl_targets = { path = "../libafl_targets", version = "0.7.0" }
libafl_qemu = { path = "../libafl_qemu", version = "0.7.0", features = ["x86_64"] } libafl_qemu = { path = "../libafl_qemu", version = "0.7.0" }
typed-builder = "0.9.0" # Implement the builder pattern at compiletime typed-builder = "0.9.1" # Implement the builder pattern at compiletime
#pyo3 = { version = "0.15", features = ["extension-module"], optional = true } #pyo3 = { version = "0.15", features = ["extension-module"], optional = true }
pyo3 = { version = "0.15", optional = true } pyo3 = { version = "0.15", optional = true }

View File

@ -30,7 +30,7 @@ use libafl::{
state::{HasCorpus, HasMetadata, StdState}, state::{HasCorpus, HasMetadata, StdState},
}; };
pub use libafl_qemu::emu; pub use libafl_qemu::emu::Emulator;
use libafl_qemu::{cmplog, edges, QemuCmpLogHelper, QemuEdgeCoverageHelper, QemuExecutor}; use libafl_qemu::{cmplog, edges, QemuCmpLogHelper, QemuEdgeCoverageHelper, QemuExecutor};
use libafl_targets::CmpLogObserver; use libafl_targets::CmpLogObserver;
@ -75,7 +75,7 @@ where
H: FnMut(&[u8]), H: FnMut(&[u8]),
{ {
#[allow(clippy::too_many_lines, clippy::similar_names)] #[allow(clippy::too_many_lines, clippy::similar_names)]
pub fn run(&mut self) { pub fn run(&mut self, emulator: &Emulator) {
let conf = match self.configuration.as_ref() { let conf = match self.configuration.as_ref() {
Some(name) => EventConfig::from_name(name), Some(name) => EventConfig::from_name(name),
None => EventConfig::AlwaysUnique, None => EventConfig::AlwaysUnique,
@ -172,6 +172,7 @@ where
if self.use_cmplog { if self.use_cmplog {
let executor = QemuExecutor::new( let executor = QemuExecutor::new(
&mut harness, &mut harness,
emulator,
tuple_list!(QemuEdgeCoverageHelper::new(), QemuCmpLogHelper::new()), tuple_list!(QemuEdgeCoverageHelper::new(), QemuCmpLogHelper::new()),
tuple_list!(edges_observer, time_observer), tuple_list!(edges_observer, time_observer),
&mut fuzzer, &mut fuzzer,
@ -247,6 +248,7 @@ where
} else { } else {
let executor = QemuExecutor::new( let executor = QemuExecutor::new(
&mut harness, &mut harness,
emulator,
tuple_list!(QemuEdgeCoverageHelper::new()), tuple_list!(QemuEdgeCoverageHelper::new()),
tuple_list!(edges_observer, time_observer), tuple_list!(edges_observer, time_observer),
&mut fuzzer, &mut fuzzer,
@ -332,6 +334,7 @@ where
pub mod pybind { pub mod pybind {
use crate::qemu; use crate::qemu;
use libafl::bolts::os::Cores; use libafl::bolts::os::Cores;
use libafl_qemu::emu::pybind::Emulator;
use pyo3::prelude::*; use pyo3::prelude::*;
use pyo3::types::PyBytes; use pyo3::types::PyBytes;
use std::path::PathBuf; use std::path::PathBuf;
@ -362,7 +365,7 @@ pub mod pybind {
} }
#[allow(clippy::needless_pass_by_value)] #[allow(clippy::needless_pass_by_value)]
pub fn run(&self, harness: PyObject) { pub fn run(&self, emulator: &Emulator, harness: PyObject) {
qemu::QemuBytesCoverageSugar::builder() qemu::QemuBytesCoverageSugar::builder()
.input_dirs(&self.input_dirs) .input_dirs(&self.input_dirs)
.output_dir(self.output_dir.clone()) .output_dir(self.output_dir.clone())
@ -377,7 +380,7 @@ pub mod pybind {
.unwrap(); .unwrap();
}) })
.build() .build()
.run(); .run(&emulator.emu);
} }
} }