From eebc412fb41018cdf32a79826b8441d90d20e409 Mon Sep 17 00:00:00 2001 From: expend20 <36543551+expend20@users.noreply.github.com> Date: Sun, 18 Sep 2022 15:33:25 +0200 Subject: [PATCH] Windows gdiplus (#789) * Initial steps * Harness code cleanup * don't panic on linux in order not to break the CI * formatting once again * restored cfg unix to unbreak linux build --- fuzzers/frida_gdiplus/.gitignore | 5 + fuzzers/frida_gdiplus/Cargo.toml | 42 ++ fuzzers/frida_gdiplus/README.md | 9 + fuzzers/frida_gdiplus/corpus/not_kitty.png | Bin 0 -> 218 bytes .../frida_gdiplus/corpus/not_kitty_alpha.png | Bin 0 -> 376 bytes .../frida_gdiplus/corpus/not_kitty_gamma.png | Bin 0 -> 228 bytes .../frida_gdiplus/corpus/not_kitty_icc.png | Bin 0 -> 427 bytes fuzzers/frida_gdiplus/harness.cc | 51 ++ fuzzers/frida_gdiplus/src/fuzzer.rs | 481 ++++++++++++++++++ fuzzers/frida_gdiplus/src/main.rs | 4 + libafl/src/bolts/os/windows_exceptions.rs | 7 +- 11 files changed, 597 insertions(+), 2 deletions(-) create mode 100644 fuzzers/frida_gdiplus/.gitignore create mode 100644 fuzzers/frida_gdiplus/Cargo.toml create mode 100644 fuzzers/frida_gdiplus/README.md create mode 100644 fuzzers/frida_gdiplus/corpus/not_kitty.png create mode 100644 fuzzers/frida_gdiplus/corpus/not_kitty_alpha.png create mode 100644 fuzzers/frida_gdiplus/corpus/not_kitty_gamma.png create mode 100644 fuzzers/frida_gdiplus/corpus/not_kitty_icc.png create mode 100644 fuzzers/frida_gdiplus/harness.cc create mode 100644 fuzzers/frida_gdiplus/src/fuzzer.rs create mode 100644 fuzzers/frida_gdiplus/src/main.rs diff --git a/fuzzers/frida_gdiplus/.gitignore b/fuzzers/frida_gdiplus/.gitignore new file mode 100644 index 0000000000..fa4015c1ef --- /dev/null +++ b/fuzzers/frida_gdiplus/.gitignore @@ -0,0 +1,5 @@ +corpus_discovered +*.exp +*.lib +*.obj + diff --git a/fuzzers/frida_gdiplus/Cargo.toml b/fuzzers/frida_gdiplus/Cargo.toml new file mode 100644 index 0000000000..f7fdc0d8fb --- /dev/null +++ b/fuzzers/frida_gdiplus/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "frida_gdiplus" +version = "0.7.0" +authors = ["Richard Johnson "] +edition = "2021" + +[features] +default = ["std"] +std = [] + +[profile.release] +lto = true +codegen-units = 1 +opt-level = 3 +debug = true + +[build-dependencies] +cc = { version = "1.0", features = ["parallel"] } +num_cpus = "1.0" +which = "4.1" +xz = "0.1.0" +flate2 = "1.0.22" +tar = "0.4.37" +reqwest = { version = "0.11.4", features = ["blocking"] } + +[dependencies] +libafl = { path = "../../libafl/", features = [ "std", "llmp_compression", "llmp_bind_public", "frida_cli" ] } #, "llmp_small_maps", "llmp_debug"]} +capstone = "0.11.0" +frida-gum = { version = "0.7.1", features = [ "auto-download", "event-sink", "invocation-listener"] } +libafl_frida = { path = "../../libafl_frida", features = ["cmplog"] } +libafl_targets = { path = "../../libafl_targets", features = ["sancov_cmplog"] } +lazy_static = "1.4.0" +libc = "0.2" +libloading = "0.7" +num-traits = "0.2" +rangemap = "0.1" +clap = { version = "3.2", features = ["derive"] } +serde = "1.0" +mimalloc = { version = "*", default-features = false } + +backtrace = "0.3" +color-backtrace = "0.5" diff --git a/fuzzers/frida_gdiplus/README.md b/fuzzers/frida_gdiplus/README.md new file mode 100644 index 0000000000..cccdefc352 --- /dev/null +++ b/fuzzers/frida_gdiplus/README.md @@ -0,0 +1,9 @@ +## Build + +To build this example, run `cargo build --release` in this folder. + +Then compile the harness `cl.exe /LD harness.cc /link /dll gdiplus.lib ole32.lib` + +## Run + +To run the example `target\release\frida_gdiplus.exe -H harness.dll -i corpus -o output` diff --git a/fuzzers/frida_gdiplus/corpus/not_kitty.png b/fuzzers/frida_gdiplus/corpus/not_kitty.png new file mode 100644 index 0000000000000000000000000000000000000000..eff7c1707b936a8f8df725814f604d454b78b5c3 GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X_yc@GT+_~+`TzevkY_wIZRYx+5&y#hyq+?%!C8<`)MX5lF!N|bSRM)^r*U&J;z}U*bz{;0L z1Vuw`eoAIqC5i?kD`P_|6GMoGiCWXn12ss3YzWRzD=AMbN@Z|N$xljE@XSq2PYp^< WOsOn9nQ8-6#Ng@b=d#Wzp$PyV*n0l} literal 0 HcmV?d00001 diff --git a/fuzzers/frida_gdiplus/corpus/not_kitty_gamma.png b/fuzzers/frida_gdiplus/corpus/not_kitty_gamma.png new file mode 100644 index 0000000000000000000000000000000000000000..939d9d29a9b9f95bac5e9a72854361ee85469921 GIT binary patch literal 228 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmTQ929t;oCfmw1AIbU z)6Sgv|NlRbXFM})=KnKxKI=t+9LW;bh?3y^w370~qErUQl>DSr1<%~X^wgl##FWay zlc_d9MbVxvjv*GO?@o5)YH;9THa`3B|5>?^8?LvjJ}xLe>!7e@k)r^sLedir0mCVe z=5sMjEm$*~tHD+}{NS_$nMdb|ABqg-@UGMMsZ=uY-X%Cq@&3vmZ%&@H{P?6&+U!yq VvuXWlo?M_c44$rjF6*2UngF4cP+$N6 literal 0 HcmV?d00001 diff --git a/fuzzers/frida_gdiplus/corpus/not_kitty_icc.png b/fuzzers/frida_gdiplus/corpus/not_kitty_icc.png new file mode 100644 index 0000000000000000000000000000000000000000..f0c7804d99829cc6307c1c6ae9915cf42d555414 GIT binary patch literal 427 zcmV;c0aX5pP)9xSWu9|B*4Isn^#g47m^r~thH)GiR<@yX0fO)OF<2Kt#qCldyUF#H?{4jV?XGw9)psxE&K1B1m^ z1_tH{2(hG@3=G>_85ksPA;eS`Ffj19FfeR8pIlm01~rBeWCZ{dbvfq;rA3DT000kA zOjJc?%*_A){{R30GnreSaefwW^{L9a%BKPWN%_+AW3auXJt}l zVPtu6$z?nM003J_L_t(I%iWVf3V=Wi12fJ3|IHp$*hSlV@t||fKp?cDK@bHXV&o_g zF_hw;3ILUGteXmeJsVfSmcVJno)^MdQwU3bFHCtNG)uY>mLcD%`0UBaIq~Fq8#dBr V12uok3~c}a002ovPDHLkV1nKBo!S5Z literal 0 HcmV?d00001 diff --git a/fuzzers/frida_gdiplus/harness.cc b/fuzzers/frida_gdiplus/harness.cc new file mode 100644 index 0000000000..11b363a388 --- /dev/null +++ b/fuzzers/frida_gdiplus/harness.cc @@ -0,0 +1,51 @@ +#include +#include +#include +#include + +#include +#include + +#include +#include + +using namespace std; +using namespace Gdiplus; + +GdiplusStartupInput gdiplusStartupInput; +ULONG_PTR gdiplusToken; + +extern "C" __declspec(dllexport) int LLVMFuzzerTestOneInput(const uint8_t *data, + size_t size) { + static DWORD init = 0; + if (!init) { + GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); + init = 1; + } + + HGLOBAL m_hBuffer = ::GlobalAlloc(GMEM_MOVEABLE, size); + if (m_hBuffer) { + void *pBuffer = ::GlobalLock(m_hBuffer); + if (pBuffer) { + CopyMemory(pBuffer, data, size); + + IStream *pStream = NULL; + if (::CreateStreamOnHGlobal(m_hBuffer, FALSE, &pStream) == S_OK) { + Gdiplus::Bitmap *m_pBitmap = Gdiplus::Bitmap::FromStream(pStream); + pStream->Release(); + if (m_pBitmap) { + if (m_pBitmap->GetLastStatus() == Gdiplus::Ok) return true; + + delete m_pBitmap; + m_pBitmap = NULL; + } + } + ::GlobalUnlock(m_hBuffer); + } + ::GlobalFree(m_hBuffer); + m_hBuffer = NULL; + } + + // GdiplusShutdown(gdiplusToken); + return 0; +} diff --git a/fuzzers/frida_gdiplus/src/fuzzer.rs b/fuzzers/frida_gdiplus/src/fuzzer.rs new file mode 100644 index 0000000000..96a72de93b --- /dev/null +++ b/fuzzers/frida_gdiplus/src/fuzzer.rs @@ -0,0 +1,481 @@ +//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts +//! The example harness is built for gdiplus. +use mimalloc::MiMalloc; +#[global_allocator] +static GLOBAL: MiMalloc = MiMalloc; + +use std::path::PathBuf; + +use frida_gum::Gum; +use libafl::{ + bolts::{ + cli::{parse_args, FuzzerOptions}, + current_nanos, + launcher::Launcher, + rands::StdRand, + shmem::{ShMemProvider, StdShMemProvider}, + tuples::{tuple_list, Merge}, + AsSlice, + }, + corpus::{ondisk::OnDiskMetadataFormat, CachedOnDiskCorpus, Corpus, OnDiskCorpus}, + events::{llmp::LlmpRestartingEventManager, EventConfig}, + executors::{inprocess::InProcessExecutor, ExitKind, ShadowExecutor}, + feedback_and_fast, feedback_or, feedback_or_fast, + feedbacks::{ConstFeedback, CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, + fuzzer::{Fuzzer, StdFuzzer}, + inputs::{BytesInput, HasTargetBytes}, + monitors::MultiMonitor, + mutators::{ + scheduled::{havoc_mutations, tokens_mutations, StdScheduledMutator}, + token_mutations::{I2SRandReplace, Tokens}, + }, + observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, + schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, + stages::{ShadowTracingStage, StdMutationalStage}, + state::{HasCorpus, HasMetadata, StdState}, + Error, +}; +#[cfg(unix)] +use libafl_frida::asan::asan_rt::AsanRuntime; +#[cfg(unix)] +use libafl_frida::asan::errors::{AsanErrorsFeedback, AsanErrorsObserver, ASAN_ERRORS}; +use libafl_frida::{ + cmplog_rt::CmpLogRuntime, + coverage_rt::{CoverageRuntime, MAP_SIZE}, + executor::FridaInProcessExecutor, + helper::FridaInstrumentationHelper, +}; +use libafl_targets::cmplog::{CmpLogObserver, CMPLOG_MAP}; + +/// The main fn, usually parsing parameters, and starting the fuzzer +pub fn main() { + color_backtrace::install(); + + let options = parse_args(); + + unsafe { + match fuzz(options) { + Ok(()) | Err(Error::ShuttingDown) => println!("\nFinished fuzzing. Good bye."), + Err(e) => panic!("Error during fuzzing: {:?}", e), + } + } +} + +/// The actual fuzzer +#[allow(clippy::too_many_lines, clippy::too_many_arguments)] +unsafe fn fuzz(options: FuzzerOptions) -> Result<(), Error> { + // '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 shmem_provider = StdShMemProvider::new()?; + + let mut run_client = |state: Option<_>, + mgr: LlmpRestartingEventManager<_, _, _, _>, + core_id| { + // The restarting state will spawn the same process again as child, then restarted it each time it crashes. + + // println!("{:?}", mgr.mgr_id()); + + let lib = libloading::Library::new(options.clone().harness.unwrap()).unwrap(); + let target_func: libloading::Symbol< + unsafe extern "C" fn(data: *const u8, size: usize) -> i32, + > = lib.get(options.harness_function.as_bytes()).unwrap(); + + let mut frida_harness = |input: &BytesInput| { + let target = input.target_bytes(); + let buf = target.as_slice(); + (target_func)(buf.as_ptr(), buf.len()); + ExitKind::Ok + }; + + if options.asan && options.asan_cores.contains(core_id) { + (|state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _, _, _>, _core_id| { + let gum = Gum::obtain(); + + let coverage = CoverageRuntime::new(); + #[cfg(unix)] + let asan = AsanRuntime::new(options.clone()); + + #[cfg(unix)] + 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)); + + // Create an observation channel using the coverage map + let edges_observer = HitcountsMapObserver::new(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"); + + // Feedback to rate the interestingness of an input + // This one is composed by two Feedbacks in OR + let mut feedback = feedback_or!( + // New maximization map feedback linked to the edges observer and the feedback state + MaxMapFeedback::new_tracking(&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 + #[cfg(unix)] + let mut objective = feedback_or_fast!( + CrashFeedback::new(), + TimeoutFeedback::new(), + // true enables the AsanErrorFeedback + feedback_and_fast!(ConstFeedback::from(true), AsanErrorsFeedback::new()) + ); + #[cfg(windows)] + let mut 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(), + &mut feedback, + &mut objective, + ) + .unwrap() + }); + + println!("We're a client, let's fuzz :)"); + + // Create a PNG dictionary if not existing + if state.metadata().get::().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 = IndexesLenTimeMinimizerScheduler::new(QueueScheduler::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(&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) + } else if options.cmplog && options.cmplog_cores.contains(core_id) { + (|state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _, _, _>, _core_id| { + let gum = Gum::obtain(); + + let coverage = CoverageRuntime::new(); + let cmplog = CmpLogRuntime::new(); + + let mut frida_helper = + FridaInstrumentationHelper::new(&gum, &options, tuple_list!(coverage, cmplog)); + + // Create an observation channel using the coverage map + let edges_observer = HitcountsMapObserver::new(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"); + + // Feedback to rate the interestingness of an input + // This one is composed by two Feedbacks in OR + let mut feedback = feedback_or!( + // New maximization map feedback linked to the edges observer and the feedback state + MaxMapFeedback::new_tracking(&edges_observer, true, false), + // Time feedback, this one does not need a feedback state + TimeFeedback::new_with_observer(&time_observer) + ); + + #[cfg(unix)] + let mut objective = feedback_or_fast!( + CrashFeedback::new(), + TimeoutFeedback::new(), + feedback_and_fast!(ConstFeedback::from(false), AsanErrorsFeedback::new()) + ); + #[cfg(windows)] + let mut 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(), + &mut feedback, + &mut objective, + ) + .unwrap() + }); + + println!("We're a client, let's fuzz :)"); + + // Create a PNG dictionary if not existing + if state.metadata().get::().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 = IndexesLenTimeMinimizerScheduler::new(QueueScheduler::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(&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", &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 { + (|state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _, _, _>, _core_id| { + let gum = Gum::obtain(); + + let coverage = CoverageRuntime::new(); + + let mut frida_helper = + FridaInstrumentationHelper::new(&gum, &options, tuple_list!(coverage)); + + // Create an observation channel using the coverage map + let edges_observer = HitcountsMapObserver::new(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"); + + // Feedback to rate the interestingness of an input + // This one is composed by two Feedbacks in OR + let mut feedback = feedback_or!( + // New maximization map feedback linked to the edges observer and the feedback state + MaxMapFeedback::new_tracking(&edges_observer, true, false), + // Time feedback, this one does not need a feedback state + TimeFeedback::new_with_observer(&time_observer) + ); + + #[cfg(unix)] + let mut objective = feedback_or_fast!( + CrashFeedback::new(), + TimeoutFeedback::new(), + feedback_and_fast!(ConstFeedback::from(false), AsanErrorsFeedback::new()) + ); + #[cfg(windows)] + let mut 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(), + &mut feedback, + &mut objective, + ) + .unwrap() + }); + + println!("We're a client, let's fuzz :)"); + + // Create a PNG dictionary if not existing + if state.metadata().get::().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 = IndexesLenTimeMinimizerScheduler::new(QueueScheduler::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(&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() + .configuration(EventConfig::AlwaysUnique) + .shmem_provider(shmem_provider) + .monitor(monitor) + .run_client(&mut run_client) + .cores(&options.cores) + .broker_port(options.broker_port) + .stdout_file(Some(&options.stdout)) + .remote_broker_addr(options.remote_broker_addr) + .build() + .launch() +} diff --git a/fuzzers/frida_gdiplus/src/main.rs b/fuzzers/frida_gdiplus/src/main.rs new file mode 100644 index 0000000000..902dfd4a82 --- /dev/null +++ b/fuzzers/frida_gdiplus/src/main.rs @@ -0,0 +1,4 @@ +mod fuzzer; +pub fn main() { + fuzzer::main(); +} diff --git a/libafl/src/bolts/os/windows_exceptions.rs b/libafl/src/bolts/os/windows_exceptions.rs index 7c41a8ba69..ee255d39da 100644 --- a/libafl/src/bolts/os/windows_exceptions.rs +++ b/libafl/src/bolts/os/windows_exceptions.rs @@ -80,6 +80,7 @@ pub const STATUS_INVALID_CRUNTIME_PARAMETER: i32 = 0xC0000417; pub const STATUS_ASSERTION_FAILURE: i32 = 0xC0000420; pub const STATUS_SXS_EARLY_DEACTIVATION: i32 = 0xC015000F; pub const STATUS_SXS_INVALID_DEACTIVATION: i32 = 0xC0150010; +pub const STATUS_NOT_IMPLEMENTED: i32 = 0xC0000002; #[derive(Debug, TryFromPrimitive, Clone, Copy)] #[repr(i32)] @@ -131,6 +132,7 @@ pub enum ExceptionCode { AssertionFailure = STATUS_ASSERTION_FAILURE, SXSEarlyDeactivation = STATUS_SXS_EARLY_DEACTIVATION, SXSInvalidDeactivation = STATUS_SXS_INVALID_DEACTIVATION, + NotImplemented = STATUS_NOT_IMPLEMENTED, #[num_enum(default)] Other, } @@ -150,7 +152,6 @@ pub static CRASH_EXCEPTIONS: &[ExceptionCode] = &[ ExceptionCode::HeapCorruption, ExceptionCode::StackBufferOverrun, ExceptionCode::AssertionFailure, - ExceptionCode::Other, ]; impl PartialEq for ExceptionCode { @@ -213,6 +214,7 @@ impl Display for ExceptionCode { ExceptionCode::AssertionFailure => write!(f, "STATUS_ASSERTION_FAILURE")?, ExceptionCode::SXSEarlyDeactivation => write!(f, "STATUS_SXS_EARLY_DEACTIVATION")?, ExceptionCode::SXSInvalidDeactivation => write!(f, "STATUS_SXS_INVALID_DEACTIVATION")?, + ExceptionCode::NotImplemented => write!(f, "STATUS_NOT_IMPLEMENTED")?, ExceptionCode::Other => write!(f, "Other/User defined exception")?, }; @@ -220,7 +222,7 @@ impl Display for ExceptionCode { } } -pub static EXCEPTION_CODES_MAPPING: [ExceptionCode; 46] = [ +pub static EXCEPTION_CODES_MAPPING: [ExceptionCode; 47] = [ ExceptionCode::AccessViolation, ExceptionCode::ArrayBoundsExceeded, ExceptionCode::Breakpoint, @@ -266,6 +268,7 @@ pub static EXCEPTION_CODES_MAPPING: [ExceptionCode; 46] = [ ExceptionCode::AssertionFailure, ExceptionCode::SXSEarlyDeactivation, ExceptionCode::SXSInvalidDeactivation, + ExceptionCode::NotImplemented, ExceptionCode::Other, ];