clang wrapper extend api

This commit is contained in:
Andrea Fioraldi 2021-03-23 14:57:44 +01:00
commit 414a66382b
22 changed files with 8971 additions and 641 deletions

View File

@ -63,5 +63,11 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Windows Build - name: Windows Build
run: cargo build --verbose run: cargo build --verbose
- name: Windows Test # TODO: Figure out how to properly build stuff with clang
run: cargo test --verbose #- name: Add clang path to $PATH env
# if: runner.os == 'Windows'
# run: echo "C:\msys64\mingw64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8
#- name: Try if clang works
# run: clang -v
#- name: Windows Test
# run: C:\Rust\.cargo\bin\cargo.exe test --verbose

View File

@ -15,4 +15,5 @@ members = [
"fuzzers/frida_libpng", "fuzzers/frida_libpng",
"fuzzers/libfuzzer_libmozjpeg", "fuzzers/libfuzzer_libmozjpeg",
"fuzzers/libfuzzer_libpng_cmpalloc", "fuzzers/libfuzzer_libpng_cmpalloc",
"fuzzers/libfuzzer_windows",
] ]

View File

@ -90,7 +90,7 @@ fn main() {
.to_command() .to_command()
.current_dir(&cwd) .current_dir(&cwd)
.arg("-I") .arg("-I")
.arg(format!("{}", &libpng)) .arg(&libpng)
//.arg("-D") //.arg("-D")
//.arg("HAS_DUMMY_CRASH=1") //.arg("HAS_DUMMY_CRASH=1")
.arg("-fPIC") .arg("-fPIC")

View File

@ -34,8 +34,6 @@ use frida_gum::{
}; };
use frida_gum::{Gum, MemoryRange, Module, NativePointer, PageProtection}; use frida_gum::{Gum, MemoryRange, Module, NativePointer, PageProtection};
use libloading;
use std::{cell::RefCell, env, ffi::c_void, path::PathBuf}; use std::{cell::RefCell, env, ffi::c_void, path::PathBuf};
/// An helper that feeds FridaInProcessExecutor with user-supplied instrumentation /// An helper that feeds FridaInProcessExecutor with user-supplied instrumentation
@ -351,9 +349,9 @@ where
)); ));
Self { Self {
base: base, base,
stalker: stalker, stalker,
helper: helper, helper,
followed: false, followed: false,
} }
} }
@ -464,10 +462,10 @@ unsafe fn fuzz(
if state.metadata().get::<Tokens>().is_none() { if state.metadata().get::<Tokens>().is_none() {
state.add_metadata(Tokens::new(vec![ state.add_metadata(Tokens::new(vec![
vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header
"IHDR".as_bytes().to_vec(), b"IHDR".to_vec(),
"IDAT".as_bytes().to_vec(), b"IDAT".to_vec(),
"PLTE".as_bytes().to_vec(), b"PLTE".to_vec(),
"IEND".as_bytes().to_vec(), b"IEND".to_vec(),
])); ]));
} }

View File

@ -89,6 +89,10 @@ fn main() {
.include(&libpng_path) .include(&libpng_path)
.cpp(true) .cpp(true)
.flag("-fsanitize-coverage=trace-pc-guard") .flag("-fsanitize-coverage=trace-pc-guard")
.flag("-Wno-void-pointer-to-int-cast")
.flag("-Wno-int-to-pointer-cast")
.flag("-Wno-sign-compare")
.flag("-Wno-format")
// .define("HAS_DUMMY_CRASH", "1") // .define("HAS_DUMMY_CRASH", "1")
.file("./harness.cc") .file("./harness.cc")
.compile("libfuzzer-harness"); .compile("libfuzzer-harness");

View File

@ -131,6 +131,10 @@ void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) {
} }
#ifdef _WIN32
#define posix_memalign(p, a, s) (((*(p)) = _aligned_malloc((s), (a))), *(p) ?0 :errno)
#endif
void *malloc(size_t size) { void *malloc(size_t size) {
uintptr_t k = (uintptr_t)__builtin_return_address(0); uintptr_t k = (uintptr_t)__builtin_return_address(0);

1
fuzzers/libfuzzer_windows/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
libpng-*

View File

@ -0,0 +1,31 @@
[package]
name = "libfuzzer_windows"
version = "0.1.0"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
edition = "2018"
build = "build.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[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"
[dependencies]
libafl = { path = "../../libafl/" }
[[example]]
name = "libfuzzer_windows"
path = "./src/fuzzer.rs"
test = false
bench = false

View File

@ -0,0 +1,25 @@
# Libfuzzer for libpng
This folder contains an example fuzzer for libpng, using LLMP for fast multi-process fuzzing and crash detection.
To show off crash detection, we added a ud2 instruction to the harness, edit harness.cc if you want a non-crashing example.
It has been tested on Linux.
## Build
To build this example, run `cargo build --example libfuzzer_libpng --release`.
This will call (the build.rs)[./builld.rs], which in turn downloads a libpng archive from the web.
Then, it will link (the fuzzer)[./src/fuzzer.rs] against (the C++ harness)[./harness.cc] and the instrumented `libpng`.
Afterwards, the fuzzer will be ready to run, from `../../target/examples/libfuzzer_libpng`.
## Run
The first time you run the binary, the broker will open a tcp port (currently on port `1337`), waiting for fuzzer clients to connect. This port is local and only used for the initial handshake. All further communication happens via shared map, to be independent of the kernel.
Each following execution will run a fuzzer client.
As this example uses in-process fuzzing, we added a Restarting Event Manager (`setup_restarting_mgr`).
This means each client will start itself again to listen for crashes and timeouts.
By restarting the actual fuzzer, it can recover from these exit conditions.
In any real-world scenario, you should use `taskset` to pin each client to an empty CPU core, the lib does not pick an empty core automatically (yet).
For convenience, you may just run `./test.sh` in this folder to test it.

View File

@ -0,0 +1,58 @@
// build.rs
#[cfg(windows)]
use std::env;
#[cfg(not(windows))]
fn main() {
println!("cargo:warning=Skipping libpng windows example on non-Windows");
return;
}
#[cfg(windows)]
fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let out_dir = out_dir.to_string_lossy().to_string();
println!("cargo:rerun-if-changed=../libfuzzer_runtime/rt.c",);
println!("cargo:rerun-if-changed=harness.cc");
// Enforce clang for its -fsanitize-coverage support.
std::env::set_var("CC", "clang");
std::env::set_var("CXX", "clang++");
/*let ldflags = match env::var("LDFLAGS") {
Ok(val) => val,
Err(_) => "".to_string(),
};*/
cc::Build::new()
.file("../libfuzzer_runtime/rt.c")
.compile("libfuzzer-sys");
cc::Build::new()
.cpp(true)
.flag("-fsanitize-coverage=trace-pc-guard")
// .define("HAS_DUMMY_CRASH", "1")
.flag("-Wno-void-pointer-to-int-cast")
.flag("-Wno-pointer-to-int-cast")
.flag("-Wno-int-to-pointer-cast")
.flag("-Wno-sign-compare")
.flag("-Wno-format")
.flag("-Wno-unused-variable")
.file("./harness.cc")
.compile("windows-harness");
println!("cargo:rustc-link-search=native={}", &out_dir);
//println!("cargo:rustc-link-search=native={}/.libs", &libpng);
//println!("cargo:rustc-link-lib=static=png16");
//Deps for libpng: -pthread -lz -lm
//println!("cargo:rustc-link-lib=dylib=m");
//println!("cargo:rustc-link-lib=dylib=z");
//For the C++ harness
//must by dylib for android
//println!("cargo:rustc-link-lib=dylib=stdc++");
println!("cargo:rerun-if-changed=build.rs");
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

View File

@ -0,0 +1,231 @@
#include <stdio.h>
#include <stdint.h>
#include <windows.h>
#define STBI_ASSERT(x)
#define STBI_NO_SIMD
#define STBI_NO_LINEAR
#define STBI_NO_STDIO
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
int target_func(const uint8_t *buf, size_t size) {
/*printf("BUF (%ld): ", size);
for (int i = 0; i < size; i++) {
printf("%02X", buf[i]);
}
printf("\n");*/
if (size == 0) return 0;
switch (buf[0]) {
case 1:
if (buf[1] == 0x44) {
//__builtin_trap();
return 8;
}
break;
case 0xff:
if (buf[2] == 0xff) {
if (buf[1] == 0x44) {
//*(char *)(0xdeadbeef) = 1;
return 9;
}
}
break;
default:
break;
}
return 1;
}
int parse_pe(const uint8_t *data, int size)
{
HANDLE file = NULL;
DWORD fileSize = NULL;
DWORD bytesRead = NULL;
LPVOID fileData = NULL;
PIMAGE_DOS_HEADER dosHeader = {};
PIMAGE_NT_HEADERS imageNTHeaders = {};
PIMAGE_SECTION_HEADER sectionHeader = {};
PIMAGE_SECTION_HEADER importSection = {};
IMAGE_IMPORT_DESCRIPTOR* importDescriptor = {};
PIMAGE_THUNK_DATA thunkData = {};
DWORD thunk = NULL;
DWORD rawOffset = NULL;
// allocate heap
fileSize = size;
fileData = (void *)data;
// IMAGE_DOS_HEADER
dosHeader = (PIMAGE_DOS_HEADER)fileData;
printf("******* DOS HEADER *******\n");
printf("\t0x%x\t\tMagic number\n", dosHeader->e_magic);
/*
printf("\t0x%x\t\tBytes on last page of file\n", dosHeader->e_cblp);
printf("\t0x%x\t\tPages in file\n", dosHeader->e_cp);
printf("\t0x%x\t\tRelocations\n", dosHeader->e_crlc);
printf("\t0x%x\t\tSize of header in paragraphs\n", dosHeader->e_cparhdr);
printf("\t0x%x\t\tMinimum extra paragraphs needed\n", dosHeader->e_minalloc);
printf("\t0x%x\t\tMaximum extra paragraphs needed\n", dosHeader->e_maxalloc);
printf("\t0x%x\t\tInitial (relative) SS value\n", dosHeader->e_ss);
printf("\t0x%x\t\tInitial SP value\n", dosHeader->e_sp);
printf("\t0x%x\t\tInitial SP value\n", dosHeader->e_sp);
printf("\t0x%x\t\tChecksum\n", dosHeader->e_csum);
printf("\t0x%x\t\tInitial IP value\n", dosHeader->e_ip);
printf("\t0x%x\t\tInitial (relative) CS value\n", dosHeader->e_cs);
printf("\t0x%x\t\tFile address of relocation table\n", dosHeader->e_lfarlc);
printf("\t0x%x\t\tOverlay number\n", dosHeader->e_ovno);
printf("\t0x%x\t\tOEM identifier (for e_oeminfo)\n", dosHeader->e_oemid);
printf("\t0x%x\t\tOEM information; e_oemid specific\n", dosHeader->e_oeminfo);
printf("\t0x%x\t\tFile address of new exe header\n", dosHeader->e_lfanew);
*/
// IMAGE_NT_HEADERS
imageNTHeaders = (PIMAGE_NT_HEADERS)((DWORD)fileData + dosHeader->e_lfanew);
/*
printf("\n******* NT HEADERS *******\n");
printf("\t%x\t\tSignature\n", imageNTHeaders->Signature);
// FILE_HEADER
printf("\n******* FILE HEADER *******\n");
printf("\t0x%x\t\tMachine\n", imageNTHeaders->FileHeader.Machine);
printf("\t0x%x\t\tNumber of Sections\n", imageNTHeaders->FileHeader.NumberOfSections);
printf("\t0x%x\tTime Stamp\n", imageNTHeaders->FileHeader.TimeDateStamp);
printf("\t0x%x\t\tPointer to Symbol Table\n", imageNTHeaders->FileHeader.PointerToSymbolTable);
printf("\t0x%x\t\tNumber of Symbols\n", imageNTHeaders->FileHeader.NumberOfSymbols);
printf("\t0x%x\t\tSize of Optional Header\n", imageNTHeaders->FileHeader.SizeOfOptionalHeader);
printf("\t0x%x\t\tCharacteristics\n", imageNTHeaders->FileHeader.Characteristics);
// OPTIONAL_HEADER
printf("\n******* OPTIONAL HEADER *******\n");
printf("\t0x%x\t\tMagic\n", imageNTHeaders->OptionalHeader.Magic);
printf("\t0x%x\t\tMajor Linker Version\n", imageNTHeaders->OptionalHeader.MajorLinkerVersion);
printf("\t0x%x\t\tMinor Linker Version\n", imageNTHeaders->OptionalHeader.MinorLinkerVersion);
printf("\t0x%x\t\tSize Of Code\n", imageNTHeaders->OptionalHeader.SizeOfCode);
printf("\t0x%x\t\tSize Of Initialized Data\n", imageNTHeaders->OptionalHeader.SizeOfInitializedData);
printf("\t0x%x\t\tSize Of UnInitialized Data\n", imageNTHeaders->OptionalHeader.SizeOfUninitializedData);
printf("\t0x%x\t\tAddress Of Entry Point (.text)\n", imageNTHeaders->OptionalHeader.AddressOfEntryPoint);
printf("\t0x%x\t\tBase Of Code\n", imageNTHeaders->OptionalHeader.BaseOfCode);
//printf("\t0x%x\t\tBase Of Data\n", imageNTHeaders->OptionalHeader.BaseOfData);
printf("\t0x%x\t\tImage Base\n", imageNTHeaders->OptionalHeader.ImageBase);
printf("\t0x%x\t\tSection Alignment\n", imageNTHeaders->OptionalHeader.SectionAlignment);
printf("\t0x%x\t\tFile Alignment\n", imageNTHeaders->OptionalHeader.FileAlignment);
printf("\t0x%x\t\tMajor Operating System Version\n", imageNTHeaders->OptionalHeader.MajorOperatingSystemVersion);
printf("\t0x%x\t\tMinor Operating System Version\n", imageNTHeaders->OptionalHeader.MinorOperatingSystemVersion);
printf("\t0x%x\t\tMajor Image Version\n", imageNTHeaders->OptionalHeader.MajorImageVersion);
printf("\t0x%x\t\tMinor Image Version\n", imageNTHeaders->OptionalHeader.MinorImageVersion);
printf("\t0x%x\t\tMajor Subsystem Version\n", imageNTHeaders->OptionalHeader.MajorSubsystemVersion);
printf("\t0x%x\t\tMinor Subsystem Version\n", imageNTHeaders->OptionalHeader.MinorSubsystemVersion);
printf("\t0x%x\t\tWin32 Version Value\n", imageNTHeaders->OptionalHeader.Win32VersionValue);
printf("\t0x%x\t\tSize Of Image\n", imageNTHeaders->OptionalHeader.SizeOfImage);
printf("\t0x%x\t\tSize Of Headers\n", imageNTHeaders->OptionalHeader.SizeOfHeaders);
printf("\t0x%x\t\tCheckSum\n", imageNTHeaders->OptionalHeader.CheckSum);
printf("\t0x%x\t\tSubsystem\n", imageNTHeaders->OptionalHeader.Subsystem);
printf("\t0x%x\t\tDllCharacteristics\n", imageNTHeaders->OptionalHeader.DllCharacteristics);
printf("\t0x%x\t\tSize Of Stack Reserve\n", imageNTHeaders->OptionalHeader.SizeOfStackReserve);
printf("\t0x%x\t\tSize Of Stack Commit\n", imageNTHeaders->OptionalHeader.SizeOfStackCommit);
printf("\t0x%x\t\tSize Of Heap Reserve\n", imageNTHeaders->OptionalHeader.SizeOfHeapReserve);
printf("\t0x%x\t\tSize Of Heap Commit\n", imageNTHeaders->OptionalHeader.SizeOfHeapCommit);
printf("\t0x%x\t\tLoader Flags\n", imageNTHeaders->OptionalHeader.LoaderFlags);
printf("\t0x%x\t\tNumber Of Rva And Sizes\n", imageNTHeaders->OptionalHeader.NumberOfRvaAndSizes);
// DATA_DIRECTORIES
printf("\n******* DATA DIRECTORIES *******\n");
printf("\tExport Directory Address: 0x%x; Size: 0x%x\n", imageNTHeaders->OptionalHeader.DataDirectory[0].VirtualAddress, imageNTHeaders->OptionalHeader.DataDirectory[0].Size);
printf("\tImport Directory Address: 0x%x; Size: 0x%x\n", imageNTHeaders->OptionalHeader.DataDirectory[1].VirtualAddress, imageNTHeaders->OptionalHeader.DataDirectory[1].Size);
*/
return 0;
// SECTION_HEADERS
printf("\n******* SECTION HEADERS *******\n");
// get offset to first section headeer
DWORD sectionLocation = (DWORD)imageNTHeaders + sizeof(DWORD) + (DWORD)(sizeof(IMAGE_FILE_HEADER)) + (DWORD)imageNTHeaders->FileHeader.SizeOfOptionalHeader;
DWORD sectionSize = (DWORD)sizeof(IMAGE_SECTION_HEADER);
// get offset to the import directory RVA
DWORD importDirectoryRVA = imageNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
// print section data
for (int i = 0; i < imageNTHeaders->FileHeader.NumberOfSections; i++) {
sectionHeader = (PIMAGE_SECTION_HEADER)sectionLocation;
printf("\t%s\n", sectionHeader->Name);
printf("\t\t0x%x\t\tVirtual Size\n", sectionHeader->Misc.VirtualSize);
printf("\t\t0x%x\t\tVirtual Address\n", sectionHeader->VirtualAddress);
/*
printf("\t\t0x%x\t\tSize Of Raw Data\n", sectionHeader->SizeOfRawData);
printf("\t\t0x%x\t\tPointer To Raw Data\n", sectionHeader->PointerToRawData);
printf("\t\t0x%x\t\tPointer To Relocations\n", sectionHeader->PointerToRelocations);
printf("\t\t0x%x\t\tPointer To Line Numbers\n", sectionHeader->PointerToLinenumbers);
printf("\t\t0x%x\t\tNumber Of Relocations\n", sectionHeader->NumberOfRelocations);
printf("\t\t0x%x\t\tNumber Of Line Numbers\n", sectionHeader->NumberOfLinenumbers);
printf("\t\t0x%x\tCharacteristics\n", sectionHeader->Characteristics);
*/
// save section that contains import directory table
if (importDirectoryRVA >= sectionHeader->VirtualAddress && importDirectoryRVA < sectionHeader->VirtualAddress + sectionHeader->Misc.VirtualSize) {
importSection = sectionHeader;
}
sectionLocation += sectionSize;
}
// get file offset to import table
rawOffset = (DWORD)fileData + importSection->PointerToRawData;
// get pointer to import descriptor's file offset. Note that the formula for calculating file offset is: imageBaseAddress + pointerToRawDataOfTheSectionContainingRVAofInterest + (RVAofInterest - SectionContainingRVAofInterest.VirtualAddress)
importDescriptor = (IMAGE_IMPORT_DESCRIPTOR*)(rawOffset + (imageNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress - importSection->VirtualAddress));
printf("\n******* DLL IMPORTS *******\n");
for (; importDescriptor->Name != 0; importDescriptor++) {
// imported dll modules
printf("\t%s\n", rawOffset + (importDescriptor->Name - importSection->VirtualAddress));
thunk = importDescriptor->OriginalFirstThunk == 0 ? importDescriptor->FirstThunk : importDescriptor->OriginalFirstThunk;
thunkData = (PIMAGE_THUNK_DATA)(rawOffset + (thunk - importSection->VirtualAddress));
// dll exported functions
for (; thunkData->u1.AddressOfData != 0; thunkData++) {
//a cheap and probably non-reliable way of checking if the function is imported via its ordinal number ¯\_(ツ)_/¯
if (thunkData->u1.AddressOfData > 0x80000000) {
//show lower bits of the value to get the ordinal ¯\_(ツ)_/¯
printf("\t\tOrdinal: %x\n", (WORD)thunkData->u1.AddressOfData);
} else {
printf("\t\t%s\n", (rawOffset + (thunkData->u1.AddressOfData - importSection->VirtualAddress + 2)));
}
}
}
return 0;
}
int load_stbi(const uint8_t *data, int size)
{
int w;
int h;
int channels;
const unsigned char * img = stbi_load_from_memory(data, size, &w, &h, &channels, 0);
if (img) { stbi_image_free((void *)img); }
// STBI_FREE((void *)img); }
return 0;
}
extern "C"
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
//return target_func(Data, Size);
if(Size > 0x4000) return 0;
int size = Size;
const unsigned char * data = Data;
//return load_stbi(data, size);
return parse_pe(data, size);
}

View File

@ -0,0 +1,180 @@
//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts
//! The example harness is built for libpng.
#[cfg(windows)]
use std::{env, path::PathBuf};
#[cfg(windows)]
use libafl::{
bolts::{shmem::Win32ShMem, tuples::tuple_list},
corpus::{
Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus,
QueueCorpusScheduler,
},
events::setup_restarting_mgr,
executors::{inprocess::InProcessExecutor, ExitKind},
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens},
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
stages::mutational::StdMutationalStage,
state::{HasCorpus, HasMetadata, State},
stats::SimpleStats,
utils::{current_nanos, StdRand},
Error,
};
/// We will interact with a C++ target, so use external c functionality
#[cfg(windows)]
extern "C" {
/// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32;
// afl_libfuzzer_init calls LLVMFUzzerInitialize()
fn afl_libfuzzer_init() -> i32;
static __lafl_edges_map: *mut u8;
static __lafl_cmp_map: *mut u8;
static __lafl_max_edges_size: u32;
}
/// The main fn, usually parsing parameters, and starting the fuzzer
pub fn main() {
// Registry the metadata types used in this fuzzer
// Needed only on no_std
//RegistryBuilder::register::<Tokens>();
#[cfg(windows)]
println!(
"Workdir: {:?}",
env::current_dir().unwrap().to_string_lossy().to_string()
);
#[cfg(not(windows))]
todo!("Example currently only supports Windows.");
#[cfg(windows)]
fuzz(
vec![PathBuf::from("./corpus")],
PathBuf::from("./crashes"),
1337,
)
.expect("An error occurred while fuzzing");
}
/// Not supported on unix right now
//#[cfg(cfg)]
//fn fuzz(_corpus_dirs: Vec<PathBuf>, _objective_dir: PathBuf, _broker_port: u16) -> Result<(), ()> {
// todo!("Example not supported on Unix");
//}
/// The actual fuzzer
#[cfg(windows)]
fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> {
// The wrapped harness function, calling out to the LLVM-style harness
let mut harness = |buf: &[u8]| {
unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()) };
ExitKind::Ok
};
// 'While the stats are state, they are usually used in the broker - which is likely never restarted
let stats = SimpleStats::new(|s| println!("{}", s));
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
let (state, mut restarting_mgr) =
match setup_restarting_mgr::<_, _, Win32ShMem, _>(stats, broker_port) {
Ok(res) => res,
Err(err) => match err {
Error::ShuttingDown => {
return Ok(());
}
_ => {
panic!("Failed to setup the restarter: {}", err);
}
},
};
// Create an observation channel using the coverage map
let edges_observer = HitcountsMapObserver::new(unsafe {
StdMapObserver::new_from_ptr("edges", __lafl_edges_map, __lafl_max_edges_size as usize)
});
// If not restarting, create a State from scratch
let mut state = state.unwrap_or_else(|| {
State::new(
// RNG
StdRand::with_seed(current_nanos()),
// Corpus that will be evolved, we keep it in memory for performance
InMemoryCorpus::new(),
// Feedbacks to rate the interestingness of an input
tuple_list!(
MaxMapFeedback::new_with_observer_track(&edges_observer, true, false),
TimeFeedback::new()
),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(objective_dir).unwrap(),
// Feedbacks to recognize an input as solution
tuple_list!(CrashFeedback::new(), TimeoutFeedback::new()),
)
});
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::new(vec![
vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header
"IHDR".as_bytes().to_vec(),
"IDAT".as_bytes().to_vec(),
"PLTE".as_bytes().to_vec(),
"IEND".as_bytes().to_vec(),
]));
}
// Setup a basic mutator with a mutational stage
let mutator = HavocBytesMutator::default();
let stage = StdMutationalStage::new(mutator);
// A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage));
// Create the executor for an in-process function with just one observer for edge coverage
let mut executor = InProcessExecutor::new(
"in-process(edges)",
&mut harness,
tuple_list!(edges_observer, TimeObserver::new("time")),
&mut state,
&mut restarting_mgr,
)?;
// The actual target run starts here.
// Call LLVMFUzzerInitialize() if present.
unsafe {
if afl_libfuzzer_init() == -1 {
println!("Warning: LLVMFuzzerInitialize failed with -1")
}
}
// In case the corpus is empty (on first run), reset
if state.corpus().count() < 1 {
state
.load_initial_inputs(
&mut executor,
&mut restarting_mgr,
fuzzer.scheduler(),
&corpus_dirs,
)
.expect(&format!(
"Failed to load initial corpus at {:?}",
&corpus_dirs
));
println!("We imported {} inputs from disk.", state.corpus().count());
}
fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr)?;
// Never reached
Ok(())
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,20 @@
mkdir crashes
del .\.libfuzzer_test.elf
cargo build --example libfuzzer_windows --release
timeout /T 1
cp ..\..\target\release\examples\libfuzzer_windows.exe .\.libfuzzer_test.exe
timeout /T 1
# The broker
start .\.libfuzzer_test.exe
# Give the broker time to spawn
timeout /T 1
echo "Spawning client"
start .\.libfuzzer_test.exe
# .\.libfuzzer_test.exe > nul
timeout /T 10
echo "Finished fuzzing for a bit"
TASKKILL /IM .libfuzzer_test.exe
del .libfuzzer_test.exe

View File

@ -102,6 +102,7 @@ use crate::{
Error, Error,
}; };
#[cfg(all(unix, feature = "std"))]
use super::shmem::HasFd; use super::shmem::HasFd;
/// We'll start off with 256 megabyte maps per fuzzer client /// We'll start off with 256 megabyte maps per fuzzer client
@ -450,6 +451,7 @@ where
} }
} }
#[cfg(all(unix, feature = "std"))]
impl<SH> LlmpConnection<SH> impl<SH> LlmpConnection<SH>
where where
SH: ShMem + HasFd, SH: ShMem + HasFd,
@ -1888,6 +1890,7 @@ where
/// `n` clients connect to a broker. They share an outgoing map with the broker, /// `n` clients connect to a broker. They share an outgoing map with the broker,
/// and get incoming messages from the shared broker bus /// and get incoming messages from the shared broker bus
/// If the Shm has a fd, we can attach to it. /// If the Shm has a fd, we can attach to it.
#[cfg(all(unix, feature = "std"))]
impl<SH> LlmpClient<SH> impl<SH> LlmpClient<SH>
where where
SH: ShMem + HasFd, SH: ShMem + HasFd,

View File

@ -307,6 +307,8 @@ unsafe extern "system" fn handle_exception(exception_pointers: *mut EXCEPTION_PO
} }
/// Setup Win32 exception handlers in a somewhat rusty way. /// Setup Win32 exception handlers in a somewhat rusty way.
/// # Safety
/// Exception handlers are usually ugly, handle with care!
pub unsafe fn setup_exception_handler<T: 'static + Handler>(handler: &mut T) -> Result<(), Error> { pub unsafe fn setup_exception_handler<T: 'static + Handler>(handler: &mut T) -> Result<(), Error> {
let exceptions = handler.exceptions(); let exceptions = handler.exceptions();
for exception_code in exceptions { for exception_code in exceptions {

View File

@ -98,6 +98,7 @@ pub trait ShMem: Sized + Debug {
} }
/// shared maps that have an id can use this trait /// shared maps that have an id can use this trait
//#[cfg(all(unix, feature = "std"))]
pub trait HasFd { pub trait HasFd {
/// Retrieve the id of this shared map /// Retrieve the id of this shared map
fn shm_id(&self) -> i32; fn shm_id(&self) -> i32;
@ -519,9 +520,8 @@ pub mod shmem {
String::from_utf8_lossy(map_str_bytes) String::from_utf8_lossy(map_str_bytes)
))); )));
} }
let map = let map = MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, map_size) as *mut u8;
MapViewOfFile(handle.clone(), FILE_MAP_ALL_ACCESS, 0, 0, map_size) as *mut u8; if map.is_null() {
if map == ptr::null_mut() {
return Err(Error::Unknown(format!( return Err(Error::Unknown(format!(
"Cannot map shared memory {}", "Cannot map shared memory {}",
String::from_utf8_lossy(map_str_bytes) String::from_utf8_lossy(map_str_bytes)
@ -558,8 +558,7 @@ pub mod shmem {
String::from_utf8_lossy(map_str_bytes) String::from_utf8_lossy(map_str_bytes)
))); )));
} }
let map = let map = MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, map_size) as *mut u8;
MapViewOfFile(handle.clone(), FILE_MAP_ALL_ACCESS, 0, 0, map_size) as *mut u8;
if map == ptr::null_mut() { if map == ptr::null_mut() {
return Err(Error::Unknown(format!( return Err(Error::Unknown(format!(
"Cannot map shared memory {}", "Cannot map shared memory {}",
@ -568,9 +567,9 @@ pub mod shmem {
} }
let mut ret = Self { let mut ret = Self {
shm_str: [0; 20], shm_str: [0; 20],
handle: handle, handle,
map: map, map,
map_size: map_size, map_size,
}; };
ret.shm_str.clone_from_slice(&map_str_bytes[0..20]); ret.shm_str.clone_from_slice(&map_str_bytes[0..20]);
Ok(ret) Ok(ret)

View File

@ -16,10 +16,14 @@ use crate::utils::{fork, ForkResult};
#[cfg(all(feature = "std", unix))] #[cfg(all(feature = "std", unix))]
use crate::bolts::shmem::UnixShMem; use crate::bolts::shmem::UnixShMem;
#[cfg(all(feature = "std", unix))]
use crate::bolts::shmem::HasFd;
use crate::{ use crate::{
bolts::{ bolts::{
llmp::{self, LlmpClient, LlmpClientDescription, LlmpSender, Tag}, llmp::{self, LlmpClient, LlmpClientDescription, LlmpSender, Tag},
shmem::{HasFd, ShMem}, shmem::ShMem,
}, },
corpus::CorpusScheduler, corpus::CorpusScheduler,
events::{BrokerEventResult, Event, EventManager}, events::{BrokerEventResult, Event, EventManager},
@ -304,6 +308,7 @@ where
} }
} }
#[cfg(all(feature = "std", unix))]
impl<I, S, SH, ST> LlmpEventManager<I, S, SH, ST> impl<I, S, SH, ST> LlmpEventManager<I, S, SH, ST>
where where
I: Input, I: Input,
@ -511,7 +516,7 @@ pub fn setup_restarting_mgr<I, S, SH, ST>(
where where
I: Input, I: Input,
S: DeserializeOwned + IfInteresting<I>, S: DeserializeOwned + IfInteresting<I>,
SH: ShMem + HasFd, // Todo: HasFd is only needed for Android SH: ShMem, // Todo: HasFd is only needed for Android
ST: Stats, ST: Stats,
{ {
let mut mgr; let mut mgr;