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
This commit is contained in:
parent
577f0be832
commit
eebc412fb4
5
fuzzers/frida_gdiplus/.gitignore
vendored
Normal file
5
fuzzers/frida_gdiplus/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
corpus_discovered
|
||||||
|
*.exp
|
||||||
|
*.lib
|
||||||
|
*.obj
|
||||||
|
|
42
fuzzers/frida_gdiplus/Cargo.toml
Normal file
42
fuzzers/frida_gdiplus/Cargo.toml
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
[package]
|
||||||
|
name = "frida_gdiplus"
|
||||||
|
version = "0.7.0"
|
||||||
|
authors = ["Richard Johnson <richinseattle@gmail.com>"]
|
||||||
|
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"
|
9
fuzzers/frida_gdiplus/README.md
Normal file
9
fuzzers/frida_gdiplus/README.md
Normal file
@ -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`
|
BIN
fuzzers/frida_gdiplus/corpus/not_kitty.png
Normal file
BIN
fuzzers/frida_gdiplus/corpus/not_kitty.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 218 B |
BIN
fuzzers/frida_gdiplus/corpus/not_kitty_alpha.png
Normal file
BIN
fuzzers/frida_gdiplus/corpus/not_kitty_alpha.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 376 B |
BIN
fuzzers/frida_gdiplus/corpus/not_kitty_gamma.png
Normal file
BIN
fuzzers/frida_gdiplus/corpus/not_kitty_gamma.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 228 B |
BIN
fuzzers/frida_gdiplus/corpus/not_kitty_icc.png
Normal file
BIN
fuzzers/frida_gdiplus/corpus/not_kitty_icc.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 427 B |
51
fuzzers/frida_gdiplus/harness.cc
Normal file
51
fuzzers/frida_gdiplus/harness.cc
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <gdiplus.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
481
fuzzers/frida_gdiplus/src/fuzzer.rs
Normal file
481
fuzzers/frida_gdiplus/src/fuzzer.rs
Normal file
@ -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::<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 = 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::<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 = 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::<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 = 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()
|
||||||
|
}
|
4
fuzzers/frida_gdiplus/src/main.rs
Normal file
4
fuzzers/frida_gdiplus/src/main.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
mod fuzzer;
|
||||||
|
pub fn main() {
|
||||||
|
fuzzer::main();
|
||||||
|
}
|
@ -80,6 +80,7 @@ pub const STATUS_INVALID_CRUNTIME_PARAMETER: i32 = 0xC0000417;
|
|||||||
pub const STATUS_ASSERTION_FAILURE: i32 = 0xC0000420;
|
pub const STATUS_ASSERTION_FAILURE: i32 = 0xC0000420;
|
||||||
pub const STATUS_SXS_EARLY_DEACTIVATION: i32 = 0xC015000F;
|
pub const STATUS_SXS_EARLY_DEACTIVATION: i32 = 0xC015000F;
|
||||||
pub const STATUS_SXS_INVALID_DEACTIVATION: i32 = 0xC0150010;
|
pub const STATUS_SXS_INVALID_DEACTIVATION: i32 = 0xC0150010;
|
||||||
|
pub const STATUS_NOT_IMPLEMENTED: i32 = 0xC0000002;
|
||||||
|
|
||||||
#[derive(Debug, TryFromPrimitive, Clone, Copy)]
|
#[derive(Debug, TryFromPrimitive, Clone, Copy)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
@ -131,6 +132,7 @@ pub enum ExceptionCode {
|
|||||||
AssertionFailure = STATUS_ASSERTION_FAILURE,
|
AssertionFailure = STATUS_ASSERTION_FAILURE,
|
||||||
SXSEarlyDeactivation = STATUS_SXS_EARLY_DEACTIVATION,
|
SXSEarlyDeactivation = STATUS_SXS_EARLY_DEACTIVATION,
|
||||||
SXSInvalidDeactivation = STATUS_SXS_INVALID_DEACTIVATION,
|
SXSInvalidDeactivation = STATUS_SXS_INVALID_DEACTIVATION,
|
||||||
|
NotImplemented = STATUS_NOT_IMPLEMENTED,
|
||||||
#[num_enum(default)]
|
#[num_enum(default)]
|
||||||
Other,
|
Other,
|
||||||
}
|
}
|
||||||
@ -150,7 +152,6 @@ pub static CRASH_EXCEPTIONS: &[ExceptionCode] = &[
|
|||||||
ExceptionCode::HeapCorruption,
|
ExceptionCode::HeapCorruption,
|
||||||
ExceptionCode::StackBufferOverrun,
|
ExceptionCode::StackBufferOverrun,
|
||||||
ExceptionCode::AssertionFailure,
|
ExceptionCode::AssertionFailure,
|
||||||
ExceptionCode::Other,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
impl PartialEq for ExceptionCode {
|
impl PartialEq for ExceptionCode {
|
||||||
@ -213,6 +214,7 @@ impl Display for ExceptionCode {
|
|||||||
ExceptionCode::AssertionFailure => write!(f, "STATUS_ASSERTION_FAILURE")?,
|
ExceptionCode::AssertionFailure => write!(f, "STATUS_ASSERTION_FAILURE")?,
|
||||||
ExceptionCode::SXSEarlyDeactivation => write!(f, "STATUS_SXS_EARLY_DEACTIVATION")?,
|
ExceptionCode::SXSEarlyDeactivation => write!(f, "STATUS_SXS_EARLY_DEACTIVATION")?,
|
||||||
ExceptionCode::SXSInvalidDeactivation => write!(f, "STATUS_SXS_INVALID_DEACTIVATION")?,
|
ExceptionCode::SXSInvalidDeactivation => write!(f, "STATUS_SXS_INVALID_DEACTIVATION")?,
|
||||||
|
ExceptionCode::NotImplemented => write!(f, "STATUS_NOT_IMPLEMENTED")?,
|
||||||
ExceptionCode::Other => write!(f, "Other/User defined exception")?,
|
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::AccessViolation,
|
||||||
ExceptionCode::ArrayBoundsExceeded,
|
ExceptionCode::ArrayBoundsExceeded,
|
||||||
ExceptionCode::Breakpoint,
|
ExceptionCode::Breakpoint,
|
||||||
@ -266,6 +268,7 @@ pub static EXCEPTION_CODES_MAPPING: [ExceptionCode; 46] = [
|
|||||||
ExceptionCode::AssertionFailure,
|
ExceptionCode::AssertionFailure,
|
||||||
ExceptionCode::SXSEarlyDeactivation,
|
ExceptionCode::SXSEarlyDeactivation,
|
||||||
ExceptionCode::SXSInvalidDeactivation,
|
ExceptionCode::SXSInvalidDeactivation,
|
||||||
|
ExceptionCode::NotImplemented,
|
||||||
ExceptionCode::Other,
|
ExceptionCode::Other,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user