Add custom GDB commands to libafl_qemu (#671)

* Add custom GDB commands

* clippy

* statically linked QEMU

* fix Calibrate

* clippy
This commit is contained in:
Andrea Fioraldi 2022-06-14 11:45:14 +02:00 committed by GitHub
parent f7c997ec65
commit 93048f6270
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 117 additions and 193 deletions

View File

@ -155,9 +155,15 @@ where
.unwrap()
.history_map;
for j in 0..map_len {
if map_first[j] != map[j] && history_map[j] != O::Entry::max_value() {
history_map[j] = O::Entry::max_value();
if history_map.len() < map_len {
history_map.resize(map_len, O::Entry::default());
}
for (first, (cur, history)) in
map_first.iter().zip(map.iter().zip(history_map.iter_mut()))
{
if *first != *cur && *history != O::Entry::max_value() {
*history = O::Entry::max_value();
unstable_entries += 1;
};
}

View File

@ -3,7 +3,7 @@ use which::which;
const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
const QEMU_REVISION: &str = "d840462c2e4cdda4428d99cd4003ddda95c5a2dc";
const QEMU_REVISION: &str = "6a9a929222cbc8b10adfb048aa24f73486e0a886";
fn build_dep_check(tools: &[&str]) {
for tool in tools {
@ -141,81 +141,18 @@ pub fn build() {
let build_dir = qemu_path.join("build");
let output_lib = build_dir.join(&format!("libqemu-{}.so", cpu_target));
if !output_lib.is_file() {
drop(
/*drop(
Command::new("make")
.current_dir(&qemu_path)
.arg("distclean")
.status(),
);
);*/
Command::new("./configure")
.current_dir(&qemu_path)
//.arg("--as-static-lib")
.arg("--as-shared-lib")
.arg(&format!("--target-list={}-linux-user", cpu_target))
.args(&[
"--audio-drv-list=",
"--disable-blobs",
"--disable-bochs",
"--disable-brlapi",
"--disable-bsd-user",
"--disable-bzip2",
"--disable-cap-ng",
"--disable-cloop",
"--disable-curl",
"--disable-curses",
"--disable-dmg",
"--disable-fdt",
"--disable-gcrypt",
"--disable-glusterfs",
"--disable-gnutls",
"--disable-gtk",
"--disable-guest-agent",
"--disable-iconv",
"--disable-libiscsi",
"--disable-libnfs",
"--disable-libssh",
"--disable-libusb",
"--disable-linux-aio",
"--disable-live-block-migration",
"--disable-lzo",
"--disable-nettle",
"--disable-numa",
"--disable-opengl",
"--disable-parallels",
"--disable-plugins",
"--disable-qcow1",
"--disable-qed",
"--disable-rbd",
"--disable-rdma",
"--disable-replication",
"--disable-sdl",
"--disable-seccomp",
"--disable-smartcard",
"--disable-snappy",
"--disable-spice",
"--disable-system",
"--disable-tools",
"--disable-tpm",
"--disable-usb-redir",
"--disable-vde",
"--disable-vdi",
"--disable-vhost-crypto",
"--disable-vhost-kernel",
"--disable-vhost-net",
"--disable-vhost-scsi",
"--disable-vhost-user",
"--disable-vhost-vdpa",
"--disable-vhost-vsock",
"--disable-virglrenderer",
"--disable-virtfs",
"--disable-vnc",
"--disable-vnc-jpeg",
"--disable-vnc-sasl",
"--disable-vte",
"--disable-vvfat",
"--disable-xen",
"--disable-xen-pci-passthrough",
])
.args(&["--disable-blobs", "--disable-bsd-user", "--disable-fdt"])
.status()
.expect("Configure failed");
if let Ok(j) = jobs {
@ -235,77 +172,71 @@ pub fn build() {
//let _ = remove_file(build_dir.join(&format!("libqemu-{}.so", cpu_target)));
}
#[cfg(feature = "python")]
{
let mut objects = vec![];
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"),
//build_dir.join("libcapstone.a.p"),
] {
for path in fs::read_dir(dir).unwrap() {
let path = path.unwrap().path();
if path.is_file() {
if let Some(name) = path.file_name() {
if name.to_string_lossy().starts_with("stubs") {
continue;
} else if let Some(ext) = path.extension() {
if ext == "o" {
objects.push(path);
}
let mut objects = vec![];
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"),
] {
for path in fs::read_dir(dir).unwrap() {
let path = path.unwrap().path();
if path.is_file() {
if let Some(name) = path.file_name() {
if name.to_string_lossy().starts_with("stubs") {
continue;
} else if let Some(ext) = path.extension() {
if ext == "o" {
objects.push(path);
}
}
}
}
}
for obj in &objects {
println!("cargo:rustc-cdylib-link-arg={}", obj.display());
}
println!("cargo:rustc-cdylib-link-arg=-Wl,--start-group");
println!("cargo:rustc-cdylib-link-arg=-Wl,--whole-archive");
println!(
"cargo:rustc-cdylib-link-arg={}/libhwcore.fa",
build_dir.display()
);
println!(
"cargo:rustc-cdylib-link-arg={}/libqom.fa",
build_dir.display()
);
println!("cargo:rustc-cdylib-link-arg=-Wl,--no-whole-archive");
println!(
"cargo:rustc-cdylib-link-arg={}/libcapstone.a",
build_dir.display()
);
println!(
"cargo:rustc-cdylib-link-arg={}/libqemuutil.a",
build_dir.display()
);
println!(
"cargo:rustc-cdylib-link-arg={}/libhwcore.fa",
build_dir.display()
);
println!(
"cargo:rustc-cdylib-link-arg={}/libqom.fa",
build_dir.display()
);
println!("cargo:rustc-cdylib-link-arg=-lrt");
println!("cargo:rustc-cdylib-link-arg=-lutil");
println!("cargo:rustc-cdylib-link-arg=-lgthread-2.0");
println!("cargo:rustc-cdylib-link-arg=-lglib-2.0");
println!("cargo:rustc-cdylib-link-arg=-lstdc++");
println!("cargo:rustc-cdylib-link-arg=-Wl,--end-group");
}
#[cfg(not(feature = "python"))]
Command::new("ld")
.current_dir(&out_dir_path)
.arg("-o")
.arg("libqemu-partially-linked.o")
.arg("-r")
.args(objects)
.arg("--start-group")
.arg("--whole-archive")
.arg(format!("{}/libhwcore.fa", build_dir.display()))
.arg(format!("{}/libqom.fa", build_dir.display()))
.arg(format!("{}/libevent-loop-base.a", build_dir.display()))
.arg("--no-whole-archive")
.arg(format!("{}/libqemuutil.a", build_dir.display()))
.arg(format!("{}/libhwcore.fa", build_dir.display()))
.arg(format!("{}/libqom.fa", build_dir.display()))
.arg(format!(
"--dynamic-list={}/plugins/qemu-plugins.symbols",
qemu_path.display()
))
.status()
.expect("Partial linked failure");
drop(
Command::new("ar")
.current_dir(&out_dir_path)
.arg("crus")
.arg("libqemu-partially-linked.a")
.arg("libqemu-partially-linked.o")
.status(),
);
println!("cargo:rustc-link-search=native={}", out_dir);
println!("cargo:rustc-link-lib=static=qemu-partially-linked");
println!("cargo:rustc-link-lib=rt");
println!("cargo:rustc-link-lib=gmodule-2.0");
println!("cargo:rustc-link-lib=glib-2.0");
println!("cargo:rustc-link-lib=stdc++");
/* #[cfg(not(feature = "python"))]
{
fs::copy(
build_dir.join(&format!("libqemu-{}.so", cpu_target)),
@ -320,7 +251,7 @@ pub fn build() {
println!("cargo:rustc-link-lib=qemu-{}", cpu_target);
println!("cargo:rustc-env=LD_LIBRARY_PATH={}", target_dir.display());
}
} */
drop(
Command::new("make")
@ -347,52 +278,3 @@ pub fn build() {
.file(src_dir.join("asan-giovese.c"))
.compile("asan_giovese");
}
/*
// Build a static library
let mut objects = vec![];
for dir in &[
build_dir.join("libcommon.fa.p"),
build_dir.join(&format!("libqemu-{}-linux-user.fa.p", cpu_target)),
build_dir.join("libqemuutil.a.p"),
build_dir.join("libqom.fa.p"),
build_dir.join("libhwcore.fa.p"),
build_dir.join("libcapstone.a.p"),
] {
for path in read_dir(dir).unwrap() {
let path = path.unwrap().path();
if path.is_file() {
if let Some(name) = path.file_name() {
if name.to_string_lossy().starts_with("stubs") {
continue;
}
else if let Some(ext) = path.extension() {
if ext == "o" {
objects.push(path);
}
}
}
}
}
}
Command::new("ar")
.current_dir(&out_dir_path)
.arg("crus")
.arg("libqemu-bridge.a")
.args(&objects)
.status()
.expect("Ar failed");
println!("cargo:rustc-link-search=native={}", &out_dir);
println!("cargo:rustc-link-lib=static=qemu-bridge");
println!("cargo:rustc-link-lib=rt");
println!("cargo:rustc-link-lib=util");
println!("cargo:rustc-link-lib=gthread-2.0");
println!("cargo:rustc-link-lib=glib-2.0");
println!("cargo:rustc-link-lib=stdc++");
}
*/

View File

@ -3,7 +3,7 @@
use core::{
convert::Into,
ffi::c_void,
mem::{transmute, MaybeUninit},
mem::MaybeUninit,
ptr::{addr_of, addr_of_mut, copy_nonoverlapping, null},
};
use libc::c_int;
@ -248,6 +248,12 @@ extern "C" {
unsafe extern "C" fn(i32, u64, u64, u64, u64, u64, u64, u64, u64) -> SyscallHookResult;
static mut libafl_post_syscall_hook:
unsafe extern "C" fn(u64, i32, u64, u64, u64, u64, u64, u64, u64, u64) -> u64;
fn libafl_qemu_add_gdb_cmd(
callback: extern "C" fn(*const u8, usize, *const ()) -> i32,
data: *const (),
);
fn libafl_qemu_gdb_reply(buf: *const u8, len: usize);
}
#[cfg_attr(feature = "python", pyclass(unsendable))]
@ -309,6 +315,25 @@ impl Drop for GuestMaps {
}
}
#[repr(C)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) struct FatPtr(*const c_void, *const c_void);
static mut GDB_COMMANDS: Vec<FatPtr> = vec![];
extern "C" fn gdb_cmd(buf: *const u8, len: usize, data: *const ()) -> i32 {
unsafe {
let closure =
&mut *(data as *mut std::boxed::Box<dyn for<'r> std::ops::FnMut(&'r str) -> bool>);
let cmd = std::str::from_utf8_unchecked(std::slice::from_raw_parts(buf, len));
if closure(cmd) {
1
} else {
0
}
}
}
static mut EMULATOR_IS_INITIALIZED: bool = false;
#[derive(Debug)]
@ -451,7 +476,7 @@ impl Emulator {
#[must_use]
pub fn g2h<T>(&self, addr: GuestAddr) -> *mut T {
unsafe { transmute(addr as usize + guest_base) }
unsafe { (addr as usize + guest_base) as *mut T }
}
#[must_use]
@ -704,12 +729,25 @@ impl Emulator {
libafl_post_syscall_hook = hook;
}
}
pub fn add_gdb_cmd(&self, callback: Box<dyn FnMut(&str) -> bool>) {
unsafe {
GDB_COMMANDS.push(core::mem::transmute(callback));
libafl_qemu_add_gdb_cmd(
gdb_cmd,
GDB_COMMANDS.last().unwrap() as *const _ as *const (),
);
}
}
pub fn gdb_reply(&self, output: &str) {
unsafe { libafl_qemu_gdb_reply(output.as_bytes().as_ptr(), output.len()) };
}
}
#[cfg(feature = "python")]
pub mod pybind {
use super::{GuestAddr, GuestUsize, MmapPerms, SyscallHookResult};
use core::mem::transmute;
use pyo3::exceptions::PyValueError;
use pyo3::{prelude::*, types::PyInt};
use std::convert::TryFrom;
@ -820,7 +858,7 @@ pub mod pybind {
}
fn h2g(&self, addr: u64) -> GuestAddr {
self.emu.h2g(unsafe { transmute::<_, *const u8>(addr) })
self.emu.h2g(addr as *const u8)
}
fn binary_path(&self) -> String {

View File

@ -14,15 +14,11 @@ use libafl::{executors::inprocess::inprocess_get_state, inputs::Input};
pub use crate::emu::SyscallHookResult;
use crate::{
emu::{Emulator, SKIP_EXEC_HOOK},
emu::{Emulator, FatPtr, SKIP_EXEC_HOOK},
helper::{QemuHelper, QemuHelperTuple},
GuestAddr,
};
#[repr(C)]
#[derive(Clone, Copy, PartialEq, Eq)]
struct FatPtr(*const c_void, *const c_void);
// all kinds of hooks
#[derive(Clone, Copy, PartialEq, Eq)]
enum Hook {

View File

@ -7,6 +7,8 @@
allow(clippy::useless_conversion)
)]
#![allow(clippy::needless_pass_by_value)]
// Till they fix this buggy lint in clippy
#![allow(clippy::borrow_deref_ref)]
use std::env;