Merge pull request #18 from schumilo/dev_aflpp
Various changes to extend the libnyx API and allow more control over qemu-nyx runners
This commit is contained in:
commit
2822aa1b14
@ -9,4 +9,5 @@ edition = "2018"
|
||||
[dependencies]
|
||||
serde ="1.0.104"
|
||||
serde_derive ="1.0.104"
|
||||
ron="0.6.2"
|
||||
ron="0.6.2"
|
||||
libc = "0.2"
|
@ -5,6 +5,8 @@ use std::fs::File;
|
||||
use std::path::{Path};
|
||||
use crate::loader::*;
|
||||
|
||||
use libc::fcntl;
|
||||
|
||||
fn into_absolute_path(path_to_sharedir: &str, path_to_file: String) -> String {
|
||||
let path_to_default_config = Path::new(&path_to_file);
|
||||
|
||||
@ -128,9 +130,6 @@ pub struct FuzzerConfig {
|
||||
pub input_buffer_size: usize,
|
||||
pub mem_limit: usize,
|
||||
pub time_limit: Duration,
|
||||
pub threads: usize,
|
||||
pub thread_id: usize,
|
||||
pub cpu_pin_start_at: usize,
|
||||
pub seed_path: Option<String>,
|
||||
pub dict: Vec<Vec<u8>>,
|
||||
pub snapshot_placement: SnapshotPlacement,
|
||||
@ -158,9 +157,6 @@ impl FuzzerConfig{
|
||||
input_buffer_size: config.input_buffer_size,
|
||||
mem_limit: config.mem_limit.or(default.mem_limit).expect("no mem_limit specified"),
|
||||
time_limit: config.time_limit.or(default.time_limit).expect("no time_limit specified"),
|
||||
threads: config.threads.or(default.threads).expect("no threads specified"),
|
||||
thread_id: config.thread_id.or(default.thread_id).expect("no thread_id specified"),
|
||||
cpu_pin_start_at: config.cpu_pin_start_at.or(default.cpu_pin_start_at).expect("no cpu_pin_start_at specified"),
|
||||
seed_path: seed_path_value,
|
||||
dict: config.dict.or(default.dict).expect("no dict specified"),
|
||||
snapshot_placement: config.snapshot_placement.or(default.snapshot_placement).expect("no snapshot_placement specified"),
|
||||
@ -178,10 +174,113 @@ impl FuzzerConfig{
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum QemuNyxRole {
|
||||
/* Standalone mode, snapshot is kept in memory and not serialized. */
|
||||
StandAlone,
|
||||
|
||||
/* Serialize the VM snapshot after the root snapshot has been created.
|
||||
* The serialized snapshot will be stored in the workdir and the snapshot
|
||||
* will later be used by the child processes. */
|
||||
Parent,
|
||||
|
||||
/* Wait for the snapshot to be created by the parent process and
|
||||
* deserialize it from the workdir. This way all child processes can
|
||||
* mmap() the snapshot files and access the snapshot directly via shared memory.
|
||||
* Consequently, this will result in a much lower memory usage compared to spawning
|
||||
* multiple StandAlone-type instances. */
|
||||
Child,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
/* runtime specific configuration */
|
||||
pub struct RuntimeConfig {
|
||||
/* Configurable option to redirect hprintf to a file descriptor.
|
||||
* If None, hprintf will be redirected to stdout via println!().
|
||||
*/
|
||||
hprintf_fd: Option<i32>,
|
||||
|
||||
/* Configurable option to specify the role of the process.
|
||||
* If StandAlone, the process will not serialize the snapshot and keep everything in memory.
|
||||
* If Parent, the process will create a snapshot and serialize it.
|
||||
* If Child, the process will wait for the parent to create a snapshot and deserialize it. */
|
||||
process_role: QemuNyxRole,
|
||||
|
||||
/* Configurable option to reuse a snapshot from a previous run (useful to avoid VM bootstrapping). */
|
||||
reuse_snapshot_path: Option<String>,
|
||||
|
||||
/* enable advanced VM debug mode (such as spawning a VNC server per VM) */
|
||||
debug_mode: bool,
|
||||
|
||||
/* worker_id of the current QEMU Nyx instance */
|
||||
worker_id: usize,
|
||||
}
|
||||
|
||||
impl RuntimeConfig{
|
||||
pub fn new() -> Self {
|
||||
Self{
|
||||
hprintf_fd: None,
|
||||
process_role: QemuNyxRole::StandAlone,
|
||||
reuse_snapshot_path: None,
|
||||
debug_mode: false,
|
||||
worker_id: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hprintf_fd(&self) -> Option<i32> {
|
||||
self.hprintf_fd
|
||||
}
|
||||
|
||||
pub fn process_role(&self) -> &QemuNyxRole {
|
||||
&self.process_role
|
||||
}
|
||||
|
||||
pub fn set_hpintf_fd(&mut self, fd: i32){
|
||||
/* sanitiy check to prevent invalid file descriptors via F_GETFD */
|
||||
unsafe {
|
||||
/* TODO: return error instead of panicking */
|
||||
assert!(fcntl(fd, libc::F_GETFD) != -1);
|
||||
};
|
||||
|
||||
self.hprintf_fd = Some(fd);
|
||||
}
|
||||
|
||||
pub fn set_process_role(&mut self, role: QemuNyxRole){
|
||||
self.process_role = role;
|
||||
}
|
||||
|
||||
pub fn reuse_root_snapshot_path(&self) -> Option<String> {
|
||||
self.reuse_snapshot_path.clone()
|
||||
}
|
||||
|
||||
pub fn set_reuse_snapshot_path(&mut self, path: String){
|
||||
let path = Path::new(&path).canonicalize().unwrap().to_str().unwrap().to_string();
|
||||
self.reuse_snapshot_path = Some(path);
|
||||
}
|
||||
|
||||
pub fn debug_mode(&self) -> bool {
|
||||
self.debug_mode
|
||||
}
|
||||
|
||||
pub fn set_debug_mode(&mut self, debug_mode: bool){
|
||||
self.debug_mode = debug_mode;
|
||||
}
|
||||
|
||||
pub fn worker_id(&self) -> usize {
|
||||
self.worker_id
|
||||
}
|
||||
|
||||
pub fn set_worker_id(&mut self, thread_id: usize){
|
||||
self.worker_id = thread_id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Config {
|
||||
pub runner: FuzzRunnerConfig,
|
||||
pub fuzz: FuzzerConfig,
|
||||
pub runtime: RuntimeConfig,
|
||||
}
|
||||
|
||||
impl Config{
|
||||
@ -189,6 +288,7 @@ impl Config{
|
||||
Self{
|
||||
runner: FuzzRunnerConfig::new_from_loader(&default_config_folder, default.runner, config.runner),
|
||||
fuzz: FuzzerConfig::new_from_loader(&sharedir, default.fuzz, config.fuzz),
|
||||
runtime: RuntimeConfig::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
extern crate serde;
|
||||
extern crate serde_derive;
|
||||
extern crate ron;
|
||||
extern crate libc;
|
||||
|
||||
mod loader;
|
||||
mod config;
|
||||
|
@ -59,9 +59,6 @@ pub struct FuzzerConfigLoader {
|
||||
pub mem_limit: Option<usize>,
|
||||
pub time_limit: Option<Duration>,
|
||||
pub target_binary: Option<String>,
|
||||
pub threads: Option<usize>,
|
||||
pub thread_id: Option<usize>,
|
||||
pub cpu_pin_start_at: Option<usize>,
|
||||
pub seed_path: Option<String>,
|
||||
pub dict: Option<Vec<Vec<u8>>>,
|
||||
pub snapshot_placement: Option<SnapshotPlacement>,
|
||||
|
@ -7,7 +7,7 @@ edition = "2018"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nix = "0.17.0"
|
||||
nix = "0.20.2"
|
||||
time = "0.2.9"
|
||||
subprocess = "0.2.4"
|
||||
libc = "0.2.68"
|
||||
|
@ -10,7 +10,6 @@ use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
extern crate config;
|
||||
use crate::config::{QemuKernelConfig, QemuSnapshotConfig, FuzzerConfig, SnapshotPath};
|
||||
|
||||
fn into_absolute_path(sharedir: &str) -> String{
|
||||
|
||||
@ -24,107 +23,9 @@ fn into_absolute_path(sharedir: &str) -> String{
|
||||
}
|
||||
}
|
||||
|
||||
pub fn qemu_process_new_from_kernel(sharedir: String, cfg: &QemuKernelConfig, fuzz_cfg: &FuzzerConfig) -> Result<QemuProcess, String> {
|
||||
let params = params::KernelVmParams {
|
||||
qemu_binary: cfg.qemu_binary.to_string(),
|
||||
kernel: cfg.kernel.to_string(),
|
||||
sharedir: into_absolute_path(&sharedir),
|
||||
ramfs: cfg.ramfs.to_string(),
|
||||
ram_size: fuzz_cfg.mem_limit,
|
||||
bitmap_size: fuzz_cfg.bitmap_size,
|
||||
debug: cfg.debug,
|
||||
dump_python_code_for_inputs: match fuzz_cfg.dump_python_code_for_inputs{
|
||||
None => false,
|
||||
Some(x) => x,
|
||||
},
|
||||
write_protected_input_buffer: fuzz_cfg.write_protected_input_buffer,
|
||||
cow_primary_size: fuzz_cfg.cow_primary_size,
|
||||
ipt_filters: fuzz_cfg.ipt_filters,
|
||||
input_buffer_size: fuzz_cfg.input_buffer_size,
|
||||
};
|
||||
let qemu_id = fuzz_cfg.thread_id;
|
||||
let qemu_params = params::QemuParams::new_from_kernel(&fuzz_cfg.workdir_path, qemu_id, ¶ms, fuzz_cfg.threads > 1);
|
||||
|
||||
/*
|
||||
if qemu_id == 0{
|
||||
qemu_process::QemuProcess::prepare_workdir(&fuzz_cfg.workdir_path, fuzz_cfg.seed_pattern.clone());
|
||||
}
|
||||
*/
|
||||
pub fn qemu_process_new(sharedir: String, cfg: &config::Config) -> Result<QemuProcess, String> {
|
||||
|
||||
|
||||
let qemu_params = params::QemuParams::new(into_absolute_path(&sharedir), cfg);
|
||||
return qemu_process::QemuProcess::new(qemu_params);
|
||||
}
|
||||
|
||||
pub fn qemu_process_new_from_snapshot(sharedir: String, cfg: &QemuSnapshotConfig, fuzz_cfg: &FuzzerConfig) -> Result<QemuProcess, String> {
|
||||
|
||||
let snapshot_path = match &cfg.snapshot_path{
|
||||
SnapshotPath::Create(_x) => panic!(),
|
||||
SnapshotPath::Reuse(x) => SnapshotPath::Reuse(x.to_string()),
|
||||
SnapshotPath::DefaultPath => {
|
||||
if fuzz_cfg.thread_id == 0 {
|
||||
SnapshotPath::Create(format!("{}/snapshot/",fuzz_cfg.workdir_path))
|
||||
} else {
|
||||
SnapshotPath::Reuse(format!("{}/snapshot/",fuzz_cfg.workdir_path))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let params = params::SnapshotVmParams {
|
||||
qemu_binary: cfg.qemu_binary.to_string(),
|
||||
hda: cfg.hda.to_string(),
|
||||
sharedir: into_absolute_path(&sharedir),
|
||||
presnapshot: cfg.presnapshot.to_string(),
|
||||
ram_size: fuzz_cfg.mem_limit,
|
||||
bitmap_size: fuzz_cfg.bitmap_size,
|
||||
debug: cfg.debug,
|
||||
snapshot_path,
|
||||
dump_python_code_for_inputs: match fuzz_cfg.dump_python_code_for_inputs{
|
||||
None => false,
|
||||
Some(x) => x,
|
||||
},
|
||||
write_protected_input_buffer: fuzz_cfg.write_protected_input_buffer,
|
||||
cow_primary_size: fuzz_cfg.cow_primary_size,
|
||||
ipt_filters: fuzz_cfg.ipt_filters,
|
||||
input_buffer_size: fuzz_cfg.input_buffer_size,
|
||||
};
|
||||
let qemu_id = fuzz_cfg.thread_id;
|
||||
let qemu_params = params::QemuParams::new_from_snapshot(&fuzz_cfg.workdir_path, qemu_id, fuzz_cfg.cpu_pin_start_at, ¶ms, fuzz_cfg.threads > 1);
|
||||
|
||||
return qemu_process::QemuProcess::new(qemu_params);
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
//use crate::aux_buffer::*;
|
||||
use super::params::*;
|
||||
use super::qemu_process::*;
|
||||
//use std::{thread, time};
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let workdir = "/tmp/workdir_test";
|
||||
let params = KernelVmParams {
|
||||
qemu_binary: "/home/kafl/NEW2/QEMU-PT_4.2.0/x86_64-softmmu/qemu-system-x86_64"
|
||||
.to_string(),
|
||||
kernel: "/home/kafl/Target-Components/linux_initramfs/bzImage-linux-4.15-rc7"
|
||||
.to_string(),
|
||||
ramfs: "/home/kafl/Target-Components/linux_initramfs/init.cpio.gz".to_string(),
|
||||
sharedir: "foo! invalid".to_string(),
|
||||
ram_size: 1000,
|
||||
bitmap_size: 0x1 << 16,
|
||||
debug: false,
|
||||
dump_python_code_for_inputs: false,
|
||||
write_protected_input_buffer: false,
|
||||
};
|
||||
let qemu_id = 1;
|
||||
let qemu_params = QemuParams::new_from_kernel(workdir, qemu_id, ¶ms);
|
||||
|
||||
QemuProcess::prepare_workdir(&workdir, None);
|
||||
|
||||
let mut qemu_process = QemuProcess::new(qemu_params);
|
||||
|
||||
for _i in 0..100 {
|
||||
qemu_process.send_payload();
|
||||
}
|
||||
println!("test done");
|
||||
}
|
||||
}
|
||||
|
@ -1,38 +1,4 @@
|
||||
use crate::config::SnapshotPath;
|
||||
use crate::config::IptFilter;
|
||||
|
||||
pub struct KernelVmParams {
|
||||
pub qemu_binary: String,
|
||||
pub kernel: String,
|
||||
pub sharedir: String,
|
||||
pub ramfs: String,
|
||||
pub ram_size: usize,
|
||||
pub bitmap_size: usize,
|
||||
pub debug: bool,
|
||||
|
||||
pub dump_python_code_for_inputs: bool,
|
||||
pub write_protected_input_buffer: bool,
|
||||
pub cow_primary_size: Option<u64>,
|
||||
pub ipt_filters: [IptFilter; 4],
|
||||
pub input_buffer_size: usize,
|
||||
}
|
||||
|
||||
pub struct SnapshotVmParams{
|
||||
pub qemu_binary: String,
|
||||
pub hda: String,
|
||||
pub sharedir: String,
|
||||
pub presnapshot: String,
|
||||
pub snapshot_path: SnapshotPath,
|
||||
pub ram_size: usize,
|
||||
pub bitmap_size: usize,
|
||||
pub debug: bool,
|
||||
|
||||
pub dump_python_code_for_inputs: bool,
|
||||
pub write_protected_input_buffer: bool,
|
||||
pub cow_primary_size: Option<u64>,
|
||||
pub ipt_filters: [IptFilter; 4],
|
||||
pub input_buffer_size: usize,
|
||||
}
|
||||
use crate::{config::{Config, FuzzRunnerConfig, QemuNyxRole}, QemuProcess};
|
||||
|
||||
pub struct QemuParams {
|
||||
pub cmd: Vec<String>,
|
||||
@ -46,34 +12,57 @@ pub struct QemuParams {
|
||||
pub dump_python_code_for_inputs: bool,
|
||||
pub write_protected_input_buffer: bool,
|
||||
pub cow_primary_size: Option<u64>,
|
||||
pub hprintf_fd: Option<i32>,
|
||||
|
||||
}
|
||||
|
||||
impl QemuParams {
|
||||
pub fn new_from_snapshot(workdir: &str, qemu_id: usize, cpu: usize, params: &SnapshotVmParams, create_snapshot_file: bool) -> QemuParams{
|
||||
|
||||
|
||||
pub fn new(sharedir: String, fuzzer_config: &Config) -> QemuParams {
|
||||
|
||||
let mut cmd = vec![];
|
||||
let qemu_id = fuzzer_config.runtime.worker_id();
|
||||
|
||||
let workdir = &fuzzer_config.fuzz.workdir_path;
|
||||
|
||||
let debug = fuzzer_config.runtime.debug_mode();
|
||||
|
||||
let qemu_aux_buffer_filename = format!("{}/aux_buffer_{}", workdir, qemu_id);
|
||||
let control_filename = format!("{}/interface_{}", workdir, qemu_id);
|
||||
|
||||
let mut cmd = vec![];
|
||||
cmd.push(params.qemu_binary.to_string());
|
||||
match fuzzer_config.runner.clone(){
|
||||
FuzzRunnerConfig::QemuKernel(x) => {
|
||||
cmd.push(x.qemu_binary.to_string());
|
||||
cmd.push("-kernel".to_string());
|
||||
cmd.push(x.kernel.to_string());
|
||||
|
||||
cmd.push("-initrd".to_string());
|
||||
cmd.push(x.ramfs.to_string());
|
||||
|
||||
cmd.push("-append".to_string());
|
||||
cmd.push("nokaslr oops=panic nopti ignore_rlimit_data".to_string());
|
||||
},
|
||||
FuzzRunnerConfig::QemuSnapshot(x) => {
|
||||
cmd.push(x.qemu_binary.to_string());
|
||||
cmd.push("-drive".to_string());
|
||||
cmd.push(format!("file={},format=raw,index=0,media=disk", x.hda.to_string()));
|
||||
},
|
||||
}
|
||||
|
||||
cmd.push("-drive".to_string());
|
||||
cmd.push(format!("file={},format=raw,index=0,media=disk", params.hda.to_string()));
|
||||
|
||||
if !params.debug {
|
||||
/* generic QEMU-Nyx parameters */
|
||||
if !debug{
|
||||
cmd.push("-display".to_string());
|
||||
cmd.push("none".to_string());
|
||||
} else {
|
||||
cmd.push("-vnc".to_string());
|
||||
cmd.push(format!(":{}",qemu_id+cpu));
|
||||
cmd.push(format!(":{}",qemu_id));
|
||||
}
|
||||
|
||||
cmd.push("-serial".to_string());
|
||||
if params.debug {
|
||||
if debug {
|
||||
cmd.push("mon:stdio".to_string());
|
||||
} else {
|
||||
cmd.push("stdio".to_string());
|
||||
cmd.push("none".to_string());
|
||||
}
|
||||
|
||||
cmd.push("-enable-kvm".to_string());
|
||||
@ -85,7 +74,7 @@ impl QemuParams {
|
||||
cmd.push("de".to_string());
|
||||
|
||||
cmd.push("-m".to_string());
|
||||
cmd.push(params.ram_size.to_string());
|
||||
cmd.push(fuzzer_config.fuzz.mem_limit.to_string());
|
||||
|
||||
cmd.push("-chardev".to_string());
|
||||
cmd.push(format!(
|
||||
@ -95,23 +84,22 @@ impl QemuParams {
|
||||
|
||||
cmd.push("-device".to_string());
|
||||
let mut nyx_ops = format!("nyx,chardev=nyx_interface");
|
||||
nyx_ops += &format!(",bitmap_size={}", params.bitmap_size);
|
||||
nyx_ops += &format!(",input_buffer_size={}", params.input_buffer_size);
|
||||
nyx_ops += &format!(",bitmap_size={}", fuzzer_config.fuzz.bitmap_size);
|
||||
nyx_ops += &format!(",input_buffer_size={}", fuzzer_config.fuzz.input_buffer_size);
|
||||
nyx_ops += &format!(",worker_id={}", qemu_id);
|
||||
nyx_ops += &format!(",workdir={}", workdir);
|
||||
nyx_ops += &format!(",sharedir={}", params.sharedir);
|
||||
nyx_ops += &format!(",sharedir={}", sharedir);
|
||||
|
||||
|
||||
let mut i = 0;
|
||||
for filter in params.ipt_filters{
|
||||
for filter in fuzzer_config.fuzz.ipt_filters{
|
||||
if filter.a != 0 && filter.b != 0 {
|
||||
nyx_ops += &format!(",ip{}_a={},ip{}_b={}", i, filter.a, i, filter.b);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if params.cow_primary_size.is_some(){
|
||||
nyx_ops += &format!(",cow_primary_size={}", params.cow_primary_size.unwrap());
|
||||
if fuzzer_config.fuzz.cow_primary_size.is_some(){
|
||||
nyx_ops += &format!(",cow_primary_size={}", fuzzer_config.fuzz.cow_primary_size.unwrap());
|
||||
}
|
||||
|
||||
cmd.push(nyx_ops);
|
||||
@ -122,132 +110,75 @@ impl QemuParams {
|
||||
cmd.push("-cpu".to_string());
|
||||
cmd.push("kAFL64-Hypervisor-v1".to_string());
|
||||
|
||||
match ¶ms.snapshot_path {
|
||||
SnapshotPath::Create(path) => {
|
||||
if create_snapshot_file {
|
||||
cmd.push("-fast_vm_reload".to_string());
|
||||
cmd.push(format!("path={},load=off,pre_path={}", path,params.presnapshot));
|
||||
}
|
||||
else{
|
||||
cmd.push("-fast_vm_reload".to_string());
|
||||
cmd.push(format!("path={},load=off,pre_path={},skip_serialization=on", path,params.presnapshot));
|
||||
}
|
||||
},
|
||||
SnapshotPath::Reuse(path) => {
|
||||
cmd.push("-fast_vm_reload".to_string());
|
||||
cmd.push(format!("path={},load=on", path));
|
||||
}
|
||||
SnapshotPath::DefaultPath => panic!(),
|
||||
}
|
||||
|
||||
return QemuParams {
|
||||
cmd,
|
||||
qemu_aux_buffer_filename,
|
||||
control_filename,
|
||||
workdir: workdir.to_string(),
|
||||
qemu_id,
|
||||
bitmap_size: params.bitmap_size,
|
||||
payload_size: params.input_buffer_size,
|
||||
dump_python_code_for_inputs: params.dump_python_code_for_inputs,
|
||||
write_protected_input_buffer: params.write_protected_input_buffer,
|
||||
cow_primary_size: params.cow_primary_size,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn new_from_kernel(workdir: &str, qemu_id: usize, params: &KernelVmParams, create_snapshot_file: bool) -> QemuParams {
|
||||
|
||||
let qemu_aux_buffer_filename = format!("{}/aux_buffer_{}", workdir, qemu_id);
|
||||
let control_filename = format!("{}/interface_{}", workdir, qemu_id);
|
||||
|
||||
let mut cmd = vec![];
|
||||
cmd.push(params.qemu_binary.to_string());
|
||||
cmd.push("-kernel".to_string());
|
||||
cmd.push(params.kernel.to_string());
|
||||
|
||||
cmd.push("-initrd".to_string());
|
||||
cmd.push(params.ramfs.to_string());
|
||||
|
||||
cmd.push("-append".to_string());
|
||||
cmd.push("nokaslr oops=panic nopti ignore_rlimit_data".to_string());
|
||||
|
||||
if !params.debug {
|
||||
cmd.push("-display".to_string());
|
||||
cmd.push("none".to_string());
|
||||
}
|
||||
|
||||
cmd.push("-serial".to_string());
|
||||
if params.debug {
|
||||
cmd.push("mon:stdio".to_string());
|
||||
} else {
|
||||
cmd.push("none".to_string());
|
||||
}
|
||||
|
||||
cmd.push("-enable-kvm".to_string());
|
||||
|
||||
cmd.push("-net".to_string());
|
||||
cmd.push("none".to_string());
|
||||
|
||||
cmd.push("-k".to_string());
|
||||
cmd.push("de".to_string());
|
||||
|
||||
cmd.push("-m".to_string());
|
||||
cmd.push(params.ram_size.to_string());
|
||||
|
||||
|
||||
cmd.push("-chardev".to_string());
|
||||
cmd.push(format!(
|
||||
"socket,server,path={},id=nyx_interface",
|
||||
control_filename
|
||||
));
|
||||
|
||||
cmd.push("-device".to_string());
|
||||
let mut nyx_ops = format!("nyx,chardev=nyx_interface");
|
||||
nyx_ops += &format!(",bitmap_size={}", params.bitmap_size);
|
||||
nyx_ops += &format!(",input_buffer_size={}", params.input_buffer_size);
|
||||
nyx_ops += &format!(",worker_id={}", qemu_id);
|
||||
nyx_ops += &format!(",workdir={}", workdir);
|
||||
nyx_ops += &format!(",sharedir={}", params.sharedir);
|
||||
|
||||
let mut i = 0;
|
||||
for filter in params.ipt_filters{
|
||||
if filter.a != 0 && filter.b != 0 {
|
||||
nyx_ops += &format!(",ip{}_a={:x},ip{}_b={:x}", i, filter.a, i, filter.b);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if params.cow_primary_size.is_some(){
|
||||
nyx_ops += &format!(",cow_primary_size={}", params.cow_primary_size.unwrap());
|
||||
}
|
||||
|
||||
cmd.push(nyx_ops);
|
||||
|
||||
cmd.push("-machine".to_string());
|
||||
cmd.push("kAFL64-v1".to_string());
|
||||
|
||||
cmd.push("-cpu".to_string());
|
||||
cmd.push("kAFL64-Hypervisor-v1,+vmx".to_string());
|
||||
|
||||
if create_snapshot_file {
|
||||
if fuzzer_config.runtime.reuse_root_snapshot_path().is_some() {
|
||||
cmd.push("-fast_vm_reload".to_string());
|
||||
if qemu_id == 0{
|
||||
cmd.push(format!("path={}/snapshot/,load=off", workdir));
|
||||
} else {
|
||||
cmd.push(format!("path={}/snapshot/,load=on", workdir));
|
||||
cmd.push(format!("path={},load=on", fuzzer_config.runtime.reuse_root_snapshot_path().unwrap()));
|
||||
}
|
||||
else{
|
||||
match fuzzer_config.runner.clone(){
|
||||
FuzzRunnerConfig::QemuKernel(_) => {
|
||||
|
||||
match fuzzer_config.runtime.process_role() {
|
||||
QemuNyxRole::StandAlone => {},
|
||||
QemuNyxRole::Parent => {
|
||||
cmd.push("-fast_vm_reload".to_string());
|
||||
cmd.push(format!("path={}/snapshot/,load=off", workdir));
|
||||
},
|
||||
QemuNyxRole::Child => {
|
||||
cmd.push("-fast_vm_reload".to_string());
|
||||
cmd.push(format!("path={}/snapshot/,load=on", workdir));
|
||||
},
|
||||
};
|
||||
},
|
||||
FuzzRunnerConfig::QemuSnapshot(x) => {
|
||||
|
||||
match fuzzer_config.runtime.process_role() {
|
||||
QemuNyxRole::StandAlone => {
|
||||
cmd.push("-fast_vm_reload".to_string());
|
||||
cmd.push(format!("path={}/snapshot/,load=off,pre_path={},skip_serialization=on", workdir, x.presnapshot));
|
||||
|
||||
},
|
||||
QemuNyxRole::Parent => {
|
||||
cmd.push("-fast_vm_reload".to_string());
|
||||
cmd.push(format!("path={}/snapshot/,load=off,pre_path={}", workdir, x.presnapshot));
|
||||
},
|
||||
QemuNyxRole::Child => {
|
||||
cmd.push("-fast_vm_reload".to_string());
|
||||
cmd.push(format!("path={}/snapshot/,load=on,pre_path={}", workdir, x.presnapshot));
|
||||
},
|
||||
};
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
match fuzzer_config.runtime.process_role() {
|
||||
QemuNyxRole::StandAlone | QemuNyxRole::Parent => {
|
||||
assert!(qemu_id == 0);
|
||||
QemuProcess::prepare_workdir(workdir, fuzzer_config.fuzz.seed_path.clone());
|
||||
},
|
||||
QemuNyxRole::Child => {
|
||||
QemuProcess::wait_for_workdir(workdir);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
return QemuParams {
|
||||
cmd,
|
||||
qemu_aux_buffer_filename,
|
||||
control_filename,
|
||||
workdir: workdir.to_string(),
|
||||
qemu_id,
|
||||
bitmap_size: params.bitmap_size,
|
||||
payload_size: params.input_buffer_size,
|
||||
dump_python_code_for_inputs: params.dump_python_code_for_inputs,
|
||||
write_protected_input_buffer: params.write_protected_input_buffer,
|
||||
cow_primary_size: params.cow_primary_size,
|
||||
};
|
||||
bitmap_size: fuzzer_config.fuzz.bitmap_size,
|
||||
payload_size: fuzzer_config.fuzz.input_buffer_size,
|
||||
dump_python_code_for_inputs: match fuzzer_config.fuzz.dump_python_code_for_inputs{
|
||||
None => false,
|
||||
Some(x) => x,
|
||||
},
|
||||
write_protected_input_buffer: fuzzer_config.fuzz.write_protected_input_buffer,
|
||||
cow_primary_size: fuzzer_config.fuzz.cow_primary_size,
|
||||
hprintf_fd: fuzzer_config.runtime.hprintf_fd(),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use core::ffi::c_void;
|
||||
use std::os::unix::prelude::FromRawFd;
|
||||
use std::path::PathBuf;
|
||||
use nix::sys::mman::*;
|
||||
use std::fs;
|
||||
@ -33,8 +34,12 @@ use crate::nyx::mem_barrier::mem_barrier;
|
||||
use crate::nyx::params::QemuParams;
|
||||
|
||||
pub struct QemuProcess {
|
||||
pub process: Child,
|
||||
pub aux: AuxBuffer,
|
||||
|
||||
process: Child,
|
||||
|
||||
/* ptr to the aux buffer */
|
||||
aux: AuxBuffer,
|
||||
|
||||
pub feedback_data: FeedbackBuffer,
|
||||
pub ijon_buffer: &'static mut [u8],
|
||||
pub ctrl: UnixStream,
|
||||
@ -46,6 +51,8 @@ pub struct QemuProcess {
|
||||
shm_work_dir: PathBuf,
|
||||
#[allow(unused)]
|
||||
shm_file_lock: File,
|
||||
|
||||
hprintf_file: Option<File>,
|
||||
}
|
||||
|
||||
fn execute_qemu(ctrl: &mut UnixStream) -> io::Result<()>{
|
||||
@ -168,12 +175,6 @@ impl QemuProcess {
|
||||
let ijon_shared = make_shared_data(&ijon_buffer_shm_f, 0x1000);
|
||||
let ijon_feedback_buffer = make_shared_ijon_data(ijon_buffer_shm_f, 0x1000);
|
||||
|
||||
|
||||
thread::sleep(time::Duration::from_secs(1));
|
||||
|
||||
thread::sleep(time::Duration::from_millis(200*params.qemu_id as u64));
|
||||
|
||||
|
||||
let mut child = if params.dump_python_code_for_inputs{
|
||||
Command::new(¶ms.cmd[0])
|
||||
.args(¶ms.cmd[1..])
|
||||
@ -188,12 +189,6 @@ impl QemuProcess {
|
||||
.expect("failed to execute process")
|
||||
};
|
||||
|
||||
|
||||
thread::sleep(time::Duration::from_secs(1));
|
||||
|
||||
thread::sleep(time::Duration::from_millis(200*params.qemu_id as u64));
|
||||
|
||||
|
||||
let mut control = loop {
|
||||
match UnixStream::connect(¶ms.control_filename) {
|
||||
Ok(stream) => break stream,
|
||||
@ -207,19 +202,15 @@ impl QemuProcess {
|
||||
return Err(format!("cannot launch QEMU-Nyx..."));
|
||||
}
|
||||
|
||||
let aux_shm_f = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(¶ms.qemu_aux_buffer_filename)
|
||||
.expect("couldn't open aux buffer file");
|
||||
aux_shm_f.set_len(0x1000).unwrap();
|
||||
let mut aux_buffer = {
|
||||
let aux_shm_f = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(¶ms.qemu_aux_buffer_filename)
|
||||
.expect("couldn't open aux buffer file");
|
||||
|
||||
let aux_shm_f = OpenOptions::new()
|
||||
.write(true)
|
||||
.read(true)
|
||||
.open(¶ms.qemu_aux_buffer_filename)
|
||||
.expect("couldn't open aux buffer file");
|
||||
let mut aux_buffer = AuxBuffer::new(aux_shm_f);
|
||||
AuxBuffer::new(aux_shm_f)
|
||||
};
|
||||
|
||||
match aux_buffer.validate_header(){
|
||||
Err(x) => {
|
||||
@ -237,12 +228,17 @@ impl QemuProcess {
|
||||
aux_buffer.config.changed = 1;
|
||||
}
|
||||
|
||||
let mut hprintf_file = match params.hprintf_fd {
|
||||
Some(fd) => Some(unsafe { File::from_raw_fd(fd) }),
|
||||
None => None,
|
||||
};
|
||||
|
||||
loop {
|
||||
|
||||
match aux_buffer.result.exec_result_code {
|
||||
NYX_HPRINTF => {
|
||||
let len = aux_buffer.misc.len;
|
||||
print!("{}", String::from_utf8_lossy(&aux_buffer.misc.data[0..len as usize]).yellow());
|
||||
QemuProcess::output_hprintf(&mut hprintf_file, &String::from_utf8_lossy(&aux_buffer.misc.data[0..len as usize]).yellow());
|
||||
},
|
||||
NYX_ABORT => {
|
||||
let len = aux_buffer.misc.len;
|
||||
@ -313,9 +309,32 @@ impl QemuProcess {
|
||||
params,
|
||||
shm_work_dir,
|
||||
shm_file_lock: file_lock,
|
||||
hprintf_file,
|
||||
});
|
||||
}
|
||||
|
||||
fn output_hprintf(hprintf_file: &mut Option<File>, msg: &str){
|
||||
match hprintf_file {
|
||||
Some(ref mut f) => {
|
||||
f.write_fmt(format_args!("{}", msg)).unwrap();
|
||||
},
|
||||
None => {
|
||||
print!("{}", msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn aux_buffer(&self) -> &AuxBuffer{
|
||||
&self.aux
|
||||
}
|
||||
|
||||
pub fn aux_buffer_mut(&mut self) -> &mut AuxBuffer{
|
||||
&mut self.aux
|
||||
}
|
||||
|
||||
pub fn set_hprintf_fd(&mut self, fd: i32){
|
||||
self.hprintf_file = unsafe { Some(File::from_raw_fd(fd)) };
|
||||
}
|
||||
|
||||
pub fn send_payload(&mut self) -> io::Result<()>{
|
||||
let mut old_address: u64 = 0;
|
||||
@ -355,7 +374,7 @@ impl QemuProcess {
|
||||
match self.aux.result.exec_result_code {
|
||||
NYX_HPRINTF => {
|
||||
let len = self.aux.misc.len;
|
||||
print!("{}", String::from_utf8_lossy(&self.aux.misc.data[0..len as usize]).yellow());
|
||||
QemuProcess::output_hprintf(&mut self.hprintf_file, &String::from_utf8_lossy(&self.aux.misc.data[0..len as usize]).yellow());
|
||||
continue;
|
||||
},
|
||||
NYX_ABORT => {
|
||||
@ -550,3 +569,38 @@ impl QemuProcess {
|
||||
(shm_work_dir_path, file_lock)
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function to remove a Nyx workdir safely. Returns an error if
|
||||
* expected sub dirs are missing or the path does not exist */
|
||||
pub fn remove_workdir_safe(workdir: &str) -> Result<(), String> {
|
||||
let folders = vec![
|
||||
"/corpus/normal",
|
||||
"/corpus/crash",
|
||||
"/corpus/kasan",
|
||||
"/corpus/timeout",
|
||||
"/imports",
|
||||
"/seeds",
|
||||
"/snapshot",
|
||||
"/forced_imports",
|
||||
];
|
||||
|
||||
if !Path::new(&format!("{}/", workdir)).exists() {
|
||||
return Err(format!("\"{}/\" does not exists", workdir));
|
||||
}
|
||||
|
||||
/* check if all sub dirs exists */
|
||||
for folder in folders.iter() {
|
||||
if !Path::new(&format!("{}/{}", workdir, folder)).exists() {
|
||||
return Err(format!("\"{}/{}\" does not exists", workdir, folder));
|
||||
}
|
||||
}
|
||||
|
||||
/* remove if all sub dirs exists */
|
||||
for folder in folders.iter() {
|
||||
let _ = fs::remove_dir_all(format!("{}/{}", workdir, folder));
|
||||
}
|
||||
|
||||
let _ = fs::remove_dir_all(workdir);
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -1,3 +1,20 @@
|
||||
/*
|
||||
libnyx FFI API
|
||||
|
||||
Copyright (C) 2021 Sergej Schumilo
|
||||
This file is part of libnyx.
|
||||
|
||||
libnyx is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
libnyx is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with libnyx. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
use libc::c_char;
|
||||
use std::ffi::CStr;
|
||||
use std::ffi::c_void;
|
||||
@ -5,16 +22,34 @@ use std::ffi::c_void;
|
||||
use fuzz_runner::nyx::aux_buffer::{NYX_CRASH, NYX_HPRINTF, NYX_ABORT};
|
||||
use super::*;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_load_config(sharedir: *const c_char) -> *mut c_void {
|
||||
let sharedir_c_str = unsafe {
|
||||
assert!(!sharedir.is_null());
|
||||
CStr::from_ptr(sharedir)
|
||||
/* Helper function to load a C string pointer and return a Rust string. */
|
||||
fn __load_c_string_ptr(pointer: *const c_char) -> String {
|
||||
let c_str = unsafe {
|
||||
assert!(!pointer.is_null());
|
||||
CStr::from_ptr(pointer)
|
||||
};
|
||||
|
||||
let sharedir_r_str = sharedir_c_str.to_str().unwrap();
|
||||
c_str.to_str().unwrap().to_string()
|
||||
}
|
||||
|
||||
let cfg: NyxConfig = match NyxConfig::load(sharedir_r_str){
|
||||
/* Helper function to check if the config pointer is valid.
|
||||
* Turns the pointer into a reference to a config object.
|
||||
*/
|
||||
fn __nyx_config_check_ptr(config: * mut c_void) -> *mut NyxConfig {
|
||||
assert!(!config.is_null());
|
||||
assert!((config as usize) % std::mem::align_of::<NyxConfig>() == 0);
|
||||
|
||||
config as *mut NyxConfig
|
||||
}
|
||||
|
||||
/* Loads a given Nyx share-dir and returns a raw pointer to the Nyx config object.
|
||||
* The pointer is later used to access the config object in other FFI config functions.
|
||||
*/
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_config_load(sharedir: *const c_char) -> *mut c_void {
|
||||
let sharedir_r_str = __load_c_string_ptr(sharedir);
|
||||
|
||||
let cfg: NyxConfig = match NyxConfig::load(&sharedir_r_str){
|
||||
Ok(x) => x,
|
||||
Err(msg) => {
|
||||
println!("[!] libnyx config reader error: {}", msg);
|
||||
@ -25,32 +60,93 @@ pub extern "C" fn nyx_load_config(sharedir: *const c_char) -> *mut c_void {
|
||||
Box::into_raw(Box::new(cfg)) as *mut c_void
|
||||
}
|
||||
|
||||
/* Simple debug function to print the entire config object to stdout. */
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_print_config(config: * mut c_void) {
|
||||
unsafe{
|
||||
assert!(!config.is_null());
|
||||
assert!((config as usize) % std::mem::align_of::<NyxConfig>() == 0);
|
||||
pub extern "C" fn nyx_config_debug(config: * mut c_void) {
|
||||
let cfg = __nyx_config_check_ptr(config);
|
||||
|
||||
let cfg = config as *mut NyxConfig;
|
||||
unsafe{
|
||||
println!("{}", *cfg);
|
||||
}
|
||||
}
|
||||
|
||||
fn nyx_process_start(sharedir: *const c_char, workdir: *const c_char, worker_id: u32, cpu_id: u32, create_snapshot: bool, input_buffer_size: Option<u32>, input_buffer_write_protection: bool) -> * mut NyxProcess {
|
||||
let sharedir_c_str = unsafe {
|
||||
assert!(!sharedir.is_null());
|
||||
CStr::from_ptr(sharedir)
|
||||
};
|
||||
|
||||
let workdir_c_str = unsafe {
|
||||
assert!(!workdir.is_null());
|
||||
CStr::from_ptr(workdir)
|
||||
};
|
||||
/* Simple debug function to print a subset of configurable options to stdout. */
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_config_print(config: * mut c_void) {
|
||||
unsafe{
|
||||
NyxConfig::print(&*__nyx_config_check_ptr(config));
|
||||
}
|
||||
}
|
||||
|
||||
let sharedir_r_str = sharedir_c_str.to_str().unwrap();
|
||||
let workdir_r_str = workdir_c_str.to_str().unwrap();
|
||||
/* FFI function to set the workdir path in the config object. */
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_config_set_workdir_path(config: * mut c_void, workdir: *const c_char) {
|
||||
let workidr_r_str = __load_c_string_ptr(workdir);
|
||||
let cfg = __nyx_config_check_ptr(config);
|
||||
|
||||
unsafe{
|
||||
NyxConfig::set_workdir_path(&mut *cfg, workidr_r_str.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
/* FFI function to set the input buffer size in the config object. */
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_config_set_input_buffer_size(config: * mut c_void, input_buffer_size: u32) {
|
||||
let cfg = __nyx_config_check_ptr(config);
|
||||
|
||||
assert_eq!(input_buffer_size > 0, true);
|
||||
unsafe{
|
||||
NyxConfig::set_input_buffer_size(&mut *cfg, input_buffer_size as usize);
|
||||
}
|
||||
}
|
||||
|
||||
/* FFI function to set the input buffer write protection in the config object. */
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_config_set_input_buffer_write_protection(config: * mut c_void, input_buffer_write_protection: bool) {
|
||||
let cfg = __nyx_config_check_ptr(config);
|
||||
|
||||
unsafe{
|
||||
NyxConfig::set_input_buffer_write_protection(&mut *cfg, input_buffer_write_protection);
|
||||
}
|
||||
}
|
||||
|
||||
/* FFI function to set the hprintf file descriptor in the config object. */
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_config_set_hprintf_fd(config: * mut c_void, hprintf_fd: i32) {
|
||||
let cfg = __nyx_config_check_ptr(config);
|
||||
|
||||
unsafe{
|
||||
NyxConfig::set_hprintf_fd(&mut *cfg, hprintf_fd);
|
||||
}
|
||||
}
|
||||
|
||||
/* FFI function to set the fuzz runner role in the config object. */
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_config_set_process_role(config: * mut c_void, role: NyxProcessRole) {
|
||||
let cfg = __nyx_config_check_ptr(config);
|
||||
|
||||
unsafe{
|
||||
NyxConfig::set_process_role(&mut *cfg, role);
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable snapshot reuse by setting the path to the snapshot folder. */
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_config_set_reuse_snapshot_path(config: * mut c_void, reuse_snapshot_path: *const c_char) {
|
||||
let reuse_snapshot_path_r_str = __load_c_string_ptr(reuse_snapshot_path);
|
||||
let cfg = __nyx_config_check_ptr(config);
|
||||
|
||||
unsafe{
|
||||
NyxConfig::set_reuse_snapshot_path(&mut *cfg, reuse_snapshot_path_r_str.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_new(config: * mut c_void, worker_id: u32) -> * mut NyxProcess {
|
||||
|
||||
match NyxProcess::process_start(sharedir_r_str, workdir_r_str, worker_id, cpu_id, create_snapshot, input_buffer_size, input_buffer_write_protection) {
|
||||
let cfg = __nyx_config_check_ptr(config);
|
||||
|
||||
match NyxProcess::new(unsafe {&mut *(cfg)}, worker_id as usize) {
|
||||
Ok(x) => Box::into_raw(Box::new(x)),
|
||||
Err(msg) => {
|
||||
println!("[!] libnyx failed to initialize QEMU-Nyx: {}", msg);
|
||||
@ -59,106 +155,68 @@ fn nyx_process_start(sharedir: *const c_char, workdir: *const c_char, worker_id:
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_new(sharedir: *const c_char, workdir: *const c_char, cpu_id: u32, input_buffer_size: u32, input_buffer_write_protection: bool) -> * mut NyxProcess {
|
||||
nyx_process_start(sharedir, workdir, 0, cpu_id, false, Some(input_buffer_size), input_buffer_write_protection)
|
||||
}
|
||||
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_new_parent(sharedir: *const c_char, workdir: *const c_char, cpu_id: u32, input_buffer_size: u32, input_buffer_write_protection: bool) -> * mut NyxProcess {
|
||||
nyx_process_start(sharedir, workdir, 0, cpu_id, true, Some(input_buffer_size), input_buffer_write_protection)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_new_child(sharedir: *const c_char, workdir: *const c_char, cpu_id: u32, worker_id: u32) -> * mut NyxProcess {
|
||||
if worker_id == 0 {
|
||||
println!("[!] libnyx failed -> worker_id=0 cannot be used for child processes");
|
||||
std::ptr::null_mut() as *mut NyxProcess
|
||||
}
|
||||
else{
|
||||
nyx_process_start(sharedir, workdir, worker_id, cpu_id, true, None, false)
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function to check if the NyxProcess pointer is valid.
|
||||
* Turns the pointer into a reference to the NyxProcess object.
|
||||
*/
|
||||
fn __nyx_process_check_ptr(nyx_process: * mut NyxProcess) -> *mut NyxProcess {
|
||||
assert!(!nyx_process.is_null());
|
||||
assert!((nyx_process as usize) % std::mem::align_of::<NyxProcess>() == 0);
|
||||
nyx_process as *mut NyxProcess
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_get_aux_buffer(nyx_process: * mut NyxProcess) -> *mut u8 {
|
||||
unsafe{
|
||||
assert!(!nyx_process.is_null());
|
||||
assert!((nyx_process as usize) % std::mem::align_of::<NyxProcess>() == 0);
|
||||
|
||||
return (*nyx_process).aux_buffer_as_mut_ptr();
|
||||
return (*__nyx_process_check_ptr(nyx_process)).aux_buffer_as_mut_ptr();
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_get_input_buffer(nyx_process: * mut NyxProcess) -> *mut u8 {
|
||||
unsafe{
|
||||
assert!(!nyx_process.is_null());
|
||||
assert!((nyx_process as usize) % std::mem::align_of::<NyxProcess>() == 0);
|
||||
|
||||
return (*nyx_process).input_buffer_mut().as_mut_ptr();
|
||||
return (*__nyx_process_check_ptr(nyx_process)).input_buffer_mut().as_mut_ptr();
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_get_bitmap_buffer(nyx_process: * mut NyxProcess) -> *mut u8 {
|
||||
unsafe{
|
||||
assert!(!nyx_process.is_null());
|
||||
assert!((nyx_process as usize) % std::mem::align_of::<NyxProcess>() == 0);
|
||||
|
||||
return (*nyx_process).bitmap_buffer_mut().as_mut_ptr();
|
||||
return (*__nyx_process_check_ptr(nyx_process)).bitmap_buffer_mut().as_mut_ptr();
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_get_bitmap_buffer_size(nyx_process: * mut NyxProcess) -> usize {
|
||||
unsafe{
|
||||
assert!(!nyx_process.is_null());
|
||||
assert!((nyx_process as usize) % std::mem::align_of::<NyxProcess>() == 0);
|
||||
//return (*nyx_process).process.bitmap.len();
|
||||
return (*nyx_process).bitmap_buffer_size();
|
||||
return (*__nyx_process_check_ptr(nyx_process)).bitmap_buffer_size();
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_shutdown(nyx_process: * mut NyxProcess) {
|
||||
unsafe{
|
||||
assert!(!nyx_process.is_null());
|
||||
assert!((nyx_process as usize) % std::mem::align_of::<NyxProcess>() == 0);
|
||||
|
||||
(*nyx_process).shutdown();
|
||||
(*__nyx_process_check_ptr(nyx_process)).shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_option_set_reload_mode(nyx_process: * mut NyxProcess, enable: bool) {
|
||||
unsafe{
|
||||
assert!(!nyx_process.is_null());
|
||||
assert!((nyx_process as usize) % std::mem::align_of::<NyxProcess>() == 0);
|
||||
|
||||
(*nyx_process).option_set_reload_mode(enable);
|
||||
(*__nyx_process_check_ptr(nyx_process)).option_set_reload_mode(enable);
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_option_set_timeout(nyx_process: * mut NyxProcess, timeout_sec: u8, timeout_usec: u32) {
|
||||
unsafe{
|
||||
assert!(!nyx_process.is_null());
|
||||
assert!((nyx_process as usize) % std::mem::align_of::<NyxProcess>() == 0);
|
||||
|
||||
(*nyx_process).option_set_timeout(timeout_sec, timeout_usec);
|
||||
(*__nyx_process_check_ptr(nyx_process)).option_set_timeout(timeout_sec, timeout_usec);
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_option_apply(nyx_process: * mut NyxProcess) {
|
||||
unsafe{
|
||||
assert!(!nyx_process.is_null());
|
||||
assert!((nyx_process as usize) % std::mem::align_of::<NyxProcess>() == 0);
|
||||
|
||||
(*nyx_process).option_apply();
|
||||
(*__nyx_process_check_ptr(nyx_process)).option_apply();
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,10 +224,7 @@ pub extern "C" fn nyx_option_apply(nyx_process: * mut NyxProcess) {
|
||||
pub extern "C" fn nyx_exec(nyx_process: * mut NyxProcess) -> NyxReturnValue {
|
||||
|
||||
unsafe{
|
||||
assert!(!nyx_process.is_null());
|
||||
assert!((nyx_process as usize) % std::mem::align_of::<NyxProcess>() == 0);
|
||||
|
||||
(*nyx_process).exec()
|
||||
(*__nyx_process_check_ptr(nyx_process)).exec()
|
||||
}
|
||||
}
|
||||
|
||||
@ -177,11 +232,8 @@ pub extern "C" fn nyx_exec(nyx_process: * mut NyxProcess) -> NyxReturnValue {
|
||||
pub extern "C" fn nyx_set_afl_input(nyx_process: * mut NyxProcess, buffer: *mut u8, size: u32) {
|
||||
|
||||
unsafe{
|
||||
assert!(!nyx_process.is_null());
|
||||
assert!((nyx_process as usize) % std::mem::align_of::<NyxProcess>() == 0);
|
||||
assert!((buffer as usize) % std::mem::align_of::<u8>() == 0);
|
||||
|
||||
(*nyx_process).set_input_ptr(buffer, size);
|
||||
(*__nyx_process_check_ptr(nyx_process)).set_input_ptr(buffer, size);
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,14 +241,14 @@ pub extern "C" fn nyx_set_afl_input(nyx_process: * mut NyxProcess, buffer: *mut
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_print_aux_buffer(nyx_process: * mut NyxProcess) {
|
||||
unsafe{
|
||||
assert!(!nyx_process.is_null());
|
||||
assert!((nyx_process as usize) % std::mem::align_of::<NyxProcess>() == 0);
|
||||
|
||||
print!("{}", format!("{:#?}", (*nyx_process).process.aux.result));
|
||||
let nyx_process = __nyx_process_check_ptr(nyx_process);
|
||||
|
||||
match (*nyx_process).process.aux.result.exec_result_code {
|
||||
print!("{}", format!("{:#?}", (*nyx_process).process.aux_buffer().result));
|
||||
|
||||
match (*nyx_process).process.aux_buffer().result.exec_result_code {
|
||||
NYX_CRASH | NYX_ABORT | NYX_HPRINTF => {
|
||||
println!("{}", std::str::from_utf8(&(*nyx_process).process.aux.misc.data).unwrap());
|
||||
println!("{}", std::str::from_utf8(&(*nyx_process).process.aux_buffer().misc.data).unwrap());
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
@ -207,12 +259,40 @@ pub extern "C" fn nyx_print_aux_buffer(nyx_process: * mut NyxProcess) {
|
||||
pub extern "C" fn nyx_get_aux_string(nyx_process: * mut NyxProcess, buffer: *mut u8, size: u32) -> u32 {
|
||||
|
||||
unsafe{
|
||||
assert!(!nyx_process.is_null());
|
||||
assert!((nyx_process as usize) % std::mem::align_of::<NyxProcess>() == 0);
|
||||
let nyx_process = __nyx_process_check_ptr(nyx_process);
|
||||
assert!((buffer as usize) % std::mem::align_of::<u8>() == 0);
|
||||
|
||||
let len = std::cmp::min( (*nyx_process).process.aux.misc.len as usize, size as usize);
|
||||
std::ptr::copy((*nyx_process).process.aux.misc.data.as_mut_ptr(), buffer, len);
|
||||
let len = std::cmp::min( (*nyx_process).process.aux_buffer().misc.len as usize, size as usize);
|
||||
std::ptr::copy((*nyx_process).process.aux_buffer_mut().misc.data.as_mut_ptr(), buffer, len);
|
||||
len as u32
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_set_hprintf_fd(nyx_process: * mut NyxProcess, fd: i32) {
|
||||
unsafe{
|
||||
(*__nyx_process_check_ptr(nyx_process)).process.set_hprintf_fd(fd);
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function to remove a given Nyx workdir safely.
|
||||
* This function will return an error if the path does not exist or it does
|
||||
* not appear to be a Nyx workdir (e.g. specific sub directories are
|
||||
* missing). */
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_remove_work_dir(workdir: *const c_char) -> bool {
|
||||
unsafe{
|
||||
let workdir = CStr::from_ptr(workdir).to_str().unwrap();
|
||||
|
||||
match remove_work_dir(workdir) {
|
||||
Ok(_) => {
|
||||
true
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("[!] libnyx failed to remove workdir: {}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,27 @@
|
||||
/*
|
||||
libnyx Rust API
|
||||
|
||||
Copyright (C) 2021 Sergej Schumilo
|
||||
This file is part of libnyx.
|
||||
|
||||
libnyx is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
libnyx is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with libnyx. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
extern crate libc;
|
||||
|
||||
use config::{Config, FuzzRunnerConfig};
|
||||
use config::{Config, FuzzRunnerConfig, QemuNyxRole};
|
||||
|
||||
use fuzz_runner::nyx::qemu_process_new_from_kernel;
|
||||
use fuzz_runner::nyx::qemu_process_new_from_snapshot;
|
||||
use fuzz_runner::nyx::qemu_process::QemuProcess;
|
||||
use fuzz_runner::nyx::aux_buffer::{NYX_SUCCESS, NYX_CRASH, NYX_TIMEOUT, NYX_INPUT_WRITE, NYX_ABORT};
|
||||
use libc::fcntl;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
@ -24,6 +40,14 @@ pub enum NyxReturnValue {
|
||||
Abort, // Abort hypercall called
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub enum NyxProcessRole {
|
||||
StandAlone,
|
||||
Parent,
|
||||
Child,
|
||||
}
|
||||
|
||||
impl fmt::Display for NyxReturnValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
|
||||
@ -48,18 +72,42 @@ pub struct NyxProcess {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NyxConfig {
|
||||
config: Config,
|
||||
sharedir_path: String,
|
||||
}
|
||||
|
||||
impl NyxConfig {
|
||||
|
||||
/* Loads a given Nyx share-dir and returns a result object containing the config object.
|
||||
* The config object is later used to access the config object via specific config functions.
|
||||
*/
|
||||
pub fn load(sharedir: &str) -> Result<NyxConfig, String> {
|
||||
/* TODO: perform some additional sanity checks on the sharedir (such as checking if the bootstrap scripts exist) */
|
||||
match Config::new_from_sharedir(&sharedir){
|
||||
Ok(x) => Ok(NyxConfig{
|
||||
config: x
|
||||
config: x,
|
||||
sharedir_path: sharedir.to_string()
|
||||
}),
|
||||
Err(x) => Err(x),
|
||||
}
|
||||
}
|
||||
|
||||
/* Simple debug function to print the entire config object to stdout. */
|
||||
pub fn print(&self){
|
||||
println!("[*] Nyx config (share-dir: {}):", self.sharedir_path());
|
||||
println!(" - workdir_path -> {}", self.workdir_path());
|
||||
println!(" - input_buffer_size -> {}", self.input_buffer_size());
|
||||
println!(" - input_buffer_write_protection -> {}", self.input_buffer_write_protection());
|
||||
println!(" - hprintf_fd -> {}", self.hprintf_fd());
|
||||
println!(" - process_role: -> {:?}", self.process_role());
|
||||
|
||||
}
|
||||
|
||||
/* Returns the path to the actual sharedir. */
|
||||
pub fn sharedir_path(&self) -> String {
|
||||
self.sharedir_path.clone()
|
||||
}
|
||||
|
||||
/* Returns the path to the configured qemu binary. */
|
||||
pub fn qemu_binary_path(&self) -> Option<String>{
|
||||
let process_cfg= match self.config.runner.clone() {
|
||||
FuzzRunnerConfig::QemuKernel(cfg) => cfg,
|
||||
@ -68,6 +116,7 @@ impl NyxConfig {
|
||||
return Some(process_cfg.qemu_binary);
|
||||
}
|
||||
|
||||
/* Returns the path to the configured kernel image (if Nyx is configured to run in kernel mode). */
|
||||
pub fn kernel_image_path(&self) -> Option<String>{
|
||||
let process_cfg= match self.config.runner.clone() {
|
||||
FuzzRunnerConfig::QemuKernel(cfg) => cfg,
|
||||
@ -76,6 +125,7 @@ impl NyxConfig {
|
||||
return Some(process_cfg.kernel);
|
||||
}
|
||||
|
||||
/* Returns the path to the configured initrd image (if Nyx is configured to run in kernel mode). */
|
||||
pub fn ramfs_image_path(&self) -> Option<String>{
|
||||
let process_cfg= match self.config.runner.clone() {
|
||||
FuzzRunnerConfig::QemuKernel(cfg) => cfg,
|
||||
@ -84,26 +134,100 @@ impl NyxConfig {
|
||||
return Some(process_cfg.ramfs);
|
||||
}
|
||||
|
||||
/* Returns the configured timeout threshold as a std::time::Duration object. */
|
||||
pub fn timeout(&self) -> std::time::Duration {
|
||||
self.config.fuzz.time_limit
|
||||
}
|
||||
|
||||
/* Returns the configured spec path (deprecated). */
|
||||
pub fn spec_path(&self) -> String{
|
||||
self.config.fuzz.spec_path.clone()
|
||||
}
|
||||
|
||||
/* Returns the configured trace bitmap size (might be reconfigured later by the agent). */
|
||||
pub fn bitmap_size(&self) -> usize{
|
||||
self.config.fuzz.bitmap_size
|
||||
}
|
||||
|
||||
/* Returns the configured workdir path. */
|
||||
pub fn workdir_path(&self) -> &str {
|
||||
&self.config.fuzz.workdir_path
|
||||
}
|
||||
|
||||
/* Returns the actual size of the input buffer. */
|
||||
pub fn input_buffer_size(&self) -> usize {
|
||||
self.config.fuzz.input_buffer_size
|
||||
}
|
||||
|
||||
/* Returns the config value of the input buffer write protection of the agent (guest). */
|
||||
pub fn input_buffer_write_protection(&self) -> bool {
|
||||
self.config.fuzz.write_protected_input_buffer
|
||||
}
|
||||
|
||||
/* Sets the path to the workdir. */
|
||||
pub fn set_workdir_path(&mut self, path: String) {
|
||||
self.config.fuzz.workdir_path = path;
|
||||
}
|
||||
|
||||
/* Set the size of the input buffer (must be a multiple of x86_64_PAGE_SIZE -> 4096). */
|
||||
pub fn set_input_buffer_size(&mut self, size: usize) {
|
||||
if size % 0x1000 != 0 {
|
||||
/* TODO: return error */
|
||||
panic!("[ ] Input buffer size must be a multiple of x86_64_PAGE_SIZE (4096)!");
|
||||
}
|
||||
self.config.fuzz.input_buffer_size = size;
|
||||
}
|
||||
|
||||
/* Set the input buffer write protection of the agent (guest). */
|
||||
pub fn set_input_buffer_write_protection(&mut self, write_protected: bool) {
|
||||
self.config.fuzz.write_protected_input_buffer = write_protected;
|
||||
}
|
||||
|
||||
/* Returns the current configured FD to redirect hprintf() calls to (returns -1 if None is set). */
|
||||
pub fn hprintf_fd(&self) -> i32 {
|
||||
/* TODO: fix me */
|
||||
match self.config.runtime.hprintf_fd() {
|
||||
Some(fd) => fd,
|
||||
None => -1,
|
||||
}
|
||||
}
|
||||
|
||||
/* Sets the FD to redirect hprintf() calls to (must be a valid FD and must not be closed after this call). */
|
||||
pub fn set_hprintf_fd(&mut self, fd: i32) {
|
||||
self.config.runtime.set_hpintf_fd(fd);
|
||||
}
|
||||
|
||||
/* Sets the process role of the fuzz runner */
|
||||
pub fn set_process_role(&mut self, role: NyxProcessRole) {
|
||||
let _role = match role {
|
||||
NyxProcessRole::Parent => QemuNyxRole::Parent,
|
||||
NyxProcessRole::Child => QemuNyxRole::Child,
|
||||
NyxProcessRole::StandAlone => QemuNyxRole::StandAlone,
|
||||
};
|
||||
|
||||
self.config.runtime.set_process_role(_role);
|
||||
}
|
||||
|
||||
/* Configures the path to the snapshot file to be reused (optional). */
|
||||
pub fn set_reuse_snapshot_path(&mut self, path: String) {
|
||||
self.config.runtime.set_reuse_snapshot_path(path);
|
||||
}
|
||||
|
||||
/* Returns the currently configured process role of the fuzz runner. */
|
||||
pub fn process_role(&self) -> &QemuNyxRole {
|
||||
self.config.runtime.process_role()
|
||||
}
|
||||
|
||||
/* Returns the current QEMU-Nyx worker ID. */
|
||||
pub fn worker_id(&self) -> usize {
|
||||
self.config.runtime.worker_id()
|
||||
}
|
||||
|
||||
/* Sets the QEMU-Nyx worker ID. */
|
||||
pub fn set_worker_id(&mut self, worker_id: usize) {
|
||||
self.config.runtime.set_worker_id(worker_id);
|
||||
}
|
||||
|
||||
pub fn dict(&self) -> Vec<Vec<u8>> {
|
||||
self.config.fuzz.dict.clone()
|
||||
}
|
||||
@ -117,56 +241,12 @@ impl fmt::Display for NyxConfig {
|
||||
|
||||
impl NyxProcess {
|
||||
|
||||
fn start_process(sharedir: &str, workdir: &str, fuzzer_config: Config, worker_id: u32) -> Result<QemuProcess, String> {
|
||||
pub fn new(config: &mut NyxConfig, worker_id: usize) -> Result<NyxProcess, String> {
|
||||
|
||||
let mut config = fuzzer_config.fuzz;
|
||||
let runner_cfg = fuzzer_config.runner;
|
||||
|
||||
config.workdir_path = format!("{}", workdir);
|
||||
|
||||
let sdir = sharedir.clone();
|
||||
|
||||
if worker_id == 0 {
|
||||
QemuProcess::prepare_workdir(&config.workdir_path, config.seed_path.clone());
|
||||
}
|
||||
else{
|
||||
QemuProcess::wait_for_workdir(&config.workdir_path);
|
||||
}
|
||||
|
||||
match runner_cfg.clone() {
|
||||
FuzzRunnerConfig::QemuSnapshot(cfg) => {
|
||||
qemu_process_new_from_snapshot(sdir.to_string(), &cfg, &config)
|
||||
},
|
||||
FuzzRunnerConfig::QemuKernel(cfg) => {
|
||||
qemu_process_new_from_kernel(sdir.to_string(), &cfg, &config)
|
||||
}
|
||||
}
|
||||
}
|
||||
let sharedir = config.sharedir_path();
|
||||
config.set_worker_id(worker_id);
|
||||
|
||||
fn process_start(sharedir: &str, workdir: &str, worker_id: u32, cpu_id: u32, create_snapshot: bool, input_buffer_size: Option<u32>, input_buffer_write_protection: bool) -> Result<NyxProcess, String> {
|
||||
let mut cfg: Config = match Config::new_from_sharedir(&sharedir){
|
||||
Ok(x) => x,
|
||||
Err(msg) => {
|
||||
return Err(format!("[!] libnyx config reader error: {}", msg));
|
||||
}
|
||||
};
|
||||
|
||||
cfg.fuzz.write_protected_input_buffer = input_buffer_write_protection;
|
||||
|
||||
/* todo: add sanity check */
|
||||
cfg.fuzz.cpu_pin_start_at = cpu_id as usize;
|
||||
|
||||
match input_buffer_size{
|
||||
Some(x) => { cfg.fuzz.input_buffer_size = x as usize; },
|
||||
None => {},
|
||||
}
|
||||
|
||||
cfg.fuzz.thread_id = worker_id as usize;
|
||||
cfg.fuzz.threads = if create_snapshot { 2 as usize } else { 1 as usize };
|
||||
|
||||
cfg.fuzz.workdir_path = format!("{}", workdir);
|
||||
|
||||
match Self::start_process(sharedir, workdir, cfg, worker_id){
|
||||
match fuzz_runner::nyx::qemu_process_new(sharedir.to_string(), &config.config){
|
||||
Ok(x) => Ok(NyxProcess{
|
||||
process: x,
|
||||
}),
|
||||
@ -174,41 +254,9 @@ impl NyxProcess {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_config(sharedir: &str, config: &NyxConfig, worker_id: u32, create_snapshot: bool) -> Result<NyxProcess, String>{
|
||||
let workdir = config.config.fuzz.workdir_path.clone();
|
||||
|
||||
let mut config= config.clone();
|
||||
config.config.fuzz.threads = if create_snapshot { 2 as usize } else { 1 as usize };
|
||||
config.config.fuzz.thread_id = worker_id as usize;
|
||||
|
||||
match Self::start_process(sharedir, &workdir, config.config.clone(), worker_id) {
|
||||
Ok(x) => Ok(NyxProcess{
|
||||
process: x,
|
||||
}),
|
||||
Err(x) => Err(x),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(sharedir: &str, workdir: &str, cpu_id: u32, input_buffer_size: u32, input_buffer_write_protection: bool) -> Result<NyxProcess, String> {
|
||||
Self::process_start(sharedir, workdir, 0, cpu_id, false, Some(input_buffer_size), input_buffer_write_protection)
|
||||
}
|
||||
|
||||
pub fn new_parent(sharedir: &str, workdir: &str, cpu_id: u32, input_buffer_size: u32, input_buffer_write_protection: bool) -> Result<NyxProcess, String> {
|
||||
Self::process_start(sharedir, workdir, 0, cpu_id, true, Some(input_buffer_size), input_buffer_write_protection)
|
||||
}
|
||||
|
||||
pub fn new_child(sharedir: &str, workdir: &str, cpu_id: u32, worker_id: u32) -> Result<NyxProcess, String> {
|
||||
if worker_id == 0 {
|
||||
println!("[!] libnyx failed -> worker_id=0 cannot be used for child processes");
|
||||
Err("worker_id=0 cannot be used for child processes".to_string())
|
||||
}
|
||||
else{
|
||||
Self::process_start(sharedir, workdir, worker_id, cpu_id, true, None, false)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn aux_buffer_as_mut_ptr(&self) -> *mut u8 {
|
||||
std::ptr::addr_of!(self.process.aux.header.magic) as *mut u8
|
||||
std::ptr::addr_of!(self.process.aux_buffer().header.magic) as *mut u8
|
||||
}
|
||||
|
||||
pub fn input_buffer(&self) -> &[u8] {
|
||||
@ -244,48 +292,48 @@ impl NyxProcess {
|
||||
}
|
||||
|
||||
pub fn option_set_reload_mode(&mut self, enable: bool) {
|
||||
self.process.aux.config.reload_mode = if enable {1} else {0};
|
||||
self.process.aux_buffer_mut().config.reload_mode = if enable {1} else {0};
|
||||
}
|
||||
|
||||
pub fn option_set_redqueen_mode(&mut self, enable: bool) {
|
||||
self.process.aux.config.redqueen_mode = if enable {1} else {0};
|
||||
self.process.aux_buffer_mut().config.redqueen_mode = if enable {1} else {0};
|
||||
}
|
||||
|
||||
pub fn option_set_trace_mode(&mut self, enable: bool) {
|
||||
self.process.aux.config.trace_mode = if enable {1} else {0};
|
||||
self.process.aux_buffer_mut().config.trace_mode = if enable {1} else {0};
|
||||
}
|
||||
|
||||
pub fn option_set_delete_incremental_snapshot(&mut self, enable: bool) {
|
||||
self.process.aux.config.discard_tmp_snapshot = if enable {1} else {0};
|
||||
self.process.aux_buffer_mut().config.discard_tmp_snapshot = if enable {1} else {0};
|
||||
}
|
||||
|
||||
pub fn option_set_timeout(&mut self, timeout_sec: u8, timeout_usec: u32) {
|
||||
self.process.aux.config.timeout_sec = timeout_sec;
|
||||
self.process.aux.config.timeout_usec = timeout_usec;
|
||||
self.process.aux_buffer_mut().config.timeout_sec = timeout_sec;
|
||||
self.process.aux_buffer_mut().config.timeout_usec = timeout_usec;
|
||||
}
|
||||
|
||||
pub fn option_apply(&mut self) {
|
||||
self.process.aux.config.changed = 1;
|
||||
self.process.aux_buffer_mut().config.changed = 1;
|
||||
}
|
||||
|
||||
pub fn aux_misc(&self) -> Vec<u8>{
|
||||
self.process.aux.misc.as_slice().to_vec()
|
||||
self.process.aux_buffer().misc.as_slice().to_vec()
|
||||
}
|
||||
|
||||
pub fn aux_tmp_snapshot_created(&self) -> bool {
|
||||
self.process.aux.result.tmp_snapshot_created != 0
|
||||
self.process.aux_buffer().result.tmp_snapshot_created != 0
|
||||
}
|
||||
|
||||
pub fn aux_string(&self) -> String {
|
||||
let len = self.process.aux.misc.len;
|
||||
String::from_utf8_lossy(&self.process.aux.misc.data[0..len as usize]).to_string()
|
||||
let len = self.process.aux_buffer().misc.len;
|
||||
String::from_utf8_lossy(&self.process.aux_buffer().misc.data[0..len as usize]).to_string()
|
||||
}
|
||||
|
||||
pub fn exec(&mut self) -> NyxReturnValue {
|
||||
match self.process.send_payload(){
|
||||
Err(_) => NyxReturnValue::IoError,
|
||||
Ok(_) => {
|
||||
match self.process.aux.result.exec_result_code {
|
||||
match self.process.aux_buffer().result.exec_result_code {
|
||||
NYX_SUCCESS => NyxReturnValue::Normal,
|
||||
NYX_CRASH => NyxReturnValue::Crash,
|
||||
NYX_TIMEOUT => NyxReturnValue::Timeout,
|
||||
@ -307,4 +355,19 @@ impl NyxProcess {
|
||||
pub fn set_input(&mut self, buffer: &[u8], size: u32) {
|
||||
self.set_input_ptr(buffer.as_ptr(), size);
|
||||
}
|
||||
|
||||
pub fn set_hprintf_fd(&mut self, fd: i32) {
|
||||
|
||||
/* sanitiy check to prevent invalid file descriptors via F_GETFD */
|
||||
unsafe {
|
||||
assert!(fcntl(fd, libc::F_GETFD) != -1);
|
||||
};
|
||||
|
||||
self.process.set_hprintf_fd(fd);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn remove_work_dir(workdir: &str) -> Result<(), String> {
|
||||
fuzz_runner::nyx::qemu_process::remove_workdir_safe(workdir)
|
||||
}
|
||||
|
144
libnyx/test.c
144
libnyx/test.c
@ -1,9 +1,14 @@
|
||||
/* Simple test program to test the C-API */
|
||||
|
||||
#include <stdio.h>
|
||||
#include "libnyx.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifndef HEXDUMP_COLS
|
||||
#define HEXDUMP_COLS 16
|
||||
@ -11,90 +16,107 @@
|
||||
|
||||
void hexdump(void *mem, unsigned int len)
|
||||
{
|
||||
unsigned int i, j;
|
||||
|
||||
for(i = 0; i < len + ((len % HEXDUMP_COLS) ? (HEXDUMP_COLS - len % HEXDUMP_COLS) : 0); i++)
|
||||
unsigned int i, j;
|
||||
|
||||
for(i = 0; i < len + ((len % HEXDUMP_COLS) ? (HEXDUMP_COLS - len % HEXDUMP_COLS) : 0); i++)
|
||||
{
|
||||
/* print offset */
|
||||
if(i % HEXDUMP_COLS == 0)
|
||||
{
|
||||
/* print offset */
|
||||
if(i % HEXDUMP_COLS == 0)
|
||||
{
|
||||
printf("0x%06x: ", i);
|
||||
}
|
||||
|
||||
/* print hex data */
|
||||
if(i < len)
|
||||
{
|
||||
printf("%02x ", 0xFF & ((char*)mem)[i]);
|
||||
}
|
||||
else /* end of block, just aligning for ASCII dump */
|
||||
{
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
/* print ASCII dump */
|
||||
if(i % HEXDUMP_COLS == (HEXDUMP_COLS - 1))
|
||||
{
|
||||
for(j = i - (HEXDUMP_COLS - 1); j <= i; j++)
|
||||
{
|
||||
if(j >= len) /* end of block, not really printing */
|
||||
{
|
||||
putchar(' ');
|
||||
}
|
||||
else if(isprint(((char*)mem)[j])) /* printable char */
|
||||
{
|
||||
putchar(0xFF & ((char*)mem)[j]);
|
||||
}
|
||||
else /* other char */
|
||||
{
|
||||
putchar('.');
|
||||
}
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
printf("0x%06x: ", i);
|
||||
}
|
||||
|
||||
/* print hex data */
|
||||
if(i < len)
|
||||
{
|
||||
printf("%02x ", 0xFF & ((char*)mem)[i]);
|
||||
}
|
||||
else /* end of block, just aligning for ASCII dump */
|
||||
{
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
/* print ASCII dump */
|
||||
if(i % HEXDUMP_COLS == (HEXDUMP_COLS - 1))
|
||||
{
|
||||
for(j = i - (HEXDUMP_COLS - 1); j <= i; j++)
|
||||
{
|
||||
if(j >= len) /* end of block, not really printing */
|
||||
{
|
||||
putchar(' ');
|
||||
}
|
||||
else if(isprint(((char*)mem)[j])) /* printable char */
|
||||
{
|
||||
putchar(0xFF & ((char*)mem)[j]);
|
||||
}
|
||||
else /* other char */
|
||||
{
|
||||
putchar('.');
|
||||
}
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define WORKDIR_PATH "/tmp/wdir"
|
||||
|
||||
int main(int argc, char** argv){
|
||||
printf("YO\n");
|
||||
|
||||
void* aux_buffer;
|
||||
|
||||
|
||||
void* aux_buffer;
|
||||
void* nyx_config = nyx_config_load("/tmp/nyx_libxml2/");
|
||||
|
||||
void* ptr = nyx_new("/tmp/nyx_bash/");
|
||||
//nyx_config_debug(nyx_config);
|
||||
|
||||
printf("QEMU Rust Object Pointer: %p\n", ptr);
|
||||
nyx_config_set_workdir_path(nyx_config, WORKDIR_PATH);
|
||||
nyx_config_set_input_buffer_size(nyx_config, 0x2000);
|
||||
|
||||
void* aux = nyx_get_aux_buffer(ptr);
|
||||
int fd = open("/tmp/nyx_test_output.log", O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
printf("Log output FD: %d\n", fd);
|
||||
nyx_config_set_hprintf_fd(nyx_config, fd);
|
||||
|
||||
printf("QEMU Rust Aux Pointer: %p\n", aux);
|
||||
nyx_config_set_process_role(nyx_config, StandAlone);
|
||||
|
||||
//nyx_config_set_reuse_snapshot_path(nyx_config, "/tmp/wdir/snapshot/");
|
||||
|
||||
hexdump(aux, 16);
|
||||
nyx_config_print(nyx_config);
|
||||
nyx_config_debug(nyx_config);
|
||||
|
||||
void* payload = nyx_get_payload_buffer(ptr);
|
||||
void* nyx_runner = nyx_new(nyx_config, 0);
|
||||
|
||||
nyx_set_afl_input(ptr, "HALLO", 5);
|
||||
printf("Nyx runner object pointer: %p\n", nyx_runner);
|
||||
|
||||
void* aux = nyx_get_aux_buffer(nyx_runner);
|
||||
|
||||
printf("QEMU Rust Payload Pointer: %p\n", payload);
|
||||
printf("QEMU rust aux pointer: %p\n", aux);
|
||||
hexdump(aux, 16);
|
||||
|
||||
nyx_option_set_reload_mode(ptr, true);
|
||||
nyx_option_apply(ptr);
|
||||
void* nyx_input = nyx_get_input_buffer(nyx_runner);
|
||||
|
||||
hexdump(payload, 16);
|
||||
nyx_set_afl_input(nyx_runner, "INPUT", 5);
|
||||
printf("QEMU Rust Payload Pointer: %p\n", nyx_input);
|
||||
|
||||
printf("About to run init\n");
|
||||
printf("INIT -> %d\n", nyx_exec(ptr));
|
||||
printf("Init done\n");
|
||||
nyx_option_set_reload_mode(nyx_runner, true);
|
||||
nyx_option_apply(nyx_runner);
|
||||
|
||||
hexdump(nyx_input, 16);
|
||||
|
||||
for(int i = 0; i < 32; i++){
|
||||
nyx_set_afl_input(ptr, "HALLO", 5);
|
||||
printf("nyx_exec -> %d\n", nyx_exec(ptr));
|
||||
//nyx_print_aux_buffer(ptr);
|
||||
}
|
||||
printf("About to run init\n");
|
||||
printf("INIT -> %d\n", nyx_exec(nyx_runner));
|
||||
printf("Init done\n");
|
||||
|
||||
nyx_shutdown(ptr);
|
||||
for(int i = 0; i < 4; i++){
|
||||
nyx_set_afl_input(nyx_runner, "INPUT", 5);
|
||||
printf("nyx_exec -> %d\n", nyx_exec(nyx_runner));
|
||||
nyx_print_aux_buffer(nyx_runner);
|
||||
}
|
||||
|
||||
nyx_shutdown(nyx_runner);
|
||||
|
||||
if(!nyx_remove_work_dir(WORKDIR_PATH) ){
|
||||
printf("Error: Failed to remove work dir\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user