diff --git a/fuzz_runner/src/exitreason.rs b/fuzz_runner/src/exitreason.rs index 2447fb7..656080c 100644 --- a/fuzz_runner/src/exitreason.rs +++ b/fuzz_runner/src/exitreason.rs @@ -22,7 +22,7 @@ impl ExitReason { }; } - pub fn is_normal(&self) -> bool{ + pub fn is_normal(&self) -> bool { use ExitReason::*; match self { Normal(_) => return true, @@ -30,7 +30,7 @@ impl ExitReason { } } - pub fn name(&self) -> &str{ + pub fn name(&self) -> &str { use ExitReason::*; match self { Normal(_) => return "normal", diff --git a/fuzz_runner/src/lib.rs b/fuzz_runner/src/lib.rs index d2d6d37..51c59cb 100644 --- a/fuzz_runner/src/lib.rs +++ b/fuzz_runner/src/lib.rs @@ -1,11 +1,10 @@ extern crate byteorder; +extern crate config; extern crate glob; extern crate nix; extern crate serde_derive; extern crate snafu; extern crate timeout_readwrite; -extern crate config; - pub mod exitreason; pub use exitreason::ExitReason; @@ -14,4 +13,3 @@ pub use exitreason::ExitReason; pub mod nyx; pub use nyx::QemuProcess; - diff --git a/fuzz_runner/src/nyx/aux_buffer.rs b/fuzz_runner/src/nyx/aux_buffer.rs index 2b911e9..1c4ddc4 100644 --- a/fuzz_runner/src/nyx/aux_buffer.rs +++ b/fuzz_runner/src/nyx/aux_buffer.rs @@ -1,24 +1,21 @@ - use nix::sys::mman::*; +use std::fmt; use std::fs::File; use std::os::unix::io::IntoRawFd; -use std::fmt; //use std::sync::atomic::compiler_fence; //use std::sync::atomic::Ordering; use crate::nyx::mem_barrier::mem_barrier; - use derivative::Derivative; /* various Nyx exec codes (aux_buffer.result.exec_result_code) */ -pub const NYX_SUCCESS: u8 = 0; -pub const NYX_CRASH: u8 = 1; -pub const NYX_HPRINTF: u8 = 2; -pub const NYX_TIMEOUT: u8 = 3; -pub const NYX_INPUT_WRITE: u8 = 4; -pub const NYX_ABORT: u8 = 5; - +pub const NYX_SUCCESS: u8 = 0; +pub const NYX_CRASH: u8 = 1; +pub const NYX_HPRINTF: u8 = 2; +pub const NYX_TIMEOUT: u8 = 3; +pub const NYX_INPUT_WRITE: u8 = 4; +pub const NYX_ABORT: u8 = 5; pub const AUX_BUFFER_SIZE: usize = 4096; @@ -50,19 +47,25 @@ pub struct AuxBuffer { } impl AuxBuffer { - pub fn new_readonly(file: File, read_only: bool, size: usize) -> Self { - let mut prot = ProtFlags::PROT_READ; - if !read_only{ - prot |= ProtFlags::PROT_WRITE; + if !read_only { + prot |= ProtFlags::PROT_WRITE; } let flags = MapFlags::MAP_SHARED; let null_addr = std::num::NonZeroUsize::new(0); let aux_buffer_alloc_size = std::num::NonZeroUsize::new(size).unwrap(); unsafe { - let ptr = mmap(null_addr, aux_buffer_alloc_size, prot, flags, file.into_raw_fd(), 0).unwrap(); + let ptr = mmap( + null_addr, + aux_buffer_alloc_size, + prot, + flags, + file.into_raw_fd(), + 0, + ) + .unwrap(); let header = (ptr.add(HEADER_OFFSET) as *mut auxilary_buffer_header_s) .as_mut() .unwrap(); @@ -99,19 +102,33 @@ impl AuxBuffer { /* This is a somewhat hacky way of returning a slice of the total misc area */ pub fn misc_slice(&self) -> &[u8] { - return unsafe { std::slice::from_raw_parts(self.misc as *const auxilary_buffer_misc_s as *const u8, self.size - MISC_OFFSET) }; + return unsafe { + std::slice::from_raw_parts( + self.misc as *const auxilary_buffer_misc_s as *const u8, + self.size - MISC_OFFSET, + ) + }; } /* same... */ pub fn misc_data_slice(&self) -> &[u8] { - return unsafe { std::slice::from_raw_parts((self.misc as *const auxilary_buffer_misc_s as *const u8).offset(std::mem::size_of::() as isize), self.size - MISC_OFFSET - std::mem::size_of::()) }; + return unsafe { + std::slice::from_raw_parts( + (self.misc as *const auxilary_buffer_misc_s as *const u8) + .offset(std::mem::size_of::() as isize), + self.size - MISC_OFFSET - std::mem::size_of::(), + ) + }; } pub fn validate_header(&self) -> Result<(), String> { mem_barrier(); let mgc = self.header.magic; if mgc != AUX_MAGIC { - return Err(format!("aux buffer magic mismatch {} != {}...\n[!] Probably the AUX buffer is corrupted?!", AUX_MAGIC, mgc)); + return Err(format!( + "aux buffer magic mismatch {} != {}...\n[!] Probably the AUX buffer is corrupted?!", + AUX_MAGIC, mgc + )); } let version = self.header.version; if version != QEMU_PT_VERSION { @@ -135,13 +152,12 @@ pub struct auxilary_buffer_header_s { #[repr(C, packed(1))] pub struct auxilary_buffer_cap_s { pub redqueen: u8, - pub agent_timeout_detection: u8, /* agent implements own timeout detection; host timeout detection is still in used, but treshold is increased by x2; */ - pub agent_trace_bitmap: u8, /* agent implements own tracing mechanism; PT tracing is disabled */ - pub agent_ijon_trace_bitmap: u8, /* agent uses the ijon shm buffer */ - - pub agent_input_buffer_size: u32, /* agent requests a custom input buffer size (if the size is 0, the minimum buffer size is used) */ - pub agent_coverage_bitmap_size: u32, /* agent requests a custom coverage bitmap size (if the size is 0, the minimum buffer size is used) */ + pub agent_timeout_detection: u8, /* agent implements own timeout detection; host timeout detection is still in used, but treshold is increased by x2; */ + pub agent_trace_bitmap: u8, /* agent implements own tracing mechanism; PT tracing is disabled */ + pub agent_ijon_trace_bitmap: u8, /* agent uses the ijon shm buffer */ + pub agent_input_buffer_size: u32, /* agent requests a custom input buffer size (if the size is 0, the minimum buffer size is used) */ + pub agent_coverage_bitmap_size: u32, /* agent requests a custom coverage bitmap size (if the size is 0, the minimum buffer size is used) */ } #[derive(Debug, Copy, Clone)] #[repr(C, packed(1))] @@ -164,7 +180,7 @@ pub struct auxilary_buffer_config_s { */ //uint8_t pt_processing_mode; pub protect_payload_buffer: u8, - /* snapshot extension */ + /* snapshot extension */ pub discard_tmp_snapshot: u8, } @@ -186,7 +202,7 @@ pub struct auxilary_buffer_result_s { pub pt_overflow: u8, pub page_not_found: u8, pub tmp_snapshot_created: u8, - #[derivative(Debug="ignore")] + #[derivative(Debug = "ignore")] pub padding_3: u8, pub page_not_found_addr: u64, @@ -195,13 +211,12 @@ pub struct auxilary_buffer_result_s { pub bb_coverage: u32, pub runtime_usec: u32, pub runtime_sec: u32, - } #[repr(C, packed(1))] pub struct auxilary_buffer_misc_s { pub len: u16, - pub data: [u8;MISC_SIZE-2], + pub data: [u8; MISC_SIZE - 2], } fn inspect_bytes(bs: &[u8]) -> String { @@ -216,12 +231,12 @@ fn inspect_bytes(bs: &[u8]) -> String { visible } -impl auxilary_buffer_misc_s{ - pub fn as_slice(&self) -> &[u8]{ +impl auxilary_buffer_misc_s { + pub fn as_slice(&self) -> &[u8] { assert!(self.len as usize <= self.data.len()); return &self.data[0..self.len as usize]; } - pub fn as_string(&self) -> String{ + pub fn as_string(&self) -> String { inspect_bytes(self.as_slice()) } } @@ -230,7 +245,7 @@ impl auxilary_buffer_misc_s{ impl fmt::Debug for auxilary_buffer_misc_s { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("auxilary_buffer_misc_s") - .field("data", &inspect_bytes(self.as_slice())) - .finish() + .field("data", &inspect_bytes(self.as_slice())) + .finish() } -} \ No newline at end of file +} diff --git a/fuzz_runner/src/nyx/ijon_data.rs b/fuzz_runner/src/nyx/ijon_data.rs index 1a4490f..2558a31 100644 --- a/fuzz_runner/src/nyx/ijon_data.rs +++ b/fuzz_runner/src/nyx/ijon_data.rs @@ -1,20 +1,20 @@ #[derive(Debug, Copy, Clone)] #[repr(C, packed(1))] -pub struct InterpreterData{ - pub executed_opcode_num: u32 +pub struct InterpreterData { + pub executed_opcode_num: u32, } #[derive(Copy, Clone)] #[repr(C)] pub struct IjonData { - pub max_data: [u64;256], + pub max_data: [u64; 256], } #[derive(Copy, Clone)] #[repr(C, packed(1))] -pub struct SharedFeedbackData{ +pub struct SharedFeedbackData { pub interpreter: InterpreterData, - pad: [u8; 0x1000/2-std::mem::size_of::()], + pad: [u8; 0x1000 / 2 - std::mem::size_of::()], pub ijon: IjonData, } @@ -22,8 +22,8 @@ pub struct FeedbackBuffer { pub shared: &'static mut SharedFeedbackData, } -impl FeedbackBuffer{ - pub fn new(shared: &'static mut SharedFeedbackData) -> Self{ - Self{shared} +impl FeedbackBuffer { + pub fn new(shared: &'static mut SharedFeedbackData) -> Self { + Self { shared } } -} \ No newline at end of file +} diff --git a/fuzz_runner/src/nyx/mod.rs b/fuzz_runner/src/nyx/mod.rs index 5184d7b..e07e52a 100644 --- a/fuzz_runner/src/nyx/mod.rs +++ b/fuzz_runner/src/nyx/mod.rs @@ -11,21 +11,21 @@ use std::path::PathBuf; extern crate config; -fn into_absolute_path(sharedir: &str) -> String{ - +fn into_absolute_path(sharedir: &str) -> String { let srcdir = PathBuf::from(&sharedir); - if srcdir.is_relative(){ - return fs::canonicalize(&srcdir).unwrap().to_str().unwrap().to_string(); - } - else{ + if srcdir.is_relative() { + return fs::canonicalize(&srcdir) + .unwrap() + .to_str() + .unwrap() + .to_string(); + } else { return sharedir.to_string(); } } pub fn qemu_process_new(sharedir: String, cfg: &config::Config) -> Result { - - let qemu_params = params::QemuParams::new(into_absolute_path(&sharedir), cfg); return qemu_process::QemuProcess::new(qemu_params); } diff --git a/fuzz_runner/src/nyx/params.rs b/fuzz_runner/src/nyx/params.rs index 736253e..f0cb0be 100644 --- a/fuzz_runner/src/nyx/params.rs +++ b/fuzz_runner/src/nyx/params.rs @@ -1,5 +1,8 @@ +use crate::{ + config::{Config, FuzzRunnerConfig, QemuNyxRole}, + QemuProcess, +}; use std::time::Duration; -use crate::{config::{Config, FuzzRunnerConfig, QemuNyxRole}, QemuProcess}; pub struct QemuParams { pub cmd: Vec, @@ -20,12 +23,10 @@ pub struct QemuParams { } impl QemuParams { - pub fn new(sharedir: String, fuzzer_config: &Config) -> QemuParams { - let mut cmd = vec![]; - let qemu_id = fuzzer_config.runtime.worker_id(); - + let qemu_id = fuzzer_config.runtime.worker_id(); + let workdir = &fuzzer_config.fuzz.workdir_path; let debug = fuzzer_config.runtime.debug_mode(); @@ -33,32 +34,32 @@ impl QemuParams { let qemu_aux_buffer_filename = format!("{}/aux_buffer_{}", workdir, qemu_id); let control_filename = format!("{}/interface_{}", workdir, qemu_id); - match fuzzer_config.runner.clone(){ + 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={},index=0,media=disk", x.hda.to_string())); - }, + } } /* generic QEMU-Nyx parameters */ - if !debug{ + if !debug { cmd.push("-display".to_string()); cmd.push("none".to_string()); } else { cmd.push("-vnc".to_string()); - cmd.push(format!(":{}",qemu_id)); + cmd.push(format!(":{}", qemu_id)); } cmd.push("-serial".to_string()); @@ -91,27 +92,36 @@ impl QemuParams { "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={}", fuzzer_config.fuzz.bitmap_size); - nyx_ops += &format!(",input_buffer_size={}", fuzzer_config.fuzz.input_buffer_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={}", sharedir); - nyx_ops += &format!(",aux_buffer_size={}", fuzzer_config.runtime.aux_buffer_size()); + nyx_ops += &format!( + ",aux_buffer_size={}", + fuzzer_config.runtime.aux_buffer_size() + ); nyx_ops += &format!(",dump_pt_trace={}", true); let mut i = 0; - for filter in fuzzer_config.fuzz.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; + i += 1; } } - if fuzzer_config.fuzz.cow_primary_size.is_some(){ - nyx_ops += &format!(",cow_primary_size={}", fuzzer_config.fuzz.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); @@ -131,48 +141,60 @@ impl QemuParams { if fuzzer_config.runtime.reuse_root_snapshot_path().is_some() { cmd.push("-fast_vm_reload".to_string()); - cmd.push(format!("path={},load=on", fuzzer_config.runtime.reuse_root_snapshot_path().unwrap())); - } - else{ - match fuzzer_config.runner.clone(){ + 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 => { cmd.push("-fast_vm_reload".to_string()); - cmd.push(format!("path={}/snapshot/,load=off,skip_serialization=on", workdir)); - }, + cmd.push(format!( + "path={}/snapshot/,load=off,skip_serialization=on", + workdir + )); + } 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()); if x.presnapshot.is_empty() { - cmd.push(format!("path={}/snapshot/,load=off,skip_serialization=on", workdir)); + cmd.push(format!( + "path={}/snapshot/,load=off,skip_serialization=on", + workdir + )); } else { - cmd.push(format!("path={}/snapshot/,load=off,pre_path={},skip_serialization=on", workdir, x.presnapshot)); + 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)); - }, + 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", workdir)); - }, + } }; - }, + } } } @@ -180,13 +202,12 @@ impl QemuParams { 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, @@ -195,7 +216,7 @@ impl QemuParams { qemu_id, 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{ + dump_python_code_for_inputs: match fuzzer_config.fuzz.dump_python_code_for_inputs { None => false, Some(x) => x, }, @@ -203,8 +224,7 @@ impl QemuParams { cow_primary_size: fuzzer_config.fuzz.cow_primary_size, hprintf_fd: fuzzer_config.runtime.hprintf_fd(), aux_buffer_size: fuzzer_config.runtime.aux_buffer_size(), - time_limit: fuzzer_config.fuzz.time_limit - } + time_limit: fuzzer_config.fuzz.time_limit, + }; } - } diff --git a/fuzz_runner/src/nyx/qemu_process.rs b/fuzz_runner/src/nyx/qemu_process.rs index d63cea7..0469572 100644 --- a/fuzz_runner/src/nyx/qemu_process.rs +++ b/fuzz_runner/src/nyx/qemu_process.rs @@ -1,20 +1,20 @@ -use std::os::unix::prelude::FromRawFd; -use std::path::PathBuf; +use fs4::FileExt; use nix::sys::mman::*; use std::fs; -use std::io; use std::fs::{File, OpenOptions}; +use std::io; use std::io::prelude::*; use std::os::unix::fs::symlink; -use std::os::unix::io::IntoRawFd; use std::os::unix::io::AsRawFd; +use std::os::unix::io::IntoRawFd; use std::os::unix::net::UnixStream; +use std::os::unix::prelude::FromRawFd; use std::path::Path; +use std::path::PathBuf; +use std::process; use std::process::Child; use std::process::Command; use std::{thread, time}; -use std::process; -use fs4::FileExt; use nix::unistd::gettid; @@ -24,16 +24,16 @@ extern crate colored; // not needed in Rust 2018 use colored::*; - use crate::nyx::aux_buffer::AuxBuffer; -use crate::nyx::aux_buffer::{NYX_SUCCESS, NYX_CRASH, NYX_HPRINTF, NYX_TIMEOUT, NYX_ABORT, NYX_INPUT_WRITE}; +use crate::nyx::aux_buffer::{ + NYX_ABORT, NYX_CRASH, NYX_HPRINTF, NYX_INPUT_WRITE, NYX_SUCCESS, NYX_TIMEOUT, +}; -use crate::nyx::ijon_data::{SharedFeedbackData, FeedbackBuffer}; +use crate::nyx::ijon_data::{FeedbackBuffer, SharedFeedbackData}; use crate::nyx::mem_barrier::mem_barrier; use crate::nyx::params::QemuParams; pub struct QemuProcess { - process: Child, /* ptr to the aux buffer */ @@ -54,18 +54,18 @@ pub struct QemuProcess { hprintf_file: Option, } -fn execute_qemu(ctrl: &mut UnixStream) -> io::Result<()>{ +fn execute_qemu(ctrl: &mut UnixStream) -> io::Result<()> { ctrl.write_all(&[120_u8])?; Ok(()) } -fn wait_qemu(ctrl: &mut UnixStream) -> io::Result<()>{ +fn wait_qemu(ctrl: &mut UnixStream) -> io::Result<()> { let mut buf = [0]; ctrl.read_exact(&mut buf)?; Ok(()) } -fn run_qemu(ctrl: &mut UnixStream) -> io::Result<()>{ +fn run_qemu(ctrl: &mut UnixStream) -> io::Result<()> { execute_qemu(ctrl)?; wait_qemu(ctrl)?; Ok(()) @@ -96,11 +96,10 @@ fn make_shared_ijon_data(file: File, size: usize) -> FeedbackBuffer { } impl QemuProcess { - pub fn new(params: QemuParams) -> Result { Self::prepare_redqueen_workdir(¶ms.workdir, params.qemu_id); - if params.qemu_id == 0{ + if params.qemu_id == 0 { println!("[!] libnyx: spawning qemu with:\n {}", params.cmd.join(" ")); } @@ -116,8 +115,7 @@ impl QemuProcess { .open(&shm_work_dir_path) .expect("couldn't open bitmap file"); - - if Path::new(&format!("{}/bitmap_{}", params.workdir, params.qemu_id)).exists(){ + if Path::new(&format!("{}/bitmap_{}", params.workdir, params.qemu_id)).exists() { fs::remove_file(format!("{}/bitmap_{}", params.workdir, params.qemu_id)).unwrap(); } @@ -137,8 +135,7 @@ impl QemuProcess { .open(&shm_work_dir_path) .expect("couldn't open bitmap file"); - - if Path::new(&format!("{}/ijon_{}", params.workdir, params.qemu_id)).exists(){ + if Path::new(&format!("{}/ijon_{}", params.workdir, params.qemu_id)).exists() { fs::remove_file(format!("{}/ijon_{}", params.workdir, params.qemu_id)).unwrap(); } @@ -158,7 +155,7 @@ impl QemuProcess { .open(&shm_work_dir_path) .expect("couldn't open payload file"); - if Path::new(&format!("{}/payload_{}", params.workdir, params.qemu_id)).exists(){ + if Path::new(&format!("{}/payload_{}", params.workdir, params.qemu_id)).exists() { fs::remove_file(format!("{}/payload_{}", params.workdir, params.qemu_id)).unwrap(); } @@ -178,26 +175,23 @@ 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); - let mut child = if params.dump_python_code_for_inputs{ + let mut child = if params.dump_python_code_for_inputs { Command::new(¶ms.cmd[0]) - .args(¶ms.cmd[1..]) - .env("DUMP_PAYLOAD_MODE", "TRUE") - .spawn() - .expect("failed to execute process") - } - else{ + .args(¶ms.cmd[1..]) + .env("DUMP_PAYLOAD_MODE", "TRUE") + .spawn() + .expect("failed to execute process") + } else { Command::new(¶ms.cmd[0]) - .args(¶ms.cmd[1..]) - .spawn() - .expect("failed to execute process") + .args(¶ms.cmd[1..]) + .spawn() + .expect("failed to execute process") }; let mut control = loop { match UnixStream::connect(¶ms.control_filename) { Ok(stream) => break stream, - _ => { - thread::sleep(time::Duration::from_millis(1)) - }, + _ => thread::sleep(time::Duration::from_millis(1)), } }; @@ -215,15 +209,15 @@ impl QemuProcess { AuxBuffer::new(aux_shm_f, params.aux_buffer_size) }; - match aux_buffer.validate_header(){ + match aux_buffer.validate_header() { Err(x) => { child.kill().unwrap(); child.wait().unwrap(); return Err(x); - }, - Ok(_) => {}, + } + Ok(_) => {} } - if params.write_protected_input_buffer{ + if params.write_protected_input_buffer { if params.qemu_id == 0 { println!("[!] libnyx: input buffer is write protected"); } @@ -232,20 +226,25 @@ impl QemuProcess { } let mut hprintf_file = match params.hprintf_fd { - Some(fd) => Some(unsafe { File::from_raw_fd(fd) }), + Some(fd) => Some(unsafe { File::from_raw_fd(fd) }), None => None, - }; + }; loop { - match aux_buffer.result.exec_result_code { - NYX_HPRINTF => { + NYX_HPRINTF => { let len = aux_buffer.misc.len; - QemuProcess::output_hprintf(&mut hprintf_file, &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; - let msg = format!("agent abort() -> \n\t{}", String::from_utf8_lossy(&aux_buffer.misc.data[0..len as usize]).red()); + let msg = format!( + "agent abort() -> \n\t{}", + String::from_utf8_lossy(&aux_buffer.misc.data[0..len as usize]).red() + ); /* get rid of this process */ child.kill().unwrap(); @@ -253,7 +252,7 @@ impl QemuProcess { return Err(msg); } - NYX_SUCCESS => {}, + NYX_SUCCESS => {} x => { panic!(" -> unkown type ? {}", x); } @@ -262,9 +261,9 @@ impl QemuProcess { if aux_buffer.result.state == 3 { break; } - if run_qemu(&mut control).is_err(){ + if run_qemu(&mut control).is_err() { return Err(format!("failed to establish fuzzing loop...")); - } + } } let mut bitmap_size = params.bitmap_size as usize; @@ -272,17 +271,23 @@ impl QemuProcess { if aux_buffer.cap.agent_coverage_bitmap_size != 0 { //let file_len = bitmap_shm_f.metadata().unwrap().len(); bitmap_size = aux_buffer.cap.agent_coverage_bitmap_size as usize; - if aux_buffer.cap.agent_coverage_bitmap_size as usize > bitmap_shared.len(){ + if aux_buffer.cap.agent_coverage_bitmap_size as usize > bitmap_shared.len() { //println!("[!] libnyx: agent requests a differnt coverage bitmap size: {:x} (current: {:x})", aux_buffer.cap.agent_coverage_bitmap_size as u32, file_len); - bitmap_shared = make_shared_data(&bitmap_shm_f, aux_buffer.cap.agent_coverage_bitmap_size as usize); + bitmap_shared = make_shared_data( + &bitmap_shm_f, + aux_buffer.cap.agent_coverage_bitmap_size as usize, + ); } } let mut input_buffer_size = params.payload_size as usize; if aux_buffer.cap.agent_input_buffer_size != 0 { input_buffer_size = aux_buffer.cap.agent_input_buffer_size as usize; - if aux_buffer.cap.agent_input_buffer_size as usize > payload_shared.len(){ - payload_shared = make_shared_data(&payload_shm_f, aux_buffer.cap.agent_input_buffer_size as usize); + if aux_buffer.cap.agent_input_buffer_size as usize > payload_shared.len() { + payload_shared = make_shared_data( + &payload_shm_f, + aux_buffer.cap.agent_input_buffer_size as usize, + ); } } @@ -316,37 +321,37 @@ impl QemuProcess { }); } - fn output_hprintf(hprintf_file: &mut Option, msg: &str){ + fn output_hprintf(hprintf_file: &mut Option, 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{ + pub fn aux_buffer(&self) -> &AuxBuffer { &self.aux } - pub fn aux_buffer_mut(&mut self) -> &mut AuxBuffer{ + pub fn aux_buffer_mut(&mut self) -> &mut AuxBuffer { &mut self.aux } - pub fn set_hprintf_fd(&mut self, fd: i32){ + 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<()>{ + pub fn send_payload(&mut self) -> io::Result<()> { let mut old_address: u64 = 0; loop { mem_barrier(); match run_qemu(&mut self.ctrl) { Err(x) => return Err(x), - Ok(_) => {}, + Ok(_) => {} } mem_barrier(); @@ -362,31 +367,37 @@ impl QemuProcess { mem_barrier(); match run_qemu(&mut self.ctrl) { Err(x) => return Err(x), - Ok(_) => {}, + Ok(_) => {} } mem_barrier(); continue; - } - else{ + } else { println!("libnyx: cannot dump missing page -> {:x}", v); } } - + match self.aux.result.exec_result_code { - NYX_HPRINTF => { + NYX_HPRINTF => { let len = self.aux.misc.len; - QemuProcess::output_hprintf(&mut self.hprintf_file, &String::from_utf8_lossy(&self.aux.misc_data_slice()[0..len as usize]).yellow()); + QemuProcess::output_hprintf( + &mut self.hprintf_file, + &String::from_utf8_lossy(&self.aux.misc_data_slice()[0..len as usize]) + .yellow(), + ); continue; - }, - NYX_ABORT => { + } + NYX_ABORT => { let len = self.aux.misc.len; - println!("[!] libnyx: agent abort() -> \"{}\"", String::from_utf8_lossy(&self.aux.misc_data_slice()[0..len as usize]).red()); + println!( + "[!] libnyx: agent abort() -> \"{}\"", + String::from_utf8_lossy(&self.aux.misc_data_slice()[0..len as usize]).red() + ); break; - }, - NYX_SUCCESS | NYX_CRASH | NYX_INPUT_WRITE | NYX_TIMEOUT => { + } + NYX_SUCCESS | NYX_CRASH | NYX_INPUT_WRITE | NYX_TIMEOUT => { break; - }, + } x => { panic!("[!] libnyx: ERROR -> unkown Nyx exec result code: {}", x); } @@ -395,7 +406,7 @@ impl QemuProcess { Ok(()) } - pub fn set_timeout(&mut self, timeout: std::time::Duration){ + pub fn set_timeout(&mut self, timeout: std::time::Duration) { self.aux.config.timeout_sec = timeout.as_secs() as u8; self.aux.config.timeout_usec = timeout.subsec_micros(); self.aux.config.changed = 1; @@ -405,18 +416,41 @@ impl QemuProcess { self.process.wait().unwrap(); } - fn remove_shm_work_dir(&mut self){ - + fn remove_shm_work_dir(&mut self) { /* move originals into workdir (in case we need the data to debug stuff) */ let shm_path = self.shm_work_dir.to_str().unwrap(); - fs::remove_file(&format!("{}/bitmap_{}", &self.params.workdir, self.params.qemu_id)).unwrap(); - fs::copy(&format!("{}/bitmap", shm_path), &format!("{}/bitmap_{}", &self.params.workdir, self.params.qemu_id)).unwrap(); + fs::remove_file(&format!( + "{}/bitmap_{}", + &self.params.workdir, self.params.qemu_id + )) + .unwrap(); + fs::copy( + &format!("{}/bitmap", shm_path), + &format!("{}/bitmap_{}", &self.params.workdir, self.params.qemu_id), + ) + .unwrap(); - fs::remove_file(&format!("{}/payload_{}", &self.params.workdir, self.params.qemu_id)).unwrap(); - fs::copy(&format!("{}/input", shm_path), &format!("{}/payload_{}", &self.params.workdir, self.params.qemu_id)).unwrap(); + fs::remove_file(&format!( + "{}/payload_{}", + &self.params.workdir, self.params.qemu_id + )) + .unwrap(); + fs::copy( + &format!("{}/input", shm_path), + &format!("{}/payload_{}", &self.params.workdir, self.params.qemu_id), + ) + .unwrap(); - fs::remove_file(&format!("{}/ijon_{}", &self.params.workdir, self.params.qemu_id)).unwrap(); - fs::copy(&format!("{}/ijon", shm_path), &format!("{}/ijon_{}", &self.params.workdir, self.params.qemu_id)).unwrap(); + fs::remove_file(&format!( + "{}/ijon_{}", + &self.params.workdir, self.params.qemu_id + )) + .unwrap(); + fs::copy( + &format!("{}/ijon", shm_path), + &format!("{}/ijon_{}", &self.params.workdir, self.params.qemu_id), + ) + .unwrap(); /* remove this shm directory */ fs::remove_dir_all(&self.shm_work_dir).unwrap(); @@ -429,17 +463,17 @@ impl QemuProcess { self.remove_shm_work_dir(); } - pub fn wait_for_workdir(workdir: &str){ + pub fn wait_for_workdir(workdir: &str) { println!("[!] libnyx: waiting for workdir to be created by parent process..."); - + let files = vec![ "page_cache.lock", "page_cache.addr", "page_cache.addr", - "snapshot/fast_snapshot.qemu_state" + "snapshot/fast_snapshot.qemu_state", ]; for file in files.iter() { - while !Path::new(&format!("{}/{}", workdir, file)).exists(){ + while !Path::new(&format!("{}/{}", workdir, file)).exists() { thread::sleep(time::Duration::from_secs(1)); } } @@ -483,62 +517,70 @@ impl QemuProcess { .open(format!("{}/page_cache.addr", workdir)) .unwrap(); - OpenOptions::new().create(true).write(true).open(format!("{}/program", workdir)).unwrap(); + OpenOptions::new() + .create(true) + .write(true) + .open(format!("{}/program", workdir)) + .unwrap(); //println!("IMPORT STUFF FOR {:?}", seed_path); if let Some(path) = seed_path { let pattern = format!("{}/*", path); //println!("IMPORT STUFF FOR {}", pattern); - for (i,p) in glob::glob(&pattern).expect("couldn't glob seed pattern??").enumerate() + for (i, p) in glob::glob(&pattern) + .expect("couldn't glob seed pattern??") + .enumerate() { - let src = p.unwrap_or_else(|e| panic!("invalid seed path found {:?}",e)); + let src = p.unwrap_or_else(|e| panic!("invalid seed path found {:?}", e)); //println!("import {} to {}/seeds/seed_{}",src.to_string_lossy(), workdir,i); - let dst = format!("{}/seeds/seed_{}.bin",workdir, i); - fs::copy(&src, &dst).unwrap_or_else(|e| panic!("couldn't copy seed {} to {} {:?}",src.to_string_lossy(),dst,e)); + let dst = format!("{}/seeds/seed_{}.bin", workdir, i); + fs::copy(&src, &dst).unwrap_or_else(|e| { + panic!( + "couldn't copy seed {} to {} {:?}", + src.to_string_lossy(), + dst, + e + ) + }); } } } fn prepare_redqueen_workdir(workdir: &str, qemu_id: usize) { fs::create_dir_all(format!("{}/redqueen_workdir_{}", workdir, qemu_id)) - .expect("couldn't initialize workdir"); + .expect("couldn't initialize workdir"); } - fn remove_unused_shm_work_dirs(){ + fn remove_unused_shm_work_dirs() { /* find and remove orphaned Nyx shm workdirs in /dev/shm */ - for p in glob::glob(&format!("/dev/shm/nyx_*")).expect("couldn't glob??"){ + for p in glob::glob(&format!("/dev/shm/nyx_*")).expect("couldn't glob??") { let mut path = p.unwrap(); - + path.push("lock"); - if path.exists(){ + if path.exists() { + let file_lock = match OpenOptions::new().read(true).open(&path) { + Err(x) => { + println!("Warning: {}", x); + Err(x) + } + x => x, + }; - let file_lock = match OpenOptions::new() - .read(true) - .open(&path){ - Err(x) => { - println!("Warning: {}", x); - Err(x) - }, - x => { - x - }, - }; - - if file_lock.is_ok(){ + if file_lock.is_ok() { path.pop(); - match file_lock.unwrap().try_lock_exclusive(){ + match file_lock.unwrap().try_lock_exclusive() { Ok(_) => { if path.starts_with("/dev/shm/") { - match fs::remove_dir_all(path){ + match fs::remove_dir_all(path) { Err(x) => { println!("Warning: {}", x); - }, - _ => {}, + } + _ => {} } } - }, - Err(_) => {}, + } + Err(_) => {} } } } @@ -572,7 +614,7 @@ impl QemuProcess { } } -/* Helper function to remove a Nyx workdir safely. Returns an error if +/* 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![