mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-10 01:01:33 +00:00
commit
41788950cc
10
README.md
10
README.md
@ -2,9 +2,9 @@
|
||||
|
||||
<img align="right" src="https://raw.githubusercontent.com/andreafioraldi/AFLplusplus-website/master/static/logo_256x256.png" alt="AFL++ Logo">
|
||||
|
||||
Release Version: [3.00c](https://github.com/AFLplusplus/AFLplusplus/releases)
|
||||
Release Version: [3.10c](https://github.com/AFLplusplus/AFLplusplus/releases)
|
||||
|
||||
Github Version: 3.01a
|
||||
Github Version: 3.11a
|
||||
|
||||
Repository: [https://github.com/AFLplusplus/AFLplusplus](https://github.com/AFLplusplus/AFLplusplus)
|
||||
|
||||
@ -25,14 +25,14 @@
|
||||
For comparisons use the fuzzbench `aflplusplus` setup, or use `afl-clang-fast`
|
||||
with `AFL_LLVM_CMPLOG=1`.
|
||||
|
||||
## Major changes in afl++ 3.0 + 3.1
|
||||
## Major changes in afl++ 3.00 + 3.10
|
||||
|
||||
With afl++ 3.1 we introduced the following changes from previous behaviours:
|
||||
With afl++ 3.10 we introduced the following changes from previous behaviours:
|
||||
* The '+' feature of the '-t' option now means to auto-calculate the timeout
|
||||
with the value given being the maximum timeout. The original meaning of
|
||||
"skipping timeouts instead of abort" is now inherent to the -t option.
|
||||
|
||||
With afl++ 3.0 we introduced changes that break some previous afl and afl++
|
||||
With afl++ 3.00 we introduced changes that break some previous afl and afl++
|
||||
behaviours and defaults:
|
||||
|
||||
* There are no llvm_mode and gcc_plugin subdirectories anymore and there is
|
||||
|
@ -49,6 +49,12 @@ if [ "$PLATFORM" = "FreeBSD" ] ; then
|
||||
sysctl kern.elf64.aslr.enable=0
|
||||
} > /dev/null
|
||||
echo Settings applied.
|
||||
cat <<EOF
|
||||
In order to suppress core file generation during fuzzing it is recommended to set
|
||||
me:\\
|
||||
:coredumpsize=0:
|
||||
in the ~/.login_conf file for the user used for fuzzing.
|
||||
EOF
|
||||
echo It is recommended to boot the kernel with lots of security off - if you are running a machine that is in a secured network - so set this:
|
||||
echo ' sysctl hw.ibrs_disable=1'
|
||||
echo 'Setting kern.pmap.pg_ps_enabled=0 into /boot/loader.conf might be helpful too.'
|
||||
@ -60,8 +66,14 @@ if [ "$PLATFORM" = "OpenBSD" ] ; then
|
||||
DONE=1
|
||||
fi
|
||||
if [ "$PLATFORM" = "DragonFly" ] ; then
|
||||
echo
|
||||
echo 'System security features cannot be disabled on DragonFly.'
|
||||
#/sbin/sysctl kern.corefile=/dev/null
|
||||
#echo Settings applied.
|
||||
cat <<EOF
|
||||
In order to suppress core file generation during fuzzing it is recommended to set
|
||||
me:\\
|
||||
:coredumpsize=0:
|
||||
in the ~/.login_conf file for the user used for fuzzing.
|
||||
EOF
|
||||
DONE=1
|
||||
fi
|
||||
if [ "$PLATFORM" = "NetBSD" ] ; then
|
||||
@ -88,7 +100,7 @@ fi
|
||||
if [ "$PLATFORM" = "Haiku" ] ; then
|
||||
SETTINGS=~/config/settings/system/debug_server/settings
|
||||
[ -r ${SETTINGS} ] && grep -qE "default_action\s+kill" ${SETTINGS} && { echo "Nothing to do"; } || { \
|
||||
echo We change the debug_server default_action from user to silenty kill; \
|
||||
echo We change the debug_server default_action from user to silently kill; \
|
||||
[ ! -r ${SETTINGS} ] && echo "default_action kill" >${SETTINGS} || { mv ${SETTINGS} s.tmp; sed -e "s/default_action\s\s*user/default_action kill/" s.tmp > ${SETTINGS}; rm s.tmp; }; \
|
||||
echo Settings applied.; \
|
||||
}
|
||||
|
10
custom_mutators/rust/.gitignore
vendored
Normal file
10
custom_mutators/rust/.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
8
custom_mutators/rust/Cargo.toml
Normal file
8
custom_mutators/rust/Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"custom_mutator-sys",
|
||||
"custom_mutator",
|
||||
"example",
|
||||
# Lain needs a nightly toolchain
|
||||
# "example_lain",
|
||||
]
|
11
custom_mutators/rust/README.md
Normal file
11
custom_mutators/rust/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Rust Custom Mutators
|
||||
|
||||
Bindings to create custom mutators in Rust.
|
||||
|
||||
These bindings are documented with rustdoc. To view the documentation run
|
||||
```cargo doc -p custom_mutator --open```.
|
||||
|
||||
A minimal example can be found in `example`. Build it using `cargo build --example example_mutator`.
|
||||
|
||||
An example using [lain](https://github.com/microsoft/lain) for structured fuzzing can be found in `example_lain`.
|
||||
Since lain requires a nightly rust toolchain, you need to set one up before you can play with it.
|
12
custom_mutators/rust/custom_mutator-sys/Cargo.toml
Normal file
12
custom_mutators/rust/custom_mutator-sys/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "custom_mutator-sys"
|
||||
version = "0.1.0"
|
||||
authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.56"
|
42
custom_mutators/rust/custom_mutator-sys/build.rs
Normal file
42
custom_mutators/rust/custom_mutator-sys/build.rs
Normal file
@ -0,0 +1,42 @@
|
||||
extern crate bindgen;
|
||||
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
// this code is largely taken straight from the handbook: https://github.com/fitzgen/bindgen-tutorial-bzip2-sys
|
||||
fn main() {
|
||||
// Tell cargo to invalidate the built crate whenever the wrapper changes
|
||||
println!("cargo:rerun-if-changed=wrapper.h");
|
||||
|
||||
// The bindgen::Builder is the main entry point
|
||||
// to bindgen, and lets you build up options for
|
||||
// the resulting bindings.
|
||||
let bindings = bindgen::Builder::default()
|
||||
// The input header we would like to generate
|
||||
// bindings for.
|
||||
.header("wrapper.h")
|
||||
.whitelist_type("afl_state_t")
|
||||
.blacklist_type(r"u\d+")
|
||||
.opaque_type(r"_.*")
|
||||
.opaque_type("FILE")
|
||||
.opaque_type("in_addr(_t)?")
|
||||
.opaque_type("in_port(_t)?")
|
||||
.opaque_type("sa_family(_t)?")
|
||||
.opaque_type("sockaddr_in(_t)?")
|
||||
.opaque_type("time_t")
|
||||
.rustfmt_bindings(true)
|
||||
.size_t_is_usize(true)
|
||||
// Tell cargo to invalidate the built crate whenever any of the
|
||||
// included header files changed.
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
|
||||
// Finish the builder and generate the bindings.
|
||||
.generate()
|
||||
// Unwrap the Result and panic on failure.
|
||||
.expect("Unable to generate bindings");
|
||||
|
||||
// Write the bindings to the $OUT_DIR/bindings.rs file.
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
bindings
|
||||
.write_to_file(out_path.join("bindings.rs"))
|
||||
.expect("Couldn't write bindings!");
|
||||
}
|
5
custom_mutators/rust/custom_mutator-sys/src/lib.rs
Normal file
5
custom_mutators/rust/custom_mutator-sys/src/lib.rs
Normal file
@ -0,0 +1,5 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
4
custom_mutators/rust/custom_mutator-sys/wrapper.h
Normal file
4
custom_mutators/rust/custom_mutator-sys/wrapper.h
Normal file
@ -0,0 +1,4 @@
|
||||
#include "../../../include/afl-fuzz.h"
|
||||
#include "../../../include/common.h"
|
||||
#include "../../../include/config.h"
|
||||
#include "../../../include/debug.h"
|
13
custom_mutators/rust/custom_mutator/Cargo.toml
Normal file
13
custom_mutators/rust/custom_mutator/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "custom_mutator"
|
||||
version = "0.1.0"
|
||||
authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
afl_internals = ["custom_mutator-sys"]
|
||||
|
||||
[dependencies]
|
||||
custom_mutator-sys = { path = "../custom_mutator-sys", optional=true }
|
634
custom_mutators/rust/custom_mutator/src/lib.rs
Normal file
634
custom_mutators/rust/custom_mutator/src/lib.rs
Normal file
@ -0,0 +1,634 @@
|
||||
//! Somewhat safe and somewhat ergonomic bindings for creating [AFL++](https://github.com/AFLplusplus/AFLplusplus) [custom mutators](https://github.com/AFLplusplus/AFLplusplus/blob/stable/docs/custom_mutators.md) in Rust.
|
||||
//!
|
||||
//! # Usage
|
||||
//! AFL++ custom mutators are expected to be dynamic libraries which expose a set of symbols.
|
||||
//! Check out [`CustomMutator`] to see which functions of the API are supported.
|
||||
//! Then use [`export_mutator`] to export the correct symbols for your mutator.
|
||||
//! In order to use the mutator, your crate needs to be a library crate and have a `crate-type` of `cdylib`.
|
||||
//! Putting
|
||||
//! ```yaml
|
||||
//! [lib]
|
||||
//! crate-type = ["cdylib"]
|
||||
//! ```
|
||||
//! into your `Cargo.toml` should do the trick.
|
||||
//! The final executable can be found in `target/(debug|release)/your_crate_name.so`.
|
||||
//! # Example
|
||||
//! See [`export_mutator`] for an example.
|
||||
//!
|
||||
//! # On `panic`s
|
||||
//! This binding is panic-safe in that it will prevent panics from unwinding into AFL++. Any panic will `abort` at the boundary between the custom mutator and AFL++.
|
||||
//!
|
||||
//! # Access to AFL++ internals
|
||||
//! This crate has an optional feature "afl_internals", which gives access to AFL++'s internal state.
|
||||
//! The state is passed to [`CustomMutator::init`], when the feature is activated.
|
||||
//!
|
||||
//! _This is completely unsafe and uses automatically generated types extracted from the AFL++ source._
|
||||
use std::{ffi::CStr, fmt::Debug};
|
||||
|
||||
#[cfg(feature = "afl_internals")]
|
||||
#[doc(hidden)]
|
||||
pub use custom_mutator_sys::afl_state;
|
||||
|
||||
#[allow(unused_variables)]
|
||||
#[doc(hidden)]
|
||||
pub trait RawCustomMutator {
|
||||
#[cfg(feature = "afl_internals")]
|
||||
fn init(afl: &'static afl_state, seed: c_uint) -> Self
|
||||
where
|
||||
Self: Sized;
|
||||
#[cfg(not(feature = "afl_internals"))]
|
||||
fn init(seed: u32) -> Self
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
fn fuzz<'b, 's: 'b>(
|
||||
&'s mut self,
|
||||
buffer: &'b mut [u8],
|
||||
add_buff: Option<&[u8]>,
|
||||
max_size: usize,
|
||||
) -> Option<&'b [u8]>;
|
||||
|
||||
fn fuzz_count(&mut self, buffer: &[u8]) -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
fn queue_new_entry(&mut self, filename_new_queue: &CStr, _filename_orig_queue: Option<&CStr>) {}
|
||||
|
||||
fn queue_get(&mut self, filename: &CStr) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn describe(&mut self, max_description: usize) -> Option<&CStr> {
|
||||
None
|
||||
}
|
||||
|
||||
fn introspection(&mut self) -> Option<&CStr> {
|
||||
None
|
||||
}
|
||||
|
||||
/*fn post_process(&self, buffer: &[u8], unsigned char **out_buf)-> usize;
|
||||
int afl_custom_init_trim(&self, buffer: &[u8]);
|
||||
size_t afl_custom_trim(&self, unsigned char **out_buf);
|
||||
int afl_custom_post_trim(&self, unsigned char success);
|
||||
size_t afl_custom_havoc_mutation(&self, buffer: &[u8], unsigned char **out_buf, size_t max_size);
|
||||
unsigned char afl_custom_havoc_mutation_probability(&self);*/
|
||||
}
|
||||
|
||||
/// Wrappers for the custom mutator which provide the bridging between the C API and CustomMutator.
|
||||
/// These wrappers are not intended to be used directly, rather export_mutator will use them to publish the custom mutator C API.
|
||||
#[doc(hidden)]
|
||||
pub mod wrappers {
|
||||
#[cfg(feature = "afl_internals")]
|
||||
use custom_mutator_sys::afl_state;
|
||||
|
||||
use core::slice;
|
||||
use std::{
|
||||
any::Any,
|
||||
convert::TryInto,
|
||||
ffi::{c_void, CStr},
|
||||
mem::ManuallyDrop,
|
||||
os::raw::c_char,
|
||||
panic::catch_unwind,
|
||||
process::abort,
|
||||
ptr::null,
|
||||
};
|
||||
|
||||
use crate::RawCustomMutator;
|
||||
|
||||
/// A structure to be used as the data pointer for our custom mutator. This was used as additional storage and is kept for now in case its needed later.
|
||||
/// Also has some convenience functions for FFI conversions (from and to ptr) and tries to make misuse hard (see [`FFIContext::from`]).
|
||||
struct FFIContext<M: RawCustomMutator> {
|
||||
mutator: M,
|
||||
}
|
||||
|
||||
impl<M: RawCustomMutator> FFIContext<M> {
|
||||
fn from(ptr: *mut c_void) -> ManuallyDrop<Box<Self>> {
|
||||
assert!(!ptr.is_null());
|
||||
ManuallyDrop::new(unsafe { Box::from_raw(ptr as *mut Self) })
|
||||
}
|
||||
|
||||
fn into_ptr(self: Box<Self>) -> *const c_void {
|
||||
Box::into_raw(self) as *const c_void
|
||||
}
|
||||
|
||||
#[cfg(feature = "afl_internals")]
|
||||
fn new(afl: &'static afl_state, seed: u32) -> Box<Self> {
|
||||
Box::new(Self {
|
||||
mutator: M::init(afl, seed),
|
||||
})
|
||||
}
|
||||
#[cfg(not(feature = "afl_internals"))]
|
||||
fn new(seed: u32) -> Box<Self> {
|
||||
Box::new(Self {
|
||||
mutator: M::init(seed),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// panic handler called for every panic
|
||||
fn panic_handler(method: &str, panic_info: Box<dyn Any + Send + 'static>) -> ! {
|
||||
use std::ops::Deref;
|
||||
let cause = panic_info
|
||||
.downcast_ref::<String>()
|
||||
.map(String::deref)
|
||||
.unwrap_or_else(|| {
|
||||
panic_info
|
||||
.downcast_ref::<&str>()
|
||||
.copied()
|
||||
.unwrap_or("<cause unknown>")
|
||||
});
|
||||
eprintln!("A panic occurred at {}: {}", method, cause);
|
||||
abort()
|
||||
}
|
||||
|
||||
/// Internal function used in the macro
|
||||
#[cfg(not(feature = "afl_internals"))]
|
||||
pub fn afl_custom_init_<M: RawCustomMutator>(seed: u32) -> *const c_void {
|
||||
match catch_unwind(|| FFIContext::<M>::new(seed).into_ptr()) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_init", err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal function used in the macro
|
||||
#[cfg(feature = "afl_internals")]
|
||||
pub fn afl_custom_init_<M: RawCustomMutator>(
|
||||
afl: Option<&'static afl_state>,
|
||||
seed: u32,
|
||||
) -> *const c_void {
|
||||
match catch_unwind(|| {
|
||||
let afl = afl.expect("mutator func called with NULL afl");
|
||||
FFIContext::<M>::new(afl, seed).into_ptr()
|
||||
}) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_init", err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal function used in the macro
|
||||
pub unsafe fn afl_custom_fuzz_<M: RawCustomMutator>(
|
||||
data: *mut c_void,
|
||||
buf: *mut u8,
|
||||
buf_size: usize,
|
||||
out_buf: *mut *const u8,
|
||||
add_buf: *mut u8,
|
||||
add_buf_size: usize,
|
||||
max_size: usize,
|
||||
) -> usize {
|
||||
match catch_unwind(|| {
|
||||
let mut context = FFIContext::<M>::from(data);
|
||||
if buf.is_null() {
|
||||
panic!("null buf passed to afl_custom_fuzz")
|
||||
}
|
||||
if out_buf.is_null() {
|
||||
panic!("null out_buf passed to afl_custom_fuzz")
|
||||
}
|
||||
let buff_slice = slice::from_raw_parts_mut(buf, buf_size);
|
||||
let add_buff_slice = if add_buf.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(slice::from_raw_parts(add_buf, add_buf_size))
|
||||
};
|
||||
match context
|
||||
.mutator
|
||||
.fuzz(buff_slice, add_buff_slice, max_size.try_into().unwrap())
|
||||
{
|
||||
Some(buffer) => {
|
||||
*out_buf = buffer.as_ptr();
|
||||
buffer.len().try_into().unwrap()
|
||||
}
|
||||
None => {
|
||||
// return the input buffer with 0-length to let AFL skip this mutation attempt
|
||||
*out_buf = buf;
|
||||
0
|
||||
}
|
||||
}
|
||||
}) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_fuzz", err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal function used in the macro
|
||||
pub unsafe fn afl_custom_fuzz_count_<M: RawCustomMutator>(
|
||||
data: *mut c_void,
|
||||
buf: *const u8,
|
||||
buf_size: usize,
|
||||
) -> u32 {
|
||||
match catch_unwind(|| {
|
||||
let mut context = FFIContext::<M>::from(data);
|
||||
if buf.is_null() {
|
||||
panic!("null buf passed to afl_custom_fuzz")
|
||||
}
|
||||
let buf_slice = slice::from_raw_parts(buf, buf_size);
|
||||
// see https://doc.rust-lang.org/nomicon/borrow-splitting.html
|
||||
let ctx = &mut **context;
|
||||
let mutator = &mut ctx.mutator;
|
||||
mutator.fuzz_count(buf_slice)
|
||||
}) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_fuzz_count", err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal function used in the macro
|
||||
pub fn afl_custom_queue_new_entry_<M: RawCustomMutator>(
|
||||
data: *mut c_void,
|
||||
filename_new_queue: *const c_char,
|
||||
filename_orig_queue: *const c_char,
|
||||
) {
|
||||
match catch_unwind(|| {
|
||||
let mut context = FFIContext::<M>::from(data);
|
||||
if filename_new_queue.is_null() {
|
||||
panic!("received null filename_new_queue in afl_custom_queue_new_entry");
|
||||
}
|
||||
let filename_new_queue = unsafe { CStr::from_ptr(filename_new_queue) };
|
||||
let filename_orig_queue = if !filename_orig_queue.is_null() {
|
||||
Some(unsafe { CStr::from_ptr(filename_orig_queue) })
|
||||
} else {
|
||||
None
|
||||
};
|
||||
context
|
||||
.mutator
|
||||
.queue_new_entry(filename_new_queue, filename_orig_queue);
|
||||
}) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_queue_new_entry", err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal function used in the macro
|
||||
pub unsafe fn afl_custom_deinit_<M: RawCustomMutator>(data: *mut c_void) {
|
||||
match catch_unwind(|| {
|
||||
// drop the context
|
||||
ManuallyDrop::into_inner(FFIContext::<M>::from(data));
|
||||
}) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_deinit", err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal function used in the macro
|
||||
pub fn afl_custom_introspection_<M: RawCustomMutator>(data: *mut c_void) -> *const c_char {
|
||||
match catch_unwind(|| {
|
||||
let mut context = FFIContext::<M>::from(data);
|
||||
if let Some(res) = context.mutator.introspection() {
|
||||
res.as_ptr()
|
||||
} else {
|
||||
null()
|
||||
}
|
||||
}) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_introspection", err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal function used in the macro
|
||||
pub fn afl_custom_describe_<M: RawCustomMutator>(
|
||||
data: *mut c_void,
|
||||
max_description_len: usize,
|
||||
) -> *const c_char {
|
||||
match catch_unwind(|| {
|
||||
let mut context = FFIContext::<M>::from(data);
|
||||
if let Some(res) = context.mutator.describe(max_description_len) {
|
||||
res.as_ptr()
|
||||
} else {
|
||||
null()
|
||||
}
|
||||
}) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_describe", err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal function used in the macro
|
||||
pub fn afl_custom_queue_get_<M: RawCustomMutator>(
|
||||
data: *mut c_void,
|
||||
filename: *const c_char,
|
||||
) -> u8 {
|
||||
match catch_unwind(|| {
|
||||
let mut context = FFIContext::<M>::from(data);
|
||||
assert!(!filename.is_null());
|
||||
|
||||
context
|
||||
.mutator
|
||||
.queue_get(unsafe { CStr::from_ptr(filename) }) as u8
|
||||
}) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_queue_get", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// exports the given Mutator as a custom mutator as the C interface that AFL++ expects.
|
||||
/// It is not possible to call this macro multiple times, because it would define the custom mutator symbols multiple times.
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate custom_mutator;
|
||||
/// # #[cfg(feature = "afl_internals")]
|
||||
/// # use custom_mutator::afl_state;
|
||||
/// # use custom_mutator::CustomMutator;
|
||||
/// struct MyMutator;
|
||||
/// impl CustomMutator for MyMutator {
|
||||
/// /// ...
|
||||
/// # type Error = ();
|
||||
/// # #[cfg(feature = "afl_internals")]
|
||||
/// # fn init(_afl_state: &afl_state, _seed: u32) -> Result<Self,()> {unimplemented!()}
|
||||
/// # #[cfg(not(feature = "afl_internals"))]
|
||||
/// # fn init(_seed: u32) -> Result<Self, Self::Error> {unimplemented!()}
|
||||
/// # fn fuzz<'b,'s:'b>(&'s mut self, _buffer: &'b mut [u8], _add_buff: Option<&[u8]>, _max_size: usize) -> Result<Option<&'b [u8]>, Self::Error> {unimplemented!()}
|
||||
/// }
|
||||
/// export_mutator!(MyMutator);
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! export_mutator {
|
||||
($mutator_type:ty) => {
|
||||
#[cfg(feature = "afl_internals")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn afl_custom_init(
|
||||
afl: ::std::option::Option<&'static $crate::afl_state>,
|
||||
seed: ::std::os::raw::c_uint,
|
||||
) -> *const ::std::os::raw::c_void {
|
||||
$crate::wrappers::afl_custom_init_::<$mutator_type>(afl, seed as u32)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "afl_internals"))]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn afl_custom_init(
|
||||
_afl: *const ::std::os::raw::c_void,
|
||||
seed: ::std::os::raw::c_uint,
|
||||
) -> *const ::std::os::raw::c_void {
|
||||
$crate::wrappers::afl_custom_init_::<$mutator_type>(seed as u32)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn afl_custom_fuzz_count(
|
||||
data: *mut ::std::os::raw::c_void,
|
||||
buf: *const u8,
|
||||
buf_size: usize,
|
||||
) -> u32 {
|
||||
unsafe {
|
||||
$crate::wrappers::afl_custom_fuzz_count_::<$mutator_type>(data, buf, buf_size)
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn afl_custom_fuzz(
|
||||
data: *mut ::std::os::raw::c_void,
|
||||
buf: *mut u8,
|
||||
buf_size: usize,
|
||||
out_buf: *mut *const u8,
|
||||
add_buf: *mut u8,
|
||||
add_buf_size: usize,
|
||||
max_size: usize,
|
||||
) -> usize {
|
||||
unsafe {
|
||||
$crate::wrappers::afl_custom_fuzz_::<$mutator_type>(
|
||||
data,
|
||||
buf,
|
||||
buf_size,
|
||||
out_buf,
|
||||
add_buf,
|
||||
add_buf_size,
|
||||
max_size,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn afl_custom_queue_new_entry(
|
||||
data: *mut ::std::os::raw::c_void,
|
||||
filename_new_queue: *const ::std::os::raw::c_char,
|
||||
filename_orig_queue: *const ::std::os::raw::c_char,
|
||||
) {
|
||||
$crate::wrappers::afl_custom_queue_new_entry_::<$mutator_type>(
|
||||
data,
|
||||
filename_new_queue,
|
||||
filename_orig_queue,
|
||||
)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn afl_custom_queue_get(
|
||||
data: *mut ::std::os::raw::c_void,
|
||||
filename: *const ::std::os::raw::c_char,
|
||||
) -> u8 {
|
||||
$crate::wrappers::afl_custom_queue_get_::<$mutator_type>(data, filename)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn afl_custom_introspection(
|
||||
data: *mut ::std::os::raw::c_void,
|
||||
) -> *const ::std::os::raw::c_char {
|
||||
$crate::wrappers::afl_custom_introspection_::<$mutator_type>(data)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn afl_custom_describe(
|
||||
data: *mut ::std::os::raw::c_void,
|
||||
max_description_len: usize,
|
||||
) -> *const ::std::os::raw::c_char {
|
||||
$crate::wrappers::afl_custom_describe_::<$mutator_type>(data, max_description_len)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn afl_custom_deinit(data: *mut ::std::os::raw::c_void) {
|
||||
unsafe { $crate::wrappers::afl_custom_deinit_::<$mutator_type>(data) }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/// this sanity test is supposed to just find out whether an empty mutator being exported by the macro compiles
|
||||
mod sanity_test {
|
||||
#[cfg(feature = "afl_internals")]
|
||||
use super::afl_state;
|
||||
|
||||
use super::{export_mutator, RawCustomMutator};
|
||||
|
||||
struct ExampleMutator;
|
||||
|
||||
impl RawCustomMutator for ExampleMutator {
|
||||
#[cfg(feature = "afl_internals")]
|
||||
fn init(_afl: &afl_state, _seed: u32) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "afl_internals"))]
|
||||
fn init(_seed: u32) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn fuzz<'b, 's: 'b>(
|
||||
&'s mut self,
|
||||
_buffer: &'b mut [u8],
|
||||
_add_buff: Option<&[u8]>,
|
||||
_max_size: usize,
|
||||
) -> Option<&'b [u8]> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
export_mutator!(ExampleMutator);
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
/// A custom mutator.
|
||||
/// [`CustomMutator::handle_error`] will be called in case any method returns an [`Result::Err`].
|
||||
pub trait CustomMutator {
|
||||
/// The error type. All methods must return the same error type.
|
||||
type Error: Debug;
|
||||
|
||||
/// The method which handles errors.
|
||||
/// By default, this method will log the error to stderr if the environment variable "`AFL_CUSTOM_MUTATOR_DEBUG`" is set and non-empty.
|
||||
/// After logging the error, execution will continue on a best-effort basis.
|
||||
///
|
||||
/// This default behaviour can be customized by implementing this method.
|
||||
fn handle_error(err: Self::Error) {
|
||||
if std::env::var("AFL_CUSTOM_MUTATOR_DEBUG")
|
||||
.map(|v| !v.is_empty())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
eprintln!("Error in custom mutator: {:?}", err)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "afl_internals")]
|
||||
fn init(afl: &'static afl_state, seed: u32) -> Result<Self, Self::Error>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
#[cfg(not(feature = "afl_internals"))]
|
||||
fn init(seed: u32) -> Result<Self, Self::Error>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
fn fuzz_count(&mut self, buffer: &[u8]) -> Result<u32, Self::Error> {
|
||||
Ok(1)
|
||||
}
|
||||
|
||||
fn fuzz<'b, 's: 'b>(
|
||||
&'s mut self,
|
||||
buffer: &'b mut [u8],
|
||||
add_buff: Option<&[u8]>,
|
||||
max_size: usize,
|
||||
) -> Result<Option<&'b [u8]>, Self::Error>;
|
||||
|
||||
fn queue_new_entry(
|
||||
&mut self,
|
||||
filename_new_queue: &CStr,
|
||||
filename_orig_queue: Option<&CStr>,
|
||||
) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn queue_get(&mut self, filename: &CStr) -> Result<bool, Self::Error> {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn describe(&mut self, max_description: usize) -> Result<Option<&CStr>, Self::Error> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn introspection(&mut self) -> Result<Option<&CStr>, Self::Error> {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<M> RawCustomMutator for M
|
||||
where
|
||||
M: CustomMutator,
|
||||
M::Error: Debug,
|
||||
{
|
||||
#[cfg(feature = "afl_internals")]
|
||||
fn init(afl: &'static afl_state, seed: u32) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
match Self::init(afl, seed) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
Self::handle_error(e);
|
||||
panic!("Error in afl_custom_init")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "afl_internals"))]
|
||||
fn init(seed: u32) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
match Self::init(seed) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
Self::handle_error(e);
|
||||
panic!("Error in afl_custom_init")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fuzz_count(&mut self, buffer: &[u8]) -> u32 {
|
||||
match self.fuzz_count(buffer) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
Self::handle_error(e);
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fuzz<'b, 's: 'b>(
|
||||
&'s mut self,
|
||||
buffer: &'b mut [u8],
|
||||
add_buff: Option<&[u8]>,
|
||||
max_size: usize,
|
||||
) -> Option<&'b [u8]> {
|
||||
match self.fuzz(buffer, add_buff, max_size) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
Self::handle_error(e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn queue_new_entry(&mut self, filename_new_queue: &CStr, filename_orig_queue: Option<&CStr>) {
|
||||
match self.queue_new_entry(filename_new_queue, filename_orig_queue) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
Self::handle_error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn queue_get(&mut self, filename: &CStr) -> bool {
|
||||
match self.queue_get(filename) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
Self::handle_error(e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn describe(&mut self, max_description: usize) -> Option<&CStr> {
|
||||
match self.describe(max_description) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
Self::handle_error(e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn introspection(&mut self) -> Option<&CStr> {
|
||||
match self.introspection() {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
Self::handle_error(e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
15
custom_mutators/rust/example/Cargo.toml
Normal file
15
custom_mutators/rust/example/Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "example_mutator"
|
||||
version = "0.1.0"
|
||||
authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
custom_mutator = { path = "../custom_mutator" }
|
||||
|
||||
[[example]]
|
||||
name = "example_mutator"
|
||||
path = "./src/example_mutator.rs"
|
||||
crate-type = ["cdylib"]
|
49
custom_mutators/rust/example/src/example_mutator.rs
Normal file
49
custom_mutators/rust/example/src/example_mutator.rs
Normal file
@ -0,0 +1,49 @@
|
||||
#![allow(unused_variables)]
|
||||
|
||||
use custom_mutator::{export_mutator, CustomMutator};
|
||||
|
||||
struct ExampleMutator;
|
||||
|
||||
impl CustomMutator for ExampleMutator {
|
||||
type Error = ();
|
||||
|
||||
fn init(seed: u32) -> Result<Self, Self::Error> {
|
||||
Ok(Self)
|
||||
}
|
||||
|
||||
fn fuzz<'b, 's: 'b>(
|
||||
&'s mut self,
|
||||
buffer: &'b mut [u8],
|
||||
add_buff: Option<&[u8]>,
|
||||
max_size: usize,
|
||||
) -> Result<Option<&'b [u8]>, Self::Error> {
|
||||
buffer.reverse();
|
||||
Ok(Some(buffer))
|
||||
}
|
||||
}
|
||||
|
||||
struct OwnBufferExampleMutator {
|
||||
own_buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
impl CustomMutator for OwnBufferExampleMutator {
|
||||
type Error = ();
|
||||
|
||||
fn init(seed: u32) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
own_buffer: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn fuzz<'b, 's: 'b>(
|
||||
&'s mut self,
|
||||
buffer: &'b mut [u8],
|
||||
add_buff: Option<&[u8]>,
|
||||
max_size: usize,
|
||||
) -> Result<Option<&'b [u8]>, ()> {
|
||||
self.own_buffer.reverse();
|
||||
Ok(Some(self.own_buffer.as_slice()))
|
||||
}
|
||||
}
|
||||
|
||||
export_mutator!(ExampleMutator);
|
16
custom_mutators/rust/example_lain/Cargo.toml
Normal file
16
custom_mutators/rust/example_lain/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "example_lain"
|
||||
version = "0.1.0"
|
||||
authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
custom_mutator = { path = "../custom_mutator" }
|
||||
lain="0.5"
|
||||
|
||||
[[example]]
|
||||
name = "example_lain"
|
||||
path = "./src/lain_mutator.rs"
|
||||
crate-type = ["cdylib"]
|
1
custom_mutators/rust/example_lain/rust-toolchain
Normal file
1
custom_mutators/rust/example_lain/rust-toolchain
Normal file
@ -0,0 +1 @@
|
||||
nightly
|
59
custom_mutators/rust/example_lain/src/lain_mutator.rs
Normal file
59
custom_mutators/rust/example_lain/src/lain_mutator.rs
Normal file
@ -0,0 +1,59 @@
|
||||
use custom_mutator::{export_mutator, CustomMutator};
|
||||
use lain::{
|
||||
mutator::Mutator,
|
||||
prelude::*,
|
||||
rand::{rngs::StdRng, SeedableRng},
|
||||
};
|
||||
|
||||
#[derive(Debug, Mutatable, NewFuzzed, BinarySerialize)]
|
||||
struct MyStruct {
|
||||
field_1: u8,
|
||||
|
||||
#[lain(bits = 3)]
|
||||
field_2: u8,
|
||||
|
||||
#[lain(bits = 5)]
|
||||
field_3: u8,
|
||||
|
||||
#[lain(min = 5, max = 10000)]
|
||||
field_4: u32,
|
||||
|
||||
#[lain(ignore)]
|
||||
ignored_field: u64,
|
||||
}
|
||||
|
||||
struct LainMutator {
|
||||
mutator: Mutator<StdRng>,
|
||||
buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
impl CustomMutator for LainMutator {
|
||||
type Error = ();
|
||||
|
||||
fn init(seed: u32) -> Result<Self, ()> {
|
||||
Ok(Self {
|
||||
mutator: Mutator::new(StdRng::seed_from_u64(seed as u64)),
|
||||
buffer: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn fuzz<'b, 's: 'b>(
|
||||
&'s mut self,
|
||||
_buffer: &'b mut [u8],
|
||||
_add_buff: Option<&[u8]>,
|
||||
max_size: usize,
|
||||
) -> Result<Option<&'b [u8]>, ()> {
|
||||
// we just sample an instance of MyStruct, ignoring the current input
|
||||
let instance = MyStruct::new_fuzzed(&mut self.mutator, None);
|
||||
let size = instance.serialized_size();
|
||||
if size > max_size {
|
||||
return Err(());
|
||||
}
|
||||
self.buffer.clear();
|
||||
self.buffer.reserve(size);
|
||||
instance.binary_serialize::<_, BigEndian>(&mut self.buffer);
|
||||
Ok(Some(self.buffer.as_slice()))
|
||||
}
|
||||
}
|
||||
|
||||
export_mutator!(LainMutator);
|
@ -9,7 +9,7 @@ Want to stay in the loop on major new features? Join our mailing list by
|
||||
sending a mail to <afl-users+subscribe@googlegroups.com>.
|
||||
|
||||
|
||||
### Version ++3.01a (dev)
|
||||
### Version ++3.10c (release)
|
||||
- Mac OS ARM64 support
|
||||
- Android support fixed and updated by Joey Jiaojg - thanks!
|
||||
- New selective instrumentation option with __AFL_COVERAGE_* commands
|
||||
@ -42,16 +42,17 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
|
||||
- switched to an even faster RNG
|
||||
- added hghwng's patch for faster trace map analysis
|
||||
- printing suggestions for mistyped `AFL_` env variables
|
||||
- added Rust bindings for custom mutators (thanks @julihoh)
|
||||
- afl-cc
|
||||
- allow instrumenting LLVMFuzzerTestOneInput
|
||||
- fixed endless loop for allow/blocklist lines starting with a
|
||||
comment (thanks to Zherya for reporting)
|
||||
- cmplog/redqueen now also tracks floating point, _ExtInt() + 128bit
|
||||
- cmplog/redqueen can now process basic libc++ and libstdc++
|
||||
std::string comparisons (though no position or length type variants)
|
||||
- added support for __afl_coverage_interesting() for LTO and
|
||||
and our own PCGUARD (llvm 10.0.1+), read more about this function
|
||||
and selective coverage in instrumentation/README.instrument_list.md
|
||||
std::string comparisons (no position or length type variants)
|
||||
- added support for __afl_coverage_interesting() for LTO and our
|
||||
own PCGUARD (llvm 10.0.1+), read more about this function and
|
||||
selective coverage in instrumentation/README.instrument_list.md
|
||||
- added AFL_LLVM_INSTRUMENT option NATIVE for native clang pc-guard
|
||||
support (less performant than our own), GCC for old afl-gcc and
|
||||
CLANG for old afl-clang
|
||||
@ -67,12 +68,12 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
|
||||
- unicornafl
|
||||
- Substantial speed gains in python bindings for certain use cases
|
||||
- Improved rust bindings
|
||||
- Added a new example harness to compare python, c, and rust bindings
|
||||
- Added a new example harness to compare python, c and rust bindings
|
||||
- afl-cmin and afl-showmap now support the -f option
|
||||
- afl_plot now also generates a graph on the discovered edges
|
||||
- changed default: no memory limit for afl-cmin and afl-cmin.bash
|
||||
- warn on any _AFL and __AFL env vars.
|
||||
- set AFL_IGNORE_UNKNOWN_ENVS to not warn on unknown AFL_... env vars.
|
||||
- set AFL_IGNORE_UNKNOWN_ENVS to not warn on unknown AFL_... env vars
|
||||
- added dummy Makefile to instrumentation/
|
||||
- Updated utils/afl_frida to be 5% faster, 7% on x86_x64
|
||||
- Added `AFL_KILL_SIGNAL` env variable (thanks @v-p-b)
|
||||
|
@ -4,6 +4,11 @@ This file describes how you can implement custom mutations to be used in AFL.
|
||||
For now, we support C/C++ library and Python module, collectivelly named as the
|
||||
custom mutator.
|
||||
|
||||
There is also experimental support for Rust in `custom_mutators/rust`.
|
||||
Please refer to that directory for documentation.
|
||||
Run ```cargo doc -p custom_mutator --open``` in that directory to view the
|
||||
documentation in your web browser.
|
||||
|
||||
Implemented by
|
||||
- C/C++ library (`*.so`): Khaled Yakdan from Code Intelligence (<yakdan@code-intelligence.de>)
|
||||
- Python module: Christian Holler from Mozilla (<choller@mozilla.com>)
|
||||
|
33
docs/docs.md
33
docs/docs.md
@ -63,6 +63,31 @@ The project does not require writing new documentation or tutorials beside the
|
||||
cheat sheet. The technical information for the cheat sheet will be provided by
|
||||
us.
|
||||
|
||||
## Metrics
|
||||
|
||||
afl++ is a the highest performant fuzzer publicly available - but is also the
|
||||
most feature rich and complex. With the publicity of afl++' success and
|
||||
deployment in Google projects internally and externally and availability as
|
||||
a package on most Linux distributions we see more and more issues being
|
||||
created and help requests on our Discord channel that would not be
|
||||
necessary if people would have read through all our documentation - which
|
||||
is unrealistic.
|
||||
|
||||
We expect the the new documenation after this project to be cleaner, easier
|
||||
accessible and lighter to digest by our users, resulting in much less
|
||||
help requests. On the other hand the amount of users using afl++ should
|
||||
increase as well as it will be more accessible which would also increase
|
||||
questions again - but overall resulting in a reduction of help requests.
|
||||
|
||||
In numbers: we currently have per week on average 5 issues on Github,
|
||||
10 questions on discord and 1 on mailing lists that would not be necessary
|
||||
with perfect documentation and perfect people.
|
||||
|
||||
We would consider this project a success if afterwards we only have
|
||||
2 issues on Github and 3 questions on discord anymore that would be answered
|
||||
by reading the documentation. The mailing list is usually used by the most
|
||||
novice users and we don't expect any less questions there.
|
||||
|
||||
## Project Budget
|
||||
|
||||
We have zero experience with technical writers, so this is very hard for us
|
||||
@ -70,13 +95,19 @@ to calculate. We expect it to be a lot of work though because of the amount
|
||||
of documentation we have that needs to be restructured and partially rewritten
|
||||
(44 documents with 13k total lines of content).
|
||||
|
||||
We assume the daily rate of a very good and experienced technical writer in
|
||||
times of a pandemic to be ~500$ (according to web research), and calculate
|
||||
the overall amout of work to be around 20 days for everything incl. the
|
||||
graphics (but again - this is basically just guessing).
|
||||
|
||||
Technical Writer 10000$
|
||||
Volunteer stipends 0$ (waved)
|
||||
T-Shirts for the top 10 contributors and helpers to this documentation project:
|
||||
10 afl++ logo t-shirts 20$ each 200$
|
||||
10 shipping cost of t-shirts 10$ each 100$
|
||||
|
||||
|
||||
Total: 10.300$
|
||||
(in the submission form 10.280$ was entered)
|
||||
|
||||
## Additional Information
|
||||
|
||||
|
@ -25,8 +25,8 @@
|
||||
|
||||
/* Version string: */
|
||||
|
||||
// c = release, d = volatile github dev, e = experimental branch
|
||||
#define VERSION "++3.01a"
|
||||
// c = release, a = volatile github dev, e = experimental branch
|
||||
#define VERSION "++3.10c"
|
||||
|
||||
/******************************************************
|
||||
* *
|
||||
@ -50,7 +50,7 @@
|
||||
#define CMPLOG_COMBINE
|
||||
|
||||
/* Minimum % of the corpus to perform cmplog on. Default: 10% */
|
||||
#define CMPLOG_CORPUS_PERCENT 10U
|
||||
#define CMPLOG_CORPUS_PERCENT 5U
|
||||
|
||||
/* Number of potential positions from which we decide if cmplog becomes
|
||||
useless, default 8096 */
|
||||
|
@ -244,8 +244,12 @@ static void __afl_map_shm(void) {
|
||||
|
||||
if (__afl_final_loc) {
|
||||
|
||||
if (__afl_final_loc % 32)
|
||||
__afl_final_loc = (((__afl_final_loc + 31) >> 5) << 5);
|
||||
if (__afl_final_loc % 64) {
|
||||
|
||||
__afl_final_loc = (((__afl_final_loc + 63) >> 6) << 6);
|
||||
|
||||
}
|
||||
|
||||
__afl_map_size = __afl_final_loc;
|
||||
|
||||
if (__afl_final_loc > MAP_SIZE) {
|
||||
|
@ -924,9 +924,7 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
|
||||
if (getenv("AFL_LLVM_LTO_DONTWRITEID") == NULL) {
|
||||
|
||||
uint32_t write_loc = afl_global_id;
|
||||
|
||||
if (afl_global_id % 32) write_loc = (((afl_global_id + 32) >> 4) << 4);
|
||||
uint32_t write_loc = (((afl_global_id + 63) >> 6) << 6);
|
||||
|
||||
GlobalVariable *AFLFinalLoc = new GlobalVariable(
|
||||
M, Int32Ty, true, GlobalValue::ExternalLinkage, 0, "__afl_final_loc");
|
||||
|
@ -374,10 +374,20 @@ if [ "$ORIG_CROSS" = "" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! command -v "$CROSS" > /dev/null
|
||||
then
|
||||
if ! command -v "$CROSS" > /dev/null ; then
|
||||
if [ "$CPU_TARGET" = "$(uname -m)" ] ; then
|
||||
echo "[+] Building afl++ qemu support libraries with CC=$CC"
|
||||
echo "[+] Building libcompcov ..."
|
||||
make -C libcompcov && echo "[+] libcompcov ready"
|
||||
echo "[+] Building unsigaction ..."
|
||||
make -C unsigaction && echo "[+] unsigaction ready"
|
||||
echo "[+] Building libqasan ..."
|
||||
make -C libqasan && echo "[+] unsigaction ready"
|
||||
else
|
||||
echo "[!] Cross compiler $CROSS could not be found, cannot compile libcompcov libqasan and unsigaction"
|
||||
fi
|
||||
else
|
||||
echo "[+] Building afl++ qemu support libraries with CC=$CROSS"
|
||||
echo "[+] Building libcompcov ..."
|
||||
make -C libcompcov CC=$CROSS && echo "[+] libcompcov ready"
|
||||
echo "[+] Building unsigaction ..."
|
||||
|
@ -26,6 +26,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "libqasan.h"
|
||||
#include "map_macro.h"
|
||||
|
||||
ssize_t (*__lq_libc_write)(int, const void *, size_t);
|
||||
ssize_t (*__lq_libc_read)(int, void *, size_t);
|
||||
char *(*__lq_libc_fgets)(char *, int, FILE *);
|
||||
int (*__lq_libc_atoi)(const char *);
|
||||
long (*__lq_libc_atol)(const char *);
|
||||
@ -35,6 +37,8 @@ void __libqasan_init_hooks(void) {
|
||||
|
||||
__libqasan_init_malloc();
|
||||
|
||||
__lq_libc_write = ASSERT_DLSYM(write);
|
||||
__lq_libc_read = ASSERT_DLSYM(read);
|
||||
__lq_libc_fgets = ASSERT_DLSYM(fgets);
|
||||
__lq_libc_atoi = ASSERT_DLSYM(atoi);
|
||||
__lq_libc_atol = ASSERT_DLSYM(atol);
|
||||
@ -42,6 +46,30 @@ void __libqasan_init_hooks(void) {
|
||||
|
||||
}
|
||||
|
||||
ssize_t write(int fd, const void *buf, size_t count) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: write(%d, %p, %zu)\n", rtv, fd, buf, count);
|
||||
ssize_t r = __lq_libc_write(fd, buf, count);
|
||||
QASAN_DEBUG("\t\t = %zd\n", r);
|
||||
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
ssize_t read(int fd, void *buf, size_t count) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: read(%d, %p, %zu)\n", rtv, fd, buf, count);
|
||||
ssize_t r = __lq_libc_read(fd, buf, count);
|
||||
QASAN_DEBUG("\t\t = %zd\n", r);
|
||||
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
size_t malloc_usable_size(const void *ptr) {
|
||||
|
||||
@ -54,7 +82,7 @@ size_t malloc_usable_size(void *ptr) {
|
||||
|
||||
QASAN_DEBUG("%14p: malloc_usable_size(%p)\n", rtv, ptr);
|
||||
size_t r = __libqasan_malloc_usable_size((void *)ptr);
|
||||
QASAN_DEBUG("\t\t = %ld\n", r);
|
||||
QASAN_DEBUG("\t\t = %zu\n", r);
|
||||
|
||||
return r;
|
||||
|
||||
@ -64,7 +92,7 @@ void *malloc(size_t size) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: malloc(%ld)\n", rtv, size);
|
||||
QASAN_DEBUG("%14p: malloc(%zu)\n", rtv, size);
|
||||
void *r = __libqasan_malloc(size);
|
||||
QASAN_DEBUG("\t\t = %p\n", r);
|
||||
|
||||
@ -76,7 +104,7 @@ void *calloc(size_t nmemb, size_t size) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: calloc(%ld, %ld)\n", rtv, nmemb, size);
|
||||
QASAN_DEBUG("%14p: calloc(%zu, %zu)\n", rtv, nmemb, size);
|
||||
void *r = __libqasan_calloc(nmemb, size);
|
||||
QASAN_DEBUG("\t\t = %p\n", r);
|
||||
|
||||
@ -88,7 +116,7 @@ void *realloc(void *ptr, size_t size) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: realloc(%p, %ld)\n", rtv, ptr, size);
|
||||
QASAN_DEBUG("%14p: realloc(%p, %zu)\n", rtv, ptr, size);
|
||||
void *r = __libqasan_realloc(ptr, size);
|
||||
QASAN_DEBUG("\t\t = %p\n", r);
|
||||
|
||||
@ -100,7 +128,7 @@ int posix_memalign(void **memptr, size_t alignment, size_t size) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: posix_memalign(%p, %ld, %ld)\n", rtv, memptr, alignment,
|
||||
QASAN_DEBUG("%14p: posix_memalign(%p, %zu, %zu)\n", rtv, memptr, alignment,
|
||||
size);
|
||||
int r = __libqasan_posix_memalign(memptr, alignment, size);
|
||||
QASAN_DEBUG("\t\t = %d [*memptr = %p]\n", r, *memptr);
|
||||
@ -113,7 +141,7 @@ void *memalign(size_t alignment, size_t size) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: memalign(%ld, %ld)\n", rtv, alignment, size);
|
||||
QASAN_DEBUG("%14p: memalign(%zu, %zu)\n", rtv, alignment, size);
|
||||
void *r = __libqasan_memalign(alignment, size);
|
||||
QASAN_DEBUG("\t\t = %p\n", r);
|
||||
|
||||
@ -125,7 +153,7 @@ void *aligned_alloc(size_t alignment, size_t size) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: aligned_alloc(%ld, %ld)\n", rtv, alignment, size);
|
||||
QASAN_DEBUG("%14p: aligned_alloc(%zu, %zu)\n", rtv, alignment, size);
|
||||
void *r = __libqasan_aligned_alloc(alignment, size);
|
||||
QASAN_DEBUG("\t\t = %p\n", r);
|
||||
|
||||
@ -137,7 +165,7 @@ void *valloc(size_t size) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: valloc(%ld)\n", rtv, size);
|
||||
QASAN_DEBUG("%14p: valloc(%zu)\n", rtv, size);
|
||||
void *r = __libqasan_memalign(sysconf(_SC_PAGESIZE), size);
|
||||
QASAN_DEBUG("\t\t = %p\n", r);
|
||||
|
||||
@ -149,7 +177,7 @@ void *pvalloc(size_t size) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: pvalloc(%ld)\n", rtv, size);
|
||||
QASAN_DEBUG("%14p: pvalloc(%zu)\n", rtv, size);
|
||||
size_t page_size = sysconf(_SC_PAGESIZE);
|
||||
size = (size & (page_size - 1)) + page_size;
|
||||
void *r = __libqasan_memalign(page_size, size);
|
||||
@ -188,7 +216,7 @@ int memcmp(const void *s1, const void *s2, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: memcmp(%p, %p, %ld)\n", rtv, s1, s2, n);
|
||||
QASAN_DEBUG("%14p: memcmp(%p, %p, %zu)\n", rtv, s1, s2, n);
|
||||
QASAN_LOAD(s1, n);
|
||||
QASAN_LOAD(s2, n);
|
||||
int r = __libqasan_memcmp(s1, s2, n);
|
||||
@ -202,7 +230,7 @@ void *memcpy(void *dest, const void *src, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: memcpy(%p, %p, %ld)\n", rtv, dest, src, n);
|
||||
QASAN_DEBUG("%14p: memcpy(%p, %p, %zu)\n", rtv, dest, src, n);
|
||||
QASAN_LOAD(src, n);
|
||||
QASAN_STORE(dest, n);
|
||||
void *r = __libqasan_memcpy(dest, src, n);
|
||||
@ -216,7 +244,7 @@ void *mempcpy(void *dest, const void *src, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: mempcpy(%p, %p, %ld)\n", rtv, dest, src, n);
|
||||
QASAN_DEBUG("%14p: mempcpy(%p, %p, %zu)\n", rtv, dest, src, n);
|
||||
QASAN_LOAD(src, n);
|
||||
QASAN_STORE(dest, n);
|
||||
void *r = (uint8_t *)__libqasan_memcpy(dest, src, n) + n;
|
||||
@ -230,7 +258,7 @@ void *memmove(void *dest, const void *src, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: memmove(%p, %p, %ld)\n", rtv, dest, src, n);
|
||||
QASAN_DEBUG("%14p: memmove(%p, %p, %zu)\n", rtv, dest, src, n);
|
||||
QASAN_LOAD(src, n);
|
||||
QASAN_STORE(dest, n);
|
||||
void *r = __libqasan_memmove(dest, src, n);
|
||||
@ -244,7 +272,7 @@ void *memset(void *s, int c, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: memset(%p, %d, %ld)\n", rtv, s, c, n);
|
||||
QASAN_DEBUG("%14p: memset(%p, %d, %zu)\n", rtv, s, c, n);
|
||||
QASAN_STORE(s, n);
|
||||
void *r = __libqasan_memset(s, c, n);
|
||||
QASAN_DEBUG("\t\t = %p\n", r);
|
||||
@ -257,7 +285,7 @@ void *memchr(const void *s, int c, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: memchr(%p, %d, %ld)\n", rtv, s, c, n);
|
||||
QASAN_DEBUG("%14p: memchr(%p, %d, %zu)\n", rtv, s, c, n);
|
||||
void *r = __libqasan_memchr(s, c, n);
|
||||
if (r == NULL)
|
||||
QASAN_LOAD(s, n);
|
||||
@ -273,7 +301,7 @@ void *memrchr(const void *s, int c, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: memrchr(%p, %d, %ld)\n", rtv, s, c, n);
|
||||
QASAN_DEBUG("%14p: memrchr(%p, %d, %zu)\n", rtv, s, c, n);
|
||||
QASAN_LOAD(s, n);
|
||||
void *r = __libqasan_memrchr(s, c, n);
|
||||
QASAN_DEBUG("\t\t = %p\n", r);
|
||||
@ -287,7 +315,7 @@ void *memmem(const void *haystack, size_t haystacklen, const void *needle,
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: memmem(%p, %ld, %p, %ld)\n", rtv, haystack, haystacklen,
|
||||
QASAN_DEBUG("%14p: memmem(%p, %zu, %p, %zu)\n", rtv, haystack, haystacklen,
|
||||
needle, needlelen);
|
||||
QASAN_LOAD(haystack, haystacklen);
|
||||
QASAN_LOAD(needle, needlelen);
|
||||
@ -303,7 +331,7 @@ void bzero(void *s, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: bzero(%p, %ld)\n", rtv, s, n);
|
||||
QASAN_DEBUG("%14p: bzero(%p, %zu)\n", rtv, s, n);
|
||||
QASAN_STORE(s, n);
|
||||
__libqasan_memset(s, 0, n);
|
||||
|
||||
@ -315,7 +343,7 @@ void explicit_bzero(void *s, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: bzero(%p, %ld)\n", rtv, s, n);
|
||||
QASAN_DEBUG("%14p: bzero(%p, %zu)\n", rtv, s, n);
|
||||
QASAN_STORE(s, n);
|
||||
__libqasan_memset(s, 0, n);
|
||||
|
||||
@ -325,7 +353,7 @@ int bcmp(const void *s1, const void *s2, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: bcmp(%p, %p, %ld)\n", rtv, s1, s2, n);
|
||||
QASAN_DEBUG("%14p: bcmp(%p, %p, %zu)\n", rtv, s1, s2, n);
|
||||
QASAN_LOAD(s1, n);
|
||||
QASAN_LOAD(s2, n);
|
||||
int r = __libqasan_bcmp(s1, s2, n);
|
||||
@ -383,7 +411,7 @@ int strncasecmp(const char *s1, const char *s2, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: strncasecmp(%p, %p, %ld)\n", rtv, s1, s2, n);
|
||||
QASAN_DEBUG("%14p: strncasecmp(%p, %p, %zu)\n", rtv, s1, s2, n);
|
||||
size_t l1 = __libqasan_strnlen(s1, n);
|
||||
QASAN_LOAD(s1, l1);
|
||||
size_t l2 = __libqasan_strnlen(s2, n);
|
||||
@ -433,7 +461,7 @@ int strncmp(const char *s1, const char *s2, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: strncmp(%p, %p, %ld)\n", rtv, s1, s2, n);
|
||||
QASAN_DEBUG("%14p: strncmp(%p, %p, %zu)\n", rtv, s1, s2, n);
|
||||
size_t l1 = __libqasan_strnlen(s1, n);
|
||||
QASAN_LOAD(s1, l1);
|
||||
size_t l2 = __libqasan_strnlen(s2, n);
|
||||
@ -464,7 +492,7 @@ char *strncpy(char *dest, const char *src, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: strncpy(%p, %p, %ld)\n", rtv, dest, src, n);
|
||||
QASAN_DEBUG("%14p: strncpy(%p, %p, %zu)\n", rtv, dest, src, n);
|
||||
size_t l = __libqasan_strnlen(src, n);
|
||||
QASAN_STORE(dest, n);
|
||||
void *r;
|
||||
@ -523,7 +551,7 @@ size_t strlen(const char *s) {
|
||||
QASAN_DEBUG("%14p: strlen(%p)\n", rtv, s);
|
||||
size_t r = __libqasan_strlen(s);
|
||||
QASAN_LOAD(s, r + 1);
|
||||
QASAN_DEBUG("\t\t = %ld\n", r);
|
||||
QASAN_DEBUG("\t\t = %zu\n", r);
|
||||
|
||||
return r;
|
||||
|
||||
@ -533,10 +561,10 @@ size_t strnlen(const char *s, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: strnlen(%p, %ld)\n", rtv, s, n);
|
||||
QASAN_DEBUG("%14p: strnlen(%p, %zu)\n", rtv, s, n);
|
||||
size_t r = __libqasan_strnlen(s, n);
|
||||
QASAN_LOAD(s, r);
|
||||
QASAN_DEBUG("\t\t = %ld\n", r);
|
||||
QASAN_DEBUG("\t\t = %zu\n", r);
|
||||
|
||||
return r;
|
||||
|
||||
@ -623,7 +651,7 @@ size_t wcslen(const wchar_t *s) {
|
||||
QASAN_DEBUG("%14p: wcslen(%p)\n", rtv, s);
|
||||
size_t r = __libqasan_wcslen(s);
|
||||
QASAN_LOAD(s, sizeof(wchar_t) * (r + 1));
|
||||
QASAN_DEBUG("\t\t = %ld\n", r);
|
||||
QASAN_DEBUG("\t\t = %zu\n", r);
|
||||
|
||||
return r;
|
||||
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 213f3b27dd099ef352181c48cd75c0f20a73e3f0
|
||||
Subproject commit e36a30ebca57ca433a5d6e20b1a32975aabb761b
|
@ -47,6 +47,10 @@ u8 be_quiet = 0;
|
||||
u8 *doc_path = "";
|
||||
u8 last_intr = 0;
|
||||
|
||||
#ifndef AFL_PATH
|
||||
#define AFL_PATH "/usr/local/lib/afl/"
|
||||
#endif
|
||||
|
||||
void detect_file_args(char **argv, u8 *prog_in, bool *use_stdin) {
|
||||
|
||||
u32 i = 0;
|
||||
@ -372,11 +376,11 @@ u8 *get_libqasan_path(u8 *own_loc) {
|
||||
|
||||
}
|
||||
|
||||
if (!access(BIN_PATH "/libqasan.so", X_OK)) {
|
||||
if (!access(AFL_PATH "/libqasan.so", X_OK)) {
|
||||
|
||||
if (cp) { ck_free(cp); }
|
||||
|
||||
return ck_strdup(BIN_PATH "/libqasan.so");
|
||||
return ck_strdup(AFL_PATH "/libqasan.so");
|
||||
|
||||
}
|
||||
|
||||
@ -1131,7 +1135,7 @@ u32 get_map_size(void) {
|
||||
|
||||
}
|
||||
|
||||
if (map_size % 32) { map_size = (((map_size >> 5) + 1) << 5); }
|
||||
if (map_size % 64) { map_size = (((map_size >> 6) + 1) << 6); }
|
||||
|
||||
}
|
||||
|
||||
|
@ -656,11 +656,11 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
|
||||
if (!fsrv->map_size) { fsrv->map_size = MAP_SIZE; }
|
||||
|
||||
if (unlikely(tmp_map_size % 32)) {
|
||||
if (unlikely(tmp_map_size % 64)) {
|
||||
|
||||
// should not happen
|
||||
WARNF("Target reported non-aligned map size of %u", tmp_map_size);
|
||||
tmp_map_size = (((tmp_map_size + 31) >> 5) << 5);
|
||||
tmp_map_size = (((tmp_map_size + 63) >> 6) << 6);
|
||||
|
||||
}
|
||||
|
||||
|
@ -8,19 +8,19 @@ The CompareCoverage and NeverZero counters features are by Andrea Fioraldi <andr
|
||||
|
||||
## 1) Introduction
|
||||
|
||||
The code in ./unicorn_mode allows you to build a standalone feature that
|
||||
leverages the Unicorn Engine and allows callers to obtain instrumentation
|
||||
The code in ./unicorn_mode allows you to build the (Unicorn Engine)[https://github.com/unicorn-engine/unicorn] with afl support.
|
||||
This means, you can run anything that can be emulated in unicorn and obtain instrumentation
|
||||
output for black-box, closed-source binary code snippets. This mechanism
|
||||
can be then used by afl-fuzz to stress-test targets that couldn't be built
|
||||
with afl-gcc or used in QEMU mode, or with other extensions such as
|
||||
TriforceAFL.
|
||||
with afl-cc or used in QEMU mode.
|
||||
|
||||
There is a significant performance penalty compared to native AFL,
|
||||
but at least we're able to use AFL++ on these binaries, right?
|
||||
|
||||
## 2) How to use
|
||||
|
||||
Requirements: you need an installed python environment.
|
||||
First, you will need a working harness for your target in unicorn, using Python, C, or Rust.
|
||||
For some pointers for more advanced emulation, take a look at [BaseSAFE](https://github.com/fgsect/BaseSAFE) and [Qiling](https://github.com/qilingframework/qiling).
|
||||
|
||||
### Building AFL++'s Unicorn Mode
|
||||
|
||||
@ -34,23 +34,23 @@ cd unicorn_mode
|
||||
```
|
||||
|
||||
NOTE: This script checks out a Unicorn Engine fork as submodule that has been tested
|
||||
and is stable-ish, based on the unicorn engine master.
|
||||
and is stable-ish, based on the unicorn engine `next` branch.
|
||||
|
||||
Building Unicorn will take a little bit (~5-10 minutes). Once it completes
|
||||
it automatically compiles a sample application and verifies that it works.
|
||||
|
||||
### Fuzzing with Unicorn Mode
|
||||
|
||||
To really use unicorn-mode effectively you need to prepare the following:
|
||||
To use unicorn-mode effectively you need to prepare the following:
|
||||
|
||||
* Relevant binary code to be fuzzed
|
||||
* Knowledge of the memory map and good starting state
|
||||
* Folder containing sample inputs to start fuzzing with
|
||||
+ Same ideas as any other AFL inputs
|
||||
+ Quality/speed of results will depend greatly on quality of starting
|
||||
+ Quality/speed of results will depend greatly on the quality of starting
|
||||
samples
|
||||
+ See AFL's guidance on how to create a sample corpus
|
||||
* Unicornafl-based test harness which:
|
||||
* Unicornafl-based test harness in Rust, C, or Python, which:
|
||||
+ Adds memory map regions
|
||||
+ Loads binary code into memory
|
||||
+ Calls uc.afl_fuzz() / uc.afl_start_forkserver
|
||||
@ -59,13 +59,13 @@ To really use unicorn-mode effectively you need to prepare the following:
|
||||
the test harness
|
||||
+ Presumably the data to be fuzzed is at a fixed buffer address
|
||||
+ If input constraints (size, invalid bytes, etc.) are known they
|
||||
should be checked after the file is loaded. If a constraint
|
||||
fails, just exit the test harness. AFL will treat the input as
|
||||
should be checked in the place_input handler. If a constraint
|
||||
fails, just return false from the handler. AFL will treat the input as
|
||||
'uninteresting' and move on.
|
||||
+ Sets up registers and memory state for beginning of test
|
||||
+ Emulates the interested code from beginning to end
|
||||
+ Emulates the interesting code from beginning to end
|
||||
+ If a crash is detected, the test harness must 'crash' by
|
||||
throwing a signal (SIGSEGV, SIGKILL, SIGABORT, etc.)
|
||||
throwing a signal (SIGSEGV, SIGKILL, SIGABORT, etc.), or indicate a crash in the crash validation callback.
|
||||
|
||||
Once you have all those things ready to go you just need to run afl-fuzz in
|
||||
'unicorn-mode' by passing in the '-U' flag:
|
||||
@ -79,11 +79,12 @@ AFL's main documentation for more info about how to use afl-fuzz effectively.
|
||||
|
||||
For a much clearer vision of what all of this looks like, please refer to the
|
||||
sample provided in the 'unicorn_mode/samples' directory. There is also a blog
|
||||
post that goes over the basics at:
|
||||
post that uses slightly older concepts, but describes the general ideas, at:
|
||||
|
||||
[https://medium.com/@njvoss299/afl-unicorn-fuzzing-arbitrary-binary-code-563ca28936bf](https://medium.com/@njvoss299/afl-unicorn-fuzzing-arbitrary-binary-code-563ca28936bf)
|
||||
|
||||
The 'helper_scripts' directory also contains several helper scripts that allow you
|
||||
|
||||
The ['helper_scripts'](./helper_scripts) directory also contains several helper scripts that allow you
|
||||
to dump context from a running process, load it, and hook heap allocations. For details
|
||||
on how to use this check out the follow-up blog post to the one linked above.
|
||||
|
||||
@ -92,10 +93,10 @@ A example use of AFL-Unicorn mode is discussed in the paper Unicorefuzz:
|
||||
|
||||
## 3) Options
|
||||
|
||||
As for the QEMU-based instrumentation, the afl-unicorn twist of afl++
|
||||
comes with a sub-instruction based instrumentation similar in purpose to laf-intel.
|
||||
As for the QEMU-based instrumentation, unicornafl comes with a sub-instruction based instrumentation similar in purpose to laf-intel.
|
||||
|
||||
The options that enable Unicorn CompareCoverage are the same used for QEMU.
|
||||
This will split up each multi-byte compare to give feedback for each correct byte.
|
||||
AFL_COMPCOV_LEVEL=1 is to instrument comparisons with only immediate values.
|
||||
|
||||
AFL_COMPCOV_LEVEL=2 instruments all comparison instructions.
|
||||
@ -119,6 +120,20 @@ unicornafl.monkeypatch()
|
||||
|
||||
This will replace all unicorn imports with unicornafl inputs.
|
||||
|
||||
Refer to the [samples/arm_example/arm_tester.c](samples/arm_example/arm_tester.c) for an example
|
||||
of how to do this properly! If you don't get this right, AFL will not
|
||||
load any mutated inputs and your fuzzing will be useless!
|
||||
5) Examples
|
||||
|
||||
Apart from reading the documentation in `afl.c` and the python bindings of unicornafl, the best documentation are the [samples/](./samples).
|
||||
The following examples exist at the time of writing:
|
||||
|
||||
- c: A simple example how to use the c bindings
|
||||
- compcov_x64: A python example that uses compcov to traverse hard-to-reach blocks
|
||||
- persistent: A c example using persistent mode for maximum speed, and resetting the target state between each iteration
|
||||
- simple: A simple python example
|
||||
- speedtest/c: The c harness for an example target, used to compare c, python, and rust bindings and fix speed issues
|
||||
- speedtest/python: Fuzzing the same target in python
|
||||
- speedtest/rust: Fuzzing the same target using a rust harness
|
||||
|
||||
Usually, the place to look at is the `harness` in each folder. The source code in each harness is pretty well documented.
|
||||
Most harnesses also have the `afl-fuzz` commandline, or even offer a `make fuzz` Makefile target.
|
||||
Targets in these folders, if x86, can usually be made using `make target` in each folder or get shipped pre-built (plus their source).
|
||||
Especially take a look at the [speedtest documentation](./samples/speedtest/README.md) to see how the languages compare.
|
@ -17,6 +17,6 @@ You shouldn't need to compile simple_target.c since a X86_64 binary version is
|
||||
pre-built and shipped in this sample folder. This file documents how the binary
|
||||
was built in case you want to rebuild it or recompile it for any reason.
|
||||
|
||||
The pre-built binary (simple_target_x86_64.bin) was built using -g -O0 in gcc.
|
||||
The pre-built binary (persistent_target_x86_64) was built using -g -O0 in gcc.
|
||||
|
||||
We then load the binary and execute the main function directly.
|
||||
|
@ -1,5 +1,6 @@
|
||||
PREFIX ?= /usr/local
|
||||
BIN_PATH = $(PREFIX)/bin
|
||||
HELPER_PATH = $(PREFIX)/lib/afl
|
||||
DOC_PATH = $(PREFIX)/share/doc/afl
|
||||
|
||||
PROGRAMS = afl-network-client afl-network-server
|
||||
@ -31,7 +32,7 @@ afl-network-client: afl-network-client.c
|
||||
$(CC) $(CFLAGS) -I../../include -o afl-network-client afl-network-client.c $(LDFLAGS)
|
||||
|
||||
afl-network-server: afl-network-server.c
|
||||
$(CC) $(CFLAGS) -I../../include -o afl-network-server afl-network-server.c ../../src/afl-forkserver.c ../../src/afl-sharedmem.c ../../src/afl-common.c -DBIN_PATH=\"$(BIN_PATH)\" $(LDFLAGS)
|
||||
$(CC) $(CFLAGS) -I../../include -o afl-network-server afl-network-server.c ../../src/afl-forkserver.c ../../src/afl-sharedmem.c ../../src/afl-common.c -DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f $(PROGRAMS) *~ core
|
||||
|
@ -70,7 +70,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
len = __AFL_FUZZ_TESTCASE_LEN; // do not use the macro directly in a call!
|
||||
|
||||
fprintf(stderr, "input: %zd \"%s\"\n", len, buf);
|
||||
// fprintf(stderr, "input: %zd \"%s\"\n", len, buf);
|
||||
|
||||
/* do we have enough data? */
|
||||
if (len < 8) continue;
|
||||
|
Loading…
x
Reference in New Issue
Block a user