Use the new bolts::cli with the frida_libpng sample (#541)
* Use the new bolts::cli with the frida_libpng sample * Fix comment and add must_use * Fix windows * Fix windows more * Fix windows more, more * Fix windows more, more, more * Remove comma * fmt
This commit is contained in:
parent
bf9d2b4c57
commit
f4c4d9044f
@ -28,7 +28,7 @@ reqwest = { version = "0.11.4", features = ["blocking"] }
|
|||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libafl = { path = "../../libafl/", features = [ "std", "llmp_compression", "llmp_bind_public" ] } #, "llmp_small_maps", "llmp_debug"]}
|
libafl = { path = "../../libafl/", features = [ "std", "llmp_compression", "llmp_bind_public", "frida_cli" ] } #, "llmp_small_maps", "llmp_debug"]}
|
||||||
capstone = "0.10.0"
|
capstone = "0.10.0"
|
||||||
frida-gum = { version = "0.6.3", features = [ "auto-download", "event-sink", "invocation-listener"] }
|
frida-gum = { version = "0.6.3", features = [ "auto-download", "event-sink", "invocation-listener"] }
|
||||||
libafl_frida = { path = "../../libafl_frida", features = ["cmplog"] }
|
libafl_frida = { path = "../../libafl_frida", features = ["cmplog"] }
|
||||||
|
@ -4,19 +4,14 @@ use mimalloc::MiMalloc;
|
|||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static GLOBAL: MiMalloc = MiMalloc;
|
static GLOBAL: MiMalloc = MiMalloc;
|
||||||
|
|
||||||
use clap::{self, StructOpt};
|
|
||||||
use frida_gum::Gum;
|
use frida_gum::Gum;
|
||||||
use std::{
|
use std::path::PathBuf;
|
||||||
env,
|
|
||||||
net::SocketAddr,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
};
|
|
||||||
|
|
||||||
use libafl::{
|
use libafl::{
|
||||||
bolts::{
|
bolts::{
|
||||||
|
cli::{parse_args, FuzzerOptions},
|
||||||
current_nanos,
|
current_nanos,
|
||||||
launcher::Launcher,
|
launcher::Launcher,
|
||||||
os::Cores,
|
|
||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
shmem::{ShMemProvider, StdShMemProvider},
|
shmem::{ShMemProvider, StdShMemProvider},
|
||||||
tuples::{tuple_list, Merge},
|
tuples::{tuple_list, Merge},
|
||||||
@ -46,7 +41,7 @@ use libafl::{
|
|||||||
|
|
||||||
use libafl_frida::{
|
use libafl_frida::{
|
||||||
coverage_rt::CoverageRuntime, coverage_rt::MAP_SIZE, executor::FridaInProcessExecutor,
|
coverage_rt::CoverageRuntime, coverage_rt::MAP_SIZE, executor::FridaInProcessExecutor,
|
||||||
helper::FridaInstrumentationHelper, FridaOptions,
|
helper::FridaInstrumentationHelper,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
@ -55,114 +50,16 @@ use libafl_targets::cmplog::{CmpLogObserver, CMPLOG_MAP};
|
|||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use libafl_frida::asan::errors::{AsanErrorsFeedback, AsanErrorsObserver, ASAN_ERRORS};
|
use libafl_frida::asan::errors::{AsanErrorsFeedback, AsanErrorsObserver, ASAN_ERRORS};
|
||||||
|
use libafl_frida::cmplog_rt::CmpLogRuntime;
|
||||||
#[derive(Debug, StructOpt)]
|
|
||||||
#[clap(
|
|
||||||
name = "libafl_frida",
|
|
||||||
version = "0.1.0",
|
|
||||||
about = "A frida-based binary-only libfuzzer-style fuzzer for with llmp-multithreading support",
|
|
||||||
author = "s1341 <github@shmarya.net>,
|
|
||||||
Dongjia Zhang <toka@aflplus.plus>, Andrea Fioraldi <andreafioraldi@gmail.com>, Dominik Maier <domenukk@gmail.com>"
|
|
||||||
)]
|
|
||||||
struct Opt {
|
|
||||||
#[clap(
|
|
||||||
short,
|
|
||||||
long,
|
|
||||||
parse(try_from_str = Cores::from_cmdline),
|
|
||||||
help = "Spawn a client in each of the provided cores. Broker runs in the 0th core. 'all' to select all available cores. 'none' to run a client without binding to any core. eg: '1,2-4,6' selects the cores 1,2,3,4,6.",
|
|
||||||
name = "CORES"
|
|
||||||
)]
|
|
||||||
cores: Cores,
|
|
||||||
|
|
||||||
#[clap(
|
|
||||||
short = 'p',
|
|
||||||
long,
|
|
||||||
help = "Choose the broker TCP port, default is 1337",
|
|
||||||
name = "PORT",
|
|
||||||
default_value = "1337"
|
|
||||||
)]
|
|
||||||
broker_port: u16,
|
|
||||||
|
|
||||||
#[clap(
|
|
||||||
parse(try_from_str),
|
|
||||||
short = 'a',
|
|
||||||
long,
|
|
||||||
help = "Specify a remote broker",
|
|
||||||
name = "REMOTE"
|
|
||||||
)]
|
|
||||||
remote_broker_addr: Option<SocketAddr>,
|
|
||||||
|
|
||||||
#[clap(
|
|
||||||
parse(try_from_str),
|
|
||||||
short,
|
|
||||||
long,
|
|
||||||
help = "Set an initial corpus directory",
|
|
||||||
name = "INPUT"
|
|
||||||
)]
|
|
||||||
input: Vec<PathBuf>,
|
|
||||||
|
|
||||||
#[clap(
|
|
||||||
short,
|
|
||||||
long,
|
|
||||||
parse(try_from_str),
|
|
||||||
help = "Set the output directory, default is ./out",
|
|
||||||
name = "OUTPUT",
|
|
||||||
default_value = "./out"
|
|
||||||
)]
|
|
||||||
output: PathBuf,
|
|
||||||
|
|
||||||
#[clap(
|
|
||||||
long,
|
|
||||||
help = "The configuration this fuzzer runs with, for multiprocessing",
|
|
||||||
name = "CONF",
|
|
||||||
default_value = "default launcher"
|
|
||||||
)]
|
|
||||||
configuration: String,
|
|
||||||
|
|
||||||
#[clap(
|
|
||||||
long,
|
|
||||||
help = "The file to redirect stdout input to (/dev/null if unset)"
|
|
||||||
)]
|
|
||||||
stdout_file: Option<String>,
|
|
||||||
|
|
||||||
#[clap(help = "The harness")]
|
|
||||||
harness: String,
|
|
||||||
|
|
||||||
#[clap(help = "The symbol name to look up and hook")]
|
|
||||||
symbol: String,
|
|
||||||
|
|
||||||
#[clap(help = "The modules to instrument, separated by colons")]
|
|
||||||
modules_to_instrument: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The main fn, usually parsing parameters, and starting the fuzzer
|
/// The main fn, usually parsing parameters, and starting the fuzzer
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
// Registry the metadata types used in this fuzzer
|
|
||||||
// Needed only on no_std
|
|
||||||
//RegistryBuilder::register::<Tokens>();
|
|
||||||
|
|
||||||
let opt = Opt::parse();
|
|
||||||
color_backtrace::install();
|
color_backtrace::install();
|
||||||
|
|
||||||
println!(
|
let options = parse_args();
|
||||||
"Workdir: {:?}",
|
|
||||||
env::current_dir().unwrap().to_string_lossy().to_string()
|
|
||||||
);
|
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
match fuzz(
|
match fuzz(options) {
|
||||||
&opt.harness,
|
|
||||||
&opt.symbol,
|
|
||||||
&opt.modules_to_instrument.split(':').collect::<Vec<_>>(),
|
|
||||||
//modules_to_instrument,
|
|
||||||
&opt.input,
|
|
||||||
&opt.output,
|
|
||||||
opt.broker_port,
|
|
||||||
&opt.cores,
|
|
||||||
opt.stdout_file.as_deref(),
|
|
||||||
opt.remote_broker_addr,
|
|
||||||
opt.configuration,
|
|
||||||
) {
|
|
||||||
Ok(()) | Err(Error::ShuttingDown) => println!("\nFinished fuzzing. Good bye."),
|
Ok(()) | Err(Error::ShuttingDown) => println!("\nFinished fuzzing. Good bye."),
|
||||||
Err(e) => panic!("Error during fuzzing: {:?}", e),
|
Err(e) => panic!("Error during fuzzing: {:?}", e),
|
||||||
}
|
}
|
||||||
@ -171,34 +68,19 @@ pub fn main() {
|
|||||||
|
|
||||||
/// The actual fuzzer
|
/// The actual fuzzer
|
||||||
#[allow(clippy::too_many_lines, clippy::too_many_arguments)]
|
#[allow(clippy::too_many_lines, clippy::too_many_arguments)]
|
||||||
unsafe fn fuzz(
|
unsafe fn fuzz(options: FuzzerOptions) -> Result<(), Error> {
|
||||||
module_name: &str,
|
|
||||||
symbol_name: &str,
|
|
||||||
modules_to_instrument: &[&str],
|
|
||||||
corpus_dirs: &[PathBuf],
|
|
||||||
objective_dir: &Path,
|
|
||||||
broker_port: u16,
|
|
||||||
cores: &Cores,
|
|
||||||
stdout_file: Option<&str>,
|
|
||||||
broker_addr: Option<SocketAddr>,
|
|
||||||
configuration: String,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
// '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 monitor = MultiMonitor::new(|s| println!("{}", s));
|
let monitor = MultiMonitor::new(|s| println!("{}", s));
|
||||||
|
|
||||||
let shmem_provider = StdShMemProvider::new()?;
|
let shmem_provider = StdShMemProvider::new()?;
|
||||||
|
|
||||||
let mut run_client = |state: Option<StdState<_, _, _, _, _>>,
|
let mut run_client = |state: Option<StdState<_, _, _, _, _>>,
|
||||||
mut mgr: LlmpRestartingEventManager<_, _, _, _>,
|
mgr: LlmpRestartingEventManager<_, _, _, _>,
|
||||||
_core_id| {
|
core_id| {
|
||||||
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
|
let lib = libloading::Library::new(options.clone().harness.unwrap()).unwrap();
|
||||||
|
|
||||||
// println!("{:?}", mgr.mgr_id());
|
|
||||||
|
|
||||||
let lib = libloading::Library::new(module_name).unwrap();
|
|
||||||
let target_func: libloading::Symbol<
|
let target_func: libloading::Symbol<
|
||||||
unsafe extern "C" fn(data: *const u8, size: usize) -> i32,
|
unsafe extern "C" fn(data: *const u8, size: usize) -> i32,
|
||||||
> = lib.get(symbol_name.as_bytes()).unwrap();
|
> = lib.get(options.harness_function.as_bytes()).unwrap();
|
||||||
|
|
||||||
let mut frida_harness = |input: &BytesInput| {
|
let mut frida_harness = |input: &BytesInput| {
|
||||||
let target = input.target_bytes();
|
let target = input.target_bytes();
|
||||||
@ -207,168 +89,403 @@ unsafe fn fuzz(
|
|||||||
ExitKind::Ok
|
ExitKind::Ok
|
||||||
};
|
};
|
||||||
|
|
||||||
let gum = Gum::obtain();
|
if options.asan && options.asan_cores.contains(core_id) {
|
||||||
let frida_options = FridaOptions::parse_env_options();
|
(|state: Option<StdState<_, _, _, _, _>>,
|
||||||
let coverage = CoverageRuntime::new();
|
mut mgr: LlmpRestartingEventManager<_, _, _, _>,
|
||||||
// let asan = AsanRuntime::new(frida_options.clone());
|
_core_id| {
|
||||||
let mut frida_helper = FridaInstrumentationHelper::new(
|
let gum = unsafe { Gum::obtain() };
|
||||||
&gum,
|
|
||||||
&frida_options,
|
|
||||||
module_name,
|
|
||||||
modules_to_instrument,
|
|
||||||
tuple_list!(coverage),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create an observation channel using the coverage map
|
let coverage = CoverageRuntime::new();
|
||||||
let edges_observer = HitcountsMapObserver::new(StdMapObserver::new_from_ptr(
|
#[cfg(unix)]
|
||||||
"edges",
|
let asan = AsanRuntime::new(options.clone());
|
||||||
frida_helper.map_ptr_mut().unwrap(),
|
|
||||||
MAP_SIZE,
|
|
||||||
));
|
|
||||||
|
|
||||||
// Create an observation channel to keep track of the execution time
|
#[cfg(unix)]
|
||||||
let time_observer = TimeObserver::new("time");
|
let mut frida_helper =
|
||||||
|
FridaInstrumentationHelper::new(&gum, &options, tuple_list!(coverage, asan));
|
||||||
|
#[cfg(windows)]
|
||||||
|
let mut frida_helper =
|
||||||
|
FridaInstrumentationHelper::new(&gum, &options, tuple_list!(coverage));
|
||||||
|
|
||||||
let feedback_state = MapFeedbackState::with_observer(&edges_observer);
|
// Create an observation channel using the coverage map
|
||||||
// Feedback to rate the interestingness of an input
|
let edges_observer = HitcountsMapObserver::new(unsafe {
|
||||||
// This one is composed by two Feedbacks in OR
|
StdMapObserver::new_from_ptr(
|
||||||
let feedback = feedback_or!(
|
"edges",
|
||||||
// New maximization map feedback linked to the edges observer and the feedback state
|
frida_helper.map_ptr_mut().unwrap(),
|
||||||
MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false),
|
MAP_SIZE,
|
||||||
// Time feedback, this one does not need a feedback state
|
)
|
||||||
TimeFeedback::new_with_observer(&time_observer)
|
});
|
||||||
);
|
|
||||||
|
|
||||||
// Feedbacks to recognize an input as solution
|
// Create an observation channel to keep track of the execution time
|
||||||
|
let time_observer = TimeObserver::new("time");
|
||||||
|
|
||||||
#[cfg(unix)]
|
let feedback_state = MapFeedbackState::with_observer(&edges_observer);
|
||||||
let objective = feedback_or_fast!(
|
|
||||||
CrashFeedback::new(),
|
|
||||||
TimeoutFeedback::new(),
|
|
||||||
AsanErrorsFeedback::new()
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
// Feedback to rate the interestingness of an input
|
||||||
let objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());
|
// This one is composed by two Feedbacks in OR
|
||||||
|
let feedback = feedback_or!(
|
||||||
|
// New maximization map feedback linked to the edges observer and the feedback state
|
||||||
|
MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false),
|
||||||
|
// Time feedback, this one does not need a feedback state
|
||||||
|
TimeFeedback::new_with_observer(&time_observer)
|
||||||
|
);
|
||||||
|
|
||||||
// If not restarting, create a State from scratch
|
// Feedbacks to recognize an input as solution
|
||||||
let mut state = state.unwrap_or_else(|| {
|
#[cfg(unix)]
|
||||||
StdState::new(
|
let objective = feedback_or_fast!(
|
||||||
// RNG
|
CrashFeedback::new(),
|
||||||
StdRand::with_seed(current_nanos()),
|
TimeoutFeedback::new(),
|
||||||
// Corpus that will be evolved, we keep it in memory for performance
|
AsanErrorsFeedback::new()
|
||||||
CachedOnDiskCorpus::new(PathBuf::from("./corpus_discovered"), 64).unwrap(),
|
);
|
||||||
// Corpus in which we store solutions (crashes in this example),
|
#[cfg(windows)]
|
||||||
// on disk so the user can get them after stopping the fuzzer
|
let objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());
|
||||||
OnDiskCorpus::new_save_meta(
|
|
||||||
objective_dir.to_path_buf(),
|
|
||||||
Some(OnDiskMetadataFormat::JsonPretty),
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
// States of the feedbacks.
|
|
||||||
// They are the data related to the feedbacks that you want to persist in the State.
|
|
||||||
tuple_list!(feedback_state),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
println!("We're a client, let's fuzz :)");
|
// If not restarting, create a State from scratch
|
||||||
|
let mut state = state.unwrap_or_else(|| {
|
||||||
|
StdState::new(
|
||||||
|
// RNG
|
||||||
|
StdRand::with_seed(current_nanos()),
|
||||||
|
// Corpus that will be evolved, we keep it in memory for performance
|
||||||
|
CachedOnDiskCorpus::new(PathBuf::from("./corpus_discovered"), 64).unwrap(),
|
||||||
|
// Corpus in which we store solutions (crashes in this example),
|
||||||
|
// on disk so the user can get them after stopping the fuzzer
|
||||||
|
OnDiskCorpus::new_save_meta(
|
||||||
|
options.output.to_path_buf(),
|
||||||
|
Some(OnDiskMetadataFormat::JsonPretty),
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
// States of the feedbacks.
|
||||||
|
// They are the data related to the feedbacks that you want to persist in the State.
|
||||||
|
tuple_list!(feedback_state),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
// Create a PNG dictionary if not existing
|
println!("We're a client, let's fuzz :)");
|
||||||
if state.metadata().get::<Tokens>().is_none() {
|
|
||||||
state.add_metadata(Tokens::from([
|
|
||||||
vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header
|
|
||||||
b"IHDR".to_vec(),
|
|
||||||
b"IDAT".to_vec(),
|
|
||||||
b"PLTE".to_vec(),
|
|
||||||
b"IEND".to_vec(),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup a basic mutator with a mutational stage
|
// Create a PNG dictionary if not existing
|
||||||
let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
|
if state.metadata().get::<Tokens>().is_none() {
|
||||||
|
state.add_metadata(Tokens::from([
|
||||||
|
vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header
|
||||||
|
b"IHDR".to_vec(),
|
||||||
|
b"IDAT".to_vec(),
|
||||||
|
b"PLTE".to_vec(),
|
||||||
|
b"IEND".to_vec(),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
// A minimization+queue policy to get testcasess from the corpus
|
// Setup a basic mutator with a mutational stage
|
||||||
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
|
let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
|
||||||
|
|
||||||
// A fuzzer with feedbacks and a corpus scheduler
|
// A minimization+queue policy to get testcasess from the corpus
|
||||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
let scheduler =
|
||||||
|
IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
|
||||||
|
|
||||||
// Create the executor for an in-process function with just one observer for edge coverage
|
// A fuzzer with feedbacks and a corpus scheduler
|
||||||
#[cfg(unix)]
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
let mut executor = FridaInProcessExecutor::new(
|
|
||||||
&gum,
|
#[cfg(unix)]
|
||||||
InProcessExecutor::new(
|
let observers = tuple_list!(
|
||||||
&mut frida_harness,
|
|
||||||
tuple_list!(
|
|
||||||
edges_observer,
|
edges_observer,
|
||||||
time_observer,
|
time_observer,
|
||||||
AsanErrorsObserver::new(&ASAN_ERRORS)
|
AsanErrorsObserver::new(unsafe { &ASAN_ERRORS })
|
||||||
),
|
);
|
||||||
&mut fuzzer,
|
#[cfg(windows)]
|
||||||
&mut state,
|
let observers = tuple_list!(edges_observer, time_observer);
|
||||||
&mut mgr,
|
|
||||||
)?,
|
|
||||||
&mut frida_helper,
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
// Create the executor for an in-process function with just one observer for edge coverage
|
||||||
let mut executor = FridaInProcessExecutor::new(
|
let mut executor = FridaInProcessExecutor::new(
|
||||||
&gum,
|
&gum,
|
||||||
InProcessExecutor::new(
|
InProcessExecutor::new(
|
||||||
&mut frida_harness,
|
&mut frida_harness,
|
||||||
tuple_list!(edges_observer, time_observer,),
|
observers,
|
||||||
&mut fuzzer,
|
&mut fuzzer,
|
||||||
&mut state,
|
&mut state,
|
||||||
&mut mgr,
|
&mut mgr,
|
||||||
)?,
|
)?,
|
||||||
&mut frida_helper,
|
&mut frida_helper,
|
||||||
);
|
);
|
||||||
|
|
||||||
// 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 {
|
||||||
state
|
state
|
||||||
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, corpus_dirs)
|
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &options.input)
|
||||||
.unwrap_or_else(|_| panic!("Failed to load initial corpus at {:?}", &corpus_dirs));
|
.unwrap_or_else(|_| {
|
||||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
panic!("Failed to load initial corpus at {:?}", &options.input)
|
||||||
}
|
});
|
||||||
|
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||||
|
}
|
||||||
|
|
||||||
if frida_options.cmplog_enabled() {
|
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
||||||
// Create an observation channel using cmplog map
|
|
||||||
let cmplog_observer = CmpLogObserver::new("cmplog", &mut CMPLOG_MAP, true);
|
|
||||||
|
|
||||||
let mut executor = ShadowExecutor::new(executor, tuple_list!(cmplog_observer));
|
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
|
||||||
|
|
||||||
let tracing = ShadowTracingStage::new(&mut executor);
|
Ok(())
|
||||||
|
})(state, mgr, core_id)
|
||||||
|
} else if options.cmplog && options.cmplog_cores.contains(core_id) {
|
||||||
|
(|state: Option<StdState<_, _, _, _, _>>,
|
||||||
|
mut mgr: LlmpRestartingEventManager<_, _, _, _>,
|
||||||
|
_core_id| {
|
||||||
|
let gum = unsafe { Gum::obtain() };
|
||||||
|
|
||||||
// Setup a randomic Input2State stage
|
let coverage = CoverageRuntime::new();
|
||||||
let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(
|
let cmplog = CmpLogRuntime::new();
|
||||||
I2SRandReplace::new()
|
|
||||||
)));
|
|
||||||
|
|
||||||
// Setup a basic mutator
|
let mut frida_helper =
|
||||||
let mutational = StdMutationalStage::new(mutator);
|
FridaInstrumentationHelper::new(&gum, &options, tuple_list!(coverage, cmplog));
|
||||||
|
|
||||||
// The order of the stages matter!
|
// Create an observation channel using the coverage map
|
||||||
let mut stages = tuple_list!(tracing, i2s, mutational);
|
let edges_observer = HitcountsMapObserver::new(unsafe {
|
||||||
|
StdMapObserver::new_from_ptr(
|
||||||
|
"edges",
|
||||||
|
frida_helper.map_ptr_mut().unwrap(),
|
||||||
|
MAP_SIZE,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
|
// Create an observation channel to keep track of the execution time
|
||||||
|
let time_observer = TimeObserver::new("time");
|
||||||
|
|
||||||
|
let feedback_state = MapFeedbackState::with_observer(&edges_observer);
|
||||||
|
|
||||||
|
// Feedback to rate the interestingness of an input
|
||||||
|
// This one is composed by two Feedbacks in OR
|
||||||
|
let feedback = feedback_or!(
|
||||||
|
// New maximization map feedback linked to the edges observer and the feedback state
|
||||||
|
MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false),
|
||||||
|
// Time feedback, this one does not need a feedback state
|
||||||
|
TimeFeedback::new_with_observer(&time_observer)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Feedbacks to recognize an input as solution
|
||||||
|
let objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());
|
||||||
|
|
||||||
|
// If not restarting, create a State from scratch
|
||||||
|
let mut state = state.unwrap_or_else(|| {
|
||||||
|
StdState::new(
|
||||||
|
// RNG
|
||||||
|
StdRand::with_seed(current_nanos()),
|
||||||
|
// Corpus that will be evolved, we keep it in memory for performance
|
||||||
|
CachedOnDiskCorpus::new(PathBuf::from("./corpus_discovered"), 64).unwrap(),
|
||||||
|
// Corpus in which we store solutions (crashes in this example),
|
||||||
|
// on disk so the user can get them after stopping the fuzzer
|
||||||
|
OnDiskCorpus::new_save_meta(
|
||||||
|
options.output.to_path_buf(),
|
||||||
|
Some(OnDiskMetadataFormat::JsonPretty),
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
// States of the feedbacks.
|
||||||
|
// They are the data related to the feedbacks that you want to persist in the State.
|
||||||
|
tuple_list!(feedback_state),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
println!("We're a client, let's fuzz :)");
|
||||||
|
|
||||||
|
// Create a PNG dictionary if not existing
|
||||||
|
if state.metadata().get::<Tokens>().is_none() {
|
||||||
|
state.add_metadata(Tokens::from([
|
||||||
|
vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header
|
||||||
|
b"IHDR".to_vec(),
|
||||||
|
b"IDAT".to_vec(),
|
||||||
|
b"PLTE".to_vec(),
|
||||||
|
b"IEND".to_vec(),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup a basic mutator with a mutational stage
|
||||||
|
let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
|
||||||
|
|
||||||
|
// A minimization+queue policy to get testcasess from the corpus
|
||||||
|
let scheduler =
|
||||||
|
IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
|
||||||
|
|
||||||
|
// A fuzzer with feedbacks and a corpus scheduler
|
||||||
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
let observers = tuple_list!(
|
||||||
|
edges_observer,
|
||||||
|
time_observer,
|
||||||
|
AsanErrorsObserver::new(unsafe { &ASAN_ERRORS })
|
||||||
|
);
|
||||||
|
#[cfg(windows)]
|
||||||
|
let observers = tuple_list!(edges_observer, time_observer,);
|
||||||
|
|
||||||
|
// Create the executor for an in-process function with just one observer for edge coverage
|
||||||
|
let mut executor = FridaInProcessExecutor::new(
|
||||||
|
&gum,
|
||||||
|
InProcessExecutor::new(
|
||||||
|
&mut frida_harness,
|
||||||
|
observers,
|
||||||
|
&mut fuzzer,
|
||||||
|
&mut state,
|
||||||
|
&mut mgr,
|
||||||
|
)?,
|
||||||
|
&mut frida_helper,
|
||||||
|
);
|
||||||
|
|
||||||
|
// In case the corpus is empty (on first run), reset
|
||||||
|
if state.corpus().count() < 1 {
|
||||||
|
state
|
||||||
|
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &options.input)
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
panic!("Failed to load initial corpus at {:?}", &options.input)
|
||||||
|
});
|
||||||
|
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an observation channel using cmplog map
|
||||||
|
let cmplog_observer =
|
||||||
|
CmpLogObserver::new("cmplog", unsafe { &mut CMPLOG_MAP }, true);
|
||||||
|
|
||||||
|
let mut executor = ShadowExecutor::new(executor, tuple_list!(cmplog_observer));
|
||||||
|
|
||||||
|
let tracing = ShadowTracingStage::new(&mut executor);
|
||||||
|
|
||||||
|
// Setup a randomic Input2State stage
|
||||||
|
let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(
|
||||||
|
I2SRandReplace::new()
|
||||||
|
)));
|
||||||
|
|
||||||
|
// Setup a basic mutator
|
||||||
|
let mutational = StdMutationalStage::new(mutator);
|
||||||
|
|
||||||
|
// The order of the stages matter!
|
||||||
|
let mut stages = tuple_list!(tracing, i2s, mutational);
|
||||||
|
|
||||||
|
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})(state, mgr, core_id)
|
||||||
} else {
|
} else {
|
||||||
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
(|state: Option<StdState<_, _, _, _, _>>,
|
||||||
|
mut mgr: LlmpRestartingEventManager<_, _, _, _>,
|
||||||
|
_core_id| {
|
||||||
|
let gum = unsafe { Gum::obtain() };
|
||||||
|
|
||||||
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
|
let coverage = CoverageRuntime::new();
|
||||||
};
|
|
||||||
Ok(())
|
let mut frida_helper =
|
||||||
|
FridaInstrumentationHelper::new(&gum, &options, tuple_list!(coverage));
|
||||||
|
|
||||||
|
// Create an observation channel using the coverage map
|
||||||
|
let edges_observer = HitcountsMapObserver::new(unsafe {
|
||||||
|
StdMapObserver::new_from_ptr(
|
||||||
|
"edges",
|
||||||
|
frida_helper.map_ptr_mut().unwrap(),
|
||||||
|
MAP_SIZE,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create an observation channel to keep track of the execution time
|
||||||
|
let time_observer = TimeObserver::new("time");
|
||||||
|
|
||||||
|
let feedback_state = MapFeedbackState::with_observer(&edges_observer);
|
||||||
|
|
||||||
|
// Feedback to rate the interestingness of an input
|
||||||
|
// This one is composed by two Feedbacks in OR
|
||||||
|
let feedback = feedback_or!(
|
||||||
|
// New maximization map feedback linked to the edges observer and the feedback state
|
||||||
|
MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false),
|
||||||
|
// Time feedback, this one does not need a feedback state
|
||||||
|
TimeFeedback::new_with_observer(&time_observer)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Feedbacks to recognize an input as solution
|
||||||
|
let objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());
|
||||||
|
|
||||||
|
// If not restarting, create a State from scratch
|
||||||
|
let mut state = state.unwrap_or_else(|| {
|
||||||
|
StdState::new(
|
||||||
|
// RNG
|
||||||
|
StdRand::with_seed(current_nanos()),
|
||||||
|
// Corpus that will be evolved, we keep it in memory for performance
|
||||||
|
CachedOnDiskCorpus::new(PathBuf::from("./corpus_discovered"), 64).unwrap(),
|
||||||
|
// Corpus in which we store solutions (crashes in this example),
|
||||||
|
// on disk so the user can get them after stopping the fuzzer
|
||||||
|
OnDiskCorpus::new_save_meta(
|
||||||
|
options.output.to_path_buf(),
|
||||||
|
Some(OnDiskMetadataFormat::JsonPretty),
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
// States of the feedbacks.
|
||||||
|
// They are the data related to the feedbacks that you want to persist in the State.
|
||||||
|
tuple_list!(feedback_state),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
println!("We're a client, let's fuzz :)");
|
||||||
|
|
||||||
|
// Create a PNG dictionary if not existing
|
||||||
|
if state.metadata().get::<Tokens>().is_none() {
|
||||||
|
state.add_metadata(Tokens::from([
|
||||||
|
vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header
|
||||||
|
b"IHDR".to_vec(),
|
||||||
|
b"IDAT".to_vec(),
|
||||||
|
b"PLTE".to_vec(),
|
||||||
|
b"IEND".to_vec(),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup a basic mutator with a mutational stage
|
||||||
|
let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
|
||||||
|
|
||||||
|
// A minimization+queue policy to get testcasess from the corpus
|
||||||
|
let scheduler =
|
||||||
|
IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
|
||||||
|
|
||||||
|
// A fuzzer with feedbacks and a corpus scheduler
|
||||||
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
let observers = tuple_list!(
|
||||||
|
edges_observer,
|
||||||
|
time_observer,
|
||||||
|
AsanErrorsObserver::new(unsafe { &ASAN_ERRORS })
|
||||||
|
);
|
||||||
|
#[cfg(windows)]
|
||||||
|
let observers = tuple_list!(edges_observer, time_observer,);
|
||||||
|
|
||||||
|
// Create the executor for an in-process function with just one observer for edge coverage
|
||||||
|
let mut executor = FridaInProcessExecutor::new(
|
||||||
|
&gum,
|
||||||
|
InProcessExecutor::new(
|
||||||
|
&mut frida_harness,
|
||||||
|
observers,
|
||||||
|
&mut fuzzer,
|
||||||
|
&mut state,
|
||||||
|
&mut mgr,
|
||||||
|
)?,
|
||||||
|
&mut frida_helper,
|
||||||
|
);
|
||||||
|
|
||||||
|
// In case the corpus is empty (on first run), reset
|
||||||
|
if state.corpus().count() < 1 {
|
||||||
|
state
|
||||||
|
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &options.input)
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
panic!("Failed to load initial corpus at {:?}", &options.input)
|
||||||
|
});
|
||||||
|
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
||||||
|
|
||||||
|
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})(state, mgr, core_id)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Launcher::builder()
|
Launcher::builder()
|
||||||
.configuration(EventConfig::from_name(&configuration))
|
.configuration(EventConfig::AlwaysUnique)
|
||||||
.shmem_provider(shmem_provider)
|
.shmem_provider(shmem_provider)
|
||||||
.monitor(monitor)
|
.monitor(monitor)
|
||||||
.run_client(&mut run_client)
|
.run_client(&mut run_client)
|
||||||
.cores(cores)
|
.cores(&options.cores)
|
||||||
.broker_port(broker_port)
|
.broker_port(options.broker_port)
|
||||||
.stdout_file(stdout_file)
|
.stdout_file(Some(&options.stdout))
|
||||||
.remote_broker_addr(broker_addr)
|
.remote_broker_addr(options.remote_broker_addr)
|
||||||
.build()
|
.build()
|
||||||
.launch()
|
.launch()
|
||||||
}
|
}
|
||||||
|
@ -64,6 +64,7 @@
|
|||||||
//!```
|
//!```
|
||||||
|
|
||||||
use clap::{App, AppSettings, IntoApp, Parser};
|
use clap::{App, AppSettings, IntoApp, Parser};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
#[cfg(feature = "frida_cli")]
|
#[cfg(feature = "frida_cli")]
|
||||||
use std::error;
|
use std::error;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
@ -102,7 +103,7 @@ fn parse_instrumentation_location(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Top-level container for cli options/arguments/subcommands
|
/// Top-level container for cli options/arguments/subcommands
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Clone, Debug, Serialize, Deserialize)]
|
||||||
#[clap(
|
#[clap(
|
||||||
setting(AppSettings::ArgRequiredElseHelp),
|
setting(AppSettings::ArgRequiredElseHelp),
|
||||||
setting(AppSettings::SubcommandPrecedenceOverArg),
|
setting(AppSettings::SubcommandPrecedenceOverArg),
|
||||||
@ -122,10 +123,21 @@ pub struct FuzzerOptions {
|
|||||||
#[clap(short, long, default_value = "/dev/null")]
|
#[clap(short, long, default_value = "/dev/null")]
|
||||||
pub stdout: String,
|
pub stdout: String,
|
||||||
|
|
||||||
|
/// the name of the configuration to use
|
||||||
|
#[clap(short, long, default_value = "default configuration")]
|
||||||
|
pub configuration: String,
|
||||||
|
|
||||||
/// enable Address Sanitizer (ASAN)
|
/// enable Address Sanitizer (ASAN)
|
||||||
#[clap(short = 'A', long, help_heading = "Fuzz Options")]
|
#[clap(short = 'A', long, help_heading = "Fuzz Options")]
|
||||||
pub asan: bool,
|
pub asan: bool,
|
||||||
|
|
||||||
|
/// Enable ASAN on each of the provided cores. Use 'all' to select all available
|
||||||
|
/// cores. 'none' to run a client without binding to any core.
|
||||||
|
/// ex: '1,2-4,6' selects the cores 1, 2, 3, 4, and 6.
|
||||||
|
#[cfg(feature = "frida_cli")]
|
||||||
|
#[clap(short, long, default_value = "0", parse(try_from_str = Cores::from_cmdline), help_heading = "ASAN Options")]
|
||||||
|
pub asan_cores: Cores,
|
||||||
|
|
||||||
/// number of fuzz iterations to perform
|
/// number of fuzz iterations to perform
|
||||||
#[clap(short = 'I', long, help_heading = "Fuzz Options", default_value = "0")]
|
#[clap(short = 'I', long, help_heading = "Fuzz Options", default_value = "0")]
|
||||||
pub iterations: usize,
|
pub iterations: usize,
|
||||||
@ -139,6 +151,21 @@ pub struct FuzzerOptions {
|
|||||||
#[clap(last = true, name = "HARNESS_ARGS")]
|
#[clap(last = true, name = "HARNESS_ARGS")]
|
||||||
pub harness_args: Vec<String>,
|
pub harness_args: Vec<String>,
|
||||||
|
|
||||||
|
/// harness function to call
|
||||||
|
#[cfg(feature = "frida_cli")]
|
||||||
|
#[clap(
|
||||||
|
short = 'F',
|
||||||
|
long,
|
||||||
|
default_value = "LLVMFuzzerTestOneInput",
|
||||||
|
help_heading = "Frida Options"
|
||||||
|
)]
|
||||||
|
pub harness_function: String,
|
||||||
|
|
||||||
|
/// additional libraries to instrument
|
||||||
|
#[cfg(feature = "frida_cli")]
|
||||||
|
#[clap(short, long, help_heading = "Frida Options")]
|
||||||
|
pub libs_to_instrument: Vec<String>,
|
||||||
|
|
||||||
/// enable CmpLog instrumentation
|
/// enable CmpLog instrumentation
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "frida_cli",
|
feature = "frida_cli",
|
||||||
@ -150,6 +177,13 @@ pub struct FuzzerOptions {
|
|||||||
)]
|
)]
|
||||||
pub cmplog: bool,
|
pub cmplog: bool,
|
||||||
|
|
||||||
|
/// Enable CmpLog on each of the provided cores. Use 'all' to select all available
|
||||||
|
/// cores. 'none' to run a client without binding to any core.
|
||||||
|
/// ex: '1,2-4,6' selects the cores 1, 2, 3, 4, and 6.
|
||||||
|
#[cfg(feature = "frida_cli")]
|
||||||
|
#[clap(short, long, default_value = "0", parse(try_from_str = Cores::from_cmdline), help_heading = "Frida Options")]
|
||||||
|
pub cmplog_cores: Cores,
|
||||||
|
|
||||||
/// enable ASAN leak detection
|
/// enable ASAN leak detection
|
||||||
#[cfg(feature = "frida_cli")]
|
#[cfg(feature = "frida_cli")]
|
||||||
#[clap(short, long, help_heading = "ASAN Options")]
|
#[clap(short, long, help_heading = "ASAN Options")]
|
||||||
|
@ -5,6 +5,7 @@ use alloc::{
|
|||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[cfg(any(unix, all(windows, feature = "std")))]
|
#[cfg(any(unix, all(windows, feature = "std")))]
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
@ -106,7 +107,7 @@ pub fn dup2(fd: i32, device: i32) -> Result<(), Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Core ID
|
/// Core ID
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct CoreId {
|
pub struct CoreId {
|
||||||
/// The id of this core
|
/// The id of this core
|
||||||
pub id: usize,
|
pub id: usize,
|
||||||
@ -155,7 +156,7 @@ impl From<core_affinity::CoreId> for CoreId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A list of [`CoreId`] to use for fuzzing
|
/// A list of [`CoreId`] to use for fuzzing
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct Cores {
|
pub struct Cores {
|
||||||
/// The original commandline used during parsing
|
/// The original commandline used during parsing
|
||||||
pub cmdline: String,
|
pub cmdline: String,
|
||||||
@ -218,6 +219,13 @@ impl Cores {
|
|||||||
ids: cores,
|
ids: cores,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if this [`Cores`] instance contains a given ``core_id``
|
||||||
|
#[must_use]
|
||||||
|
pub fn contains(&self, core_id: usize) -> bool {
|
||||||
|
let core_id = CoreId::from(core_id);
|
||||||
|
self.ids.contains(&core_id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&[usize]> for Cores {
|
impl From<&[usize]> for Cores {
|
||||||
|
@ -19,7 +19,7 @@ cmplog = []
|
|||||||
cc = { version = "1.0", features = ["parallel"] }
|
cc = { version = "1.0", features = ["parallel"] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libafl = { path = "../libafl", version = "0.7.1", features = ["std", "libafl_derive"] }
|
libafl = { path = "../libafl", version = "0.7.1", features = ["std", "libafl_derive", "frida_cli"] }
|
||||||
libafl_targets = { path = "../libafl_targets", version = "0.7.1", features = ["std", "sancov_cmplog"] }
|
libafl_targets = { path = "../libafl_targets", version = "0.7.1", features = ["std", "sancov_cmplog"] }
|
||||||
|
|
||||||
nix = "0.23"
|
nix = "0.23"
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use frida_gum::{PageProtection, RangeDetails};
|
use frida_gum::{PageProtection, RangeDetails};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
|
use libafl::bolts::cli::FuzzerOptions;
|
||||||
use nix::{
|
use nix::{
|
||||||
libc::memset,
|
libc::memset,
|
||||||
sys::mman::{mmap, MapFlags, ProtFlags},
|
sys::mman::{mmap, MapFlags, ProtFlags},
|
||||||
@ -22,17 +23,14 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::io;
|
use std::io;
|
||||||
use std::{collections::BTreeMap, ffi::c_void};
|
use std::{collections::BTreeMap, ffi::c_void};
|
||||||
|
|
||||||
use crate::{
|
use crate::asan::errors::{AsanError, AsanErrors};
|
||||||
asan::errors::{AsanError, AsanErrors},
|
|
||||||
FridaOptions,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// An allocator wrapper with binary-only address sanitization
|
/// An allocator wrapper with binary-only address sanitization
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub struct Allocator {
|
pub struct Allocator {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
options: FridaOptions,
|
options: FuzzerOptions,
|
||||||
page_size: usize,
|
page_size: usize,
|
||||||
shadow_offset: usize,
|
shadow_offset: usize,
|
||||||
shadow_bit: usize,
|
shadow_bit: usize,
|
||||||
@ -78,7 +76,7 @@ impl Allocator {
|
|||||||
all(target_arch = "aarch64", target_os = "android")
|
all(target_arch = "aarch64", target_os = "android")
|
||||||
)))]
|
)))]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(_: FridaOptions) -> Self {
|
pub fn new(_: FuzzerOptions) -> Self {
|
||||||
todo!("Shadow region not yet supported for this platform!");
|
todo!("Shadow region not yet supported for this platform!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,7 +88,7 @@ impl Allocator {
|
|||||||
))]
|
))]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
pub fn new(options: FridaOptions) -> Self {
|
pub fn new(options: FuzzerOptions) -> Self {
|
||||||
let ret = unsafe { sysconf(_SC_PAGESIZE) };
|
let ret = unsafe { sysconf(_SC_PAGESIZE) };
|
||||||
assert!(
|
assert!(
|
||||||
ret >= 0,
|
ret >= 0,
|
||||||
@ -259,9 +257,9 @@ impl Allocator {
|
|||||||
} else {
|
} else {
|
||||||
size
|
size
|
||||||
};
|
};
|
||||||
if size > self.options.asan_max_allocation() {
|
if size > self.options.max_allocation {
|
||||||
#[allow(clippy::manual_assert)]
|
#[allow(clippy::manual_assert)]
|
||||||
if self.options.asan_max_allocation_panics() {
|
if self.options.max_allocation_panics {
|
||||||
panic!("ASAN: Allocation is too large: 0x{:x}", size);
|
panic!("ASAN: Allocation is too large: 0x{:x}", size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,7 +267,7 @@ impl Allocator {
|
|||||||
}
|
}
|
||||||
let rounded_up_size = self.round_up_to_page(size) + 2 * self.page_size;
|
let rounded_up_size = self.round_up_to_page(size) + 2 * self.page_size;
|
||||||
|
|
||||||
if self.total_allocation_size + rounded_up_size > self.options.asan_max_total_allocation() {
|
if self.total_allocation_size + rounded_up_size > self.options.max_total_allocation {
|
||||||
return std::ptr::null_mut();
|
return std::ptr::null_mut();
|
||||||
}
|
}
|
||||||
self.total_allocation_size += rounded_up_size;
|
self.total_allocation_size += rounded_up_size;
|
||||||
@ -278,7 +276,7 @@ impl Allocator {
|
|||||||
//println!("reusing allocation at {:x}, (actual mapping starts at {:x}) size {:x}", metadata.address, metadata.address - self.page_size, size);
|
//println!("reusing allocation at {:x}, (actual mapping starts at {:x}) size {:x}", metadata.address, metadata.address - self.page_size, size);
|
||||||
metadata.is_malloc_zero = is_malloc_zero;
|
metadata.is_malloc_zero = is_malloc_zero;
|
||||||
metadata.size = size;
|
metadata.size = size;
|
||||||
if self.options.enable_asan_allocation_backtraces {
|
if self.options.allocation_backtraces {
|
||||||
metadata.allocation_site_backtrace = Some(Backtrace::new_unresolved());
|
metadata.allocation_site_backtrace = Some(Backtrace::new_unresolved());
|
||||||
}
|
}
|
||||||
metadata
|
metadata
|
||||||
@ -311,7 +309,7 @@ impl Allocator {
|
|||||||
actual_size: rounded_up_size,
|
actual_size: rounded_up_size,
|
||||||
..AllocationMetadata::default()
|
..AllocationMetadata::default()
|
||||||
};
|
};
|
||||||
if self.options.enable_asan_allocation_backtraces {
|
if self.options.allocation_backtraces {
|
||||||
metadata.allocation_site_backtrace = Some(Backtrace::new_unresolved());
|
metadata.allocation_site_backtrace = Some(Backtrace::new_unresolved());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,7 +354,7 @@ impl Allocator {
|
|||||||
let shadow_mapping_start = map_to_shadow!(self, ptr as usize);
|
let shadow_mapping_start = map_to_shadow!(self, ptr as usize);
|
||||||
|
|
||||||
metadata.freed = true;
|
metadata.freed = true;
|
||||||
if self.options.enable_asan_allocation_backtraces {
|
if self.options.allocation_backtraces {
|
||||||
metadata.release_site_backtrace = Some(Backtrace::new_unresolved());
|
metadata.release_site_backtrace = Some(Backtrace::new_unresolved());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ use core::{
|
|||||||
};
|
};
|
||||||
use frida_gum::{ModuleDetails, NativePointer, RangeDetails};
|
use frida_gum::{ModuleDetails, NativePointer, RangeDetails};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use libafl::bolts::AsSlice;
|
use libafl::bolts::{cli::FuzzerOptions, AsSlice};
|
||||||
use nix::sys::mman::{mmap, MapFlags, ProtFlags};
|
use nix::sys::mman::{mmap, MapFlags, ProtFlags};
|
||||||
use rangemap::RangeMap;
|
use rangemap::RangeMap;
|
||||||
|
|
||||||
@ -58,7 +58,6 @@ use crate::{
|
|||||||
asan::errors::{AsanError, AsanErrors, AsanReadWriteError, ASAN_ERRORS},
|
asan::errors::{AsanError, AsanErrors, AsanReadWriteError, ASAN_ERRORS},
|
||||||
helper::FridaRuntime,
|
helper::FridaRuntime,
|
||||||
utils::writer_register,
|
utils::writer_register,
|
||||||
FridaOptions,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
@ -135,7 +134,7 @@ pub struct AsanRuntime {
|
|||||||
blob_check_mem_48bytes: Option<Box<[u8]>>,
|
blob_check_mem_48bytes: Option<Box<[u8]>>,
|
||||||
blob_check_mem_64bytes: Option<Box<[u8]>>,
|
blob_check_mem_64bytes: Option<Box<[u8]>>,
|
||||||
stalked_addresses: HashMap<usize, usize>,
|
stalked_addresses: HashMap<usize, usize>,
|
||||||
options: FridaOptions,
|
options: FuzzerOptions,
|
||||||
module_map: Option<ModuleMap>,
|
module_map: Option<ModuleMap>,
|
||||||
suppressed_addresses: Vec<usize>,
|
suppressed_addresses: Vec<usize>,
|
||||||
shadow_check_func: Option<extern "C" fn(*const c_void, usize) -> bool>,
|
shadow_check_func: Option<extern "C" fn(*const c_void, usize) -> bool>,
|
||||||
@ -172,8 +171,8 @@ impl FridaRuntime for AsanRuntime {
|
|||||||
self.unpoison_all_existing_memory();
|
self.unpoison_all_existing_memory();
|
||||||
|
|
||||||
self.module_map = Some(ModuleMap::new_from_names(modules_to_instrument));
|
self.module_map = Some(ModuleMap::new_from_names(modules_to_instrument));
|
||||||
if let Some(suppressed_specifiers) = self.options.dont_instrument_locations() {
|
if !self.options.dont_instrument.is_empty() {
|
||||||
for (module_name, offset) in suppressed_specifiers {
|
for (module_name, offset) in self.options.dont_instrument.clone() {
|
||||||
let module_details = ModuleDetails::with_name(module_name).unwrap();
|
let module_details = ModuleDetails::with_name(module_name).unwrap();
|
||||||
let lib_start = module_details.range().base_address().0 as usize;
|
let lib_start = module_details.range().base_address().0 as usize;
|
||||||
self.suppressed_addresses.push(lib_start + offset);
|
self.suppressed_addresses.push(lib_start + offset);
|
||||||
@ -288,9 +287,9 @@ impl FridaRuntime for AsanRuntime {
|
|||||||
impl AsanRuntime {
|
impl AsanRuntime {
|
||||||
/// Create a new `AsanRuntime`
|
/// Create a new `AsanRuntime`
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(options: FridaOptions) -> AsanRuntime {
|
pub fn new(options: FuzzerOptions) -> AsanRuntime {
|
||||||
Self {
|
Self {
|
||||||
check_for_leaks_enabled: options.asan_detect_leaks(),
|
check_for_leaks_enabled: options.detect_leaks,
|
||||||
current_report_impl: 0,
|
current_report_impl: 0,
|
||||||
allocator: Allocator::new(options.clone()),
|
allocator: Allocator::new(options.clone()),
|
||||||
regs: [0; ASAN_SAVE_REGISTER_COUNT],
|
regs: [0; ASAN_SAVE_REGISTER_COUNT],
|
||||||
|
@ -8,7 +8,7 @@ use color_backtrace::{default_output_stream, BacktracePrinter, Verbosity};
|
|||||||
use frida_gum::interceptor::Interceptor;
|
use frida_gum::interceptor::Interceptor;
|
||||||
use frida_gum::ModuleDetails;
|
use frida_gum::ModuleDetails;
|
||||||
use libafl::{
|
use libafl::{
|
||||||
bolts::{ownedref::OwnedPtr, tuples::Named},
|
bolts::{cli::FuzzerOptions, ownedref::OwnedPtr, tuples::Named},
|
||||||
corpus::Testcase,
|
corpus::Testcase,
|
||||||
events::EventFirer,
|
events::EventFirer,
|
||||||
executors::ExitKind,
|
executors::ExitKind,
|
||||||
@ -22,7 +22,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use termcolor::{Color, ColorSpec, WriteColor};
|
use termcolor::{Color, ColorSpec, WriteColor};
|
||||||
|
|
||||||
use crate::{alloc::AllocationMetadata, asan::asan_rt::ASAN_SAVE_REGISTER_COUNT, FridaOptions};
|
use crate::{alloc::AllocationMetadata, asan::asan_rt::ASAN_SAVE_REGISTER_COUNT};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub(crate) struct AsanReadWriteError {
|
pub(crate) struct AsanReadWriteError {
|
||||||
@ -94,14 +94,14 @@ impl AsanError {
|
|||||||
#[allow(clippy::unsafe_derive_deserialize)]
|
#[allow(clippy::unsafe_derive_deserialize)]
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, SerdeAny)]
|
#[derive(Debug, Clone, Serialize, Deserialize, SerdeAny)]
|
||||||
pub struct AsanErrors {
|
pub struct AsanErrors {
|
||||||
options: FridaOptions,
|
options: FuzzerOptions,
|
||||||
errors: Vec<AsanError>,
|
errors: Vec<AsanError>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsanErrors {
|
impl AsanErrors {
|
||||||
/// Creates a new `AsanErrors` struct
|
/// Creates a new `AsanErrors` struct
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(options: FridaOptions) -> Self {
|
pub fn new(options: FuzzerOptions) -> Self {
|
||||||
Self {
|
Self {
|
||||||
options,
|
options,
|
||||||
errors: Vec::new(),
|
errors: Vec::new(),
|
||||||
@ -534,7 +534,7 @@ impl AsanErrors {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[allow(clippy::manual_assert)]
|
#[allow(clippy::manual_assert)]
|
||||||
if !self.options.asan_continue_after_error() {
|
if !self.options.continue_on_error {
|
||||||
panic!("ASAN: Crashing target!");
|
panic!("ASAN: Crashing target!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use libafl::{
|
use libafl::{
|
||||||
bolts::tuples::MatchFirstType,
|
bolts::{cli::FuzzerOptions, tuples::MatchFirstType},
|
||||||
inputs::{HasTargetBytes, Input},
|
inputs::{HasTargetBytes, Input},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
@ -10,10 +10,8 @@ use libafl_targets::drcov::DrCovBasicBlock;
|
|||||||
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
|
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
|
||||||
use crate::cmplog_rt::CmpLogRuntime;
|
use crate::cmplog_rt::CmpLogRuntime;
|
||||||
use crate::coverage_rt::CoverageRuntime;
|
use crate::coverage_rt::CoverageRuntime;
|
||||||
#[cfg(windows)]
|
|
||||||
use crate::FridaOptions;
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use crate::{asan::asan_rt::AsanRuntime, drcov_rt::DrCovRuntime, FridaOptions};
|
use crate::{asan::asan_rt::AsanRuntime, drcov_rt::DrCovRuntime};
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
use capstone::{
|
use capstone::{
|
||||||
arch::{self, BuildsCapstone},
|
arch::{self, BuildsCapstone},
|
||||||
@ -124,7 +122,7 @@ pub struct FridaInstrumentationHelper<'a, RT> {
|
|||||||
capstone: Capstone,
|
capstone: Capstone,
|
||||||
ranges: RangeMap<usize, (u16, String)>,
|
ranges: RangeMap<usize, (u16, String)>,
|
||||||
module_map: ModuleMap,
|
module_map: ModuleMap,
|
||||||
options: &'a FridaOptions,
|
options: &'a FuzzerOptions,
|
||||||
runtimes: RT,
|
runtimes: RT,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,13 +168,7 @@ where
|
|||||||
/// Constructor function to create a new [`FridaInstrumentationHelper`], given a `module_name`.
|
/// Constructor function to create a new [`FridaInstrumentationHelper`], given a `module_name`.
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(
|
pub fn new(gum: &'a Gum, options: &'a FuzzerOptions, runtimes: RT) -> Self {
|
||||||
gum: &'a Gum,
|
|
||||||
options: &'a FridaOptions,
|
|
||||||
_harness_module_name: &str,
|
|
||||||
modules_to_instrument: &'a [&str],
|
|
||||||
runtimes: RT,
|
|
||||||
) -> Self {
|
|
||||||
// workaround frida's frida-gum-allocate-near bug:
|
// workaround frida's frida-gum-allocate-near bug:
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -202,6 +194,16 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut modules_to_instrument = vec![options
|
||||||
|
.harness
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string()];
|
||||||
|
modules_to_instrument.append(&mut options.libs_to_instrument.clone());
|
||||||
|
let modules_to_instrument: Vec<&str> =
|
||||||
|
modules_to_instrument.iter().map(AsRef::as_ref).collect();
|
||||||
|
|
||||||
let mut helper = Self {
|
let mut helper = Self {
|
||||||
transformer: None,
|
transformer: None,
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
@ -219,12 +221,12 @@ where
|
|||||||
.build()
|
.build()
|
||||||
.expect("Failed to create Capstone object"),
|
.expect("Failed to create Capstone object"),
|
||||||
ranges: RangeMap::new(),
|
ranges: RangeMap::new(),
|
||||||
module_map: ModuleMap::new_from_names(modules_to_instrument),
|
module_map: ModuleMap::new_from_names(&modules_to_instrument),
|
||||||
options,
|
options,
|
||||||
runtimes,
|
runtimes,
|
||||||
};
|
};
|
||||||
|
|
||||||
if helper.options().stalker_enabled() {
|
if options.cmplog || options.asan || !options.disable_coverage {
|
||||||
for (i, module) in helper.module_map.values().iter().enumerate() {
|
for (i, module) in helper.module_map.values().iter().enumerate() {
|
||||||
let range = module.range();
|
let range = module.range();
|
||||||
let start = range.base_address().0 as usize;
|
let start = range.base_address().0 as usize;
|
||||||
@ -233,8 +235,8 @@ where
|
|||||||
.ranges
|
.ranges
|
||||||
.insert(start..(start + range.size()), (i as u16, module.path()));
|
.insert(start..(start + range.size()), (i as u16, module.path()));
|
||||||
}
|
}
|
||||||
if let Some(suppressed_specifiers) = helper.options().dont_instrument_locations() {
|
if !options.dont_instrument.is_empty() {
|
||||||
for (module_name, offset) in suppressed_specifiers {
|
for (module_name, offset) in options.dont_instrument.clone() {
|
||||||
let module_details = ModuleDetails::with_name(module_name).unwrap();
|
let module_details = ModuleDetails::with_name(module_name).unwrap();
|
||||||
let lib_start = module_details.range().base_address().0 as usize;
|
let lib_start = module_details.range().base_address().0 as usize;
|
||||||
// println!("removing address: {:#x}", lib_start + offset);
|
// println!("removing address: {:#x}", lib_start + offset);
|
||||||
@ -354,7 +356,7 @@ where
|
|||||||
helper.transformer = Some(transformer);
|
helper.transformer = Some(transformer);
|
||||||
helper
|
helper
|
||||||
.runtimes
|
.runtimes
|
||||||
.init_all(gum, &helper.ranges, modules_to_instrument);
|
.init_all(gum, &helper.ranges, &modules_to_instrument);
|
||||||
}
|
}
|
||||||
helper
|
helper
|
||||||
}
|
}
|
||||||
@ -402,7 +404,7 @@ where
|
|||||||
|
|
||||||
/// If stalker is enabled
|
/// If stalker is enabled
|
||||||
pub fn stalker_enabled(&self) -> bool {
|
pub fn stalker_enabled(&self) -> bool {
|
||||||
self.options.stalker_enabled()
|
self.options.cmplog || self.options.asan || !self.options.disable_coverage
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pointer to coverage map
|
/// Pointer to coverage map
|
||||||
@ -423,7 +425,7 @@ where
|
|||||||
|
|
||||||
/// Return the ref to options
|
/// Return the ref to options
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn options(&self) -> &FridaOptions {
|
pub fn options(&self) -> &FuzzerOptions {
|
||||||
self.options
|
self.options
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user