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:
parent
d697554810
commit
6274ad4594
@ -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]
|
||||
|
@ -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(),
|
||||
|
@ -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.
@ -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),
|
||||
|
@ -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"] }
|
||||
|
||||
|
@ -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"),
|
||||
|
@ -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(¤t)
|
||||
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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))
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 }
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user