QEMU Synchronous Exit + Syx Snapshot update (#1681)
* Fix: typo in variable name. * Fix: thread-safe static for emulator initialization. * Initial support for synchronous exit from QEMU. * New commands for the sync exit feature. Supports physical and virtual address requests. Updated for new SyxSnapshot naming. * update qemu commit and fix some things * - Removed lazy_static dependency - Compiles for usermode - Format * Fix warnings * Fixed sync_exit for missing architectures --------- Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
This commit is contained in:
parent
28f34e076e
commit
43c9100f59
@ -51,6 +51,7 @@ libafl_qemu_sys = { path = "./libafl_qemu_sys", version = "0.11.1" }
|
|||||||
serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib
|
serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib
|
||||||
hashbrown = { version = "0.14", features = ["serde"] } # A faster hashmap, nostd compatible
|
hashbrown = { version = "0.14", features = ["serde"] } # A faster hashmap, nostd compatible
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
|
num-derive = "0.4"
|
||||||
num_enum = "0.7"
|
num_enum = "0.7"
|
||||||
goblin = "0.7"
|
goblin = "0.7"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
@ -64,6 +65,7 @@ rangemap = "1.3"
|
|||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
addr2line = "0.21"
|
addr2line = "0.21"
|
||||||
typed-arena = "2.0"
|
typed-arena = "2.0"
|
||||||
|
enum-map = "2.7"
|
||||||
|
|
||||||
pyo3 = { version = "0.18", optional = true }
|
pyo3 = { version = "0.18", optional = true }
|
||||||
|
|
||||||
|
@ -76,6 +76,8 @@ const WRAPPER_HEADER: &str = r#"
|
|||||||
|
|
||||||
#include "qemu/plugin-memory.h"
|
#include "qemu/plugin-memory.h"
|
||||||
|
|
||||||
|
#include "libafl_extras/exit.h"
|
||||||
|
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
pub fn generate(
|
pub fn generate(
|
||||||
@ -107,7 +109,11 @@ pub fn generate(
|
|||||||
.allowlist_type("qemu_plugin_mem_rw")
|
.allowlist_type("qemu_plugin_mem_rw")
|
||||||
.allowlist_type("MemOpIdx")
|
.allowlist_type("MemOpIdx")
|
||||||
.allowlist_type("MemOp")
|
.allowlist_type("MemOp")
|
||||||
.allowlist_type("device_snapshot_kind_t")
|
.allowlist_type("DeviceSnapshotKind")
|
||||||
|
.allowlist_type("libafl_exit_reason")
|
||||||
|
.allowlist_type("libafl_exit_reason_kind")
|
||||||
|
.allowlist_type("libafl_exit_reason_sync_backdoor")
|
||||||
|
.allowlist_type("libafl_exit_reason_breakpoint")
|
||||||
.allowlist_function("qemu_user_init")
|
.allowlist_function("qemu_user_init")
|
||||||
.allowlist_function("target_mmap")
|
.allowlist_function("target_mmap")
|
||||||
.allowlist_function("target_mprotect")
|
.allowlist_function("target_mprotect")
|
||||||
@ -123,10 +129,11 @@ pub fn generate(
|
|||||||
.allowlist_function("qemu_plugin_get_hwaddr")
|
.allowlist_function("qemu_plugin_get_hwaddr")
|
||||||
.allowlist_function("qemu_target_page_size")
|
.allowlist_function("qemu_target_page_size")
|
||||||
.allowlist_function("syx_snapshot_init")
|
.allowlist_function("syx_snapshot_init")
|
||||||
.allowlist_function("syx_snapshot_create")
|
.allowlist_function("syx_snapshot_new")
|
||||||
.allowlist_function("syx_snapshot_root_restore")
|
.allowlist_function("syx_snapshot_root_restore")
|
||||||
.allowlist_function("syx_snapshot_dirty_list_add")
|
.allowlist_function("syx_snapshot_dirty_list_add")
|
||||||
.allowlist_function("device_list_all")
|
.allowlist_function("device_list_all")
|
||||||
|
.allowlist_function("libafl_get_exit_reason")
|
||||||
.blocklist_function("main_loop_wait") // bindgen issue #1313
|
.blocklist_function("main_loop_wait") // bindgen issue #1313
|
||||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks));
|
.parse_callbacks(Box::new(bindgen::CargoCallbacks));
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ use which::which;
|
|||||||
|
|
||||||
const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
|
const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
|
||||||
const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
|
const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
|
||||||
const QEMU_REVISION: &str = "b0c827246517e36b480ad501cba5ac6e2c3f26f5";
|
const QEMU_REVISION: &str = "8db5524416b52c999459f1fe3373846bdcb23ac1";
|
||||||
|
|
||||||
fn build_dep_check(tools: &[&str]) {
|
fn build_dep_check(tools: &[&str]) {
|
||||||
for tool in tools {
|
for tool in tools {
|
||||||
@ -43,9 +43,9 @@ pub fn build(
|
|||||||
cpu_target += "el";
|
cpu_target += "el";
|
||||||
}
|
}
|
||||||
|
|
||||||
let custum_qemu_dir = env::var_os("CUSTOM_QEMU_DIR").map(|x| x.to_string_lossy().to_string());
|
let custom_qemu_dir = env::var_os("CUSTOM_QEMU_DIR").map(|x| x.to_string_lossy().to_string());
|
||||||
let custum_qemu_no_build = env::var("CUSTOM_QEMU_NO_BUILD").is_ok();
|
let custom_qemu_no_build = env::var("CUSTOM_QEMU_NO_BUILD").is_ok();
|
||||||
let custum_qemu_no_configure = env::var("CUSTOM_QEMU_NO_CONFIGURE").is_ok();
|
let custom_qemu_no_configure = env::var("CUSTOM_QEMU_NO_CONFIGURE").is_ok();
|
||||||
println!("cargo:rerun-if-env-changed=CUSTOM_QEMU_DIR");
|
println!("cargo:rerun-if-env-changed=CUSTOM_QEMU_DIR");
|
||||||
println!("cargo:rerun-if-env-changed=CUSTOM_QEMU_NO_BUILD");
|
println!("cargo:rerun-if-env-changed=CUSTOM_QEMU_NO_BUILD");
|
||||||
println!("cargo:rerun-if-env-changed=CUSTOM_QEMU_NO_CONFIGURE");
|
println!("cargo:rerun-if-env-changed=CUSTOM_QEMU_NO_CONFIGURE");
|
||||||
@ -63,7 +63,7 @@ pub fn build(
|
|||||||
let cc_compiler = cc::Build::new().cpp(false).get_compiler();
|
let cc_compiler = cc::Build::new().cpp(false).get_compiler();
|
||||||
let cpp_compiler = cc::Build::new().cpp(true).get_compiler();
|
let cpp_compiler = cc::Build::new().cpp(true).get_compiler();
|
||||||
|
|
||||||
let qemu_path = if let Some(qemu_dir) = custum_qemu_dir.as_ref() {
|
let qemu_path = if let Some(qemu_dir) = custom_qemu_dir.as_ref() {
|
||||||
Path::new(&qemu_dir).to_path_buf()
|
Path::new(&qemu_dir).to_path_buf()
|
||||||
} else {
|
} else {
|
||||||
let qemu_path = target_dir.join(QEMU_DIRNAME);
|
let qemu_path = target_dir.join(QEMU_DIRNAME);
|
||||||
@ -128,14 +128,14 @@ pub fn build(
|
|||||||
|
|
||||||
println!("cargo:rerun-if-changed={}", output_lib.to_string_lossy());
|
println!("cargo:rerun-if-changed={}", output_lib.to_string_lossy());
|
||||||
|
|
||||||
if !output_lib.is_file() || (custum_qemu_dir.is_some() && !custum_qemu_no_build) {
|
if !output_lib.is_file() || (custom_qemu_dir.is_some() && !custom_qemu_no_build) {
|
||||||
/*drop(
|
/*drop(
|
||||||
Command::new("make")
|
Command::new("make")
|
||||||
.current_dir(&qemu_path)
|
.current_dir(&qemu_path)
|
||||||
.arg("distclean")
|
.arg("distclean")
|
||||||
.status(),
|
.status(),
|
||||||
);*/
|
);*/
|
||||||
if is_usermode && !custum_qemu_no_configure {
|
if is_usermode && !custom_qemu_no_configure {
|
||||||
let mut cmd = Command::new("./configure");
|
let mut cmd = Command::new("./configure");
|
||||||
cmd.current_dir(&qemu_path)
|
cmd.current_dir(&qemu_path)
|
||||||
//.arg("--as-static-lib")
|
//.arg("--as-static-lib")
|
||||||
@ -162,7 +162,7 @@ pub fn build(
|
|||||||
cmd.arg("--enable-debug");
|
cmd.arg("--enable-debug");
|
||||||
}
|
}
|
||||||
cmd.status().expect("Configure failed");
|
cmd.status().expect("Configure failed");
|
||||||
} else if !custum_qemu_no_configure {
|
} else if !custom_qemu_no_configure {
|
||||||
let mut cmd = Command::new("./configure");
|
let mut cmd = Command::new("./configure");
|
||||||
cmd.current_dir(&qemu_path)
|
cmd.current_dir(&qemu_path)
|
||||||
//.arg("--as-static-lib")
|
//.arg("--as-static-lib")
|
||||||
@ -290,7 +290,8 @@ pub fn build(
|
|||||||
.arg("--disable-xen")
|
.arg("--disable-xen")
|
||||||
.arg("--disable-xen-pci-passthrough")
|
.arg("--disable-xen-pci-passthrough")
|
||||||
.arg("--disable-xkbcommon")
|
.arg("--disable-xkbcommon")
|
||||||
.arg("--disable-zstd");
|
.arg("--disable-zstd")
|
||||||
|
.arg("--disable-tests");
|
||||||
if cfg!(feature = "debug_assertions") {
|
if cfg!(feature = "debug_assertions") {
|
||||||
cmd.arg("--enable-debug");
|
cmd.arg("--enable-debug");
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
use capstone::arch::BuildsCapstone;
|
use capstone::arch::BuildsCapstone;
|
||||||
|
use enum_map::{enum_map, EnumMap};
|
||||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
#[cfg(feature = "python")]
|
#[cfg(feature = "python")]
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::aarch64::*;
|
pub use syscall_numbers::aarch64::*;
|
||||||
|
|
||||||
use crate::CallingConvention;
|
use crate::{sync_backdoor::SyncBackdoorArgs, CallingConvention};
|
||||||
|
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
@ -46,6 +49,23 @@ pub enum Regs {
|
|||||||
Pstate = 33,
|
Pstate = 33,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SYNC_BACKDOOR_ARCH_REGS: OnceLock<EnumMap<SyncBackdoorArgs, Regs>> = OnceLock::new();
|
||||||
|
|
||||||
|
pub fn get_sync_backdoor_arch_regs() -> &'static EnumMap<SyncBackdoorArgs, Regs> {
|
||||||
|
SYNC_BACKDOOR_ARCH_REGS.get_or_init(|| {
|
||||||
|
enum_map! {
|
||||||
|
SyncBackdoorArgs::Ret => Regs::X0,
|
||||||
|
SyncBackdoorArgs::Cmd => Regs::X0,
|
||||||
|
SyncBackdoorArgs::Arg1 => Regs::X1,
|
||||||
|
SyncBackdoorArgs::Arg2 => Regs::X2,
|
||||||
|
SyncBackdoorArgs::Arg3 => Regs::X3,
|
||||||
|
SyncBackdoorArgs::Arg4 => Regs::X4,
|
||||||
|
SyncBackdoorArgs::Arg5 => Regs::X5,
|
||||||
|
SyncBackdoorArgs::Arg6 => Regs::X6,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// alias registers
|
/// alias registers
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
impl Regs {
|
impl Regs {
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
use capstone::arch::BuildsCapstone;
|
use capstone::arch::BuildsCapstone;
|
||||||
|
use enum_map::{enum_map, EnumMap};
|
||||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
#[cfg(feature = "python")]
|
#[cfg(feature = "python")]
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::arm::*;
|
pub use syscall_numbers::arm::*;
|
||||||
|
|
||||||
use crate::CallingConvention;
|
use crate::{sync_backdoor::SyncBackdoorArgs, CallingConvention};
|
||||||
|
|
||||||
/// Registers for the ARM instruction set.
|
/// Registers for the ARM instruction set.
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
@ -30,6 +33,23 @@ pub enum Regs {
|
|||||||
R25 = 25,
|
R25 = 25,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SYNC_BACKDOOR_ARCH_REGS: OnceLock<EnumMap<SyncBackdoorArgs, Regs>> = OnceLock::new();
|
||||||
|
|
||||||
|
pub fn get_sync_backdoor_arch_regs() -> &'static EnumMap<SyncBackdoorArgs, Regs> {
|
||||||
|
SYNC_BACKDOOR_ARCH_REGS.get_or_init(|| {
|
||||||
|
enum_map! {
|
||||||
|
SyncBackdoorArgs::Ret => Regs::R0,
|
||||||
|
SyncBackdoorArgs::Cmd => Regs::R0,
|
||||||
|
SyncBackdoorArgs::Arg1 => Regs::R1,
|
||||||
|
SyncBackdoorArgs::Arg2 => Regs::R2,
|
||||||
|
SyncBackdoorArgs::Arg3 => Regs::R3,
|
||||||
|
SyncBackdoorArgs::Arg4 => Regs::R4,
|
||||||
|
SyncBackdoorArgs::Arg5 => Regs::R5,
|
||||||
|
SyncBackdoorArgs::Arg6 => Regs::R6,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// alias registers
|
/// alias registers
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
impl Regs {
|
impl Regs {
|
||||||
|
@ -4,7 +4,7 @@ use core::{
|
|||||||
convert::Into,
|
convert::Into,
|
||||||
ffi::c_void,
|
ffi::c_void,
|
||||||
fmt,
|
fmt,
|
||||||
mem::MaybeUninit,
|
mem::{transmute, MaybeUninit},
|
||||||
ptr::{addr_of, copy_nonoverlapping, null},
|
ptr::{addr_of, copy_nonoverlapping, null},
|
||||||
};
|
};
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
@ -14,7 +14,11 @@ use std::{
|
|||||||
ffi::{CStr, CString},
|
ffi::{CStr, CString},
|
||||||
ptr::null_mut,
|
ptr::null_mut,
|
||||||
};
|
};
|
||||||
use std::{slice::from_raw_parts, str::from_utf8_unchecked};
|
use std::{
|
||||||
|
slice::from_raw_parts,
|
||||||
|
str::from_utf8_unchecked,
|
||||||
|
sync::{Mutex, OnceLock},
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
use libc::c_int;
|
use libc::c_int;
|
||||||
@ -28,13 +32,28 @@ use crate::{GuestReg, Regs};
|
|||||||
pub type GuestAddr = libafl_qemu_sys::target_ulong;
|
pub type GuestAddr = libafl_qemu_sys::target_ulong;
|
||||||
pub type GuestUsize = libafl_qemu_sys::target_ulong;
|
pub type GuestUsize = libafl_qemu_sys::target_ulong;
|
||||||
pub type GuestIsize = libafl_qemu_sys::target_long;
|
pub type GuestIsize = libafl_qemu_sys::target_long;
|
||||||
pub type GuestVirtAddr = libafl_qemu_sys::hwaddr;
|
pub type GuestVirtAddr = libafl_qemu_sys::vaddr;
|
||||||
pub type GuestPhysAddr = libafl_qemu_sys::hwaddr;
|
pub type GuestPhysAddr = libafl_qemu_sys::hwaddr;
|
||||||
|
|
||||||
pub type GuestHwAddrInfo = libafl_qemu_sys::qemu_plugin_hwaddr;
|
pub type GuestHwAddrInfo = libafl_qemu_sys::qemu_plugin_hwaddr;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum GuestAddrKind {
|
||||||
|
Physical(GuestPhysAddr),
|
||||||
|
Virtual(GuestVirtAddr),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for GuestAddrKind {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
GuestAddrKind::Physical(phys_addr) => write!(f, "hwaddr 0x{:x}", phys_addr),
|
||||||
|
GuestAddrKind::Virtual(virt_addr) => write!(f, "vaddr 0x{:x}", virt_addr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
pub type FastSnapshot = *mut libafl_qemu_sys::syx_snapshot_t;
|
pub type FastSnapshot = *mut libafl_qemu_sys::SyxSnapshot;
|
||||||
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
pub enum DeviceSnapshotFilter {
|
pub enum DeviceSnapshotFilter {
|
||||||
@ -45,16 +64,14 @@ pub enum DeviceSnapshotFilter {
|
|||||||
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
impl DeviceSnapshotFilter {
|
impl DeviceSnapshotFilter {
|
||||||
fn enum_id(&self) -> libafl_qemu_sys::device_snapshot_kind_t {
|
fn enum_id(&self) -> libafl_qemu_sys::DeviceSnapshotKind {
|
||||||
match self {
|
match self {
|
||||||
DeviceSnapshotFilter::All => {
|
DeviceSnapshotFilter::All => libafl_qemu_sys::DeviceSnapshotKind_DEVICE_SNAPSHOT_ALL,
|
||||||
libafl_qemu_sys::device_snapshot_kind_e_DEVICE_SNAPSHOT_ALL
|
|
||||||
}
|
|
||||||
DeviceSnapshotFilter::AllowList(_) => {
|
DeviceSnapshotFilter::AllowList(_) => {
|
||||||
libafl_qemu_sys::device_snapshot_kind_e_DEVICE_SNAPSHOT_ALLOWLIST
|
libafl_qemu_sys::DeviceSnapshotKind_DEVICE_SNAPSHOT_ALLOWLIST
|
||||||
}
|
}
|
||||||
DeviceSnapshotFilter::DenyList(_) => {
|
DeviceSnapshotFilter::DenyList(_) => {
|
||||||
libafl_qemu_sys::device_snapshot_kind_e_DEVICE_SNAPSHOT_DENYLIST
|
libafl_qemu_sys::DeviceSnapshotKind_DEVICE_SNAPSHOT_DENYLIST
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,9 +153,13 @@ pub const SKIP_EXEC_HOOK: u64 = u64::MAX;
|
|||||||
|
|
||||||
pub use libafl_qemu_sys::{CPUArchState, CPUState};
|
pub use libafl_qemu_sys::{CPUArchState, CPUState};
|
||||||
|
|
||||||
|
use crate::sync_backdoor::{SyncBackdoor, SyncBackdoorError};
|
||||||
|
|
||||||
pub type CPUStatePtr = *mut libafl_qemu_sys::CPUState;
|
pub type CPUStatePtr = *mut libafl_qemu_sys::CPUState;
|
||||||
pub type CPUArchStatePtr = *mut libafl_qemu_sys::CPUArchState;
|
pub type CPUArchStatePtr = *mut libafl_qemu_sys::CPUArchState;
|
||||||
|
|
||||||
|
pub type ExitReasonPtr = *mut libafl_qemu_sys::libafl_exit_reason;
|
||||||
|
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter, PartialEq, Eq)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter, PartialEq, Eq)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
pub enum MmapPerms {
|
pub enum MmapPerms {
|
||||||
@ -360,6 +381,9 @@ extern "C" {
|
|||||||
// CPUState* libafl_qemu_current_cpu(void);
|
// CPUState* libafl_qemu_current_cpu(void);
|
||||||
fn libafl_qemu_current_cpu() -> CPUStatePtr;
|
fn libafl_qemu_current_cpu() -> CPUStatePtr;
|
||||||
|
|
||||||
|
// struct libafl_exit_reason* libafl_get_exit_reason(void);
|
||||||
|
fn libafl_get_exit_reason() -> ExitReasonPtr;
|
||||||
|
|
||||||
fn libafl_qemu_cpu_index(cpu: CPUStatePtr) -> i32;
|
fn libafl_qemu_cpu_index(cpu: CPUStatePtr) -> i32;
|
||||||
|
|
||||||
fn libafl_qemu_write_reg(cpu: CPUStatePtr, reg: i32, val: *const u8) -> i32;
|
fn libafl_qemu_write_reg(cpu: CPUStatePtr, reg: i32, val: *const u8) -> i32;
|
||||||
@ -821,7 +845,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static mut EMULATOR_IS_INITIALIZED: bool = false;
|
static EMULATOR_IS_INITIALIZED: OnceLock<Mutex<bool>> = OnceLock::new();
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Emulator {
|
pub struct Emulator {
|
||||||
@ -835,6 +859,60 @@ pub enum EmuError {
|
|||||||
TooManyArgs(usize),
|
TooManyArgs(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum EmuExitReason {
|
||||||
|
End, // QEMU ended for some reason.
|
||||||
|
Breakpoint(GuestVirtAddr), // Breakpoint triggered. Contains the virtual address of the trigger.
|
||||||
|
SyncBackdoor(SyncBackdoor), // Synchronous backdoor: The guest triggered a backdoor and should return to LibAFL.
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for EmuExitReason {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
EmuExitReason::End => write!(f, "End"),
|
||||||
|
EmuExitReason::Breakpoint(vaddr) => write!(f, "Breakpoint @vaddr 0x{:x}", vaddr),
|
||||||
|
EmuExitReason::SyncBackdoor(sync_backdoor) => {
|
||||||
|
write!(f, "Sync backdoor exit: {}", sync_backdoor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum EmuExitReasonError {
|
||||||
|
UnknownKind(),
|
||||||
|
UnexpectedExit,
|
||||||
|
SyncBackdoorError(SyncBackdoorError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SyncBackdoorError> for EmuExitReasonError {
|
||||||
|
fn from(sync_backdoor_error: SyncBackdoorError) -> Self {
|
||||||
|
EmuExitReasonError::SyncBackdoorError(sync_backdoor_error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&Emulator> for EmuExitReason {
|
||||||
|
type Error = EmuExitReasonError;
|
||||||
|
fn try_from(emu: &Emulator) -> Result<Self, Self::Error> {
|
||||||
|
let exit_reason = unsafe { libafl_get_exit_reason() };
|
||||||
|
if exit_reason.is_null() {
|
||||||
|
Err(EmuExitReasonError::UnexpectedExit)
|
||||||
|
} else {
|
||||||
|
let exit_reason: &mut libafl_qemu_sys::libafl_exit_reason =
|
||||||
|
unsafe { transmute(exit_reason) };
|
||||||
|
Ok(match exit_reason.kind {
|
||||||
|
libafl_qemu_sys::libafl_exit_reason_kind_BREAKPOINT => unsafe {
|
||||||
|
EmuExitReason::Breakpoint(exit_reason.data.breakpoint.addr.into())
|
||||||
|
},
|
||||||
|
libafl_qemu_sys::libafl_exit_reason_kind_SYNC_BACKDOOR => {
|
||||||
|
EmuExitReason::SyncBackdoor(emu.try_into()?)
|
||||||
|
}
|
||||||
|
_ => return Err(EmuExitReasonError::UnknownKind()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::error::Error for EmuError {}
|
impl std::error::Error for EmuError {}
|
||||||
|
|
||||||
impl fmt::Display for EmuError {
|
impl fmt::Display for EmuError {
|
||||||
@ -866,11 +944,15 @@ impl From<EmuError> for libafl::Error {
|
|||||||
impl Emulator {
|
impl Emulator {
|
||||||
#[allow(clippy::must_use_candidate, clippy::similar_names)]
|
#[allow(clippy::must_use_candidate, clippy::similar_names)]
|
||||||
pub fn new(args: &[String], env: &[(String, String)]) -> Result<Emulator, EmuError> {
|
pub fn new(args: &[String], env: &[(String, String)]) -> Result<Emulator, EmuError> {
|
||||||
unsafe {
|
let mut is_initialized = EMULATOR_IS_INITIALIZED
|
||||||
if EMULATOR_IS_INITIALIZED {
|
.get_or_init(|| Mutex::new(false))
|
||||||
|
.lock()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if *is_initialized {
|
||||||
return Err(EmuError::MultipleInstances);
|
return Err(EmuError::MultipleInstances);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return Err(EmuError::EmptyArgs);
|
return Err(EmuError::EmptyArgs);
|
||||||
}
|
}
|
||||||
@ -903,7 +985,7 @@ impl Emulator {
|
|||||||
libc::atexit(qemu_cleanup_atexit);
|
libc::atexit(qemu_cleanup_atexit);
|
||||||
libafl_qemu_sys::syx_snapshot_init();
|
libafl_qemu_sys::syx_snapshot_init();
|
||||||
}
|
}
|
||||||
EMULATOR_IS_INITIALIZED = true;
|
*is_initialized = true;
|
||||||
}
|
}
|
||||||
Ok(Emulator { _private: () })
|
Ok(Emulator { _private: () })
|
||||||
}
|
}
|
||||||
@ -1047,7 +1129,8 @@ impl Emulator {
|
|||||||
pub fn entry_break(&self, addr: GuestAddr) {
|
pub fn entry_break(&self, addr: GuestAddr) {
|
||||||
self.set_breakpoint(addr);
|
self.set_breakpoint(addr);
|
||||||
unsafe {
|
unsafe {
|
||||||
self.run();
|
// TODO: decide what to do with sync exit here: ignore or check for bp exit?
|
||||||
|
let _ = self.run();
|
||||||
}
|
}
|
||||||
self.remove_breakpoint(addr);
|
self.remove_breakpoint(addr);
|
||||||
}
|
}
|
||||||
@ -1079,7 +1162,7 @@ impl Emulator {
|
|||||||
///
|
///
|
||||||
/// Should, in general, be safe to call.
|
/// Should, in general, be safe to call.
|
||||||
/// Of course, the emulated target is not contained securely and can corrupt state or interact with the operating system.
|
/// Of course, the emulated target is not contained securely and can corrupt state or interact with the operating system.
|
||||||
pub unsafe fn run(&self) {
|
pub unsafe fn run(&self) -> Result<EmuExitReason, EmuExitReasonError> {
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
libafl_qemu_run();
|
libafl_qemu_run();
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
@ -1087,6 +1170,7 @@ impl Emulator {
|
|||||||
vm_start();
|
vm_start();
|
||||||
qemu_main_loop();
|
qemu_main_loop();
|
||||||
}
|
}
|
||||||
|
EmuExitReason::try_from(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
@ -1283,9 +1367,9 @@ impl Emulator {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn create_fast_snapshot(&self, track: bool) -> FastSnapshot {
|
pub fn create_fast_snapshot(&self, track: bool) -> FastSnapshot {
|
||||||
unsafe {
|
unsafe {
|
||||||
libafl_qemu_sys::syx_snapshot_create(
|
libafl_qemu_sys::syx_snapshot_new(
|
||||||
track,
|
track,
|
||||||
libafl_qemu_sys::device_snapshot_kind_e_DEVICE_SNAPSHOT_ALL,
|
libafl_qemu_sys::DeviceSnapshotKind_DEVICE_SNAPSHOT_ALL,
|
||||||
null_mut(),
|
null_mut(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1300,7 +1384,7 @@ impl Emulator {
|
|||||||
) -> FastSnapshot {
|
) -> FastSnapshot {
|
||||||
let mut v = vec![];
|
let mut v = vec![];
|
||||||
unsafe {
|
unsafe {
|
||||||
libafl_qemu_sys::syx_snapshot_create(
|
libafl_qemu_sys::syx_snapshot_new(
|
||||||
track,
|
track,
|
||||||
device_filter.enum_id(),
|
device_filter.enum_id(),
|
||||||
device_filter.devices(&mut v),
|
device_filter.devices(&mut v),
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
use enum_map::{enum_map, EnumMap};
|
||||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
#[cfg(feature = "python")]
|
#[cfg(feature = "python")]
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
|
|
||||||
use crate::CallingConvention;
|
use crate::{sync_backdoor::SyncBackdoorArgs, CallingConvention};
|
||||||
|
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
@ -61,6 +64,23 @@ pub enum Regs {
|
|||||||
Pktcnthi = 51,
|
Pktcnthi = 51,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SYNC_BACKDOOR_ARCH_REGS: OnceLock<EnumMap<SyncBackdoorArgs, Regs>> = OnceLock::new();
|
||||||
|
|
||||||
|
pub fn get_sync_backdoor_arch_regs() -> &'static EnumMap<SyncBackdoorArgs, Regs> {
|
||||||
|
SYNC_BACKDOOR_ARCH_REGS.get_or_init(|| {
|
||||||
|
enum_map! {
|
||||||
|
SyncBackdoorArgs::Ret => Regs::R0,
|
||||||
|
SyncBackdoorArgs::Cmd => Regs::R0,
|
||||||
|
SyncBackdoorArgs::Arg1 => Regs::R1,
|
||||||
|
SyncBackdoorArgs::Arg2 => Regs::R2,
|
||||||
|
SyncBackdoorArgs::Arg3 => Regs::R3,
|
||||||
|
SyncBackdoorArgs::Arg4 => Regs::R4,
|
||||||
|
SyncBackdoorArgs::Arg5 => Regs::R5,
|
||||||
|
SyncBackdoorArgs::Arg6 => Regs::R6,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// alias registers
|
/// alias registers
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
impl Regs {
|
impl Regs {
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
use std::mem::size_of;
|
use std::{mem::size_of, sync::OnceLock};
|
||||||
|
|
||||||
use capstone::arch::BuildsCapstone;
|
use capstone::arch::BuildsCapstone;
|
||||||
|
use enum_map::{enum_map, EnumMap};
|
||||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
#[cfg(feature = "python")]
|
#[cfg(feature = "python")]
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::x86::*;
|
pub use syscall_numbers::x86::*;
|
||||||
|
|
||||||
use crate::{CallingConvention, GuestAddr};
|
use crate::{sync_backdoor::SyncBackdoorArgs, CallingConvention, GuestAddr};
|
||||||
|
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
@ -24,6 +25,23 @@ pub enum Regs {
|
|||||||
Eflags = 9,
|
Eflags = 9,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SYNC_BACKDOOR_ARCH_REGS: OnceLock<EnumMap<SyncBackdoorArgs, Regs>> = OnceLock::new();
|
||||||
|
|
||||||
|
pub fn get_sync_backdoor_arch_regs() -> &'static EnumMap<SyncBackdoorArgs, Regs> {
|
||||||
|
SYNC_BACKDOOR_ARCH_REGS.get_or_init(|| {
|
||||||
|
enum_map! {
|
||||||
|
SyncBackdoorArgs::Ret => Regs::Eax,
|
||||||
|
SyncBackdoorArgs::Cmd => Regs::Eax,
|
||||||
|
SyncBackdoorArgs::Arg1 => Regs::Edi,
|
||||||
|
SyncBackdoorArgs::Arg2 => Regs::Esi,
|
||||||
|
SyncBackdoorArgs::Arg3 => Regs::Edx,
|
||||||
|
SyncBackdoorArgs::Arg4 => Regs::Ebx,
|
||||||
|
SyncBackdoorArgs::Arg5 => Regs::Ecx,
|
||||||
|
SyncBackdoorArgs::Arg6 => Regs::Ebp,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// alias registers
|
/// alias registers
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
impl Regs {
|
impl Regs {
|
||||||
|
@ -99,6 +99,8 @@ pub use executor::QemuForkExecutor;
|
|||||||
pub mod emu;
|
pub mod emu;
|
||||||
pub use emu::*;
|
pub use emu::*;
|
||||||
|
|
||||||
|
pub mod sync_backdoor;
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn filter_qemu_args() -> Vec<String> {
|
pub fn filter_qemu_args() -> Vec<String> {
|
||||||
let mut args = vec![env::args().next().unwrap()];
|
let mut args = vec![env::args().next().unwrap()];
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
use enum_map::{enum_map, EnumMap};
|
||||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
#[cfg(feature = "python")]
|
#[cfg(feature = "python")]
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::mips::*;
|
pub use syscall_numbers::mips::*;
|
||||||
|
|
||||||
use crate::CallingConvention;
|
use crate::{sync_backdoor::SyncBackdoorArgs, CallingConvention};
|
||||||
|
|
||||||
/// Registers for the MIPS instruction set.
|
/// Registers for the MIPS instruction set.
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
@ -46,6 +49,23 @@ pub enum Regs {
|
|||||||
Pc = 37,
|
Pc = 37,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SYNC_BACKDOOR_ARCH_REGS: OnceLock<EnumMap<SyncBackdoorArgs, Regs>> = OnceLock::new();
|
||||||
|
|
||||||
|
pub fn get_sync_backdoor_arch_regs() -> &'static EnumMap<SyncBackdoorArgs, Regs> {
|
||||||
|
SYNC_BACKDOOR_ARCH_REGS.get_or_init(|| {
|
||||||
|
enum_map! {
|
||||||
|
SyncBackdoorArgs::Ret => Regs::V0,
|
||||||
|
SyncBackdoorArgs::Cmd => Regs::V0,
|
||||||
|
SyncBackdoorArgs::Arg1 => Regs::A0,
|
||||||
|
SyncBackdoorArgs::Arg2 => Regs::A1,
|
||||||
|
SyncBackdoorArgs::Arg3 => Regs::A2,
|
||||||
|
SyncBackdoorArgs::Arg4 => Regs::A3,
|
||||||
|
SyncBackdoorArgs::Arg5 => Regs::T0,
|
||||||
|
SyncBackdoorArgs::Arg6 => Regs::T1,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// alias registers
|
/// alias registers
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
impl Regs {
|
impl Regs {
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
use enum_map::{enum_map, EnumMap};
|
||||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
#[cfg(feature = "python")]
|
#[cfg(feature = "python")]
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::powerpc::*;
|
pub use syscall_numbers::powerpc::*;
|
||||||
|
|
||||||
use crate::CallingConvention;
|
use crate::{sync_backdoor::SyncBackdoorArgs, CallingConvention};
|
||||||
|
|
||||||
/// Registers for the MIPS instruction set.
|
/// Registers for the MIPS instruction set.
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
@ -85,6 +88,23 @@ pub enum Regs {
|
|||||||
Fpscr = 70,
|
Fpscr = 70,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SYNC_BACKDOOR_ARCH_REGS: OnceLock<EnumMap<SyncBackdoorArgs, Regs>> = OnceLock::new();
|
||||||
|
|
||||||
|
pub fn get_sync_backdoor_arch_regs() -> &'static EnumMap<SyncBackdoorArgs, Regs> {
|
||||||
|
SYNC_BACKDOOR_ARCH_REGS.get_or_init(|| {
|
||||||
|
enum_map! {
|
||||||
|
SyncBackdoorArgs::Ret => Regs::R3,
|
||||||
|
SyncBackdoorArgs::Cmd => Regs::R0,
|
||||||
|
SyncBackdoorArgs::Arg1 => Regs::R3,
|
||||||
|
SyncBackdoorArgs::Arg2 => Regs::R4,
|
||||||
|
SyncBackdoorArgs::Arg3 => Regs::R5,
|
||||||
|
SyncBackdoorArgs::Arg4 => Regs::R6,
|
||||||
|
SyncBackdoorArgs::Arg5 => Regs::R7,
|
||||||
|
SyncBackdoorArgs::Arg6 => Regs::R8,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// alias registers
|
/// alias registers
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
impl Regs {
|
impl Regs {
|
||||||
|
248
libafl_qemu/src/sync_backdoor.rs
Normal file
248
libafl_qemu/src/sync_backdoor.rs
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
use std::{
|
||||||
|
fmt::{Display, Formatter},
|
||||||
|
sync::OnceLock,
|
||||||
|
};
|
||||||
|
|
||||||
|
use enum_map::{enum_map, Enum, EnumMap};
|
||||||
|
use libafl::executors::ExitKind;
|
||||||
|
use num_enum::{TryFromPrimitive, TryFromPrimitiveError};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
get_sync_backdoor_arch_regs, Emulator, GuestAddrKind, GuestPhysAddr, GuestReg, GuestVirtAddr,
|
||||||
|
Regs,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum SyncBackdoorError {
|
||||||
|
UnknownCommand(GuestReg),
|
||||||
|
RegError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for SyncBackdoorError {
|
||||||
|
fn from(error_string: String) -> Self {
|
||||||
|
SyncBackdoorError::RegError(error_string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Enum)]
|
||||||
|
pub enum SyncBackdoorArgs {
|
||||||
|
Ret,
|
||||||
|
Cmd,
|
||||||
|
Arg1,
|
||||||
|
Arg2,
|
||||||
|
Arg3,
|
||||||
|
Arg4,
|
||||||
|
Arg5,
|
||||||
|
Arg6,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Move in a separate header file to have a central definition of native definitions,
|
||||||
|
// reusable in targets directly.
|
||||||
|
#[derive(Debug, Clone, TryFromPrimitive)]
|
||||||
|
#[repr(u64)]
|
||||||
|
pub enum NativeSyncBackdoorCommand {
|
||||||
|
Save = 0, // Save the VM
|
||||||
|
Load = 1, // Reload the target without ending the run?
|
||||||
|
InputVirt = 2, // The address is a virtual address using the paging currently running in the VM.
|
||||||
|
InputPhys = 3, // The address is a physical address
|
||||||
|
End = 4, // Implies reloading of the target. The first argument gives the exit status.
|
||||||
|
StartVirt = 5, // Shortcut for Save + InputVirt
|
||||||
|
StartPhys = 6, // Shortcut for Save + InputPhys
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Enum, TryFromPrimitive)]
|
||||||
|
#[repr(u64)]
|
||||||
|
pub enum NativeExitKind {
|
||||||
|
Unknown = 0, // Should not be used
|
||||||
|
Ok = 1, // Normal exit
|
||||||
|
Crash = 2, // Crash reported in the VM
|
||||||
|
}
|
||||||
|
|
||||||
|
static EMU_EXIT_KIND_MAP: OnceLock<EnumMap<NativeExitKind, Option<ExitKind>>> = OnceLock::new();
|
||||||
|
|
||||||
|
impl From<TryFromPrimitiveError<NativeSyncBackdoorCommand>> for SyncBackdoorError {
|
||||||
|
fn from(error: TryFromPrimitiveError<NativeSyncBackdoorCommand>) -> Self {
|
||||||
|
SyncBackdoorError::UnknownCommand(error.number.try_into().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct CommandInput {
|
||||||
|
addr: GuestAddrKind,
|
||||||
|
max_input_size: GuestReg,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommandInput {
|
||||||
|
pub fn exec(&self, emu: &Emulator, backdoor: &SyncBackdoor, input: &[u8]) {
|
||||||
|
match self.addr {
|
||||||
|
GuestAddrKind::Physical(hwaddr) => unsafe {
|
||||||
|
#[cfg(emulation_mode = "usermode")]
|
||||||
|
{
|
||||||
|
// For now the default behaviour is to fall back to virtual addresses
|
||||||
|
emu.write_mem(hwaddr.try_into().unwrap(), input)
|
||||||
|
}
|
||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
{
|
||||||
|
emu.write_phys_mem(hwaddr, input)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
GuestAddrKind::Virtual(vaddr) => unsafe {
|
||||||
|
emu.write_mem(vaddr.try_into().unwrap(), input)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
backdoor.ret(&emu, input.len().try_into().unwrap()).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for CommandInput {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{} ({:x} max nb bytes)", self.addr, self.max_input_size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Command {
|
||||||
|
Save,
|
||||||
|
Load,
|
||||||
|
Input(CommandInput),
|
||||||
|
Start(CommandInput),
|
||||||
|
Exit(Option<ExitKind>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Command {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Command::Save => write!(f, "Save VM"),
|
||||||
|
Command::Load => write!(f, "Reload VM"),
|
||||||
|
Command::Input(command_input) => write!(f, "Set fuzzing input @{}", command_input),
|
||||||
|
Command::Start(command_input) => {
|
||||||
|
write!(f, "Start fuzzing with input @{}", command_input)
|
||||||
|
}
|
||||||
|
Command::Exit(exit_kind) => write!(f, "Exit of kind {:?}", exit_kind),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SyncBackdoor {
|
||||||
|
command: Command,
|
||||||
|
arch_regs_map: &'static EnumMap<SyncBackdoorArgs, Regs>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SyncBackdoor {
|
||||||
|
pub fn command(&self) -> &Command {
|
||||||
|
&self.command
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ret(&self, emu: &Emulator, value: GuestReg) -> Result<(), SyncBackdoorError> {
|
||||||
|
Ok(emu.write_reg(self.arch_regs_map[SyncBackdoorArgs::Ret], value)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for SyncBackdoor {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.command)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&Emulator> for SyncBackdoor {
|
||||||
|
type Error = SyncBackdoorError;
|
||||||
|
|
||||||
|
fn try_from(emu: &Emulator) -> Result<Self, Self::Error> {
|
||||||
|
let arch_regs_map: &'static EnumMap<SyncBackdoorArgs, Regs> = get_sync_backdoor_arch_regs();
|
||||||
|
let cmd_id: GuestReg =
|
||||||
|
emu.read_reg::<Regs, GuestReg>(arch_regs_map[SyncBackdoorArgs::Cmd])?;
|
||||||
|
|
||||||
|
Ok(match u64::from(cmd_id).try_into()? {
|
||||||
|
NativeSyncBackdoorCommand::Save => SyncBackdoor {
|
||||||
|
command: Command::Save,
|
||||||
|
arch_regs_map,
|
||||||
|
},
|
||||||
|
NativeSyncBackdoorCommand::Load => SyncBackdoor {
|
||||||
|
command: Command::Load,
|
||||||
|
arch_regs_map,
|
||||||
|
},
|
||||||
|
NativeSyncBackdoorCommand::InputVirt => {
|
||||||
|
let virt_addr: GuestVirtAddr =
|
||||||
|
emu.read_reg(arch_regs_map[SyncBackdoorArgs::Arg1])?;
|
||||||
|
let max_input_size: GuestReg =
|
||||||
|
emu.read_reg(arch_regs_map[SyncBackdoorArgs::Arg2])?;
|
||||||
|
|
||||||
|
SyncBackdoor {
|
||||||
|
command: Command::Input(CommandInput {
|
||||||
|
addr: GuestAddrKind::Virtual(virt_addr),
|
||||||
|
max_input_size,
|
||||||
|
}),
|
||||||
|
arch_regs_map,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NativeSyncBackdoorCommand::InputPhys => {
|
||||||
|
let phys_addr: GuestPhysAddr =
|
||||||
|
emu.read_reg(arch_regs_map[SyncBackdoorArgs::Arg1])?;
|
||||||
|
let max_input_size: GuestReg =
|
||||||
|
emu.read_reg(arch_regs_map[SyncBackdoorArgs::Arg2])?;
|
||||||
|
|
||||||
|
SyncBackdoor {
|
||||||
|
command: Command::Input(CommandInput {
|
||||||
|
addr: GuestAddrKind::Physical(phys_addr),
|
||||||
|
max_input_size,
|
||||||
|
}),
|
||||||
|
arch_regs_map,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NativeSyncBackdoorCommand::End => {
|
||||||
|
let native_exit_kind: GuestReg =
|
||||||
|
emu.read_reg(arch_regs_map[SyncBackdoorArgs::Arg1])?;
|
||||||
|
let native_exit_kind: Result<NativeExitKind, _> =
|
||||||
|
u64::from(native_exit_kind).try_into();
|
||||||
|
|
||||||
|
let exit_kind = native_exit_kind
|
||||||
|
.ok()
|
||||||
|
.map(|k| {
|
||||||
|
EMU_EXIT_KIND_MAP.get_or_init(|| {
|
||||||
|
enum_map! {
|
||||||
|
NativeExitKind::Unknown => None,
|
||||||
|
NativeExitKind::Ok => Some(ExitKind::Ok),
|
||||||
|
NativeExitKind::Crash => Some(ExitKind::Crash)
|
||||||
|
}
|
||||||
|
})[k]
|
||||||
|
})
|
||||||
|
.flatten();
|
||||||
|
|
||||||
|
SyncBackdoor {
|
||||||
|
command: Command::Exit(exit_kind),
|
||||||
|
arch_regs_map,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NativeSyncBackdoorCommand::StartPhys => {
|
||||||
|
let input_phys_addr: GuestPhysAddr =
|
||||||
|
emu.read_reg(arch_regs_map[SyncBackdoorArgs::Arg1])?;
|
||||||
|
let max_input_size: GuestReg =
|
||||||
|
emu.read_reg(arch_regs_map[SyncBackdoorArgs::Arg2])?;
|
||||||
|
|
||||||
|
SyncBackdoor {
|
||||||
|
command: Command::Start(CommandInput {
|
||||||
|
addr: GuestAddrKind::Physical(input_phys_addr),
|
||||||
|
max_input_size,
|
||||||
|
}),
|
||||||
|
arch_regs_map,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NativeSyncBackdoorCommand::StartVirt => {
|
||||||
|
let input_virt_addr: GuestVirtAddr =
|
||||||
|
emu.read_reg(arch_regs_map[SyncBackdoorArgs::Arg1])?;
|
||||||
|
let max_input_size: GuestReg =
|
||||||
|
emu.read_reg(arch_regs_map[SyncBackdoorArgs::Arg2])?;
|
||||||
|
|
||||||
|
SyncBackdoor {
|
||||||
|
command: Command::Start(CommandInput {
|
||||||
|
addr: GuestAddrKind::Virtual(input_virt_addr),
|
||||||
|
max_input_size,
|
||||||
|
}),
|
||||||
|
arch_regs_map,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,14 @@
|
|||||||
use std::mem::size_of;
|
use std::{mem::size_of, sync::OnceLock};
|
||||||
|
|
||||||
use capstone::arch::BuildsCapstone;
|
use capstone::arch::BuildsCapstone;
|
||||||
|
use enum_map::{enum_map, EnumMap};
|
||||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
#[cfg(feature = "python")]
|
#[cfg(feature = "python")]
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::x86_64::*;
|
pub use syscall_numbers::x86_64::*;
|
||||||
|
|
||||||
use crate::CallingConvention;
|
use crate::{sync_backdoor::SyncBackdoorArgs, CallingConvention};
|
||||||
|
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
@ -32,6 +33,23 @@ pub enum Regs {
|
|||||||
Rflags = 17,
|
Rflags = 17,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SYNC_BACKDOOR_ARCH_REGS: OnceLock<EnumMap<SyncBackdoorArgs, Regs>> = OnceLock::new();
|
||||||
|
|
||||||
|
pub fn get_sync_backdoor_arch_regs() -> &'static EnumMap<SyncBackdoorArgs, Regs> {
|
||||||
|
SYNC_BACKDOOR_ARCH_REGS.get_or_init(|| {
|
||||||
|
enum_map! {
|
||||||
|
SyncBackdoorArgs::Ret => Regs::Rax,
|
||||||
|
SyncBackdoorArgs::Cmd => Regs::Rax,
|
||||||
|
SyncBackdoorArgs::Arg1 => Regs::Rdi,
|
||||||
|
SyncBackdoorArgs::Arg2 => Regs::Rsi,
|
||||||
|
SyncBackdoorArgs::Arg3 => Regs::Rdx,
|
||||||
|
SyncBackdoorArgs::Arg4 => Regs::R10,
|
||||||
|
SyncBackdoorArgs::Arg5 => Regs::R8,
|
||||||
|
SyncBackdoorArgs::Arg6 => Regs::R9,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// alias registers
|
/// alias registers
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
impl Regs {
|
impl Regs {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user