better mutator

This commit is contained in:
Andrea Fioraldi 2021-02-12 23:05:00 +01:00
parent b4b97fe34f
commit ebc9571410
7 changed files with 63 additions and 148 deletions

View File

@ -120,7 +120,7 @@ pub fn buffer_copy(dst: &mut [u8], src: &[u8], from: usize, to: usize, len: usiz
#[inline] #[inline]
fn buffer_set(data: &mut [u8], from: usize, len: usize, val: u8) { fn buffer_set(data: &mut [u8], from: usize, len: usize, val: u8) {
debug_assert!(from + len <= data.len()); debug_assert!(from + len <= data.len());
for p in &mut data[from..from + len] { for p in &mut data[from..(from + len)] {
*p = val *p = val
} }
} }
@ -491,11 +491,19 @@ where
if size == 0 { if size == 0 {
return Ok(MutationResult::Skipped); return Ok(MutationResult::Skipped);
} }
let off = rand.below(size as u64 - 1) as usize; let off = rand.below(size as u64) as usize;
let len = rand.below(min(16, mutator.max_size() as u64 - 1) + 1) as usize; let mut len = 1 + rand.below(16) as usize;
if size + len > mutator.max_size() {
if mutator.max_size() > size {
len = mutator.max_size() - size;
} else {
return Ok(MutationResult::Skipped);
}
}
input.bytes_mut().resize(size + len, 0); input.bytes_mut().resize(size + len, 0);
buffer_self_copy(input.bytes_mut(), 0, 0 + off, len); buffer_self_copy(input.bytes_mut(), off, off + len, size - off);
Ok(MutationResult::Mutated) Ok(MutationResult::Mutated)
} }
@ -511,21 +519,25 @@ where
I: Input + HasBytesVec, I: Input + HasBytesVec,
R: Rand, R: Rand,
{ {
let mut size = input.bytes().len(); let size = input.bytes().len();
if size == 0 { if size == 0 {
// Generate random bytes if we're empty. return Ok(MutationResult::Skipped);
input }
.bytes_mut() let off = rand.below(size as u64) as usize;
.append(&mut rand.next().to_be_bytes().to_vec()); let mut len = 1 + rand.below(16) as usize;
size = input.bytes().len();
if size + len > mutator.max_size() {
if mutator.max_size() > size {
len = mutator.max_size() - size;
} else {
return Ok(MutationResult::Skipped);
}
} }
let off = rand.below(size as u64 - 1) as usize;
let len = rand.below(max(16, mutator.max_size() as u64)) as usize;
let val = input.bytes()[rand.below(size as u64) as usize]; let val = input.bytes()[rand.below(size as u64) as usize];
input.bytes_mut().resize(max(size, off + (2 * len) + 1), 0); input.bytes_mut().resize(size + len, 0);
buffer_self_copy(input.bytes_mut(), off, off + len, len); buffer_self_copy(input.bytes_mut(), off, off + len, size - off);
buffer_set(input.bytes_mut(), off, len, val); buffer_set(input.bytes_mut(), off, len, val);
Ok(MutationResult::Mutated) Ok(MutationResult::Mutated)
@ -542,20 +554,25 @@ where
I: Input + HasBytesVec, I: Input + HasBytesVec,
R: Rand, R: Rand,
{ {
let mut size = input.bytes().len(); let size = input.bytes().len();
if size == 0 { if size == 0 {
input return Ok(MutationResult::Skipped);
.bytes_mut() }
.append(&mut rand.next().to_le_bytes().to_vec()); let off = rand.below(size as u64) as usize;
size = input.bytes().len(); let mut len = 1 + rand.below(16) as usize;
if size + len > mutator.max_size() {
if mutator.max_size() > size {
len = mutator.max_size() - size;
} else {
return Ok(MutationResult::Skipped);
}
} }
let off = rand.below(size as u64 - 1) as usize;
let len = rand.below(core::cmp::min(16, mutator.max_size() as u64)) as usize;
let val = rand.below(256) as u8; let val = rand.below(256) as u8;
input.bytes_mut().resize(max(size, off + (2 * len) + 1), 0); input.bytes_mut().resize(size + len, 0);
buffer_self_copy(input.bytes_mut(), off, off + len, len); buffer_self_copy(input.bytes_mut(), off, off + len, size - off);
buffer_set(input.bytes_mut(), off, len, val); buffer_set(input.bytes_mut(), off, len, val);
Ok(MutationResult::Mutated) Ok(MutationResult::Mutated)
@ -575,16 +592,12 @@ where
if size == 0 { if size == 0 {
return Ok(MutationResult::Skipped); return Ok(MutationResult::Skipped);
} }
let off = rand.below(size as u64) as usize;
let len = 1 + rand.below(min(16, size - off) as u64) as usize;
let val = input.bytes()[rand.below(size as u64) as usize]; let val = input.bytes()[rand.below(size as u64) as usize];
let start = if size == 1 {
0
} else {
rand.below(size as u64 - 1) as usize
};
let end = rand.between(start as u64, size as u64) as usize; buffer_set(input.bytes_mut(), off, len, val);
buffer_set(input.bytes_mut(), start, end - start, val);
Ok(MutationResult::Mutated) Ok(MutationResult::Mutated)
} }
@ -603,16 +616,12 @@ where
if size == 0 { if size == 0 {
return Ok(MutationResult::Skipped); return Ok(MutationResult::Skipped);
} }
let off = rand.below(size as u64) as usize;
let len = 1 + rand.below(min(16, size - off) as u64) as usize;
let val = rand.below(256) as u8; let val = rand.below(256) as u8;
let start = if size == 1 {
0
} else {
rand.below(size as u64 - 1) as usize
};
let len = rand.below((size - start) as u64) as usize; buffer_set(input.bytes_mut(), off, len, val);
buffer_set(input.bytes_mut(), start, len, val);
Ok(MutationResult::Mutated) Ok(MutationResult::Mutated)
} }
@ -632,9 +641,9 @@ where
return Ok(MutationResult::Skipped); return Ok(MutationResult::Skipped);
} }
let from = rand.below(input.bytes().len() as u64 - 1) as usize; let from = rand.below(input.bytes().len() as u64) as usize;
let to = rand.below(input.bytes().len() as u64 - 1) as usize; let to = rand.below(input.bytes().len() as u64) as usize;
let len = rand.below((size - core::cmp::max(from, to)) as u64) as usize; let len = 1 + rand.below((size - max(from, to)) as u64) as usize;
buffer_self_copy(input.bytes_mut(), from, to, len); buffer_self_copy(input.bytes_mut(), from, to, len);
@ -656,11 +665,11 @@ where
return Ok(MutationResult::Skipped); return Ok(MutationResult::Skipped);
} }
let first = rand.below(input.bytes().len() as u64 - 1) as usize; let first = rand.below(input.bytes().len() as u64) as usize;
let second = rand.below(input.bytes().len() as u64 - 1) as usize; let second = rand.below(input.bytes().len() as u64) as usize;
let len = max(rand.below((size - max(first, second)) as u64) as usize, 1); let len = 1 + rand.below((size - max(first, second)) as u64) as usize;
let tmp = input.bytes()[first..=first + len].to_vec(); let tmp = input.bytes()[first..(first + len)].to_vec();
buffer_self_copy(input.bytes_mut(), second, first, len); buffer_self_copy(input.bytes_mut(), second, first, len);
buffer_copy(input.bytes_mut(), &tmp, 0, second, len); buffer_copy(input.bytes_mut(), &tmp, 0, second, len);

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

View File

@ -1,12 +1,8 @@
//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts //! A libfuzzer-like fuzzer with llmp-multithreading support and restarts
//! The example harness is built for libpng. //! The example harness is built for libpng.
#[macro_use]
extern crate clap;
// extern crate libc; // extern crate libc;
use clap::{App, Arg};
use std::{env, path::PathBuf}; use std::{env, path::PathBuf};
use afl::{ use afl::{
@ -55,69 +51,12 @@ where
/// The main fn, parsing parameters, and starting the fuzzer /// The main fn, parsing parameters, and starting the fuzzer
pub fn main() { pub fn main() {
let matches = App::new("libAFLrs fuzzer harness") println!("Workdir: {:?}", env::current_dir().unwrap().to_string_lossy().to_string());
.about("libAFLrs fuzzer harness help options.") fuzz(vec![PathBuf::from("./corpus")], 1337).expect("An error occurred while fuzzing");
.arg(
Arg::with_name("port")
.short("p")
.value_name("PORT")
.takes_value(true)
.help("Broker TCP port to use."),
)
.arg(
Arg::with_name("dictionary")
.short("x")
.value_name("DICTIONARY")
.takes_value(true)
.multiple(true)
.help("Dictionary file to use, can be specified multiple times."),
)
.arg(
Arg::with_name("statstime")
.short("T")
.value_name("STATSTIME")
.takes_value(true)
.help("How often to print statistics in seconds [default: 5, disable: 0]"),
)
.arg(Arg::with_name("workdir")
.help("Where to write the corpus, also reads the data on start. If more than one is supplied the first will be the work directory, all others will just be initially read from.")
.multiple(true)
.value_name("WORKDIR")
)
.get_matches();
let _ = value_t!(matches, "statstime", u32).unwrap_or(5);
let broker_port = value_t!(matches, "port", u16).unwrap_or(1337);
let workdir = if matches.is_present("workdir") {
matches.value_of("workdir").unwrap().to_string()
} else {
env::current_dir().unwrap().to_string_lossy().to_string()
};
let mut dictionary: Option<Vec<PathBuf>> = None;
if matches.is_present("dictionary") {
dictionary = Some(values_t!(matches, "dictionary", PathBuf).unwrap_or_else(|e| e.exit()));
}
let mut input: Option<Vec<PathBuf>> = None;
if matches.is_present("workdir") {
input = Some(values_t!(matches, "workdir", PathBuf).unwrap_or_else(|e| e.exit()));
}
if dictionary != None || input != None {
println!("Information: the first process started is the broker and only processes the \'-p PORT\' option if present.");
}
println!("Workdir: {:?}", workdir);
fuzz(Some(vec![PathBuf::from("./in1")]), broker_port).expect("An error occurred while fuzzing");
//fuzz(input, broker_port).expect("An error occurred while fuzzing");
} }
/// The actual fuzzer /// The actual fuzzer
fn fuzz(input: Option<Vec<PathBuf>>, broker_port: u16) -> Result<(), AflError> { fn fuzz(corpus_dirs: Vec<PathBuf>, broker_port: u16) -> Result<(), AflError> {
let mut rand = StdRand::new(0); let mut rand = StdRand::new(0);
// 'While the stats are state, they are usually used in the broker - which is likely never restarted // 'While the stats are state, they are usually used in the broker - which is likely never restarted
let stats = SimpleStats::new(|s| println!("{}", s)); let stats = SimpleStats::new(|s| println!("{}", s));
@ -165,36 +104,13 @@ fn fuzz(input: Option<Vec<PathBuf>>, broker_port: u16) -> Result<(), AflError> {
} }
} }
/*
// TODO close fds in a rusty way
unsafe {
let null_fname = std::ffi::CString::new("/dev/null").unwrap();
let null_file = libc::open(null_fname.as_ptr(), libc::O_RDWR);
libc::dup2(null_file, 1);
libc::dup2(null_file, 2);
}
*/
// in case the corpus is empty (on first run), reset // in case the corpus is empty (on first run), reset
if state.corpus().count() < 1 { if state.corpus().count() < 1 {
match input { state
Some(x) => state .load_initial_inputs(&mut executor, &mut restarting_mgr, &corpus_dirs)
.load_initial_inputs(&mut executor, &mut restarting_mgr, &x) .expect(&format!("Failed to load initial corpus at {:?}", &corpus_dirs));
.expect(&format!("Failed to load initial corpus at {:?}", &x)),
None => (),
}
println!("We imported {} inputs from disk.", state.corpus().count()); println!("We imported {} inputs from disk.", state.corpus().count());
} }
/*
if state.corpus().count() < 1 {
println!("Generating random inputs");
let mut generator = RandPrintablesGenerator::new(32);
state
.generate_initial_inputs(&mut rand, &mut executor, &mut generator, &mut restarting_mgr, 4)
.expect("Failed to generate initial inputs");
println!("We generated {} inputs.", state.corpus().count());
}
*/
fuzzer.fuzz_loop(&mut rand, &mut executor, &mut state, &mut restarting_mgr) fuzzer.fuzz_loop(&mut rand, &mut executor, &mut state, &mut restarting_mgr)
} }

14
fuzzers/libfuzzer_libpng/test.sh Normal file → Executable file
View File

@ -1,18 +1,8 @@
#!/bin/sh #!/bin/sh
cargo build --release || exit 1 cargo build --release || exit 1
cp ./target/release/libfuzzer ./.libfuzzer_test.elf cp ../../target/release/libfuzzer ./.libfuzzer_test.elf
RUST_BACKTRACE=full taskset -c 0 ./.libfuzzer_test.elf & RUST_BACKTRACE=full ./.libfuzzer_test.elf
test "$!" -gt 0 && {
usleep 250
RUST_BACKTRACE=full taskset -c 1 ./.libfuzzer_test.elf &
}
sleep 20
echo "[+] Done"
killall .libfuzzer_test.elf
rm -rf ./.libfuzzer_test.elf rm -rf ./.libfuzzer_test.elf