Add systemmode to libafl_qemu

Use the new configuration feature systemmode
This commit is contained in:
Alwin Berger 2022-01-10 00:10:17 +01:00
parent d460bab404
commit edff095401
5 changed files with 145 additions and 5 deletions

View File

@ -22,6 +22,8 @@ aarch64 = [] # build qemu for aarch64
clippy = [] # special feature for clippy, don't use in normal projects§ clippy = [] # special feature for clippy, don't use in normal projects§
systemmode = [] # Emulate system images instead of user-mode binaries
[dependencies] [dependencies]
libafl = { path = "../libafl", version = "0.7.1" } libafl = { path = "../libafl", version = "0.7.1" }
libafl_targets = { path = "../libafl_targets", version = "0.7.1" } libafl_targets = { path = "../libafl_targets", version = "0.7.1" }

View File

@ -145,6 +145,9 @@ fn main() {
} }
let build_dir = qemu_path.join("build"); let build_dir = qemu_path.join("build");
#[cfg(feature = "systemmode")]
let output_lib = build_dir.join(&format!("libqemu-system-{}.so", cpu_target));
#[cfg(not(feature = "systemmode"))]
let output_lib = build_dir.join(&format!("libqemu-{}.so", cpu_target)); let output_lib = build_dir.join(&format!("libqemu-{}.so", cpu_target));
if !output_lib.is_file() { if !output_lib.is_file() {
drop( drop(
@ -157,7 +160,7 @@ fn main() {
.current_dir(&qemu_path) .current_dir(&qemu_path)
//.arg("--as-static-lib") //.arg("--as-static-lib")
.arg("--as-shared-lib") .arg("--as-shared-lib")
.arg(&format!("--target-list={}-linux-user", cpu_target)) .arg(&format!("--target-list={}-linux-user,{}-softmmu", cpu_target,cpu_target))
.args(&[ .args(&[
"--audio-drv-list=", "--audio-drv-list=",
"--disable-blobs", "--disable-blobs",
@ -170,7 +173,7 @@ fn main() {
"--disable-curl", "--disable-curl",
"--disable-curses", "--disable-curses",
"--disable-dmg", "--disable-dmg",
"--disable-fdt", "--enable-fdt",
"--disable-gcrypt", "--disable-gcrypt",
"--disable-glusterfs", "--disable-glusterfs",
"--disable-gnutls", "--disable-gnutls",
@ -199,7 +202,7 @@ fn main() {
"--disable-smartcard", "--disable-smartcard",
"--disable-snappy", "--disable-snappy",
"--disable-spice", "--disable-spice",
"--disable-system", "--enable-system",
"--disable-tools", "--disable-tools",
"--disable-tpm", "--disable-tpm",
"--disable-usb-redir", "--disable-usb-redir",
@ -248,6 +251,9 @@ fn main() {
let mut objects = vec![]; let mut objects = vec![];
for dir in &[ for dir in &[
build_dir.join("libcommon.fa.p"), build_dir.join("libcommon.fa.p"),
#[cfg(feature = "systemmode")]
build_dir.join(&format!("libqemu-{}-softmmu.fa.p", cpu_target)),
#[cfg(not(feature = "systemmode"))]
build_dir.join(&format!("libqemu-{}-linux-user.fa.p", cpu_target)), build_dir.join(&format!("libqemu-{}-linux-user.fa.p", cpu_target)),
build_dir.join("libcommon-user.fa.p"), build_dir.join("libcommon-user.fa.p"),
//build_dir.join("libqemuutil.a.p"), //build_dir.join("libqemuutil.a.p"),
@ -315,6 +321,13 @@ fn main() {
#[cfg(not(feature = "python"))] #[cfg(not(feature = "python"))]
{ {
#[cfg(feature = "systemmode")]
fs::copy(
build_dir.join(&format!("libqemu-system-{}.so", cpu_target)),
target_dir.join(&format!("libqemu-system-{}.so", cpu_target)),
)
.expect("Failed to copy the QEMU shared object");
#[cfg(not(feature = "systemmode"))]
fs::copy( fs::copy(
build_dir.join(&format!("libqemu-{}.so", cpu_target)), build_dir.join(&format!("libqemu-{}.so", cpu_target)),
target_dir.join(&format!("libqemu-{}.so", cpu_target)), target_dir.join(&format!("libqemu-{}.so", cpu_target)),
@ -325,6 +338,9 @@ fn main() {
"cargo:rustc-link-search=native={}", "cargo:rustc-link-search=native={}",
&target_dir.to_string_lossy().to_string() &target_dir.to_string_lossy().to_string()
); );
#[cfg(feature = "systemmode")]
println!("cargo:rustc-link-lib=qemu-system-{}", cpu_target);
#[cfg(not(feature = "systemmode"))]
println!("cargo:rustc-link-lib=qemu-{}", cpu_target); println!("cargo:rustc-link-lib=qemu-{}", cpu_target);
println!("cargo:rustc-env=LD_LIBRARY_PATH={}", target_dir.display()); println!("cargo:rustc-env=LD_LIBRARY_PATH={}", target_dir.display());

View File

@ -1,5 +1,6 @@
//! Expose QEMU user `LibAFL` C api to Rust //! Expose QEMU user `LibAFL` C api to Rust
use libc::c_char;
use core::{ use core::{
convert::Into, convert::Into,
ffi::c_void, ffi::c_void,
@ -170,12 +171,18 @@ impl MapInfo {
} }
extern "C" { extern "C" {
#[cfg(not(feature = "systemmode"))]
fn qemu_user_init(argc: i32, argv: *const *const u8, envp: *const *const u8) -> i32; fn qemu_user_init(argc: i32, argv: *const *const u8, envp: *const *const u8) -> i32;
#[cfg(feature = "systemmode")]
fn libafl_qemu_sys_init(argc: i32, argv: *const *const u8, envp: *const *const u8) -> i32;
fn libafl_qemu_write_reg(reg: i32, val: *const u8) -> i32; fn libafl_qemu_write_reg(reg: i32, val: *const u8) -> i32;
fn libafl_qemu_read_reg(reg: i32, val: *mut u8) -> i32; fn libafl_qemu_read_reg(reg: i32, val: *mut u8) -> i32;
fn libafl_qemu_num_regs() -> i32; fn libafl_qemu_num_regs() -> i32;
#[cfg(not(feature = "systemmode"))]
fn libafl_qemu_set_breakpoint(addr: u64) -> i32; fn libafl_qemu_set_breakpoint(addr: u64) -> i32;
#[cfg(feature = "systemmode")]
fn libafl_qemu_set_native_breakpoint(addr: u64) -> i32;
fn libafl_qemu_remove_breakpoint(addr: u64) -> i32; fn libafl_qemu_remove_breakpoint(addr: u64) -> i32;
fn libafl_qemu_set_hook(addr: u64, callback: extern "C" fn(u64), val: u64) -> i32; fn libafl_qemu_set_hook(addr: u64, callback: extern "C" fn(u64), val: u64) -> i32;
fn libafl_qemu_remove_hook(addr: u64) -> i32; fn libafl_qemu_remove_hook(addr: u64) -> i32;
@ -184,6 +191,15 @@ extern "C" {
fn libafl_get_brk() -> u64; fn libafl_get_brk() -> u64;
fn libafl_set_brk(brk: u64) -> u64; fn libafl_set_brk(brk: u64) -> u64;
#[cfg(feature = "systemmode")]
fn libafl_phys_write(addr: u64, buf: *const u8, len: i32);
#[cfg(feature = "systemmode")]
fn libafl_phys_read(addr: u64, buf: *mut u8, len: i32);
#[cfg(feature = "systemmode")]
fn libafl_snapshot_save(name: *const c_char) -> i32;
#[cfg(feature = "systemmode")]
fn libafl_snapshot_load(name: *const c_char) -> i32;
fn strlen(s: *const u8) -> usize; fn strlen(s: *const u8) -> usize;
/// abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot, int flags, int fd, abi_ulong offset) /// abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot, int flags, int fd, abi_ulong offset)
@ -318,11 +334,18 @@ impl Emulator {
#[allow(clippy::cast_possible_wrap)] #[allow(clippy::cast_possible_wrap)]
let argc = argv.len() as i32; let argc = argv.len() as i32;
unsafe { unsafe {
#[cfg(not(feature = "systemmode"))]
qemu_user_init( qemu_user_init(
argc, argc,
argv.as_ptr() as *const *const u8, argv.as_ptr() as *const *const u8,
envp.as_ptr() as *const *const u8, envp.as_ptr() as *const *const u8,
); );
#[cfg(feature = "systemmode")]
libafl_qemu_sys_init(
argc,
argv.as_ptr() as *const *const u8,
envp.as_ptr() as *const *const u8,
);
EMULATOR_IS_INITIALIZED = true; EMULATOR_IS_INITIALIZED = true;
} }
Emulator { _private: () } Emulator { _private: () }
@ -338,6 +361,11 @@ impl Emulator {
GuestMaps::new() GuestMaps::new()
} }
#[cfg(feature = "systemmode")]
pub unsafe fn write_mem(&self, addr: u64, buf: &[u8]) {
unsafe { libafl_phys_write(addr, buf.as_ptr(), buf.len().try_into().unwrap()); }
}
#[cfg(not(feature = "systemmode"))]
pub unsafe fn write_mem<T>(&self, addr: u64, buf: &[T]) { pub unsafe fn write_mem<T>(&self, addr: u64, buf: &[T]) {
let host_addr = self.g2h(addr); let host_addr = self.g2h(addr);
copy_nonoverlapping( copy_nonoverlapping(
@ -347,6 +375,11 @@ impl Emulator {
); );
} }
#[cfg(feature = "systemmode")]
pub unsafe fn read_mem(&self, addr: u64, buf: &mut [u8]) {
unsafe { libafl_phys_read(addr, buf.as_mut_ptr(), buf.len().try_into().unwrap()); }
}
#[cfg(not(feature = "systemmode"))]
pub unsafe fn read_mem<T>(&self, addr: u64, buf: &mut [T]) { pub unsafe fn read_mem<T>(&self, addr: u64, buf: &mut [T]) {
let host_addr = self.g2h(addr); let host_addr = self.g2h(addr);
copy_nonoverlapping( copy_nonoverlapping(
@ -392,6 +425,9 @@ impl Emulator {
pub fn set_breakpoint(&self, addr: u64) { pub fn set_breakpoint(&self, addr: u64) {
unsafe { unsafe {
#[cfg(feature = "systemmode")]
libafl_qemu_set_native_breakpoint(addr);
#[cfg(not(feature = "systemmode"))]
libafl_qemu_set_breakpoint(addr); libafl_qemu_set_breakpoint(addr);
} }
} }
@ -619,6 +655,25 @@ impl Emulator {
libafl_post_syscall_hook = hook; libafl_post_syscall_hook = hook;
} }
} }
#[cfg(feature = "systemmode")]
pub fn snapshot_save(&self, name: &str) -> bool{
let cname = std::ffi::CString::new(name).expect("Snapshot name not CString compatible");
let ret = unsafe { libafl_snapshot_save(cname.as_ptr()) };
match ret {
0 => false,
_ => true,
}
}
#[cfg(feature = "systemmode")]
pub fn snapshot_load(&self, name: &str) -> bool{
let cname = std::ffi::CString::new(name).expect("Snapshot name not CString compatible");
let ret = unsafe { libafl_snapshot_load(cname.as_ptr()) };
match ret {
0 => false,
_ => true,
}
}
} }
#[cfg(feature = "python")] #[cfg(feature = "python")]

View File

@ -35,10 +35,14 @@ pub use edges::QemuEdgeCoverageHelper;
pub mod cmplog; pub mod cmplog;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub use cmplog::QemuCmpLogHelper; pub use cmplog::QemuCmpLogHelper;
#[cfg(target_os = "linux")] #[cfg(all(target_os = "linux",not(feature = "systemmode")))]
pub mod snapshot; pub mod snapshot;
#[cfg(target_os = "linux")] #[cfg(all(target_os = "linux",not(feature = "systemmode")))]
pub use snapshot::QemuSnapshotHelper; pub use snapshot::QemuSnapshotHelper;
#[cfg(all(target_os = "linux",feature = "systemmode"))]
pub mod snapshot_sys;
#[cfg(all(target_os = "linux",feature = "systemmode"))]
pub use snapshot_sys::QemuSysSnapshotHelper;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub mod asan; pub mod asan;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]

View File

@ -0,0 +1,63 @@
use crate::Emulator;
use crate::QemuExecutor;
use crate::QemuHelper;
use crate::QemuHelperTuple;
use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata};
use crate::{
emu,
};
// TODO be thread-safe maybe with https://amanieu.github.io/thread_local-rs/thread_local/index.html
#[derive(Debug)]
pub struct QemuSysSnapshotHelper {
pub empty: bool,
}
impl QemuSysSnapshotHelper {
#[must_use]
pub fn new() -> Self {
Self {
empty: true,
}
}
pub fn snapshot(&mut self, emulator: &Emulator) {
if self.empty {
let ret = emulator.snapshot_save("Start");
if !ret { panic!("QemuSysSnapshotHelper failed to take a snapshot") };
self.empty = false;
}
}
pub fn reset(&mut self, emulator: &Emulator) {
let ret = emulator.snapshot_load("Start");
if !ret { panic!("QemuSysSnapshotHelper failed to load a snapshot") };
}
}
impl Default for QemuSysSnapshotHelper {
fn default() -> Self {
Self::new()
}
}
impl<I, S> QemuHelper<I, S> for QemuSysSnapshotHelper
where
I: Input,
S: HasMetadata,
{
fn init<'a, H, OT, QT>(&self, _executor: &QemuExecutor<'a, H, I, OT, QT, S>)
where
H: FnMut(&I) -> ExitKind,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>,
{
}
fn pre_exec(&mut self, emulator: &Emulator, _input: &I) {
if self.empty {
self.snapshot(emulator);
} else {
self.reset(emulator);
}
}
}