Raw API for full-system libafl_qemu (#692)
* full system build * start supporting more cpus * first proto working * more Emulator methods * fix * fix * backdoor * fix * libvduse.a * hash * clippy * debug * working usermode * Fix userspace arm * clippy * clippy * clippy
This commit is contained in:
parent
0aba272a7d
commit
90f0f06ef5
@ -12,8 +12,8 @@ edition = "2021"
|
|||||||
categories = ["development-tools::testing", "emulators", "embedded", "os", "no-std"]
|
categories = ["development-tools::testing", "emulators", "embedded", "os", "no-std"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
default = ["usermode"]
|
||||||
python = ["pyo3", "pyo3-build-config"]
|
python = ["pyo3", "pyo3-build-config"]
|
||||||
default = []
|
|
||||||
|
|
||||||
# The following architecture features are mutually exclusive.
|
# The following architecture features are mutually exclusive.
|
||||||
x86_64 = [] # build qemu for x86_64 (default)
|
x86_64 = [] # build qemu for x86_64 (default)
|
||||||
@ -21,6 +21,8 @@ i386 = [] # build qemu for i386
|
|||||||
arm = [] # build qemu for arm
|
arm = [] # build qemu for arm
|
||||||
aarch64 = [] # build qemu for aarch64
|
aarch64 = [] # build qemu for aarch64
|
||||||
|
|
||||||
|
usermode = []
|
||||||
|
|
||||||
clippy = [] # special feature for clippy, don't use in normal projects§
|
clippy = [] # special feature for clippy, don't use in normal projects§
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
@ -3,7 +3,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 = "03e283c85800496b60fb757d68a7df2821fb7a90";
|
const QEMU_REVISION: &str = "03fad12e801581536cd10830073acfce69e381fe";
|
||||||
|
|
||||||
fn build_dep_check(tools: &[&str]) {
|
fn build_dep_check(tools: &[&str]) {
|
||||||
for tool in tools {
|
for tool in tools {
|
||||||
@ -29,6 +29,7 @@ pub fn build() {
|
|||||||
println!("cargo:rerun-if-changed=build.rs");
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
println!("cargo:rerun-if-changed=src/asan-giovese.c");
|
println!("cargo:rerun-if-changed=src/asan-giovese.c");
|
||||||
println!("cargo:rerun-if-changed=src/asan-giovese.h");
|
println!("cargo:rerun-if-changed=src/asan-giovese.h");
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
println!("cargo:rerun-if-env-changed=CROSS_CC");
|
println!("cargo:rerun-if-env-changed=CROSS_CC");
|
||||||
|
|
||||||
// Make sure we have at most one architecutre feature set
|
// Make sure we have at most one architecutre feature set
|
||||||
@ -54,6 +55,7 @@ pub fn build() {
|
|||||||
|
|
||||||
let jobs = env::var("NUM_JOBS");
|
let jobs = env::var("NUM_JOBS");
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
let cross_cc = env::var("CROSS_CC").unwrap_or_else(|_| {
|
let cross_cc = env::var("CROSS_CC").unwrap_or_else(|_| {
|
||||||
println!("cargo:warning=CROSS_CC is not set, default to cc (things can go wrong if the selected cpu target ({}) is not the host arch ({}))", cpu_target, env::consts::ARCH);
|
println!("cargo:warning=CROSS_CC is not set, default to cc (things can go wrong if the selected cpu target ({}) is not the host arch ({}))", cpu_target, env::consts::ARCH);
|
||||||
"cc".to_owned()
|
"cc".to_owned()
|
||||||
@ -65,6 +67,9 @@ pub fn build() {
|
|||||||
return; // only build when we're not generating docs
|
return; // only build when we're not generating docs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let custum_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 out_dir = env::var_os("OUT_DIR").unwrap();
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
let out_dir = out_dir.to_string_lossy().to_string();
|
let out_dir = out_dir.to_string_lossy().to_string();
|
||||||
let out_dir_path = Path::new(&out_dir);
|
let out_dir_path = Path::new(&out_dir);
|
||||||
@ -72,89 +77,100 @@ pub fn build() {
|
|||||||
target_dir.pop();
|
target_dir.pop();
|
||||||
target_dir.pop();
|
target_dir.pop();
|
||||||
target_dir.pop();
|
target_dir.pop();
|
||||||
let qasan_dir = Path::new("libqasan");
|
|
||||||
let qasan_dir = fs::canonicalize(&qasan_dir).unwrap();
|
|
||||||
let src_dir = Path::new("src");
|
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=libqasan");
|
println!("cargo:rerun-if-changed=libqasan");
|
||||||
|
|
||||||
build_dep_check(&["git", "make"]);
|
build_dep_check(&["git", "make"]);
|
||||||
|
|
||||||
let qemu_rev = out_dir_path.join("QEMU_REVISION");
|
let qemu_path = if let Some(qemu_dir) = custum_qemu_dir.as_ref() {
|
||||||
let qemu_path = out_dir_path.join(QEMU_DIRNAME);
|
Path::new(&qemu_dir).to_path_buf()
|
||||||
|
} else {
|
||||||
|
let qemu_path = out_dir_path.join(QEMU_DIRNAME);
|
||||||
|
|
||||||
if qemu_rev.exists()
|
let qemu_rev = out_dir_path.join("QEMU_REVISION");
|
||||||
&& fs::read_to_string(&qemu_rev).expect("Failed to read QEMU_REVISION") != QEMU_REVISION
|
if qemu_rev.exists()
|
||||||
{
|
&& fs::read_to_string(&qemu_rev).expect("Failed to read QEMU_REVISION") != QEMU_REVISION
|
||||||
drop(fs::remove_dir_all(&qemu_path));
|
{
|
||||||
}
|
drop(fs::remove_dir_all(&qemu_path));
|
||||||
|
}
|
||||||
|
|
||||||
if !qemu_path.is_dir() {
|
if !qemu_path.is_dir() {
|
||||||
println!(
|
println!(
|
||||||
"cargo:warning=Qemu not found, cloning with git ({})...",
|
"cargo:warning=Qemu not found, cloning with git ({})...",
|
||||||
QEMU_REVISION
|
QEMU_REVISION
|
||||||
);
|
);
|
||||||
fs::create_dir_all(&qemu_path).unwrap();
|
fs::create_dir_all(&qemu_path).unwrap();
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.current_dir(&qemu_path)
|
.current_dir(&qemu_path)
|
||||||
.arg("init")
|
.arg("init")
|
||||||
.status()
|
.status()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.current_dir(&qemu_path)
|
.current_dir(&qemu_path)
|
||||||
.arg("remote")
|
.arg("remote")
|
||||||
.arg("add")
|
.arg("add")
|
||||||
.arg("origin")
|
.arg("origin")
|
||||||
.arg(QEMU_URL)
|
.arg(QEMU_URL)
|
||||||
.status()
|
.status()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.current_dir(&qemu_path)
|
.current_dir(&qemu_path)
|
||||||
.arg("fetch")
|
.arg("fetch")
|
||||||
.arg("--depth")
|
.arg("--depth")
|
||||||
.arg("1")
|
.arg("1")
|
||||||
.arg("origin")
|
.arg("origin")
|
||||||
.arg(QEMU_REVISION)
|
.arg(QEMU_REVISION)
|
||||||
.status()
|
.status()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.current_dir(&qemu_path)
|
.current_dir(&qemu_path)
|
||||||
.arg("checkout")
|
.arg("checkout")
|
||||||
.arg("FETCH_HEAD")
|
.arg("FETCH_HEAD")
|
||||||
.status()
|
.status()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
/*Command::new("git")
|
fs::write(&qemu_rev, QEMU_REVISION).unwrap();
|
||||||
.current_dir(&out_dir_path)
|
}
|
||||||
.arg("clone")
|
|
||||||
.arg(QEMU_URL)
|
qemu_path
|
||||||
.status()
|
};
|
||||||
.unwrap();
|
|
||||||
Command::new("git")
|
#[cfg(feature = "usermode")]
|
||||||
.current_dir(&qemu_path)
|
let target_suffix = "linux-user";
|
||||||
.arg("checkout")
|
#[cfg(not(feature = "usermode"))]
|
||||||
.arg(QEMU_REVISION)
|
let target_suffix = "softmmu";
|
||||||
.status()
|
|
||||||
.unwrap();*/
|
|
||||||
fs::write(&qemu_rev, QEMU_REVISION).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let build_dir = qemu_path.join("build");
|
let build_dir = qemu_path.join("build");
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
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() {
|
#[cfg(not(feature = "usermode"))]
|
||||||
|
let output_lib = build_dir.join(&format!("libqemu-system-{}.so", cpu_target));
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-changed={}", output_lib.to_string_lossy());
|
||||||
|
|
||||||
|
if !output_lib.is_file() || (custum_qemu_dir.is_some() && !custum_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(),
|
||||||
);*/
|
);*/
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
Command::new("./configure")
|
Command::new("./configure")
|
||||||
.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={}-{}", cpu_target, target_suffix))
|
||||||
.args(&["--disable-blobs", "--disable-bsd-user", "--disable-fdt"])
|
.args(&["--disable-blobs", "--disable-bsd-user", "--disable-fdt"])
|
||||||
.status()
|
.status()
|
||||||
.expect("Configure failed");
|
.expect("Configure failed");
|
||||||
|
#[cfg(not(feature = "usermode"))]
|
||||||
|
Command::new("./configure")
|
||||||
|
.current_dir(&qemu_path)
|
||||||
|
//.arg("--as-static-lib")
|
||||||
|
.arg("--as-shared-lib")
|
||||||
|
.arg(&format!("--target-list={}-{}", cpu_target, target_suffix))
|
||||||
|
.status()
|
||||||
|
.expect("Configure failed");
|
||||||
if let Ok(j) = jobs {
|
if let Ok(j) = jobs {
|
||||||
Command::new("make")
|
Command::new("make")
|
||||||
.current_dir(&qemu_path)
|
.current_dir(&qemu_path)
|
||||||
@ -169,17 +185,12 @@ pub fn build() {
|
|||||||
.status()
|
.status()
|
||||||
.expect("Make failed");
|
.expect("Make failed");
|
||||||
}
|
}
|
||||||
//let _ = remove_file(build_dir.join(&format!("libqemu-{}.so", cpu_target)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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"),
|
||||||
build_dir.join(&format!("libqemu-{}-linux-user.fa.p", cpu_target)),
|
build_dir.join(&format!("libqemu-{}-{}.fa.p", cpu_target, target_suffix)),
|
||||||
//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() {
|
for path in fs::read_dir(dir).unwrap() {
|
||||||
let path = path.unwrap().path();
|
let path = path.unwrap().path();
|
||||||
@ -197,6 +208,7 @@ pub fn build() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
Command::new("ld")
|
Command::new("ld")
|
||||||
.current_dir(&out_dir_path)
|
.current_dir(&out_dir_path)
|
||||||
.arg("-o")
|
.arg("-o")
|
||||||
@ -219,18 +231,102 @@ pub fn build() {
|
|||||||
.status()
|
.status()
|
||||||
.expect("Partial linked failure");
|
.expect("Partial linked failure");
|
||||||
|
|
||||||
drop(
|
#[cfg(not(feature = "usermode"))]
|
||||||
Command::new("ar")
|
Command::new("ld")
|
||||||
.current_dir(&out_dir_path)
|
.current_dir(&out_dir_path)
|
||||||
.arg("crus")
|
.arg("-o")
|
||||||
.arg("libqemu-partially-linked.a")
|
.arg("libqemu-partially-linked.o")
|
||||||
.arg("libqemu-partially-linked.o")
|
.arg("-r")
|
||||||
.status(),
|
.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(format!("{}/libio.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libcrypto.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libauthz.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libblockdev.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libblock.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libchardev.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libqmp.fa", build_dir.display()))
|
||||||
|
.arg("--no-whole-archive")
|
||||||
|
.arg(format!("{}/libqemuutil.a", build_dir.display()))
|
||||||
|
.arg(format!(
|
||||||
|
"{}/subprojects/libvhost-user/libvhost-user-glib.a",
|
||||||
|
build_dir.display()
|
||||||
|
))
|
||||||
|
.arg(format!(
|
||||||
|
"{}/subprojects/libvhost-user/libvhost-user.a",
|
||||||
|
build_dir.display()
|
||||||
|
))
|
||||||
|
.arg(format!(
|
||||||
|
"{}/subprojects/libvduse/libvduse.a",
|
||||||
|
build_dir.display()
|
||||||
|
))
|
||||||
|
.arg(format!("{}/libfdt.a", build_dir.display()))
|
||||||
|
.arg(format!("{}/libslirp.a", build_dir.display()))
|
||||||
|
.arg(format!("{}/libmigration.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libhwcore.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libqom.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libio.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libcrypto.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libauthz.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libblockdev.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libblock.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libchardev.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libqmp.fa", build_dir.display()))
|
||||||
|
.arg(format!(
|
||||||
|
"--dynamic-list={}/plugins/qemu-plugins.symbols",
|
||||||
|
qemu_path.display()
|
||||||
|
))
|
||||||
|
.status()
|
||||||
|
.expect("Partial linked failure");
|
||||||
|
|
||||||
|
Command::new("ar")
|
||||||
|
.current_dir(&out_dir_path)
|
||||||
|
.arg("crus")
|
||||||
|
.arg("libqemu-partially-linked.a")
|
||||||
|
.arg("libqemu-partially-linked.o")
|
||||||
|
.status()
|
||||||
|
.expect("Ar creation");
|
||||||
|
|
||||||
println!("cargo:rustc-link-search=native={}", out_dir);
|
println!("cargo:rustc-link-search=native={}", out_dir);
|
||||||
println!("cargo:rustc-link-lib=static=qemu-partially-linked");
|
println!("cargo:rustc-link-lib=static=qemu-partially-linked");
|
||||||
|
|
||||||
|
#[cfg(not(feature = "usermode"))]
|
||||||
|
{
|
||||||
|
println!("cargo:rustc-link-lib=png");
|
||||||
|
println!("cargo:rustc-link-lib=z");
|
||||||
|
println!("cargo:rustc-link-lib=gio-2.0");
|
||||||
|
println!("cargo:rustc-link-lib=gobject-2.0");
|
||||||
|
println!("cargo:rustc-link-lib=ncursesw");
|
||||||
|
println!("cargo:rustc-link-lib=tinfo");
|
||||||
|
println!("cargo:rustc-link-lib=gtk-3");
|
||||||
|
println!("cargo:rustc-link-lib=gdk-3");
|
||||||
|
println!("cargo:rustc-link-lib=pangocairo-1.0");
|
||||||
|
println!("cargo:rustc-link-lib=pango-1.0");
|
||||||
|
println!("cargo:rustc-link-lib=harfbuzz");
|
||||||
|
println!("cargo:rustc-link-lib=atk-1.0");
|
||||||
|
println!("cargo:rustc-link-lib=cairo-gobject");
|
||||||
|
println!("cargo:rustc-link-lib=cairo");
|
||||||
|
println!("cargo:rustc-link-lib=gdk_pixbuf-2.0");
|
||||||
|
println!("cargo:rustc-link-lib=X11");
|
||||||
|
println!("cargo:rustc-link-lib=epoxy");
|
||||||
|
println!("cargo:rustc-link-lib=pixman-1");
|
||||||
|
|
||||||
|
fs::create_dir_all(target_dir.join("pc-bios")).unwrap();
|
||||||
|
for path in fs::read_dir(build_dir.join("pc-bios")).unwrap() {
|
||||||
|
let path = path.unwrap().path();
|
||||||
|
if path.is_file() {
|
||||||
|
if let Some(name) = path.file_name() {
|
||||||
|
fs::copy(&path, target_dir.join("pc-bios").join(name))
|
||||||
|
.expect("Failed to copy a pc-bios folder file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
println!("cargo:rustc-link-lib=rt");
|
println!("cargo:rustc-link-lib=rt");
|
||||||
println!("cargo:rustc-link-lib=gmodule-2.0");
|
println!("cargo:rustc-link-lib=gmodule-2.0");
|
||||||
println!("cargo:rustc-link-lib=glib-2.0");
|
println!("cargo:rustc-link-lib=glib-2.0");
|
||||||
@ -246,35 +342,42 @@ pub fn build() {
|
|||||||
|
|
||||||
println!(
|
println!(
|
||||||
"cargo:rustc-link-search=native={}",
|
"cargo:rustc-link-search=native={}",
|
||||||
&target_dir.to_string_lossy().to_string()
|
&target_dir.to_string_lossy()
|
||||||
);
|
);
|
||||||
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());
|
||||||
} */
|
} */
|
||||||
|
|
||||||
assert!(Command::new("make")
|
#[cfg(feature = "usermode")]
|
||||||
.current_dir(&out_dir_path)
|
{
|
||||||
.env("CC", &cross_cc)
|
let qasan_dir = Path::new("libqasan");
|
||||||
.env("OUT_DIR", &target_dir)
|
let qasan_dir = fs::canonicalize(&qasan_dir).unwrap();
|
||||||
.arg("-C")
|
let src_dir = Path::new("src");
|
||||||
.arg(&qasan_dir)
|
|
||||||
.arg("clean")
|
|
||||||
.status()
|
|
||||||
.expect("make failed")
|
|
||||||
.success());
|
|
||||||
assert!(Command::new("make")
|
|
||||||
.current_dir(&out_dir_path)
|
|
||||||
.env("CC", &cross_cc)
|
|
||||||
.env("OUT_DIR", &target_dir)
|
|
||||||
.arg("-C")
|
|
||||||
.arg(&qasan_dir)
|
|
||||||
.status()
|
|
||||||
.expect("make failed")
|
|
||||||
.success());
|
|
||||||
|
|
||||||
cc::Build::new()
|
assert!(Command::new("make")
|
||||||
.warnings(false)
|
.current_dir(&out_dir_path)
|
||||||
.file(src_dir.join("asan-giovese.c"))
|
.env("CC", &cross_cc)
|
||||||
.compile("asan_giovese");
|
.env("OUT_DIR", &target_dir)
|
||||||
|
.arg("-C")
|
||||||
|
.arg(&qasan_dir)
|
||||||
|
.arg("clean")
|
||||||
|
.status()
|
||||||
|
.expect("make failed")
|
||||||
|
.success());
|
||||||
|
assert!(Command::new("make")
|
||||||
|
.current_dir(&out_dir_path)
|
||||||
|
.env("CC", &cross_cc)
|
||||||
|
.env("OUT_DIR", &target_dir)
|
||||||
|
.arg("-C")
|
||||||
|
.arg(&qasan_dir)
|
||||||
|
.status()
|
||||||
|
.expect("make failed")
|
||||||
|
.success());
|
||||||
|
|
||||||
|
cc::Build::new()
|
||||||
|
.warnings(false)
|
||||||
|
.file(src_dir.join("asan-giovese.c"))
|
||||||
|
.compile("asan_giovese");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,7 +128,20 @@ where
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut code = unsafe { std::slice::from_raw_parts(emu.g2h(pc), 512) };
|
#[allow(unused_mut)]
|
||||||
|
let mut code = {
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
|
unsafe {
|
||||||
|
std::slice::from_raw_parts(emu.g2h(pc), 512)
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "usermode"))]
|
||||||
|
&mut [0; 512]
|
||||||
|
};
|
||||||
|
#[cfg(not(feature = "usermode"))]
|
||||||
|
unsafe {
|
||||||
|
emu.read_mem(pc, code)
|
||||||
|
}; // TODO handle faults
|
||||||
|
|
||||||
let mut iaddr = pc;
|
let mut iaddr = pc;
|
||||||
|
|
||||||
'disasm: while let Ok(insns) = h.cs.disasm_count(code, iaddr, 1) {
|
'disasm: while let Ok(insns) = h.cs.disasm_count(code, iaddr, 1) {
|
||||||
@ -174,7 +187,14 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
iaddr += insn.bytes().len() as u64;
|
iaddr += insn.bytes().len() as u64;
|
||||||
code = unsafe { std::slice::from_raw_parts(emu.g2h(iaddr), 512) };
|
#[cfg(feature = "usermode")]
|
||||||
|
unsafe {
|
||||||
|
code = std::slice::from_raw_parts(emu.g2h(iaddr), 512);
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "usermode"))]
|
||||||
|
unsafe {
|
||||||
|
emu.read_mem(pc, code);
|
||||||
|
} // TODO handle faults
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,9 +3,11 @@
|
|||||||
use core::{
|
use core::{
|
||||||
convert::Into,
|
convert::Into,
|
||||||
ffi::c_void,
|
ffi::c_void,
|
||||||
mem::MaybeUninit,
|
ptr::{addr_of, addr_of_mut, null},
|
||||||
ptr::{addr_of, addr_of_mut, copy_nonoverlapping, null},
|
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
|
use core::{mem::MaybeUninit, ptr::copy_nonoverlapping};
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
use libc::c_int;
|
use libc::c_int;
|
||||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
use num_traits::Num;
|
use num_traits::Num;
|
||||||
@ -27,6 +29,8 @@ use pyo3::{prelude::*, PyIterProtocol};
|
|||||||
|
|
||||||
pub const SKIP_EXEC_HOOK: u64 = u64::MAX;
|
pub const SKIP_EXEC_HOOK: u64 = u64::MAX;
|
||||||
|
|
||||||
|
type CPUStatePtr = *const c_void;
|
||||||
|
|
||||||
#[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 {
|
||||||
@ -180,30 +184,16 @@ impl MapInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
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;
|
||||||
|
|
||||||
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_num_regs() -> i32;
|
|
||||||
fn libafl_qemu_set_breakpoint(addr: u64) -> i32;
|
|
||||||
fn libafl_qemu_remove_breakpoint(addr: u64) -> i32;
|
|
||||||
fn libafl_flush_jit();
|
|
||||||
fn libafl_qemu_set_hook(
|
|
||||||
addr: GuestAddr,
|
|
||||||
callback: extern "C" fn(GuestAddr, u64),
|
|
||||||
data: u64,
|
|
||||||
invalidate_block: i32,
|
|
||||||
) -> usize;
|
|
||||||
// fn libafl_qemu_remove_hook(num: usize, invalidate_block: i32) -> i32;
|
|
||||||
fn libafl_qemu_remove_hooks_at(addr: GuestAddr, invalidate_block: i32) -> usize;
|
|
||||||
fn libafl_qemu_run() -> i32;
|
fn libafl_qemu_run() -> i32;
|
||||||
|
|
||||||
fn libafl_load_addr() -> u64;
|
fn libafl_load_addr() -> u64;
|
||||||
fn libafl_get_brk() -> u64;
|
fn libafl_get_brk() -> u64;
|
||||||
fn libafl_set_brk(brk: u64) -> u64;
|
fn libafl_set_brk(brk: u64) -> u64;
|
||||||
|
|
||||||
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)
|
||||||
fn target_mmap(start: u64, len: u64, target_prot: i32, flags: i32, fd: i32, offset: u64)
|
fn target_mmap(start: u64, len: u64, target_prot: i32, flags: i32, fd: i32, offset: u64)
|
||||||
-> u64;
|
-> u64;
|
||||||
@ -223,6 +213,77 @@ extern "C" {
|
|||||||
static guest_base: usize;
|
static guest_base: usize;
|
||||||
static mut mmap_next_start: GuestAddr;
|
static mut mmap_next_start: GuestAddr;
|
||||||
|
|
||||||
|
static mut libafl_on_thread_hook: unsafe extern "C" fn(u32);
|
||||||
|
|
||||||
|
static mut libafl_pre_syscall_hook:
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "usermode"))]
|
||||||
|
extern "C" {
|
||||||
|
fn qemu_init(argc: i32, argv: *const *const u8, envp: *const *const u8);
|
||||||
|
|
||||||
|
fn qemu_main_loop();
|
||||||
|
fn qemu_cleanup();
|
||||||
|
|
||||||
|
// void libafl_cpu_thread_fn(CPUState *cpu)
|
||||||
|
fn libafl_cpu_thread_fn(cpu: CPUStatePtr);
|
||||||
|
|
||||||
|
// int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr,
|
||||||
|
// uint8_t *buf, int len, int is_write);
|
||||||
|
fn cpu_memory_rw_debug(
|
||||||
|
cpu: CPUStatePtr,
|
||||||
|
addr: GuestAddr,
|
||||||
|
buf: *mut u8,
|
||||||
|
len: i32,
|
||||||
|
is_write: i32,
|
||||||
|
);
|
||||||
|
|
||||||
|
static mut libafl_start_vcpu: extern "C" fn(cpu: CPUStatePtr);
|
||||||
|
|
||||||
|
fn libafl_save_qemu_snapshot(name: *const u8);
|
||||||
|
fn libafl_load_qemu_snapshot(name: *const u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "usermode"))]
|
||||||
|
extern "C" fn qemu_cleanup_atexit() {
|
||||||
|
unsafe {
|
||||||
|
qemu_cleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
// CPUState* libafl_qemu_get_cpu(int cpu_index);
|
||||||
|
fn libafl_qemu_get_cpu(cpu_index: i32) -> CPUStatePtr;
|
||||||
|
// int libafl_qemu_num_cpus(void);
|
||||||
|
fn libafl_qemu_num_cpus() -> i32;
|
||||||
|
// CPUState* libafl_qemu_current_cpu(void);
|
||||||
|
fn libafl_qemu_current_cpu() -> CPUStatePtr;
|
||||||
|
|
||||||
|
fn libafl_qemu_cpu_index(cpu: CPUStatePtr) -> i32;
|
||||||
|
|
||||||
|
fn libafl_qemu_write_reg(cpu: CPUStatePtr, reg: i32, val: *const u8) -> i32;
|
||||||
|
fn libafl_qemu_read_reg(cpu: CPUStatePtr, reg: i32, val: *mut u8) -> i32;
|
||||||
|
fn libafl_qemu_num_regs(cpu: CPUStatePtr) -> i32;
|
||||||
|
|
||||||
|
fn libafl_qemu_set_breakpoint(addr: u64) -> i32;
|
||||||
|
fn libafl_qemu_remove_breakpoint(addr: u64) -> i32;
|
||||||
|
fn libafl_flush_jit();
|
||||||
|
fn libafl_qemu_trigger_breakpoint(cpu: CPUStatePtr);
|
||||||
|
|
||||||
|
fn libafl_qemu_set_hook(
|
||||||
|
addr: GuestAddr,
|
||||||
|
callback: extern "C" fn(GuestAddr, u64),
|
||||||
|
data: u64,
|
||||||
|
invalidate_block: i32,
|
||||||
|
) -> usize;
|
||||||
|
// fn libafl_qemu_remove_hook(num: usize, invalidate_block: i32) -> i32;
|
||||||
|
fn libafl_qemu_remove_hooks_at(addr: GuestAddr, invalidate_block: i32) -> usize;
|
||||||
|
|
||||||
|
fn strlen(s: *const u8) -> usize;
|
||||||
|
|
||||||
// void libafl_add_edge_hook(uint64_t (*gen)(target_ulong src, target_ulong dst), void (*exec)(uint64_t id));
|
// void libafl_add_edge_hook(uint64_t (*gen)(target_ulong src, target_ulong dst), void (*exec)(uint64_t id));
|
||||||
fn libafl_add_edge_hook(
|
fn libafl_add_edge_hook(
|
||||||
gen: Option<extern "C" fn(GuestAddr, GuestAddr, u64) -> u64>,
|
gen: Option<extern "C" fn(GuestAddr, GuestAddr, u64) -> u64>,
|
||||||
@ -286,12 +347,9 @@ extern "C" {
|
|||||||
data: u64,
|
data: u64,
|
||||||
);
|
);
|
||||||
|
|
||||||
static mut libafl_on_thread_hook: unsafe extern "C" fn(u32);
|
// void libafl_add_backdoor_hook(void (*exec)(uint64_t id, uint64_t data),
|
||||||
|
// uint64_t data)
|
||||||
static mut libafl_pre_syscall_hook:
|
fn libafl_add_backdoor_hook(exec: extern "C" fn(GuestAddr, u64), data: u64);
|
||||||
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(
|
fn libafl_qemu_add_gdb_cmd(
|
||||||
callback: extern "C" fn(*const u8, usize, *const ()) -> i32,
|
callback: extern "C" fn(*const u8, usize, *const ()) -> i32,
|
||||||
@ -300,6 +358,7 @@ extern "C" {
|
|||||||
fn libafl_qemu_gdb_reply(buf: *const u8, len: usize);
|
fn libafl_qemu_gdb_reply(buf: *const u8, len: usize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
#[cfg_attr(feature = "python", pyclass(unsendable))]
|
#[cfg_attr(feature = "python", pyclass(unsendable))]
|
||||||
pub struct GuestMaps {
|
pub struct GuestMaps {
|
||||||
orig_c_iter: *const c_void,
|
orig_c_iter: *const c_void,
|
||||||
@ -307,6 +366,7 @@ pub struct GuestMaps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Consider a private new only for Emulator
|
// Consider a private new only for Emulator
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
impl GuestMaps {
|
impl GuestMaps {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
@ -320,6 +380,7 @@ impl GuestMaps {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
impl Iterator for GuestMaps {
|
impl Iterator for GuestMaps {
|
||||||
type Item = MapInfo;
|
type Item = MapInfo;
|
||||||
|
|
||||||
@ -340,7 +401,7 @@ impl Iterator for GuestMaps {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
#[cfg(all(feature = "usermode", feature = "python"))]
|
||||||
#[pyproto]
|
#[pyproto]
|
||||||
impl PyIterProtocol for GuestMaps {
|
impl PyIterProtocol for GuestMaps {
|
||||||
fn __iter__(slf: PyRef<Self>) -> PyRef<Self> {
|
fn __iter__(slf: PyRef<Self>) -> PyRef<Self> {
|
||||||
@ -351,6 +412,7 @@ impl PyIterProtocol for GuestMaps {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
impl Drop for GuestMaps {
|
impl Drop for GuestMaps {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -378,6 +440,110 @@ extern "C" fn gdb_cmd(buf: *const u8, len: usize, data: *const ()) -> i32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct CPU {
|
||||||
|
ptr: CPUStatePtr,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unused_self)]
|
||||||
|
impl CPU {
|
||||||
|
#[must_use]
|
||||||
|
pub fn emulator(&self) -> Emulator {
|
||||||
|
Emulator::new_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
#[allow(clippy::cast_sign_loss)]
|
||||||
|
pub fn index(&self) -> usize {
|
||||||
|
unsafe { libafl_qemu_cpu_index(self.ptr) as usize }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trigger_breakpoint(&self) {
|
||||||
|
unsafe {
|
||||||
|
libafl_qemu_trigger_breakpoint(self.ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
|
#[must_use]
|
||||||
|
pub fn g2h<T>(&self, addr: GuestAddr) -> *mut T {
|
||||||
|
unsafe { (addr as usize + guest_base) as *mut T }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
|
#[must_use]
|
||||||
|
pub fn h2g<T>(&self, addr: *const T) -> GuestAddr {
|
||||||
|
unsafe { (addr as usize - guest_base) as GuestAddr }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write a value to a guest address.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// This will write to a translated guest address (using `g2h`).
|
||||||
|
/// It just adds `guest_base` and writes to that location, without checking the bounds.
|
||||||
|
/// This may only be safely used for valid guest addresses!
|
||||||
|
pub unsafe fn write_mem(&self, addr: GuestAddr, buf: &[u8]) {
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
|
{
|
||||||
|
let host_addr = Emulator::new_empty().g2h(addr);
|
||||||
|
copy_nonoverlapping(buf.as_ptr(), host_addr, buf.len());
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "usermode"))]
|
||||||
|
cpu_memory_rw_debug(self.ptr, addr, buf.as_ptr() as *mut u8, buf.len() as i32, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a value from a guest address.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// This will read from a translated guest address (using `g2h`).
|
||||||
|
/// It just adds `guest_base` and writes to that location, without checking the bounds.
|
||||||
|
/// This may only be safely used for valid guest addresses!
|
||||||
|
pub unsafe fn read_mem(&self, addr: GuestAddr, buf: &mut [u8]) {
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
|
{
|
||||||
|
let host_addr = Emulator::new_empty().g2h(addr);
|
||||||
|
copy_nonoverlapping(host_addr, buf.as_mut_ptr(), buf.len());
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "usermode"))]
|
||||||
|
cpu_memory_rw_debug(self.ptr, addr, buf.as_mut_ptr(), buf.len() as i32, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn num_regs(&self) -> i32 {
|
||||||
|
unsafe { libafl_qemu_num_regs(self.ptr) }
|
||||||
|
}
|
||||||
|
|
||||||
|
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(self.ptr, reg, addr_of!(val) as *const u8) };
|
||||||
|
if success == 0 {
|
||||||
|
Err(format!("Failed to write to register {}", reg))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(self.ptr, reg, addr_of_mut!(val) as *mut u8) };
|
||||||
|
if success == 0 {
|
||||||
|
Err(format!("Failed to read register {}", reg))
|
||||||
|
} else {
|
||||||
|
Ok(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static mut EMULATOR_IS_INITIALIZED: bool = false;
|
static mut EMULATOR_IS_INITIALIZED: bool = false;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -408,11 +574,21 @@ 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(feature = "usermode")]
|
||||||
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(not(feature = "usermode"))]
|
||||||
|
{
|
||||||
|
qemu_init(
|
||||||
|
argc,
|
||||||
|
argv.as_ptr() as *const *const u8,
|
||||||
|
envp.as_ptr() as *const *const u8,
|
||||||
|
);
|
||||||
|
libc::atexit(qemu_cleanup_atexit);
|
||||||
|
}
|
||||||
EMULATOR_IS_INITIALIZED = true;
|
EMULATOR_IS_INITIALIZED = true;
|
||||||
}
|
}
|
||||||
Emulator { _private: () }
|
Emulator { _private: () }
|
||||||
@ -423,37 +599,70 @@ impl Emulator {
|
|||||||
Emulator { _private: () }
|
Emulator { _private: () }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "usermode"))]
|
||||||
|
pub fn start(&self, cpu: &CPU) {
|
||||||
|
unsafe {
|
||||||
|
libafl_cpu_thread_fn(cpu.ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This function gets the memory mappings from the emulator.
|
/// This function gets the memory mappings from the emulator.
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn mappings(&self) -> GuestMaps {
|
pub fn mappings(&self) -> GuestMaps {
|
||||||
GuestMaps::new()
|
GuestMaps::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write a value to a guest address.
|
#[must_use]
|
||||||
///
|
#[allow(clippy::cast_possible_wrap)]
|
||||||
/// # Safety
|
#[allow(clippy::cast_sign_loss)]
|
||||||
/// This will write to a translated guest address (using `g2h`).
|
pub fn num_cpus(&self) -> usize {
|
||||||
/// It just adds `guest_base` and writes to that location, without checking the bounds.
|
unsafe { libafl_qemu_num_cpus() as usize }
|
||||||
/// This may only be safely used for valid guest addresses!
|
}
|
||||||
pub unsafe fn write_mem(&self, addr: GuestAddr, buf: &[u8]) {
|
|
||||||
let host_addr = self.g2h(addr);
|
#[must_use]
|
||||||
copy_nonoverlapping(buf.as_ptr(), host_addr, buf.len());
|
pub fn current_cpu(&self) -> Option<CPU> {
|
||||||
|
let ptr = unsafe { libafl_qemu_current_cpu() };
|
||||||
|
if ptr.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(CPU { ptr })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
#[allow(clippy::cast_possible_wrap)]
|
||||||
|
pub fn cpu_from_index(&self, index: usize) -> CPU {
|
||||||
|
unsafe {
|
||||||
|
CPU {
|
||||||
|
ptr: libafl_qemu_get_cpu(index as i32),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
|
#[must_use]
|
||||||
|
pub fn g2h<T>(&self, addr: GuestAddr) -> *mut T {
|
||||||
|
unsafe { (addr as usize + guest_base) as *mut T }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
|
#[must_use]
|
||||||
|
pub fn h2g<T>(&self, addr: *const T) -> GuestAddr {
|
||||||
|
unsafe { (addr as usize - guest_base) as GuestAddr }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn write_mem(&self, addr: GuestAddr, buf: &[u8]) {
|
||||||
|
self.current_cpu().unwrap().write_mem(addr, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a value from a guest address.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
/// This will read from a translated guest address (using `g2h`).
|
|
||||||
/// It just adds `guest_base` and writes to that location, without checking the bounds.
|
|
||||||
/// This may only be safely used for valid guest addresses!
|
|
||||||
pub unsafe fn read_mem(&self, addr: GuestAddr, buf: &mut [u8]) {
|
pub unsafe fn read_mem(&self, addr: GuestAddr, buf: &mut [u8]) {
|
||||||
let host_addr = self.g2h(addr);
|
self.current_cpu().unwrap().read_mem(addr, buf);
|
||||||
copy_nonoverlapping(host_addr, buf.as_mut_ptr(), buf.len());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn num_regs(&self) -> i32 {
|
pub fn num_regs(&self) -> i32 {
|
||||||
unsafe { libafl_qemu_num_regs() }
|
self.current_cpu().unwrap().num_regs()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_reg<R, T>(&self, reg: R, val: T) -> Result<(), String>
|
pub fn write_reg<R, T>(&self, reg: R, val: T) -> Result<(), String>
|
||||||
@ -461,13 +670,7 @@ impl Emulator {
|
|||||||
T: Num + PartialOrd + Copy,
|
T: Num + PartialOrd + Copy,
|
||||||
R: Into<i32>,
|
R: Into<i32>,
|
||||||
{
|
{
|
||||||
let reg = reg.into();
|
self.current_cpu().unwrap().write_reg(reg, val)
|
||||||
let success = unsafe { libafl_qemu_write_reg(reg, addr_of!(val) as *const u8) };
|
|
||||||
if success == 0 {
|
|
||||||
Err(format!("Failed to write to register {}", reg))
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_reg<R, T>(&self, reg: R) -> Result<T, String>
|
pub fn read_reg<R, T>(&self, reg: R) -> Result<T, String>
|
||||||
@ -475,14 +678,7 @@ impl Emulator {
|
|||||||
T: Num + PartialOrd + Copy,
|
T: Num + PartialOrd + Copy,
|
||||||
R: Into<i32>,
|
R: Into<i32>,
|
||||||
{
|
{
|
||||||
let reg = reg.into();
|
self.current_cpu().unwrap().read_reg(reg)
|
||||||
let mut val = T::zero();
|
|
||||||
let success = unsafe { libafl_qemu_read_reg(reg, addr_of_mut!(val) as *mut u8) };
|
|
||||||
if success == 0 {
|
|
||||||
Err(format!("Failed to read register {}", reg))
|
|
||||||
} else {
|
|
||||||
Ok(val)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_breakpoint(&self, addr: GuestAddr) {
|
pub fn set_breakpoint(&self, addr: GuestAddr) {
|
||||||
@ -518,47 +714,47 @@ 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) {
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
libafl_qemu_run();
|
libafl_qemu_run();
|
||||||
|
#[cfg(not(feature = "usermode"))]
|
||||||
|
qemu_main_loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[cfg(feature = "usermode")]
|
||||||
pub fn g2h<T>(&self, addr: GuestAddr) -> *mut T {
|
|
||||||
unsafe { (addr as usize + guest_base) as *mut T }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn h2g<T>(&self, addr: *const T) -> GuestAddr {
|
|
||||||
unsafe { (addr as usize - guest_base) as GuestAddr }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn binary_path<'a>(&self) -> &'a str {
|
pub fn binary_path<'a>(&self) -> &'a str {
|
||||||
unsafe { from_utf8_unchecked(from_raw_parts(exec_path, strlen(exec_path))) }
|
unsafe { from_utf8_unchecked(from_raw_parts(exec_path, strlen(exec_path))) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn load_addr(&self) -> GuestAddr {
|
pub fn load_addr(&self) -> GuestAddr {
|
||||||
unsafe { libafl_load_addr() as GuestAddr }
|
unsafe { libafl_load_addr() as GuestAddr }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_brk(&self) -> GuestAddr {
|
pub fn get_brk(&self) -> GuestAddr {
|
||||||
unsafe { libafl_get_brk() as GuestAddr }
|
unsafe { libafl_get_brk() as GuestAddr }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
pub fn set_brk(&self, brk: GuestAddr) {
|
pub fn set_brk(&self, brk: GuestAddr) {
|
||||||
unsafe { libafl_set_brk(brk.into()) };
|
unsafe { libafl_set_brk(brk.into()) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_mmap_start(&self) -> GuestAddr {
|
pub fn get_mmap_start(&self) -> GuestAddr {
|
||||||
unsafe { mmap_next_start }
|
unsafe { mmap_next_start }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
pub fn set_mmap_start(&self, start: GuestAddr) {
|
pub fn set_mmap_start(&self, start: GuestAddr) {
|
||||||
unsafe { mmap_next_start = start };
|
unsafe { mmap_next_start = start };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
fn mmap(
|
fn mmap(
|
||||||
&self,
|
&self,
|
||||||
addr: GuestAddr,
|
addr: GuestAddr,
|
||||||
@ -574,6 +770,7 @@ impl Emulator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
pub fn map_private(
|
pub fn map_private(
|
||||||
&self,
|
&self,
|
||||||
addr: GuestAddr,
|
addr: GuestAddr,
|
||||||
@ -585,6 +782,7 @@ impl Emulator {
|
|||||||
.map(|addr| addr as GuestAddr)
|
.map(|addr| addr as GuestAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
pub fn map_fixed(
|
pub fn map_fixed(
|
||||||
&self,
|
&self,
|
||||||
addr: GuestAddr,
|
addr: GuestAddr,
|
||||||
@ -601,6 +799,7 @@ impl Emulator {
|
|||||||
.map(|addr| addr as GuestAddr)
|
.map(|addr| addr as GuestAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
pub fn mprotect(&self, addr: GuestAddr, size: usize, perms: MmapPerms) -> Result<(), String> {
|
pub fn mprotect(&self, addr: GuestAddr, size: usize, perms: MmapPerms) -> Result<(), String> {
|
||||||
let res = unsafe { target_mprotect(addr.into(), size as u64, perms.into()) };
|
let res = unsafe { target_mprotect(addr.into(), size as u64, perms.into()) };
|
||||||
if res == 0 {
|
if res == 0 {
|
||||||
@ -610,6 +809,7 @@ impl Emulator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
pub fn unmap(&self, addr: GuestAddr, size: usize) -> Result<(), String> {
|
pub fn unmap(&self, addr: GuestAddr, size: usize) -> Result<(), String> {
|
||||||
if unsafe { target_munmap(addr.into(), size as u64) } == 0 {
|
if unsafe { target_munmap(addr.into(), size as u64) } == 0 {
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -680,12 +880,37 @@ impl Emulator {
|
|||||||
unsafe { libafl_add_cmp_hook(gen, exec1, exec2, exec4, exec8, data) }
|
unsafe { libafl_add_cmp_hook(gen, exec1, exec2, exec4, exec8, data) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_backdoor_hook(&self, exec: extern "C" fn(GuestAddr, u64), data: u64) {
|
||||||
|
unsafe { libafl_add_backdoor_hook(exec, data) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "usermode"))]
|
||||||
|
pub fn set_vcpu_start(&self, hook: extern "C" fn(cpu: CPU)) {
|
||||||
|
unsafe {
|
||||||
|
libafl_start_vcpu = core::mem::transmute(hook);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
pub fn set_on_thread_hook(&self, hook: extern "C" fn(tid: u32)) {
|
pub fn set_on_thread_hook(&self, hook: extern "C" fn(tid: u32)) {
|
||||||
unsafe {
|
unsafe {
|
||||||
libafl_on_thread_hook = hook;
|
libafl_on_thread_hook = hook;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*#[cfg(not(feature = "usermode"))]
|
||||||
|
pub fn save_snapshot(&self, name: &str) {
|
||||||
|
let s = CString::new(name).expect("Invalid snapshot name");
|
||||||
|
unsafe { libafl_save_qemu_snapshot(s.as_ptr() as *const _) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "usermode"))]
|
||||||
|
pub fn load_snapshot(&self, name: &str) {
|
||||||
|
let s = CString::new(name).expect("Invalid snapshot name");
|
||||||
|
unsafe { libafl_load_qemu_snapshot(s.as_ptr() as *const _) };
|
||||||
|
}*/
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
pub fn set_pre_syscall_hook(
|
pub fn set_pre_syscall_hook(
|
||||||
&self,
|
&self,
|
||||||
hook: extern "C" fn(i32, u64, u64, u64, u64, u64, u64, u64, u64) -> SyscallHookResult,
|
hook: extern "C" fn(i32, u64, u64, u64, u64, u64, u64, u64, u64) -> SyscallHookResult,
|
||||||
@ -695,6 +920,7 @@ impl Emulator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
pub fn set_post_syscall_hook(
|
pub fn set_post_syscall_hook(
|
||||||
&self,
|
&self,
|
||||||
hook: extern "C" fn(u64, i32, u64, u64, u64, u64, u64, u64, u64, u64) -> u64,
|
hook: extern "C" fn(u64, i32, u64, u64, u64, u64, u64, u64, u64, u64) -> u64,
|
||||||
|
@ -23,6 +23,7 @@ use crate::{
|
|||||||
enum Hook {
|
enum Hook {
|
||||||
Function(*const c_void),
|
Function(*const c_void),
|
||||||
Closure(FatPtr),
|
Closure(FatPtr),
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
Once(FatPtr),
|
Once(FatPtr),
|
||||||
Empty,
|
Empty,
|
||||||
}
|
}
|
||||||
@ -425,7 +426,9 @@ define_cmp_exec_hook!(exec_cmp2_hook_wrapper, 2, u16);
|
|||||||
define_cmp_exec_hook!(exec_cmp4_hook_wrapper, 3, u32);
|
define_cmp_exec_hook!(exec_cmp4_hook_wrapper, 3, u32);
|
||||||
define_cmp_exec_hook!(exec_cmp8_hook_wrapper, 4, u64);
|
define_cmp_exec_hook!(exec_cmp8_hook_wrapper, 4, u64);
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
static mut ON_THREAD_HOOKS: Vec<Hook> = vec![];
|
static mut ON_THREAD_HOOKS: Vec<Hook> = vec![];
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
extern "C" fn on_thread_hooks_wrapper<I, QT, S>(tid: u32)
|
extern "C" fn on_thread_hooks_wrapper<I, QT, S>(tid: u32)
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
@ -461,7 +464,9 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
static mut SYSCALL_HOOKS: Vec<Hook> = vec![];
|
static mut SYSCALL_HOOKS: Vec<Hook> = vec![];
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
extern "C" fn syscall_hooks_wrapper<I, QT, S>(
|
extern "C" fn syscall_hooks_wrapper<I, QT, S>(
|
||||||
sys_num: i32,
|
sys_num: i32,
|
||||||
a0: u64,
|
a0: u64,
|
||||||
@ -561,7 +566,9 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
static mut SYSCALL_POST_HOOKS: Vec<Hook> = vec![];
|
static mut SYSCALL_POST_HOOKS: Vec<Hook> = vec![];
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
extern "C" fn syscall_after_hooks_wrapper<I, QT, S>(
|
extern "C" fn syscall_after_hooks_wrapper<I, QT, S>(
|
||||||
result: u64,
|
result: u64,
|
||||||
sys_num: i32,
|
sys_num: i32,
|
||||||
@ -1419,6 +1426,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
pub fn thread_creation(&self, hook: fn(&mut Self, Option<&mut S>, tid: u32)) {
|
pub fn thread_creation(&self, hook: fn(&mut Self, Option<&mut S>, tid: u32)) {
|
||||||
unsafe {
|
unsafe {
|
||||||
ON_THREAD_HOOKS.push(Hook::Function(hook as *const libc::c_void));
|
ON_THREAD_HOOKS.push(Hook::Function(hook as *const libc::c_void));
|
||||||
@ -1427,6 +1435,7 @@ where
|
|||||||
.set_on_thread_hook(on_thread_hooks_wrapper::<I, QT, S>);
|
.set_on_thread_hook(on_thread_hooks_wrapper::<I, QT, S>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
pub fn thread_creation_closure(
|
pub fn thread_creation_closure(
|
||||||
&self,
|
&self,
|
||||||
hook: Box<dyn FnMut(&'a mut Self, Option<&'a mut S>, u32) + 'a>,
|
hook: Box<dyn FnMut(&'a mut Self, Option<&'a mut S>, u32) + 'a>,
|
||||||
@ -1438,6 +1447,7 @@ where
|
|||||||
.set_on_thread_hook(on_thread_hooks_wrapper::<I, QT, S>);
|
.set_on_thread_hook(on_thread_hooks_wrapper::<I, QT, S>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
pub fn thread_creation_once(&self, hook: Box<dyn FnOnce(&mut Self, Option<&mut S>, u32) + 'a>) {
|
pub fn thread_creation_once(&self, hook: Box<dyn FnOnce(&mut Self, Option<&mut S>, u32) + 'a>) {
|
||||||
unsafe {
|
unsafe {
|
||||||
ON_THREAD_HOOKS.push(Hook::Once(transmute(hook)));
|
ON_THREAD_HOOKS.push(Hook::Once(transmute(hook)));
|
||||||
@ -1446,6 +1456,7 @@ where
|
|||||||
.set_on_thread_hook(on_thread_hooks_wrapper::<I, QT, S>);
|
.set_on_thread_hook(on_thread_hooks_wrapper::<I, QT, S>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
pub fn syscalls(
|
pub fn syscalls(
|
||||||
&self,
|
&self,
|
||||||
@ -1470,6 +1481,7 @@ where
|
|||||||
.set_pre_syscall_hook(syscall_hooks_wrapper::<I, QT, S>);
|
.set_pre_syscall_hook(syscall_hooks_wrapper::<I, QT, S>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
pub fn syscalls_closure(
|
pub fn syscalls_closure(
|
||||||
&self,
|
&self,
|
||||||
@ -1496,6 +1508,7 @@ where
|
|||||||
.set_pre_syscall_hook(syscall_hooks_wrapper::<I, QT, S>);
|
.set_pre_syscall_hook(syscall_hooks_wrapper::<I, QT, S>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
pub fn after_syscalls(
|
pub fn after_syscalls(
|
||||||
&self,
|
&self,
|
||||||
@ -1521,6 +1534,7 @@ where
|
|||||||
.set_post_syscall_hook(syscall_after_hooks_wrapper::<I, QT, S>);
|
.set_post_syscall_hook(syscall_after_hooks_wrapper::<I, QT, S>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
pub fn after_syscalls_closure(
|
pub fn after_syscalls_closure(
|
||||||
&self,
|
&self,
|
||||||
|
@ -50,9 +50,13 @@ pub mod edges;
|
|||||||
pub use edges::QemuEdgeCoverageHelper;
|
pub use edges::QemuEdgeCoverageHelper;
|
||||||
pub mod cmplog;
|
pub mod cmplog;
|
||||||
pub use cmplog::QemuCmpLogHelper;
|
pub use cmplog::QemuCmpLogHelper;
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
pub mod snapshot;
|
pub mod snapshot;
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
pub use snapshot::QemuSnapshotHelper;
|
pub use snapshot::QemuSnapshotHelper;
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
pub mod asan;
|
pub mod asan;
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
pub use asan::{init_with_asan, QemuAsanHelper};
|
pub use asan::{init_with_asan, QemuAsanHelper};
|
||||||
|
|
||||||
pub mod calls;
|
pub mod calls;
|
||||||
|
@ -358,9 +358,7 @@ where
|
|||||||
h.add_mapped(result as GuestAddr, a1 as usize, Some(prot));
|
h.add_mapped(result as GuestAddr, a1 as usize, Some(prot));
|
||||||
}
|
}
|
||||||
} else if i64::from(sys_num) == SYS_mremap {
|
} else if i64::from(sys_num) == SYS_mremap {
|
||||||
let h = helpers
|
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
||||||
.match_first_type_mut::<QemuSnapshotHelper>()
|
|
||||||
.unwrap();
|
|
||||||
h.add_mapped(result as GuestAddr, a2 as usize, None);
|
h.add_mapped(result as GuestAddr, a2 as usize, None);
|
||||||
} else if i64::from(sys_num) == SYS_mprotect {
|
} else if i64::from(sys_num) == SYS_mprotect {
|
||||||
if let Ok(prot) = MmapPerms::try_from(a2 as i32) {
|
if let Ok(prot) = MmapPerms::try_from(a2 as i32) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user