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]
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"] }
[build-dependencies]

View File

@ -52,7 +52,7 @@ use libafl_qemu::{
edges,
edges::QemuEdgeCoverageHelper,
elf::EasyElf,
emu,
emu::Emulator,
filter_qemu_args,
//snapshot::QemuSnapshotHelper,
MmapPerms,
@ -66,10 +66,6 @@ pub fn main() {
// Needed only on no_std
//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")
.version("0.4.0")
.author("AFLplusplus team")
@ -170,34 +166,37 @@ fn fuzz(
logfile: PathBuf,
timeout: Duration,
) -> 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 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
.resolve_symbol("LLVMFuzzerTestOneInput", emu::load_addr())
.resolve_symbol("LLVMFuzzerTestOneInput", emu.load_addr())
.expect("Symbol LLVMFuzzerTestOneInput not found");
println!("LLVMFuzzerTestOneInput @ {:#x}", test_one_input_ptr);
emu::set_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
emu::run();
emu.set_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
unsafe { emu.run() };
println!(
"Break at {:#x}",
emu::read_reg::<_, u64>(Regs::Rip).unwrap()
);
println!("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];
emu::read_mem(stack_ptr, &mut ret_addr);
unsafe { emu.read_mem(stack_ptr, &mut ret_addr) };
let ret_addr = ret_addr[0];
println!("Stack pointer = {:#x}", stack_ptr);
println!("Return address = {:#x}", ret_addr);
emu::remove_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
emu::set_breakpoint(ret_addr); // LLVMFuzzerTestOneInput ret addr
emu.remove_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
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);
let log = RefCell::new(
@ -309,20 +308,23 @@ fn fuzz(
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::Rsi, len).unwrap();
emu::write_reg(Regs::Rip, test_one_input_ptr).unwrap();
emu::write_reg(Regs::Rsp, stack_ptr).unwrap();
emu.write_reg(Regs::Rdi, input_addr).unwrap();
emu.write_reg(Regs::Rsi, len).unwrap();
emu.write_reg(Regs::Rip, test_one_input_ptr).unwrap();
emu.write_reg(Regs::Rsp, stack_ptr).unwrap();
emu::run();
emu.run();
}
ExitKind::Ok
};
let executor = QemuExecutor::new(
&mut harness,
&emu,
tuple_list!(
QemuEdgeCoverageHelper::new(),
QemuCmpLogHelper::new(),

View File

@ -6,38 +6,38 @@ import lief
MAX_SIZE = 0x100
BINARY_PATH = './a.out'
qemu.init(['qemu-x86_64', BINARY_PATH], [])
emu = qemu.Emulator(['qemu-x86_64', BINARY_PATH], [])
elf = lief.parse(BINARY_PATH)
test_one_input = elf.get_function_address("LLVMFuzzerTestOneInput")
if elf.is_pie:
test_one_input += qemu.load_addr()
test_one_input += emu.load_addr()
print('LLVMFuzzerTestOneInput @ 0x%x' % test_one_input)
qemu.set_breakpoint(test_one_input)
qemu.run()
emu.set_breakpoint(test_one_input)
emu.run()
sp = qemu.read_reg(qemu.amd64.Rsp)
sp = emu.read_reg(qemu.regs.Rsp)
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)
inp = qemu.map_private(0, MAX_SIZE, qemu.mmap.ReadWrite)
inp = emu.map_private(0, MAX_SIZE, qemu.mmap.ReadWrite)
assert(inp > 0)
qemu.remove_breakpoint(test_one_input)
qemu.set_breakpoint(retaddr)
emu.remove_breakpoint(test_one_input)
emu.set_breakpoint(retaddr)
def harness(b):
if len(b) > MAX_SIZE:
b = b[:MAX_SIZE]
qemu.write_mem(inp, b)
qemu.write_reg(qemu.amd64.Rsi, len(b))
qemu.write_reg(qemu.amd64.Rdi, inp)
qemu.write_reg(qemu.amd64.Rsp, sp)
qemu.write_reg(qemu.amd64.Rip, test_one_input)
qemu.run()
emu.write_mem(inp, b)
emu.write_reg(qemu.regs.Rsi, len(b))
emu.write_reg(qemu.regs.Rdi, inp)
emu.write_reg(qemu.regs.Rsp, sp)
emu.write_reg(qemu.regs.Rip, test_one_input)
emu.run()
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,
};
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() {
@ -42,39 +53,37 @@ pub fn fuzz() {
let objective_dir = PathBuf::from("./crashes");
// Initialize QEMU
env::remove_var("LD_LIBRARY_PATH");
let args: Vec<String> = env::args().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 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
.resolve_symbol("LLVMFuzzerTestOneInput", emu::load_addr())
.resolve_symbol("LLVMFuzzerTestOneInput", emu.load_addr())
.expect("Symbol LLVMFuzzerTestOneInput not found");
println!("LLVMFuzzerTestOneInput @ {:#x}", test_one_input_ptr);
emu::set_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
emu::run();
emu.set_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
unsafe { emu.run() };
println!(
"Break at {:#x}",
emu::read_reg::<_, u64>(Regs::Rip).unwrap()
);
println!("Break at {:#x}", emu.read_reg::<_, u64>(Regs::Rip).unwrap());
// 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];
emu::read_mem(stack_ptr, &mut ret_addr);
unsafe { emu.read_mem(stack_ptr, &mut ret_addr) };
let ret_addr = ret_addr[0];
println!("Stack pointer = {:#x}", stack_ptr);
println!("Return address = {:#x}", ret_addr);
emu::remove_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
emu::set_breakpoint(ret_addr); // LLVMFuzzerTestOneInput ret addr
emu.remove_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
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);
// The wrapped harness function, calling out to the LLVM-style harness
@ -87,14 +96,16 @@ pub fn fuzz() {
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::Rsi, len).unwrap();
emu::write_reg(Regs::Rip, test_one_input_ptr).unwrap();
emu::write_reg(Regs::Rsp, stack_ptr).unwrap();
emu.write_reg(Regs::Rdi, input_addr).unwrap();
emu.write_reg(Regs::Rsi, len).unwrap();
emu.write_reg(Regs::Rip, test_one_input_ptr).unwrap();
emu.write_reg(Regs::Rsp, stack_ptr).unwrap();
emu::run();
emu.run();
}
ExitKind::Ok
};
@ -149,6 +160,7 @@ pub fn fuzz() {
// Create a QEMU in-process executor
let executor = QemuExecutor::new(
&mut harness,
&emu,
// The QEMU helpers define common hooks like coverage tracking hooks
tuple_list!(QemuEdgeCoverageHelper::new()),
tuple_list!(edges_observer, time_observer),

View File

@ -55,7 +55,7 @@ bincode = {version = "1.3", optional = true }
static_assertions = "1.1.0"
ctor = "0.1.20"
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
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_DIRNAME: &str = "qemu-libafl-bridge";
const QEMU_REVISION: &str = "652155bab71357380420f90a944e463ba4bcd372";
const QEMU_REVISION: &str = "fa2b9c4a25f548f15b3d1b1afcfdb75cc7165f9a";
fn build_dep_check(tools: &[&str]) {
for tool in tools {
@ -36,34 +36,25 @@ fn main() {
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 :)
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") {
// assume x86_64 for clippy
"x86_64"
let cpu_target = if cfg!(feature = "x86_64") {
"x86_64".to_string()
} else if cfg!(feature = "arm") {
"arm"
"arm".to_string()
} else if cfg!(feature = "aarch64") {
"aarch64"
"aarch64".to_string()
} else if cfg!(feature = "i386") {
"368"
"i368".to_string()
} else {
// if cfg!(feature = "x86_64") {
"x86_64"
/*} else {
panic!("No architecture feture enabled for libafl_qemu");
*/
env::var("CPU_TARGET").unwrap_or_else(|_| {
println!(
"cargo:warning=No architecture feature enabled or CPU_TARGET env specified for libafl_qemu, supported: arm, aarch64, i386, x86_64 - defaulting to x86_64"
);
"x86_64".to_string()
})
};
let jobs = env::var("CARGO_BUILD_JOBS");
@ -112,8 +103,6 @@ fn main() {
Command::new("git")
.current_dir(&out_dir_path)
.arg("clone")
.arg("--depth")
.arg("1")
.arg(QEMU_URL)
.status()
.unwrap();
@ -231,6 +220,7 @@ fn main() {
for dir in &[
build_dir.join("libcommon.fa.p"),
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("libqom.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 crate::{
emu,
emu::SyscallHookResult,
emu::{Emulator, SyscallHookResult},
executor::QemuExecutor,
helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
Regs,
@ -114,7 +113,7 @@ unsafe extern "C" fn asan_giovese_populate_context(ctx: *mut CallContext, _pc: u
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());
let current = env::current_exe().unwrap();
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_INITED = true;
}
emu::init(args, env)
Emulator::new(args, env)
}
// TODO intrumentation filter
@ -172,7 +171,7 @@ pub struct QemuAsanHelper {
impl QemuAsanHelper {
#[must_use]
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 {
enabled: true,
filter: QemuInstrumentationFilter::None,
@ -202,7 +201,7 @@ impl QemuAsanHelper {
}
#[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 {
let ctx: *const CallContext =
libc::calloc(core::mem::size_of::<CallContext>(), 1) as *const _;
@ -211,188 +210,188 @@ impl QemuAsanHelper {
}
#[allow(clippy::unused_self)]
pub fn dealloc(&mut self, addr: u64) {
pub fn dealloc(&mut self, emulator: &Emulator, addr: u64) {
unsafe {
let ckinfo = asan_giovese_alloc_search(addr);
if let Some(ck) = ckinfo.as_mut() {
if ck.start != addr {
// 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 =
libc::calloc(core::mem::size_of::<CallContext>(), 1) as *const _;
ck.free_ctx = ctx;
} else {
// 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)]
#[must_use]
pub fn is_poisoned(&self, addr: u64, size: usize) -> bool {
unsafe { asan_giovese_loadN(emu::g2h(addr), size) != 0 }
pub fn is_poisoned(&self, emulator: &Emulator, addr: u64, size: usize) -> bool {
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 {
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(
0,
addr,
1,
emu::read_reg(Regs::Pc).unwrap_or(u64::MAX),
emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX),
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 {
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(
0,
addr,
2,
emu::read_reg(Regs::Pc).unwrap_or(u64::MAX),
emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX),
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 {
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(
0,
addr,
4,
emu::read_reg(Regs::Pc).unwrap_or(u64::MAX),
emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX),
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 {
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(
0,
addr,
8,
emu::read_reg(Regs::Pc).unwrap_or(u64::MAX),
emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX),
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 {
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(
0,
addr,
size,
emu::read_reg(Regs::Pc).unwrap_or(u64::MAX),
emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX),
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 {
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(
1,
addr,
1,
emu::read_reg(Regs::Pc).unwrap_or(u64::MAX),
emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX),
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 {
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(
1,
addr,
2,
emu::read_reg(Regs::Pc).unwrap_or(u64::MAX),
emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX),
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 {
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(
1,
addr,
4,
emu::read_reg(Regs::Pc).unwrap_or(u64::MAX),
emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX),
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 {
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(
1,
addr,
8,
emu::read_reg(Regs::Pc).unwrap_or(u64::MAX),
emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX),
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 {
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(
1,
addr,
size,
emu::read_reg(Regs::Pc).unwrap_or(u64::MAX),
emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX),
0,
emu::read_reg(Regs::Sp).unwrap_or(u64::MAX),
emulator.read_reg(Regs::Sp).unwrap_or(u64::MAX),
);
}
}
}
#[allow(clippy::unused_self)]
pub fn poison(&mut self, addr: u64, size: usize, poison: PoisonKind) {
unsafe { asan_giovese_poison_region(emu::g2h(addr), size, poison.into()) };
pub fn poison(&mut self, emulator: &Emulator, addr: u64, size: usize, poison: PoisonKind) {
unsafe { asan_giovese_poison_region(emulator.g2h(addr), size, poison.into()) };
}
#[allow(clippy::unused_self)]
pub fn unpoison(&mut self, addr: u64, size: usize) {
unsafe { asan_giovese_unpoison_region(emu::g2h(addr), size) };
pub fn unpoison(&mut self, emulator: &Emulator, addr: u64, size: usize) {
unsafe { asan_giovese_unpoison_region(emulator.g2h(addr), size) };
}
#[allow(clippy::unused_self)]
@ -435,13 +434,14 @@ where
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();
}
}
// TODO add pc to generation hooks
pub fn gen_readwrite_asan<I, QT, S>(
_emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
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)
where
pub fn trace_read1_asan<I, QT, S>(
emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
addr: u64,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
{
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)
where
pub fn trace_read2_asan<I, QT, S>(
emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
addr: u64,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
{
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)
where
pub fn trace_read4_asan<I, QT, S>(
emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
addr: u64,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
{
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)
where
pub fn trace_read8_asan<I, QT, S>(
emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
addr: u64,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
{
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>(
emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
@ -506,46 +527,67 @@ pub fn trace_read_n_asan<I, QT, S>(
QT: QemuHelperTuple<I, S>,
{
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)
where
pub fn trace_write1_asan<I, QT, S>(
emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
addr: u64,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
{
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)
where
pub fn trace_write2_asan<I, QT, S>(
emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
addr: u64,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
{
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)
where
pub fn trace_write4_asan<I, QT, S>(
emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
addr: u64,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
{
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)
where
pub fn trace_write8_asan<I, QT, S>(
emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
addr: u64,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
{
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>(
emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
@ -556,11 +598,12 @@ pub fn trace_write_n_asan<I, QT, S>(
QT: QemuHelperTuple<I, S>,
{
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)]
pub fn qasan_fake_syscall<I, QT, S>(
emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
sys_num: i32,
@ -582,30 +625,35 @@ where
let mut r = 0;
match QasanAction::try_from(a0).expect("Invalid QASan action number") {
QasanAction::CheckLoad => {
h.read_n(a1, a2 as usize);
h.read_n(emulator, a1, a2 as usize);
}
QasanAction::CheckStore => {
h.write_n(a1, a2 as usize);
h.write_n(emulator, a1, a2 as usize);
}
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 => {
h.poison(a1, a2 as usize, PoisonKind::User);
h.poison(emulator, a1, a2 as usize, PoisonKind::User);
}
QasanAction::UnPoison => {
h.unpoison(a1, a2 as usize);
h.unpoison(emulator, a1, a2 as usize);
}
QasanAction::IsPoison => {
if h.is_poisoned(a1, a2 as usize) {
if h.is_poisoned(emulator, a1, a2 as usize) {
r = 1;
}
}
QasanAction::Alloc => {
h.alloc(a1, a2);
h.alloc(emulator, a1, a2);
}
QasanAction::Dealloc => {
h.dealloc(a1);
h.dealloc(emulator, a1);
}
QasanAction::Enable => {
h.set_enabled(true);

View File

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

View File

@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
use std::{cell::UnsafeCell, cmp::max};
use crate::{
emu,
emu::Emulator,
executor::QemuExecutor,
helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
};
@ -69,7 +69,7 @@ where
QT: QemuHelperTuple<I, 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>(
_emulator: &Emulator,
helpers: &mut QT,
state: &mut S,
src: u64,
@ -128,6 +129,7 @@ where
}
pub fn gen_hashed_edge_ids<I, QT, S>(
_emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
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)
}
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))
}

View File

@ -226,31 +226,10 @@ extern "C" {
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_syscall_hook:
static mut libafl_pre_syscall_hook:
unsafe extern "C" fn(i32, u64, u64, u64, u64, u64, u64, u64, u64) -> SyscallHookResult;
}
#[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,
)
}
static mut libafl_post_syscall_hook:
unsafe extern "C" fn(u64, i32, u64, u64, u64, u64, u64, u64, u64, u64) -> u64;
}
#[cfg_attr(feature = "python", pyclass(unsendable))]
@ -259,9 +238,10 @@ pub struct GuestMaps {
c_iter: *const c_void,
}
impl Default for GuestMaps {
// Consider a private new only for Emulator
impl GuestMaps {
#[must_use]
fn default() -> Self {
pub(crate) fn new() -> Self {
unsafe {
let maps = read_self_maps();
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 {
type Item = MapInfo;
@ -329,38 +291,80 @@ impl Drop for GuestMaps {
}
}
pub fn write_mem<T>(addr: u64, buf: &[T]) {
let host_addr = g2h(addr);
static mut EMULATOR_IS_INITIALIZED: bool = false;
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(
buf.as_ptr() as *const _ as *const u8,
host_addr,
buf.len() * size_of::<T>(),
);
}
}
pub fn read_mem<T>(addr: u64, buf: &mut [T]) {
let host_addr = g2h(addr);
unsafe {
pub unsafe fn read_mem<T>(&self, addr: u64, buf: &mut [T]) {
let host_addr = self.g2h(addr);
copy_nonoverlapping(
host_addr as *const u8,
buf.as_mut_ptr() as *mut _ as *mut u8,
buf.len() * size_of::<T>(),
);
}
}
#[must_use]
pub fn num_regs() -> i32 {
#[must_use]
pub fn num_regs(&self) -> i32 {
unsafe { libafl_qemu_num_regs() }
}
}
pub fn write_reg<R, T>(reg: R, val: T) -> Result<(), String>
where
pub fn write_reg<R, T>(&self, 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 {
@ -368,13 +372,13 @@ where
} else {
Ok(())
}
}
}
pub fn read_reg<R, T>(reg: R) -> Result<T, String>
where
pub fn read_reg<R, T>(&self, reg: R) -> Result<T, String>
where
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) };
@ -383,68 +387,66 @@ where
} else {
Ok(val)
}
}
}
pub fn set_breakpoint(addr: u64) {
pub fn set_breakpoint(&self, addr: u64) {
unsafe {
libafl_qemu_set_breakpoint(addr);
}
}
}
pub fn remove_breakpoint(addr: u64) {
pub fn remove_breakpoint(&self, addr: u64) {
unsafe {
libafl_qemu_remove_breakpoint(addr);
}
}
}
pub fn set_hook(addr: u64, callback: extern "C" fn(u64), val: u64) {
pub fn set_hook(&self, addr: u64, callback: extern "C" fn(u64), val: u64) {
unsafe {
libafl_qemu_set_hook(addr, callback, val);
}
}
}
pub fn remove_hook(addr: u64) {
pub fn remove_hook(&self, addr: u64) {
unsafe {
libafl_qemu_remove_hook(addr);
}
}
}
pub fn run() {
unsafe {
pub unsafe fn run(&self) {
libafl_qemu_run();
}
}
#[must_use]
pub fn g2h<T>(addr: u64) -> *mut T {
#[must_use]
pub fn g2h<T>(&self, addr: u64) -> *mut T {
unsafe { transmute(addr + guest_base as u64) }
}
}
#[must_use]
pub fn h2g<T>(addr: *const T) -> 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>() -> &'a str {
#[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() -> u64 {
#[must_use]
pub fn load_addr(&self) -> u64 {
unsafe { libafl_load_addr() }
}
}
#[must_use]
pub fn get_brk() -> u64 {
#[must_use]
pub fn get_brk(&self) -> u64 {
unsafe { libafl_get_brk() }
}
}
pub fn set_brk(brk: u64) {
pub fn set_brk(&self, brk: u64) {
unsafe { libafl_set_brk(brk) };
}
}
pub fn map_private(addr: u64, size: usize, perms: MmapPerms) -> Result<u64, String> {
pub fn map_private(&self, addr: u64, size: usize, perms: MmapPerms) -> Result<u64, String> {
let res = unsafe {
target_mmap(
addr,
@ -460,149 +462,323 @@ pub fn map_private(addr: u64, size: usize, perms: MmapPerms) -> Result<u64, Stri
} else {
Ok(res)
}
}
}
pub fn unmap(addr: u64, size: usize) -> Result<(), String> {
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
// TODO add has_X_hook() and panic when setting a hook for the second time
pub fn set_exec_edge_hook(hook: extern "C" fn(id: u64)) {
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(hook: extern "C" fn(src: u64, dest: u64) -> u64) {
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(hook: extern "C" fn(pc: u64)) {
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(hook: extern "C" fn(pc: u64) -> u64) {
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(hook: extern "C" fn(id: u64, addr: u64)) {
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(hook: extern "C" fn(id: u64, addr: u64)) {
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(hook: extern "C" fn(id: u64, addr: u64)) {
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(hook: extern "C" fn(id: u64, addr: u64)) {
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(hook: extern "C" fn(id: u64, addr: u64, size: u32)) {
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(hook: extern "C" fn(size: u32) -> u64) {
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(hook: extern "C" fn(id: u64, addr: u64)) {
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(hook: extern "C" fn(id: u64, addr: u64)) {
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(hook: extern "C" fn(id: u64, addr: u64)) {
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(hook: extern "C" fn(id: u64, addr: u64)) {
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(hook: extern "C" fn(id: u64, addr: u64, size: u32)) {
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(hook: extern "C" fn(size: u32) -> u64) {
// 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(hook: extern "C" fn(id: u64, v0: u8, v1: u8)) {
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(hook: extern "C" fn(id: u64, v0: u16, v1: u16)) {
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(hook: extern "C" fn(id: u64, v0: u32, v1: u32)) {
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(hook: extern "C" fn(id: u64, v0: u64, v1: u64)) {
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(hook: extern "C" fn(pc: u64, size: u32) -> u64) {
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_syscall_hook(
pub fn set_pre_syscall_hook(
&self,
hook: extern "C" fn(i32, u64, u64, u64, u64, u64, u64, u64, u64) -> SyscallHookResult,
) {
) {
unsafe {
libafl_syscall_hook = hook;
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;
}
}
}
#[cfg(feature = "python")]
pub mod pybind {
use super::{MmapPerms, SyscallHookResult};
use core::mem::transmute;
use pyo3::exceptions::PyValueError;
use pyo3::{prelude::*, types::PyInt};
use std::convert::TryFrom;
static mut PY_SYSCALL_HOOK: Option<PyObject> = None;
static mut PY_GENERIC_HOOKS: Vec<(u64, PyObject)> = vec![];
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")
}
}
})
},
)
}
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");
});
}
#[pyclass(unsendable)]
pub struct Emulator {
pub emu: super::Emulator,
}
#[pymethods]
impl Emulator {
#[allow(clippy::needless_pass_by_value)]
#[new]
fn new(args: Vec<String>, env: Vec<(String, String)>) -> Emulator {
Emulator {
emu: super::Emulator::new(&args, &env),
}
}
fn write_mem(&self, addr: u64, buf: &[u8]) {
unsafe {
self.emu.write_mem(addr, buf);
}
}
fn read_mem(&self, addr: u64, size: usize) -> Vec<u8> {
let mut buf = vec![0; size];
unsafe {
self.emu.read_mem(addr, &mut buf);
}
buf
}
fn num_regs(&self) -> i32 {
self.emu.num_regs()
}
fn write_reg(&self, reg: i32, val: u64) -> PyResult<()> {
self.emu.write_reg(reg, val).map_err(PyValueError::new_err)
}
fn read_reg(&self, reg: i32) -> PyResult<u64> {
self.emu.read_reg(reg).map_err(PyValueError::new_err)
}
fn set_breakpoint(&self, addr: u64) {
self.emu.set_breakpoint(addr);
}
fn remove_breakpoint(&self, addr: u64) {
self.emu.remove_breakpoint(addr);
}
fn run(&self) {
unsafe {
self.emu.run();
}
}
fn g2h(&self, addr: u64) -> u64 {
self.emu.g2h::<*const u8>(addr) as u64
}
fn h2g(&self, addr: u64) -> u64 {
self.emu.h2g(unsafe { transmute::<_, *const u8>(addr) })
}
fn binary_path(&self) -> String {
self.emu.binary_path().to_owned()
}
fn load_addr(&self) -> u64 {
self.emu.load_addr()
}
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)
.map_err(PyValueError::new_err)
} else {
Err(PyValueError::new_err("Invalid perms"))
}
}
fn unmap(&self, addr: u64, size: usize) -> PyResult<()> {
self.emu.unmap(addr, size).map_err(PyValueError::new_err)
}
fn set_syscall_hook(&self, hook: PyObject) {
unsafe {
PY_SYSCALL_HOOK = Some(hook);
}
self.emu.set_pre_syscall_hook(py_syscall_hook_wrapper);
}
fn set_hook(&self, addr: u64, hook: PyObject) {
unsafe {
let idx = PY_GENERIC_HOOKS.len();
PY_GENERIC_HOOKS.push((addr, hook));
self.emu.set_hook(addr, py_generic_hook_wrapper, idx as u64);
}
}
fn remove_hook(&self, addr: u64) {
unsafe {
PY_GENERIC_HOOKS.retain(|(a, _)| *a != addr);
}
self.emu.remove_hook(addr);
}
}
}

View File

@ -15,7 +15,10 @@ use libafl::{
};
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();
@ -28,8 +31,10 @@ where
unsafe {
let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap();
let state = inprocess_get_state::<S>().unwrap();
let func: fn(&mut QT, &mut S, u64, u64) -> Option<u64> = transmute(GEN_EDGE_HOOK_PTR);
(func)(helpers, state, src, dst).map_or(SKIP_EXEC_HOOK, |id| id)
let emulator = Emulator::new_empty();
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 state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &EDGE_HOOKS } {
let func: fn(&mut QT, &mut S, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id);
let func: fn(&Emulator, &mut QT, &mut S, u64) = unsafe { transmute(*hook) };
(func)(&emulator, helpers, state, id);
}
}
@ -56,8 +62,9 @@ where
unsafe {
let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap();
let state = inprocess_get_state::<S>().unwrap();
let func: fn(&mut QT, &mut S, u64) -> Option<u64> = transmute(GEN_EDGE_HOOK_PTR);
(func)(helpers, state, pc).map_or(SKIP_EXEC_HOOK, |id| id)
let emulator = Emulator::new_empty();
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 state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &BLOCK_HOOKS } {
let func: fn(&mut QT, &mut S, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id);
let func: fn(&Emulator, &mut QT, &mut S, u64) = unsafe { transmute(*hook) };
(func)(&emulator, helpers, state, id);
}
}
@ -84,8 +92,10 @@ where
unsafe {
let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap();
let state = inprocess_get_state::<S>().unwrap();
let func: fn(&mut QT, &mut S, usize) -> Option<u64> = transmute(GEN_READ_HOOK_PTR);
(func)(helpers, state, size as usize).map_or(SKIP_EXEC_HOOK, |id| id)
let emulator = Emulator::new_empty();
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 {
let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap();
let state = inprocess_get_state::<S>().unwrap();
let func: fn(&mut QT, &mut S, usize) -> Option<u64> = transmute(GEN_WRITE_HOOK_PTR);
(func)(helpers, state, size as usize).map_or(SKIP_EXEC_HOOK, |id| id)
let emulator = Emulator::new_empty();
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 state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &READ1_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr);
let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(&emulator, helpers, state, id, addr);
}
}
@ -125,9 +138,10 @@ where
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &READ2_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr);
let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(&emulator, helpers, state, id, addr);
}
}
@ -139,9 +153,10 @@ where
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &READ4_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr);
let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(&emulator, helpers, state, id, addr);
}
}
@ -153,9 +168,10 @@ where
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &READ8_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr);
let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(&emulator, helpers, state, id, addr);
}
}
@ -167,9 +183,10 @@ where
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &READ_N_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64, usize) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr, size as usize);
let func: fn(&Emulator, &mut QT, &mut S, u64, u64, usize) = unsafe { transmute(*hook) };
(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 state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &WRITE1_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr);
let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(&emulator, helpers, state, id, addr);
}
}
@ -195,9 +213,10 @@ where
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &WRITE2_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr);
let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(&emulator, helpers, state, id, addr);
}
}
@ -209,9 +228,10 @@ where
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &WRITE4_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr);
let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(&emulator, helpers, state, id, addr);
}
}
@ -223,9 +243,10 @@ where
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &WRITE8_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr);
let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
(func)(&emulator, helpers, state, id, addr);
}
}
@ -237,9 +258,10 @@ where
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &WRITE_N_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64, usize) = unsafe { transmute(*hook) };
(func)(helpers, state, id, addr, size as usize);
let func: fn(&Emulator, &mut QT, &mut S, u64, u64, usize) = unsafe { transmute(*hook) };
(func)(&emulator, helpers, state, id, addr, size as usize);
}
}
@ -252,8 +274,10 @@ where
unsafe {
let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap();
let state = inprocess_get_state::<S>().unwrap();
let func: fn(&mut QT, &mut S, u64, usize) -> Option<u64> = transmute(GEN_CMP_HOOK_PTR);
(func)(helpers, state, pc, size as usize).map_or(SKIP_EXEC_HOOK, |id| id)
let emulator = Emulator::new_empty();
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 state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &CMP1_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u8, u8) = unsafe { transmute(*hook) };
(func)(helpers, state, id, v0, v1);
let func: fn(&Emulator, &mut QT, &mut S, u64, u8, u8) = unsafe { transmute(*hook) };
(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 state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &CMP2_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u16, u16) = unsafe { transmute(*hook) };
(func)(helpers, state, id, v0, v1);
let func: fn(&Emulator, &mut QT, &mut S, u64, u16, u16) = unsafe { transmute(*hook) };
(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 state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &CMP4_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u32, u32) = unsafe { transmute(*hook) };
(func)(helpers, state, id, v0, v1);
let func: fn(&Emulator, &mut QT, &mut S, u64, u32, u32) = unsafe { transmute(*hook) };
(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 state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &CMP8_HOOKS } {
let func: fn(&mut QT, &mut S, u64, u64, u64) = unsafe { transmute(*hook) };
(func)(helpers, state, id, v0, v1);
let func: fn(&Emulator, &mut QT, &mut S, u64, u64, u64) = unsafe { transmute(*hook) };
(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 state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
let mut res = SyscallHookResult::new(None);
for hook in unsafe { &SYSCALL_HOOKS } {
#[allow(clippy::type_complexity)]
let func: fn(
&Emulator,
&mut QT,
&mut S,
i32,
@ -347,7 +377,9 @@ where
u64,
u64,
) -> 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 {
res.skip_syscall = true;
res.retval = r.retval;
@ -356,6 +388,51 @@ where
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>
where
H: FnMut(&I) -> ExitKind,
@ -364,6 +441,7 @@ where
QT: QemuHelperTuple<I, S>,
{
helpers: QT,
emulator: &'a Emulator,
inner: InProcessExecutor<'a, H, I, OT, S>,
}
@ -376,6 +454,7 @@ where
{
pub fn new<EM, OC, OF, Z>(
harness_fn: &'a mut H,
emulator: &'a Emulator,
helpers: QT,
observers: OT,
fuzzer: &mut Z,
@ -391,6 +470,7 @@ where
{
let slf = Self {
helpers,
emulator,
inner: InProcessExecutor::new(harness_fn, observers, fuzzer, state, event_mgr)?,
};
slf.helpers.init_all(&slf);
@ -405,184 +485,230 @@ where
&mut self.inner
}
pub fn emulator(&self) -> &Emulator {
self.emulator
}
#[allow(clippy::unused_self)]
pub fn hook_edge_generation(
&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 {
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)]
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 {
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)]
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 {
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)]
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 {
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)]
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 {
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)]
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 {
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)]
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 {
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)]
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 {
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)]
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 {
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)]
pub fn hook_read_n_execution(
&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 {
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)]
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 {
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)]
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 {
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)]
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 {
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)]
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 {
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)]
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 {
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)]
pub fn hook_write_n_execution(
&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 {
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)]
pub fn hook_cmp_generation(
&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 {
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)]
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 {
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)]
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 {
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)]
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 {
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)]
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 {
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)]
@ -590,6 +716,7 @@ where
pub fn hook_syscalls(
&self,
hook: fn(
&Emulator,
&mut QT,
&mut S,
sys_num: i32,
@ -606,7 +733,35 @@ where
unsafe {
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,
) -> Result<ExitKind, Error> {
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);
self.helpers.post_exec_all(input);
self.helpers.post_exec_all(self.emulator, input);
unsafe { QEMU_HELPERS_PTR = ptr::null() };
r
}

View File

@ -3,7 +3,7 @@ use libafl::{
};
use std::ops::Range;
use crate::executor::QemuExecutor;
use crate::{emu::Emulator, executor::QemuExecutor};
// TODO remove 'static when specialization will be stable
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
@ -33,9 +33,9 @@ where
OT: ObserversTuple<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 ()
@ -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)
@ -71,14 +71,14 @@ where
self.1.init_all(executor);
}
fn pre_exec_all(&mut self, input: &I) {
self.0.pre_exec(input);
self.1.pre_exec_all(input);
fn pre_exec_all(&mut self, emulator: &Emulator, input: &I) {
self.0.pre_exec(emulator, input);
self.1.pre_exec_all(emulator, input);
}
fn post_exec_all(&mut self, input: &I) {
self.0.post_exec(input);
self.1.post_exec_all(input);
fn post_exec_all(&mut self, emulator: &Emulator, input: &I) {
self.0.post_exec(emulator, input);
self.1.post_exec_all(emulator, input);
}
}

View File

@ -1,40 +1,23 @@
use std::env;
#[cfg(feature = "aarch64")]
#[cfg(cpu_target = "aarch64")]
pub mod aarch64;
#[cfg(all(feature = "aarch64", not(feature = "clippy")))]
#[cfg(all(cpu_target = "aarch64", not(feature = "clippy")))]
pub use aarch64::*;
#[cfg(feature = "arm")]
#[cfg(cpu_target = "arm")]
pub mod arm;
#[cfg(all(feature = "arm", not(feature = "clippy")))]
#[cfg(all(cpu_target = "arm", not(feature = "clippy")))]
pub use arm::*;
#[cfg(feature = "i386")]
#[cfg(cpu_target = "i386")]
pub mod i386;
#[cfg(all(feature = "i386", not(feature = "clippy")))]
#[cfg(all(cpu_target = "i386", not(feature = "clippy")))]
pub use i386::*;
// We default to x86_64, having a default makes CI easier :)
#[cfg(any(
feature = "x86_64",
not(any(
feature = "arm",
feature = "aarch64",
feature = "i386",
feature = "x86_64"
))
))]
#[cfg(cpu_target = "x86_64")]
pub mod x86_64;
#[cfg(any(
feature = "x86_64",
not(any(
feature = "arm",
feature = "aarch64",
feature = "i386",
feature = "x86_64"
))
))]
#[cfg(cpu_target = "x86_64")]
pub use x86_64::*;
pub mod elf;
@ -89,159 +72,15 @@ pub fn filter_qemu_args() -> Vec<String> {
}
#[cfg(all(target_os = "linux", feature = "python"))]
use pyo3::{prelude::*, types::PyInt};
#[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![];
use pyo3::prelude::*;
#[cfg(all(target_os = "linux", feature = "python"))]
#[pymodule]
#[pyo3(name = "libafl_qemu")]
#[allow(clippy::items_after_statements, clippy::too_many_lines)]
pub fn python_module(py: Python, m: &PyModule) -> PyResult<()> {
use core::mem::transmute;
use pyo3::exceptions::PyValueError;
use std::convert::TryFrom;
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")?;
for r in Regs::iter() {
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::GuestMaps>()?;
m.add_class::<emu::SyscallHookResult>()?;
m.add_class::<emu::pybind::Emulator>()?;
Ok(())
}

View File

@ -2,11 +2,10 @@ use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, stat
use std::collections::HashMap;
use crate::{
emu,
emu::{GuestMaps, SyscallHookResult},
emu::Emulator,
executor::QemuExecutor,
helper::{QemuHelper, QemuHelperTuple},
SYS_mmap,
SYS_mmap, SYS_mremap,
};
pub const SNAPSHOT_PAGE_SIZE: usize = 4096;
@ -42,10 +41,10 @@ impl QemuSnapshotHelper {
}
}
pub fn snapshot(&mut self) {
self.brk = emu::get_brk();
pub fn snapshot(&mut self, emulator: &Emulator) {
self.brk = emulator.get_brk();
self.pages.clear();
for map in GuestMaps::new() {
for map in emulator.mappings() {
// TODO track all the pages OR track mproctect
if !map.flags().is_w() {
continue;
@ -57,7 +56,7 @@ impl QemuSnapshotHelper {
dirty: false,
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);
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_idx = 0;
while let Some(page) = self.dirty.pop() {
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;
}
}
emu::set_brk(self.brk);
self.reset_maps();
emulator.set_brk(self.brk);
self.reset_maps(emulator);
}
pub fn add_mapped(&mut self, start: u64, size: usize) {
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 {
drop(emu::unmap(*addr, *size));
drop(emulator.unmap(*addr, *size));
}
self.new_maps.clear();
}
@ -142,20 +141,25 @@ where
executor.hook_write1_execution(trace_write1_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 {
self.snapshot();
self.snapshot(emulator);
} else {
self.reset();
self.reset(emulator);
}
}
}
pub fn trace_write1_snapshot<I, QT, S>(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64)
where
pub fn trace_write1_snapshot<I, QT, S>(
_emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
addr: u64,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
{
@ -165,8 +169,13 @@ where
h.access(addr, 1);
}
pub fn trace_write2_snapshot<I, QT, S>(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64)
where
pub fn trace_write2_snapshot<I, QT, S>(
_emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
addr: u64,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
{
@ -176,8 +185,13 @@ where
h.access(addr, 2);
}
pub fn trace_write4_snapshot<I, QT, S>(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64)
where
pub fn trace_write4_snapshot<I, QT, S>(
_emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
addr: u64,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
{
@ -187,8 +201,13 @@ where
h.access(addr, 4);
}
pub fn trace_write8_snapshot<I, QT, S>(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64)
where
pub fn trace_write8_snapshot<I, QT, S>(
_emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
addr: u64,
) where
I: Input,
QT: QemuHelperTuple<I, S>,
{
@ -199,6 +218,7 @@ where
}
pub fn trace_write_n_snapshot<I, QT, S>(
_emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
_id: u64,
@ -216,27 +236,39 @@ pub fn trace_write_n_snapshot<I, QT, S>(
#[allow(clippy::too_many_arguments)]
pub fn trace_mmap_snapshot<I, QT, S>(
_emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
result: u64,
sys_num: i32,
a0: u64,
a1: u64,
_a2: u64,
a2: u64,
_a3: u64,
_a4: u64,
_a5: u64,
_a6: u64,
_a7: u64,
) -> SyscallHookResult
) -> u64
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
if result == u64::MAX
/* -1 */
{
return result;
}
if i64::from(sys_num) == SYS_mmap {
let h = helpers
.match_first_type_mut::<QemuSnapshotHelper>()
.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"]
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]
pyo3-build-config = { version = "0.14.5", optional = true }
[dependencies]
libafl = { path = "../libafl", 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"] }
typed-builder = "0.9.0" # Implement the builder pattern at compiletime
libafl_qemu = { path = "../libafl_qemu", version = "0.7.0" }
typed-builder = "0.9.1" # Implement the builder pattern at compiletime
#pyo3 = { version = "0.15", features = ["extension-module"], optional = true }
pyo3 = { version = "0.15", optional = true }

View File

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