add AFL++ support and other improvements
This commit is contained in:
parent
1927a0ab83
commit
a199ed31e7
@ -18,6 +18,12 @@ fn into_absolute_path(path_to_sharedir: &str, path_to_file: String) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Serialize, Deserialize)]
|
||||
pub struct IptFilter {
|
||||
pub a: u64,
|
||||
pub b: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct QemuKernelConfig {
|
||||
pub qemu_binary: String,
|
||||
@ -132,6 +138,7 @@ pub struct FuzzerConfig {
|
||||
pub exit_after_first_crash: bool,
|
||||
pub write_protected_input_buffer: bool,
|
||||
pub cow_primary_size: Option<u64>,
|
||||
pub ipt_filters: [IptFilter;4],
|
||||
}
|
||||
impl FuzzerConfig{
|
||||
pub fn new_from_loader(sharedir: &str, default: FuzzerConfigLoader, config: FuzzerConfigLoader) -> Self {
|
||||
@ -161,6 +168,12 @@ impl FuzzerConfig{
|
||||
exit_after_first_crash: config.exit_after_first_crash.unwrap_or(default.exit_after_first_crash.unwrap_or(false)),
|
||||
write_protected_input_buffer: config.write_protected_input_buffer,
|
||||
cow_primary_size: if config.cow_primary_size != 0 { Some( config.cow_primary_size as u64) } else { None },
|
||||
ipt_filters: [
|
||||
config.ip0,
|
||||
config.ip1,
|
||||
config.ip2,
|
||||
config.ip3,
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -179,18 +192,38 @@ impl Config{
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_from_sharedir(sharedir: &str) -> Self {
|
||||
pub fn new_from_sharedir(sharedir: &str) -> Result<Self, String> {
|
||||
let path_to_config = format!("{}/config.ron", sharedir);
|
||||
let cfg_file = File::open(&path_to_config).expect("could not open config file");
|
||||
let mut cfg: ConfigLoader = ron::de::from_reader(cfg_file).unwrap();
|
||||
|
||||
let default_path = into_absolute_path(sharedir, cfg.include_default_config_path.unwrap());
|
||||
let cfg_file = match File::open(&path_to_config){
|
||||
Ok(x) => {x},
|
||||
Err(_) => return Err(format!("file or folder not found ({})!", path_to_config)),
|
||||
};
|
||||
|
||||
let mut cfg: ConfigLoader = match ron::de::from_reader(cfg_file){
|
||||
Ok(x) => {x},
|
||||
Err(x) => return Err(format!("invalid configuration ({})!", x)),
|
||||
};
|
||||
|
||||
let include_default_config_path = match cfg.include_default_config_path{
|
||||
Some(x) => {x},
|
||||
None => return Err(format!("no path to default configuration given!")),
|
||||
};
|
||||
|
||||
let default_path = into_absolute_path(sharedir, include_default_config_path);
|
||||
let default_config_folder = Path::new(&default_path).parent().unwrap().to_str().unwrap();
|
||||
cfg.include_default_config_path = Some(default_path.clone());
|
||||
|
||||
let default_file = File::open(cfg.include_default_config_path.as_ref().expect("no default config given")).expect("could not open config file");
|
||||
let default: ConfigLoader = ron::de::from_reader(default_file).unwrap();
|
||||
let default_file = match File::open(default_path.clone()){
|
||||
Ok(x) => x,
|
||||
Err(_) => return Err(format!("default config not found ({})!", default_path)),
|
||||
};
|
||||
|
||||
let default: ConfigLoader = match ron::de::from_reader(default_file){
|
||||
Ok(x) => {x},
|
||||
Err(x) => return Err(format!("invalid default configuration ({})!", x)),
|
||||
};
|
||||
|
||||
Self::new_from_loader(&sharedir, &default_config_folder, default, cfg)
|
||||
Ok(Self::new_from_loader(&sharedir, &default_config_folder, default, cfg))
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,15 @@ pub struct FuzzerConfigLoader {
|
||||
#[serde(default = "default_cow_primary_size")]
|
||||
pub cow_primary_size: u64,
|
||||
|
||||
#[serde(default = "default_ipt_filter")]
|
||||
pub ip0: IptFilter,
|
||||
#[serde(default = "default_ipt_filter")]
|
||||
pub ip1: IptFilter,
|
||||
#[serde(default = "default_ipt_filter")]
|
||||
pub ip2: IptFilter,
|
||||
#[serde(default = "default_ipt_filter")]
|
||||
pub ip3: IptFilter,
|
||||
|
||||
pub workdir_path: Option<String>,
|
||||
pub bitmap_size: Option<usize>,
|
||||
pub mem_limit: Option<usize>,
|
||||
@ -65,6 +74,13 @@ fn default_cow_primary_size() -> u64 {
|
||||
0
|
||||
}
|
||||
|
||||
fn default_ipt_filter() -> IptFilter {
|
||||
IptFilter{
|
||||
a: 0,
|
||||
b: 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct ConfigLoader {
|
||||
pub include_default_config_path: Option<String>,
|
||||
|
@ -146,8 +146,11 @@ impl FuzzRunner for ForkServer {
|
||||
*/
|
||||
impl FuzzRunner for QemuProcess {
|
||||
fn run_test(&mut self) -> Result<TestInfo, Box<dyn Error>> {
|
||||
self.send_payload();
|
||||
self.send_payload().unwrap();
|
||||
let ops_used = self.feedback_data.shared.interpreter.executed_opcode_num;
|
||||
if self.aux.result.abort != 0 {
|
||||
panic!("Abort called!\n");
|
||||
}
|
||||
if self.aux.result.crash_found != 0 {
|
||||
return Ok(TestInfo {ops_used, exitreason: ExitReason::Crash(self.aux.misc.as_slice().to_vec())});
|
||||
}
|
||||
@ -169,7 +172,7 @@ impl FuzzRunner for QemuProcess {
|
||||
|
||||
fn run_create_snapshot(&mut self) -> bool{
|
||||
assert_eq!(self.aux.result.tmp_snapshot_created,0);
|
||||
self.send_payload();
|
||||
self.send_payload().unwrap();
|
||||
return self.aux.result.tmp_snapshot_created == 1;
|
||||
}
|
||||
|
||||
@ -177,7 +180,7 @@ impl FuzzRunner for QemuProcess {
|
||||
if self.aux.result.tmp_snapshot_created != 0 {
|
||||
self.aux.config.changed = 1;
|
||||
self.aux.config.discard_tmp_snapshot = 1;
|
||||
self.send_payload();
|
||||
self.send_payload().unwrap();
|
||||
if self.aux.result.tmp_snapshot_created != 0 {
|
||||
println!("AUX BUFFER {:#?}",self.aux);
|
||||
}
|
||||
@ -190,7 +193,7 @@ impl FuzzRunner for QemuProcess {
|
||||
fn run_redqueen(&mut self) -> Result<RedqueenInfo, Box<dyn Error>> {
|
||||
self.aux.config.changed = 1;
|
||||
self.aux.config.redqueen_mode=1;
|
||||
self.send_payload();
|
||||
self.send_payload().unwrap();
|
||||
self.aux.config.changed = 1;
|
||||
self.aux.config.redqueen_mode=0;
|
||||
let rq_file = format!("{}/redqueen_workdir_{}/redqueen_results.txt",self.params.workdir,self.params.qemu_id);
|
||||
@ -201,7 +204,7 @@ impl FuzzRunner for QemuProcess {
|
||||
//println!("TRACE!!!!");
|
||||
self.aux.config.trace_mode=1;
|
||||
self.aux.config.changed = 1;
|
||||
self.send_payload();
|
||||
self.send_payload().unwrap();
|
||||
self.aux.config.changed = 1;
|
||||
self.aux.config.trace_mode=0;
|
||||
return Ok(CFGInfo {});
|
||||
|
@ -17,7 +17,8 @@ use derivative::Derivative;
|
||||
const AUX_BUFFER_SIZE: usize = 4096;
|
||||
|
||||
const AUX_MAGIC: u64 = 0x54502d554d4551_u64;
|
||||
const QEMU_PT_VERSION: u16 = 1; /* let's start at 1 for the initial version using the aux buffer */
|
||||
const QEMU_PT_VERSION: u16 = 2; /* let's start at 1 for the initial version using the aux buffer */
|
||||
const QEMU_PT_HASH: u16 = 82;
|
||||
|
||||
const HEADER_SIZE: usize = 128;
|
||||
const CAP_SIZE: usize = 256;
|
||||
@ -82,14 +83,21 @@ impl AuxBuffer {
|
||||
return AuxBuffer::new_readonly(file, false);
|
||||
}
|
||||
|
||||
pub fn validate_header(&self) {
|
||||
pub fn validate_header(&self) -> Result<(), String> {
|
||||
mem_barrier();
|
||||
let mgc = self.header.magic;
|
||||
assert_eq!(mgc, AUX_MAGIC);
|
||||
if mgc != AUX_MAGIC {
|
||||
return Err(format!("aux buffer magic mismatch {} != {}...\n[!] Probably the AUX buffer is corrupted?!", AUX_MAGIC, mgc));
|
||||
}
|
||||
let version = self.header.version;
|
||||
assert_eq!(version, QEMU_PT_VERSION);
|
||||
if version != QEMU_PT_VERSION {
|
||||
return Err(format!("aux buffer version mismatch {} != {}...\n[!] You are probably using either an outdated version of libnyx or QEMU-Nyx...", QEMU_PT_VERSION, version));
|
||||
}
|
||||
let hash = self.header.hash;
|
||||
assert_eq!(hash, 81);
|
||||
if hash != QEMU_PT_HASH {
|
||||
return Err(format!("aux buffer hash mismatch {} != {}...\n[!] You are probably using either an outdated version of libnyx or QEMU-Nyx...", QEMU_PT_HASH, hash));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
@ -176,7 +184,7 @@ pub struct auxilary_buffer_result_s {
|
||||
pub dirty_pages: u32,
|
||||
pub pt_trace_size: u32,
|
||||
pub payload_write_attempt_found: u8,
|
||||
|
||||
pub abort: u8,
|
||||
}
|
||||
|
||||
#[repr(C, packed(1))]
|
||||
|
@ -24,7 +24,7 @@ fn into_absolute_path(sharedir: &str) -> String{
|
||||
}
|
||||
}
|
||||
|
||||
pub fn qemu_process_new_from_kernel(sharedir: String, cfg: &QemuKernelConfig, fuzz_cfg: &FuzzerConfig) -> qemu_process::QemuProcess {
|
||||
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(),
|
||||
@ -39,6 +39,7 @@ pub fn qemu_process_new_from_kernel(sharedir: String, cfg: &QemuKernelConfig, fu
|
||||
},
|
||||
write_protected_input_buffer: fuzz_cfg.write_protected_input_buffer,
|
||||
cow_primary_size: fuzz_cfg.cow_primary_size,
|
||||
ipt_filters: fuzz_cfg.ipt_filters,
|
||||
};
|
||||
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);
|
||||
@ -51,7 +52,7 @@ pub fn qemu_process_new_from_kernel(sharedir: String, cfg: &QemuKernelConfig, fu
|
||||
return qemu_process::QemuProcess::new(qemu_params);
|
||||
}
|
||||
|
||||
pub fn qemu_process_new_from_snapshot(sharedir: String, cfg: &QemuSnapshotConfig, fuzz_cfg: &FuzzerConfig) -> qemu_process::QemuProcess {
|
||||
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!(),
|
||||
@ -80,6 +81,7 @@ pub fn qemu_process_new_from_snapshot(sharedir: String, cfg: &QemuSnapshotConfig
|
||||
},
|
||||
write_protected_input_buffer: fuzz_cfg.write_protected_input_buffer,
|
||||
cow_primary_size: fuzz_cfg.cow_primary_size,
|
||||
ipt_filters: fuzz_cfg.ipt_filters,
|
||||
};
|
||||
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);
|
||||
|
@ -1,5 +1,6 @@
|
||||
use std::path::Path;
|
||||
use crate::config::SnapshotPath;
|
||||
use crate::config::IptFilter;
|
||||
|
||||
pub struct KernelVmParams {
|
||||
pub qemu_binary: String,
|
||||
@ -13,6 +14,7 @@ pub struct KernelVmParams {
|
||||
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 struct SnapshotVmParams{
|
||||
@ -28,6 +30,8 @@ pub struct SnapshotVmParams{
|
||||
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 struct QemuParams {
|
||||
@ -50,7 +54,6 @@ pub struct QemuParams {
|
||||
impl QemuParams {
|
||||
pub fn new_from_snapshot(workdir: &str, qemu_id: usize, cpu: usize, params: &SnapshotVmParams, create_snapshot_file: bool) -> QemuParams{
|
||||
|
||||
assert!(!(!create_snapshot_file && qemu_id == 1));
|
||||
let project_name = Path::new(workdir)
|
||||
.file_name()
|
||||
.expect("Couldn't get project name from workdir!")
|
||||
@ -112,13 +115,19 @@ impl QemuParams {
|
||||
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={},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());
|
||||
}
|
||||
|
||||
//nyx_ops += &format!(",ip0_a=0x1000,ip0_b=0x7ffffffff000");
|
||||
//nyx_ops += &format!(",ip0_a=ffff800000000000,ip0_b=ffffffffffffffff");
|
||||
|
||||
cmd.push(nyx_ops);
|
||||
|
||||
cmd.push("-machine".to_string());
|
||||
@ -171,7 +180,6 @@ impl QemuParams {
|
||||
pub fn new_from_kernel(workdir: &str, qemu_id: usize, params: &KernelVmParams, create_snapshot_file: bool) -> QemuParams {
|
||||
//prepare_working_dir(workdir)
|
||||
|
||||
assert!(!(!create_snapshot_file && qemu_id == 1));
|
||||
let project_name = Path::new(workdir)
|
||||
.file_name()
|
||||
.expect("Couldn't get project name from workdir!")
|
||||
@ -236,13 +244,18 @@ impl QemuParams {
|
||||
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());
|
||||
}
|
||||
|
||||
//nyx_ops += &format!(",ip0_a=0x1000,ip0_b=0x7ffffffff000");
|
||||
//nyx_ops += &format!(",ip0_a=ffff800000000000,ip0_b=ffffffffffffffff");
|
||||
|
||||
cmd.push(nyx_ops);
|
||||
|
||||
cmd.push("-machine".to_string());
|
||||
|
@ -1,6 +1,7 @@
|
||||
use core::ffi::c_void;
|
||||
use nix::sys::mman::*;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::prelude::*;
|
||||
use std::os::unix::fs::symlink;
|
||||
@ -31,21 +32,23 @@ pub struct QemuProcess {
|
||||
pub bitmap: &'static mut [u8],
|
||||
pub payload: &'static mut [u8],
|
||||
pub params: QemuParams,
|
||||
hprintf_log: File,
|
||||
}
|
||||
|
||||
fn execute_qemu(ctrl: &mut UnixStream) {
|
||||
ctrl.write_all(&[120_u8]).unwrap();
|
||||
fn execute_qemu(ctrl: &mut UnixStream) -> io::Result<()>{
|
||||
ctrl.write_all(&[120_u8])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn wait_qemu(ctrl: &mut UnixStream) {
|
||||
fn wait_qemu(ctrl: &mut UnixStream) -> io::Result<()>{
|
||||
let mut buf = [0];
|
||||
ctrl.read_exact(&mut buf).unwrap();
|
||||
ctrl.read_exact(&mut buf)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_qemu(ctrl: &mut UnixStream) {
|
||||
execute_qemu(ctrl);
|
||||
wait_qemu(ctrl);
|
||||
fn run_qemu(ctrl: &mut UnixStream) -> io::Result<()>{
|
||||
execute_qemu(ctrl)?;
|
||||
wait_qemu(ctrl)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn make_shared_data(file: File, size: usize) -> &'static mut [u8] {
|
||||
@ -69,7 +72,7 @@ fn make_shared_ijon_data(file: File, size: usize) -> FeedbackBuffer {
|
||||
}
|
||||
|
||||
impl QemuProcess {
|
||||
pub fn new(params: QemuParams) -> QemuProcess {
|
||||
pub fn new(params: QemuParams) -> Result<QemuProcess, String> {
|
||||
Self::prepare_redqueen_workdir(¶ms.workdir, params.qemu_id);
|
||||
|
||||
if params.qemu_id == 0{
|
||||
@ -89,11 +92,20 @@ impl QemuProcess {
|
||||
.open(¶ms.payload_filename)
|
||||
.expect("couldn't open payload file");
|
||||
|
||||
if Path::new(&format!("{}/bitmap_{}", params.workdir, params.qemu_id)).exists(){
|
||||
fs::remove_file(format!("{}/bitmap_{}", params.workdir, params.qemu_id)).unwrap();
|
||||
}
|
||||
|
||||
symlink(
|
||||
¶ms.bitmap_filename,
|
||||
format!("{}/bitmap_{}", params.workdir, params.qemu_id),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if Path::new(&format!("{}/payload_{}", params.workdir, params.qemu_id)).exists(){
|
||||
fs::remove_file(format!("{}/payload_{}", params.workdir, params.qemu_id)).unwrap();
|
||||
}
|
||||
|
||||
symlink(
|
||||
¶ms.payload_filename,
|
||||
format!("{}/payload_{}", params.workdir, params.qemu_id),
|
||||
@ -123,7 +135,7 @@ impl QemuProcess {
|
||||
thread::sleep(time::Duration::from_millis(200*params.qemu_id as u64));
|
||||
|
||||
|
||||
let 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")
|
||||
@ -159,7 +171,9 @@ impl QemuProcess {
|
||||
|
||||
// dry_run
|
||||
//println!("TRHEAD {} run QEMU initial",params.qemu_id);
|
||||
run_qemu(&mut control);
|
||||
if run_qemu(&mut control).is_err() {
|
||||
return Err(format!("cannot launch QEMU-Nyx..."));
|
||||
}
|
||||
|
||||
let aux_shm_f = OpenOptions::new()
|
||||
.read(true)
|
||||
@ -175,7 +189,14 @@ impl QemuProcess {
|
||||
.expect("couldn't open aux buffer file");
|
||||
let mut aux_buffer = AuxBuffer::new(aux_shm_f);
|
||||
|
||||
aux_buffer.validate_header();
|
||||
match aux_buffer.validate_header(){
|
||||
Err(x) => {
|
||||
child.kill().unwrap();
|
||||
child.wait().unwrap();
|
||||
return Err(x);
|
||||
},
|
||||
Ok(_) => {},
|
||||
}
|
||||
if params.write_protected_input_buffer{
|
||||
if params.qemu_id == 0 {
|
||||
println!("[!] libnyx: input buffer is write protected");
|
||||
@ -185,22 +206,30 @@ impl QemuProcess {
|
||||
}
|
||||
|
||||
loop {
|
||||
if aux_buffer.result.abort == 1 {
|
||||
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());
|
||||
|
||||
/* get rid of this process */
|
||||
child.kill().unwrap();
|
||||
child.wait().unwrap();
|
||||
|
||||
return Err(msg);
|
||||
}
|
||||
|
||||
if aux_buffer.result.hprintf == 1 {
|
||||
let len = aux_buffer.misc.len;
|
||||
print!("{}", String::from_utf8_lossy(&aux_buffer.misc.data[0..len as usize]).yellow());
|
||||
}
|
||||
else{
|
||||
//println!("QEMU NOT READY");
|
||||
}
|
||||
}
|
||||
|
||||
if aux_buffer.result.state == 3 {
|
||||
break;
|
||||
}
|
||||
//println!("QEMU NOT READY");
|
||||
//println!("TRHEAD {} run QEMU NOT READY",params.qemu_id);
|
||||
run_qemu(&mut control);
|
||||
if run_qemu(&mut control).is_err(){
|
||||
return Err(format!("failed to establish fuzzing loop..."));
|
||||
}
|
||||
//run_qemu(&mut control).unwrap();
|
||||
}
|
||||
//println!("QEMU READY");
|
||||
println!("[!] libnyx: qemu #{} is ready:", params.qemu_id);
|
||||
|
||||
aux_buffer.config.reload_mode = 1;
|
||||
@ -208,16 +237,7 @@ impl QemuProcess {
|
||||
aux_buffer.config.timeout_usec = 500_000;
|
||||
aux_buffer.config.changed = 1;
|
||||
|
||||
//run_qemu(&mut control);
|
||||
//run_qemu(&mut control);
|
||||
|
||||
let mut option = OpenOptions::new();
|
||||
option.read(true);
|
||||
option.write(true);
|
||||
option.create(true);
|
||||
let hprintf_log = option.open(format!("{}/hprintf_log_{}", params.workdir, params.qemu_id)).unwrap();
|
||||
|
||||
return QemuProcess {
|
||||
return Ok(QemuProcess {
|
||||
process: child,
|
||||
aux: aux_buffer,
|
||||
feedback_data: ijon_shared,
|
||||
@ -225,49 +245,32 @@ impl QemuProcess {
|
||||
bitmap: bitmap_shared,
|
||||
payload: payload_shared,
|
||||
params,
|
||||
hprintf_log,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
pub fn send_payload(&mut self) {
|
||||
pub fn send_payload(&mut self) -> io::Result<()>{
|
||||
let mut old_address: u64 = 0;
|
||||
//use rand::Rng;
|
||||
//println!("RUN INPUT");
|
||||
//std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
//let time = std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH).unwrap().as_nanos();
|
||||
//self.hprintf_log.write_all(&format!("===({})===\n", time).as_bytes()).unwrap();
|
||||
|
||||
loop {
|
||||
mem_barrier();
|
||||
run_qemu(&mut self.ctrl);
|
||||
match run_qemu(&mut self.ctrl) {
|
||||
Err(x) => return Err(x),
|
||||
Ok(_) => {},
|
||||
}
|
||||
mem_barrier();
|
||||
|
||||
if self.aux.result.hprintf != 0 {
|
||||
self.hprintf_log.write_all(&format!("{}\n", self.aux.misc.as_string()).as_bytes()).unwrap();
|
||||
//println!("HPRINTF {}", self.aux.misc.as_string());
|
||||
if self.aux.result.hprintf == 1 {
|
||||
let len = self.aux.misc.len;
|
||||
|
||||
|
||||
|
||||
print!("{}", String::from_utf8_lossy(&self.aux.misc.data[0..len as usize]).yellow());
|
||||
//print!("{}", "".clear());
|
||||
println!("TEST\n");
|
||||
|
||||
continue;
|
||||
}
|
||||
//println!("pt trace size {:x} bytes",self.aux.result.pt_trace_size);
|
||||
//println!("{:} dirty pages",self.aux.result.dirty_pages);
|
||||
//println!("interpreter ran {} ops",self.feedback_data.shared.interpreter.executed_opcode_num);
|
||||
//let max_v = 0;
|
||||
//let max_i = 0;
|
||||
//for (i,v) in self.feedback_data.shared.ijon.max_data.iter().enumerate(){
|
||||
// if *v > max_v{
|
||||
// max_v=*v;
|
||||
// max_i=i;
|
||||
//
|
||||
// }
|
||||
//}
|
||||
//println!("found IJON MAX: {}\t{:x}",max_i,max_v);
|
||||
|
||||
if self.aux.result.abort == 1 {
|
||||
let len = self.aux.misc.len;
|
||||
println!("[!] libnyx: agent abort() -> \"{}\"", String::from_utf8_lossy(&self.aux.misc.data[0..len as usize]).red());
|
||||
break;
|
||||
}
|
||||
|
||||
if self.aux.result.success != 0 || self.aux.result.crash_found != 0 || self.aux.result.asan_found != 0 || self.aux.result.payload_write_attempt_found != 0 {
|
||||
break;
|
||||
@ -284,15 +287,8 @@ impl QemuProcess {
|
||||
self.aux.config.page_dump_mode = 1;
|
||||
self.aux.config.changed = 1;
|
||||
}
|
||||
//else {
|
||||
// break;
|
||||
//}
|
||||
|
||||
}
|
||||
//std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
//if self.aux.result.tmp_snapshot_created != 0 {
|
||||
// //println!("created snapshot!!!!!!\n");
|
||||
//}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_timeout(&mut self, timeout: std::time::Duration){
|
||||
@ -306,11 +302,26 @@ impl QemuProcess {
|
||||
}
|
||||
|
||||
pub fn shutdown(&mut self) {
|
||||
println!("Let's kill QEMU!");
|
||||
println!("[!] libnyx: sending SIGKILL to QEMU-Nyx process...");
|
||||
self.process.kill().unwrap();
|
||||
self.wait();
|
||||
}
|
||||
|
||||
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",
|
||||
];
|
||||
for file in files.iter() {
|
||||
while !Path::new(&format!("{}/{}", workdir, file)).exists(){
|
||||
thread::sleep(time::Duration::from_secs(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prepare_workdir(workdir: &str, seed_path: Option<String>) {
|
||||
Self::clear_workdir(workdir);
|
||||
let folders = vec![
|
||||
|
@ -16,11 +16,13 @@ pub enum NyxReturnValue {
|
||||
Asan,
|
||||
Timout,
|
||||
InvalidWriteToPayload,
|
||||
Error
|
||||
Error,
|
||||
IoError, // QEMU process has died for some reason
|
||||
Abort, // Abort hypercall called
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_new(sharedir: *const c_char, workdir: *const c_char, worker_id: u32, create_snapshot: bool) -> * mut QemuProcess {
|
||||
pub extern "C" fn nyx_new(sharedir: *const c_char, workdir: *const c_char, worker_id: u32, cpu_id: u32, create_snapshot: bool) -> * mut QemuProcess {
|
||||
let sharedir_c_str = unsafe {
|
||||
assert!(!sharedir.is_null());
|
||||
CStr::from_ptr(sharedir)
|
||||
@ -35,18 +37,20 @@ pub extern "C" fn nyx_new(sharedir: *const c_char, workdir: *const c_char, worke
|
||||
let sharedir_r_str = sharedir_c_str.to_str().unwrap();
|
||||
let workdir_r_str = workdir_c_str.to_str().unwrap();
|
||||
|
||||
println!("r_str: {}", sharedir_r_str);
|
||||
let cfg: Config = Config::new_from_sharedir(&sharedir_r_str);
|
||||
println!("config {}", cfg.fuzz.bitmap_size);
|
||||
|
||||
|
||||
let cfg: Config = match Config::new_from_sharedir(&sharedir_r_str){
|
||||
Ok(x) => x,
|
||||
Err(msg) => {
|
||||
println!("[!] libnyx config reader error: {}", msg);
|
||||
return std::ptr::null_mut() as *mut QemuProcess;
|
||||
}
|
||||
};
|
||||
|
||||
let mut config = cfg.fuzz;
|
||||
let runner_cfg = cfg.runner;
|
||||
|
||||
|
||||
/* todo: add sanity check */
|
||||
config.cpu_pin_start_at = worker_id as usize;
|
||||
config.cpu_pin_start_at = cpu_id as usize;
|
||||
|
||||
config.thread_id = worker_id as usize;
|
||||
config.threads = if create_snapshot { 2 as usize } else { 1 as usize };
|
||||
@ -59,16 +63,25 @@ pub extern "C" fn nyx_new(sharedir: *const c_char, workdir: *const c_char, worke
|
||||
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() {
|
||||
let runner = match runner_cfg.clone() {
|
||||
FuzzRunnerConfig::QemuSnapshot(cfg) => {
|
||||
let runner = qemu_process_new_from_snapshot(sdir.to_string(), &cfg, &config);
|
||||
return Box::into_raw(Box::new(runner));
|
||||
}
|
||||
qemu_process_new_from_snapshot(sdir.to_string(), &cfg, &config)
|
||||
},
|
||||
FuzzRunnerConfig::QemuKernel(cfg) => {
|
||||
let runner = qemu_process_new_from_kernel(sdir.to_string(), &cfg, &config);
|
||||
return Box::into_raw(Box::new(runner));
|
||||
qemu_process_new_from_kernel(sdir.to_string(), &cfg, &config)
|
||||
}
|
||||
};
|
||||
|
||||
match runner {
|
||||
Ok(x) => Box::into_raw(Box::new(x)),
|
||||
Err(msg) => {
|
||||
println!("[!] libnyx failed to initialize QEMU-Nyx: {}", msg);
|
||||
std::ptr::null_mut() as *mut QemuProcess
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,6 +117,15 @@ pub extern "C" fn nyx_get_bitmap_buffer(qemu_process: * mut QemuProcess) -> *mut
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_get_bitmap_buffer_size(qemu_process: * mut QemuProcess) -> usize {
|
||||
unsafe{
|
||||
assert!(!qemu_process.is_null());
|
||||
assert!((qemu_process as usize) % std::mem::align_of::<QemuProcess>() == 0);
|
||||
return (*qemu_process).bitmap.len();
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyx_shutdown(qemu_process: * mut QemuProcess) {
|
||||
unsafe{
|
||||
@ -152,25 +174,30 @@ pub extern "C" fn nyx_exec(qemu_process: * mut QemuProcess) -> NyxReturnValue {
|
||||
assert!(!qemu_process.is_null());
|
||||
assert!((qemu_process as usize) % std::mem::align_of::<QemuProcess>() == 0);
|
||||
|
||||
(*qemu_process).send_payload();
|
||||
|
||||
if (*qemu_process).aux.result.crash_found != 0 {
|
||||
return NyxReturnValue::Crash;
|
||||
match (*qemu_process).send_payload(){
|
||||
Err(_) => return NyxReturnValue::IoError,
|
||||
Ok(_) => {
|
||||
if (*qemu_process).aux.result.abort != 0 {
|
||||
return NyxReturnValue::Abort;
|
||||
}
|
||||
if (*qemu_process).aux.result.crash_found != 0 {
|
||||
return NyxReturnValue::Crash;
|
||||
}
|
||||
if (*qemu_process).aux.result.asan_found != 0 {
|
||||
return NyxReturnValue::Asan;
|
||||
}
|
||||
if (*qemu_process).aux.result.timeout_found != 0 {
|
||||
return NyxReturnValue::Timout;
|
||||
}
|
||||
if (*qemu_process).aux.result.payload_write_attempt_found != 0 {
|
||||
return NyxReturnValue::InvalidWriteToPayload;
|
||||
}
|
||||
if (*qemu_process).aux.result.success != 0 {
|
||||
return NyxReturnValue::Normal;
|
||||
}
|
||||
return NyxReturnValue::Error;
|
||||
}
|
||||
}
|
||||
if (*qemu_process).aux.result.asan_found != 0 {
|
||||
return NyxReturnValue::Asan;
|
||||
}
|
||||
if (*qemu_process).aux.result.timeout_found != 0 {
|
||||
return NyxReturnValue::Timout;
|
||||
}
|
||||
if (*qemu_process).aux.result.payload_write_attempt_found != 0 {
|
||||
return NyxReturnValue::InvalidWriteToPayload;
|
||||
}
|
||||
if (*qemu_process).aux.result.success != 0 {
|
||||
return NyxReturnValue::Normal;
|
||||
}
|
||||
println!("unknown exeuction result!!");
|
||||
return NyxReturnValue::Error;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user