dictionary mutations

This commit is contained in:
Andrea Fioraldi 2021-02-12 23:49:58 +01:00
parent 9bc4d81b76
commit ffab1e021e
4 changed files with 82 additions and 45 deletions

View File

@ -488,10 +488,7 @@ where
R: Rand,
{
let size = input.bytes().len();
if size == 0 {
return Ok(MutationResult::Skipped);
}
let off = rand.below(size as u64) as usize;
let off = rand.below((size +1) as u64) as usize;
let mut len = 1 + rand.below(16) as usize;
if size + len > mutator.max_size() {
@ -520,10 +517,7 @@ where
R: Rand,
{
let size = input.bytes().len();
if size == 0 {
return Ok(MutationResult::Skipped);
}
let off = rand.below(size as u64) as usize;
let off = rand.below((size +1) as u64) as usize;
let mut len = 1 + rand.below(16) as usize;
if size + len > mutator.max_size() {
@ -555,10 +549,7 @@ where
R: Rand,
{
let size = input.bytes().len();
if size == 0 {
return Ok(MutationResult::Skipped);
}
let off = rand.below(size as u64) as usize;
let off = rand.below((size +1) as u64) as usize;
let mut len = 1 + rand.below(16) as usize;
if size + len > mutator.max_size() {
@ -678,12 +669,13 @@ where
/// Crossover insert mutation
pub fn mutation_crossover_insert<C, I, M, R, S>(
_: &mut M,
mutator: &mut M,
rand: &mut R,
state: &mut S,
input: &mut I,
) -> Result<MutationResult, AflError>
where
M: HasMaxSize,
C: Corpus<I, R>,
I: Input + HasBytesVec,
R: Rand,
@ -705,12 +697,20 @@ where
return Ok(MutationResult::Skipped);
}
let from = rand.below(other_size as u64 - 1) as usize;
let to = rand.below(size as u64 - 1) as usize;
let len = rand.below((other_size - from) as u64) as usize;
let from = rand.below(other_size as u64) as usize;
let to = rand.below(size as u64) as usize;
let mut len = rand.below((other_size - from) as u64) 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(max(size, to + (2 * len) + 1), 0);
buffer_self_copy(input.bytes_mut(), to, to + len, len);
input.bytes_mut().resize(size + len, 0);
buffer_self_copy(input.bytes_mut(), to, to + len, size - to);
buffer_copy(input.bytes_mut(), other.bytes(), from, to, len);
Ok(MutationResult::Mutated)
@ -745,9 +745,9 @@ where
return Ok(MutationResult::Skipped);
}
let from = rand.below(other_size as u64 - 1) as usize;
let from = rand.below(other_size as u64) as usize;
let len = rand.below(min(other_size - from, size) as u64) as usize;
let to = rand.below((size - len) as u64 - 1) as usize;
let to = rand.below((size - len) as u64) as usize;
buffer_copy(input.bytes_mut(), other.bytes(), from, to, len);

View File

@ -303,9 +303,8 @@ where
scheduled.add_mutation(mutation_bytescopy);
scheduled.add_mutation(mutation_bytesswap);
/* TODO
scheduled.add_mutation(mutation_tokeninsert);
scheduled.add_mutation(mutation_tokenreplace);
/*scheduled.add_mutation(mutation_tokenreplace);
*/
scheduled.add_mutation(mutation_crossover_insert);

View File

@ -30,6 +30,14 @@ impl SerdeAny for TokensMetadata {
}
}
impl TokensMetadata {
pub fn new(tokens: Vec<Vec<u8>>) -> Self {
Self {
tokens: tokens
}
}
}
/// Insert a dictionary token
pub fn mutation_tokeninsert<I, M, R, S>(
mutator: &mut M,
@ -56,36 +64,43 @@ where
return Ok(MutationResult::Skipped);
}
let token = &meta.tokens[rand.below(meta.tokens.len() as u64) as usize];
let token_len = token.len();
let size = input.bytes().len();
let off = if size == 0 {
0
} else {
rand.below(core::cmp::min(
size as u64,
(mutator.max_size() - token_len) as u64,
)) as usize
} as usize;
let off = rand.below((size +1) as u64) as usize;
let mut len = token.len();
input.bytes_mut().resize(size + token_len, 0);
buffer_copy(input.bytes_mut(), token, 0, off, size);
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);
buffer_self_copy(input.bytes_mut(), off, off + len, size - off);
buffer_copy(input.bytes_mut(), token, 0, off, len);
Ok(MutationResult::Mutated)
}
/// Overwrite with a dictionary token
pub fn mutation_tokenreplace<I, M, R, S>(
mutator: &mut M,
_: &mut M,
rand: &mut R,
state: &S,
input: &mut I,
) -> Result<MutationResult, AflError>
where
M: HasMaxSize,
I: Input + HasBytesVec,
R: Rand,
S: HasMetadata,
{
let size = input.bytes().len();
if size == 0 {
return Ok(MutationResult::Skipped);
}
let meta;
match state.metadata().get::<TokensMetadata>() {
Some(t) => {
@ -99,11 +114,15 @@ where
return Ok(MutationResult::Skipped);
}
let token = &meta.tokens[rand.below(meta.tokens.len() as u64) as usize];
let token_len = token.len();
let size = input.bytes().len();
let off = rand.below((mutator.max_size() - token_len) as u64) as usize;
let off = rand.below(size as u64) as usize;
let mut len = token.len();
if off + len > size {
len = size - off;
}
buffer_copy(input.bytes_mut(), token, 0, off, size);
buffer_copy(input.bytes_mut(), token, 0, off, len);
Ok(MutationResult::Mutated)
}

View File

@ -1,12 +1,10 @@
//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts
//! The example harness is built for libpng.
// extern crate libc;
use std::{env, path::PathBuf};
use afl::{
bolts::{shmem::AflShmem, tuples::tuple_list},
bolts::{shmem::AflShmem, tuples::tuple_list, serdeany::RegistryBuilder},
corpus::{Corpus, InMemoryCorpus},
events::setup_restarting_mgr,
events::SimpleStats,
@ -14,9 +12,10 @@ use afl::{
feedbacks::MaxMapFeedback,
inputs::Input,
mutators::scheduled::HavocBytesMutator,
mutators::token_mutations::TokensMetadata,
observers::StdMapObserver,
stages::mutational::StdMutationalStage,
state::{HasCorpus, State},
state::{HasCorpus, HasMetadata, State},
utils::StdRand,
AflError, Fuzzer, StdFuzzer,
};
@ -43,6 +42,7 @@ where
E: Executor<I>,
I: Input,
{
// println!("{:?}", buf);
unsafe {
LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len());
}
@ -51,6 +51,10 @@ where
/// The main fn, parsing parameters, and starting the fuzzer
pub fn main() {
// Registry the metadata types used in this fuzzer
RegistryBuilder::register::<TokensMetadata>();
RegistryBuilder::finalize();
println!(
"Workdir: {:?}",
env::current_dir().unwrap().to_string_lossy().to_string()
@ -64,16 +68,18 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, broker_port: u16) -> Result<(), AflError> {
// 'While the stats are state, they are usually used in the broker - which is likely never restarted
let stats = SimpleStats::new(|s| println!("{}", s));
// The restarting state will spawn the same process again as child, then restartet it each time it crashes.
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
let (state, mut restarting_mgr) =
setup_restarting_mgr::<_, _, _, _, AflShmem, _>(stats, broker_port)
.expect("Failed to setup the restarter".into());
// Create an observation channel using the coverage map
let edges_observer =
StdMapObserver::new_from_ptr(&NAME_COV_MAP, unsafe { __lafl_edges_map }, unsafe {
__lafl_max_edges_size as usize
});
// If not retsrating, create a State from scratch
let mut state = state.unwrap_or(State::new(
InMemoryCorpus::new(),
tuple_list!(MaxMapFeedback::new_with_observer(
@ -84,6 +90,20 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, broker_port: u16) -> Result<(), AflError> {
println!("We're a client, let's fuzz :)");
// Create a PNG dictionary of not existing
if state.metadata().get::<TokensMetadata>().is_none() {
state.add_metadata(TokensMetadata::new(
vec![
vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header
"IHDR".as_bytes().to_vec(),
"IDAT".as_bytes().to_vec(),
"PLTE".as_bytes().to_vec(),
"IEND".as_bytes().to_vec(),
]
));
}
// Setup a basic mutator with a mutational stage
let mutator = HavocBytesMutator::new_default();
let stage = StdMutationalStage::new(mutator);
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
@ -98,7 +118,6 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, broker_port: u16) -> Result<(), AflError> {
);
// The actual target run starts here.
// Call LLVMFUzzerInitialize() if present.
unsafe {
if afl_libfuzzer_init() == -1 {