Frida Executor Example (#27)

* inprocess: Allow InProcessExecutor to take a function pointer or a closure

* frida: initial working (but slow + buggy) frida helper

Issues:
- it's slow as ****
- there is an Llmp exception after the 227th corpus entry is found
- Cargo.toml lines currently import from a local ../frida-rust dir, as frida-rust is still under development

* inprocess: let the InProcessExecutor take a closure or a function pointer

* frida: working FridaHelper with InProcessExecutor

* frida: Apply suggestions; Move to RefCell; Cleanup warnings

* frida: link libstdc++_static.a on android

* take an FnMut in InProcessExecutor

* adapt libfuzzer_libpng to FnMut in InProcessExecutor

* create FridaInProcessExecutor and FridaEdgeCoverageHelper

* fix frida build.rs

* frida: move gum to main, get rid of lazy_static; use PageProtection enum

* stalker exclude

* frida: implement inline map-update for x86_64

* inprocess: add harness/harness_mut accessors

* format

* remove get_module_size from FridaEdgeCoverageHelper

* frida: implement aarch64 inline map update

* frida: add missing IndexMode

* add timeouts for executors

* move timeouts to observer

* add with_timeout constructor for Observer

* cast to i64 later in pre_exec

* add cfg(unix) guards

* add TimeoutExecutor

* add TimeoutFeedback and send ExitKind::Timeout from the handler

* pass Duration and move timeout stuff to post_exec

* format

* add timeouts to libpng_libfuzzer

* 10 sec timeout

* timeout executor file

* fix timeout executor no_std

* format

* todos

* Win32ShMem

* win32 exceptions

* fixes

* fix win32 build.rs

* fix win32 build.rs

* fixes fro win32

* fixes for win32

* fixes for win32

* fixes for win32

* fixes for win32

* fixes for win32

* fixes for win32

* fixes for win32

* fixes for win32

* fixes for win32

* fixes for win32

* inprocess::windows_exception_handler

* inprocess::windows_exception_handler fixes

* windows_exception_handler in InProcessExecutor

* inprocess::windows_exception_handler fix

* fix windows exceptions mapping

* format

* format

* inprocess: Allow InProcessExecutor to take a function pointer or a closure

* frida: initial working (but slow + buggy) frida helper

Issues:
- it's slow as ****
- there is an Llmp exception after the 227th corpus entry is found
- Cargo.toml lines currently import from a local ../frida-rust dir, as frida-rust is still under development

* inprocess: let the InProcessExecutor take a closure or a function pointer

* frida: Apply suggestions; Move to RefCell; Cleanup warnings

* take an FnMut in InProcessExecutor

* adapt libfuzzer_libpng to FnMut in InProcessExecutor

* reenabled ci for prs

* frida: update to frida-rust 0.3.2

* frida: fix buid errors

* frida: fix build_and_test.yml

* frida: uses crates.io for frida-gum and frida-gum-sys

* fix merge errors

* fix typo

* frida: x86_64 now working

Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
Co-authored-by: toka <tokazerkje@outlook.com>
Co-authored-by: Dominik Maier <domenukk@gmail.com>
This commit is contained in:
s1341 2021-03-22 13:45:38 +02:00 committed by GitHub
parent 245379c020
commit a02cc39bba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 971 additions and 75 deletions

View File

@ -11,6 +11,7 @@ members = [
#example fuzzers
"fuzzers/libfuzzer_libpng",
"fuzzers/frida_libpng",
"fuzzers/libfuzzer_libmozjpeg",
"fuzzers/libfuzzer_libpng_cmpalloc",
]

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

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

View File

@ -0,0 +1,39 @@
[package]
name = "frida_libpng"
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", "frida"]
std = []
frida = ["frida-gum", "frida-gum-sys"]
#[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/" }
#frida-gum = { path = "../../../frida-rust/frida-gum", version = "0.2.3", optional = true, features = ["auto-download", "event-sink", "invocation-listener"] }
#frida-gum-sys = { path = "../../../frida-rust/frida-gum-sys", version = "0.2.2", optional = true, features = ["auto-download", "event-sink", "invocation-listener"] }
frida-gum = { version = "0.3.2", optional = true, features = ["auto-download", "event-sink", "invocation-listener"] }
frida-gum-sys = { version = "0.2.2", optional = true, features = ["auto-download", "event-sink", "invocation-listener"] }
lazy_static = "1.4.0"
libc = "0.2"
libloading = "0.7.0"
[[example]]
name = "frida_libpng"
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,114 @@
// build.rs
use std::{
env,
path::Path,
process::{exit, Command},
};
const LIBPNG_URL: &str =
"https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz";
fn main() {
if cfg!(windows) {
println!("cargo:warning=Skipping libpng example on Windows");
exit(0);
}
let out_dir = env::var_os("OUT_DIR").unwrap();
let cwd = env::current_dir().unwrap().to_string_lossy().to_string();
let out_dir = out_dir.to_string_lossy().to_string();
let out_dir_path = Path::new(&out_dir);
std::fs::create_dir_all(&out_dir).expect(&format!("Failed to create {}", &out_dir));
println!("cargo:rerun-if-changed=../libfuzzer_runtime/rt.c",);
println!("cargo:rerun-if-changed=harness.cc");
let libpng = format!("{}/libpng-1.6.37", &out_dir);
let libpng_path = Path::new(&libpng);
let libpng_tar = format!("{}/libpng-1.6.37.tar.xz", &cwd);
// 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(),
};
// println!("cargo:warning=output path is {}", libpng);
if !libpng_path.is_dir() {
if !Path::new(&libpng_tar).is_file() {
println!("cargo:warning=Libpng not found, downloading...");
// Download libpng
Command::new("wget")
.arg("-c")
.arg(LIBPNG_URL)
.arg("-O")
.arg(&libpng_tar)
.status()
.unwrap();
}
Command::new("tar")
.current_dir(&out_dir_path)
.arg("xvf")
.arg(&libpng_tar)
.status()
.unwrap();
Command::new(format!("{}/configure", &libpng))
.current_dir(&libpng_path)
.args(&[
"--disable-shared",
&format!("--host={}", env::var("TARGET").unwrap())[..],
])
.env("CC", "clang")
.env("CXX", "clang++")
.env(
"CFLAGS",
"-O3 -g -D_DEFAULT_SOURCE -fPIC -fno-omit-frame-pointer",
)
.env(
"CXXFLAGS",
"-O3 -g -D_DEFAULT_SOURCE -fPIC -fno-omit-frame-pointer",
)
.env(
"LDFLAGS",
//format!("-g -fPIE -fsanitize=address {}", ldflags),
format!("-g -fPIE {}", ldflags),
)
.status()
.unwrap();
Command::new("make")
.current_dir(&libpng_path)
.status()
.unwrap();
}
let status = cc::Build::new()
.cpp(true)
.get_compiler()
.to_command()
.current_dir(&cwd)
.arg("-I")
.arg(format!("{}", &libpng))
//.arg("-D")
//.arg("HAS_DUMMY_CRASH=1")
.arg("-fPIC")
.arg("-shared")
.arg(if env::var("CARGO_CFG_TARGET_OS").unwrap() == "android" {
"-static-libstdc++"
} else {
""
})
.arg("-o")
.arg(format!("{}/libpng-harness.so", &out_dir))
.arg("./harness.cc")
.arg(format!("{}/.libs/libpng16.a", &libpng))
.arg("-l")
.arg("z")
.status()
.unwrap();
assert!(status.success());
println!("cargo:rerun-if-changed=build.rs");
}

View File

@ -0,0 +1,201 @@
// libpng_read_fuzzer.cc
// Copyright 2017-2018 Glenn Randers-Pehrson
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that may
// be found in the LICENSE file https://cs.chromium.org/chromium/src/LICENSE
// Last changed in libpng 1.6.35 [July 15, 2018]
// The modifications in 2017 by Glenn Randers-Pehrson include
// 1. addition of a PNG_CLEANUP macro,
// 2. setting the option to ignore ADLER32 checksums,
// 3. adding "#include <string.h>" which is needed on some platforms
// to provide memcpy().
// 4. adding read_end_info() and creating an end_info structure.
// 5. adding calls to png_set_*() transforms commonly used by browsers.
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <vector>
#define PNG_INTERNAL
#include "png.h"
#define PNG_CLEANUP \
if(png_handler.png_ptr) \
{ \
if (png_handler.row_ptr) \
png_free(png_handler.png_ptr, png_handler.row_ptr); \
if (png_handler.end_info_ptr) \
png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr,\
&png_handler.end_info_ptr); \
else if (png_handler.info_ptr) \
png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr,\
nullptr); \
else \
png_destroy_read_struct(&png_handler.png_ptr, nullptr, nullptr); \
png_handler.png_ptr = nullptr; \
png_handler.row_ptr = nullptr; \
png_handler.info_ptr = nullptr; \
png_handler.end_info_ptr = nullptr; \
}
struct BufState {
const uint8_t* data;
size_t bytes_left;
};
struct PngObjectHandler {
png_infop info_ptr = nullptr;
png_structp png_ptr = nullptr;
png_infop end_info_ptr = nullptr;
png_voidp row_ptr = nullptr;
BufState* buf_state = nullptr;
~PngObjectHandler() {
if (row_ptr)
png_free(png_ptr, row_ptr);
if (end_info_ptr)
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info_ptr);
else if (info_ptr)
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
else
png_destroy_read_struct(&png_ptr, nullptr, nullptr);
delete buf_state;
}
};
void user_read_data(png_structp png_ptr, png_bytep data, size_t length) {
BufState* buf_state = static_cast<BufState*>(png_get_io_ptr(png_ptr));
if (length > buf_state->bytes_left) {
png_error(png_ptr, "read error");
}
memcpy(data, buf_state->data, length);
buf_state->bytes_left -= length;
buf_state->data += length;
}
static const int kPngHeaderSize = 8;
extern "C" int afl_libfuzzer_init() {
return 0;
}
// Entry point for LibFuzzer.
// Roughly follows the libpng book example:
// http://www.libpng.org/pub/png/book/chapter13.html
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size < kPngHeaderSize) {
return 0;
}
std::vector<unsigned char> v(data, data + size);
if (png_sig_cmp(v.data(), 0, kPngHeaderSize)) {
// not a PNG.
return 0;
}
PngObjectHandler png_handler;
png_handler.png_ptr = nullptr;
png_handler.row_ptr = nullptr;
png_handler.info_ptr = nullptr;
png_handler.end_info_ptr = nullptr;
png_handler.png_ptr = png_create_read_struct
(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (!png_handler.png_ptr) {
return 0;
}
png_handler.info_ptr = png_create_info_struct(png_handler.png_ptr);
if (!png_handler.info_ptr) {
PNG_CLEANUP
return 0;
}
png_handler.end_info_ptr = png_create_info_struct(png_handler.png_ptr);
if (!png_handler.end_info_ptr) {
PNG_CLEANUP
return 0;
}
png_set_crc_action(png_handler.png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
#ifdef PNG_IGNORE_ADLER32
png_set_option(png_handler.png_ptr, PNG_IGNORE_ADLER32, PNG_OPTION_ON);
#endif
// Setting up reading from buffer.
png_handler.buf_state = new BufState();
png_handler.buf_state->data = data + kPngHeaderSize;
png_handler.buf_state->bytes_left = size - kPngHeaderSize;
png_set_read_fn(png_handler.png_ptr, png_handler.buf_state, user_read_data);
png_set_sig_bytes(png_handler.png_ptr, kPngHeaderSize);
if (setjmp(png_jmpbuf(png_handler.png_ptr))) {
PNG_CLEANUP
return 0;
}
// Reading.
png_read_info(png_handler.png_ptr, png_handler.info_ptr);
// reset error handler to put png_deleter into scope.
if (setjmp(png_jmpbuf(png_handler.png_ptr))) {
PNG_CLEANUP
return 0;
}
png_uint_32 width, height;
int bit_depth, color_type, interlace_type, compression_type;
int filter_type;
if (!png_get_IHDR(png_handler.png_ptr, png_handler.info_ptr, &width,
&height, &bit_depth, &color_type, &interlace_type,
&compression_type, &filter_type)) {
PNG_CLEANUP
return 0;
}
// This is going to be too slow.
if (width && height > 100000000 / width) {
PNG_CLEANUP
#ifdef HAS_DUMMY_CRASH
#ifdef __aarch64__
asm volatile (".word 0xf7f0a000\n");
#else
asm("ud2");
#endif
#endif
return 0;
}
// Set several transforms that browsers typically use:
png_set_gray_to_rgb(png_handler.png_ptr);
png_set_expand(png_handler.png_ptr);
png_set_packing(png_handler.png_ptr);
png_set_scale_16(png_handler.png_ptr);
png_set_tRNS_to_alpha(png_handler.png_ptr);
int passes = png_set_interlace_handling(png_handler.png_ptr);
png_read_update_info(png_handler.png_ptr, png_handler.info_ptr);
png_handler.row_ptr = png_malloc(
png_handler.png_ptr, png_get_rowbytes(png_handler.png_ptr,
png_handler.info_ptr));
for (int pass = 0; pass < passes; ++pass) {
for (png_uint_32 y = 0; y < height; ++y) {
png_read_row(png_handler.png_ptr,
static_cast<png_bytep>(png_handler.row_ptr), nullptr);
}
}
png_read_end(png_handler.png_ptr, png_handler.end_info_ptr);
PNG_CLEANUP
return 0;
}

View File

@ -0,0 +1,524 @@
//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts
//! The example harness is built for libpng.
use libafl::{
bolts::{
shmem::UnixShMem,
tuples::{tuple_list, Named},
},
corpus::{
Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus,
QueueCorpusScheduler,
},
events::{setup_restarting_mgr, EventManager},
executors::{inprocess::InProcessExecutor, Executor, ExitKind, HasObservers},
feedbacks::{CrashFeedback, MaxMapFeedback},
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
inputs::{HasTargetBytes, Input},
mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens},
observers::{HitcountsMapObserver, ObserversTuple, StdMapObserver},
stages::mutational::StdMutationalStage,
state::{HasCorpus, HasMetadata, State},
stats::SimpleStats,
utils::{current_nanos, StdRand},
Error,
};
#[cfg(target_arch = "x86_64")]
use frida_gum::instruction_writer::X86Register;
#[cfg(target_arch = "aarch64")]
use frida_gum::instruction_writer::{Aarch64Register, IndexMode};
use frida_gum::{
instruction_writer::InstructionWriter,
stalker::{NoneEventSink, Stalker, Transformer},
};
use frida_gum::{Gum, MemoryRange, Module, NativePointer, PageProtection};
use libloading;
use std::{cell::RefCell, env, ffi::c_void, path::PathBuf};
/// An helper that feeds FridaInProcessExecutor with user-supplied instrumentation
pub trait FridaHelper<'a> {
fn transformer(&self) -> &Transformer<'a>;
}
const MAP_SIZE: usize = 64 * 1024;
/// An helper that feeds FridaInProcessExecutor with edge-coverage instrumentation
struct FridaEdgeCoverageHelper<'a> {
map: [u8; MAP_SIZE],
previous_pc: RefCell<u64>,
base_address: u64,
size: usize,
current_log_impl: u64,
/// Transformer that has to be passed to FridaInProcessExecutor
transformer: Option<Transformer<'a>>,
}
impl<'a> FridaHelper<'a> for FridaEdgeCoverageHelper<'a> {
fn transformer(&self) -> &Transformer<'a> {
self.transformer.as_ref().unwrap()
}
}
/// Helper function to get the size of a module's CODE section from frida
pub fn get_module_size(module_name: &str) -> usize {
let mut code_size = 0;
let code_size_ref = &mut code_size;
Module::enumerate_ranges(module_name, PageProtection::ReadExecute, move |details| {
*code_size_ref = details.memory_range().size() as usize;
true
});
code_size
}
/// A minimal maybe_log implementation. We insert this into the transformed instruction stream
/// every time we need a copy that is within a direct branch of the start of the transformed basic
/// block.
#[cfg(target_arch = "x86_64")]
const MAYBE_LOG_CODE: [u8; 69] = [
0x9c, // pushfq
0x50, // push rax
0x51, // push rcx
0x52, // push rdx
0x56, // push rsi
0x89, 0xf8, // mov eax, edi
0xc1, 0xe0, 0x08, // shl eax, 8
0xc1, 0xef, 0x04, // shr edi, 4
0x31, 0xc7, // xor edi, eax
0x0f, 0xb7, 0xc7, // movzx eax, di
0x48, 0x8d, 0x0d, 0x34, 0x00, 0x00, 0x00, // lea rcx, sym._afl_area_ptr_ptr
0x48, 0x8b, 0x09, // mov rcx, qword [rcx]
0x48, 0x8d, 0x15, 0x22, 0x00, 0x00, 0x00, // lea rdx, sym._afl_prev_loc_ptr
0x48, 0x8b, 0x32, // mov rsi, qword [rdx]
0x48, 0x8b, 0x36, // mov rsi, qword [rsi]
0x48, 0x31, 0xc6, // xor rsi, rax
0x48, 0x81, 0xe6, 0xff, 0x1f, 0x00,
0x00, // and rsi, 0x1fff (8 * 1024 - 1) TODO: make this variable
0xfe, 0x04, 0x31, // inc byte [rcx + rsi]
0x48, 0xd1, 0xe8, // shr rax, 1
0x48, 0x8b, 0x0a, // mov rcx, qword [rdx]
0x48, 0x89, 0x01, // mov qword [rcx], rax
0x5e, // pop rsi
0x5a, // pop rdx
0x59, // pop rcx
0x58, // pop rax
0x9d, // popfq
0xc3, // ret
// Read-only data goes here:
// uint64_t* afl_prev_loc_ptr
// uint8_t** afl_area_ptr_ptr
// unsigned int afl_instr_rms
];
#[cfg(target_arch = "aarch64")]
const MAYBE_LOG_CODE: [u8; 104] = [
0xE1, 0x0B, 0xBF, 0xA9, // stp x1, x2, [sp, -0x10]!
0xE3, 0x13, 0xBF, 0xA9, // stp x3, x4, [sp, -0x10]!
0xE1, 0x03, 0x00, 0xAA, // mov x1, x0
0x00, 0xDC, 0x78, 0xD3, // lsl x0, x0, #8
0x21, 0xFC, 0x44, 0xD3, // lsr x1, x1, #4
0x00, 0x00, 0x01, 0xCA, // eor x0, x0, x1
0x00, 0x3C, 0x00, 0x53, // uxth w0, w0
0xa1, 0x02, 0x00, 0x58, // ldr x1, =area_ptr
0x42, 0x02, 0x00, 0x58, // ldr x2, =pc_ptr
0x43, 0x00, 0x40, 0xF9, // ldr x3, [x2]
0x63, 0x00, 0x00, 0xCA, // eor x3, x3, x0
0x63, 0x40, 0x40, 0x92, // and x3, x3, #0x1ffff
0x21, 0x00, 0x03, 0x8B, // add x1, x1, x3
0x24, 0x00, 0x40, 0x39, // ldrb w4, [x1, #0
0x84, 0x04, 0x00, 0x91, // add x4, x4, #1
0x24, 0x00, 0x00, 0x39, // strb w4, [x1, #0]
0x00, 0xFC, 0x41, 0xD3, // lsr x0, x0, #1
0x40, 0x00, 0x00, 0xF9, // str x0, [x2]
0xE3, 0x13, 0xc1, 0xA8, // ldp x3, x4, [sp], #0x10
0xE1, 0x0B, 0xc1, 0xA8, // ldp x1, x2, [sp], #0x10
0xC0, 0x03, 0x5F, 0xD6, // ret
0x1f, 0x20, 0x03, 0xD5, // nop
0x1f, 0x20, 0x03, 0xD5, // nop
0x1f, 0x20, 0x03, 0xD5, // nop
0x1f, 0x20, 0x03, 0xD5, // nop
0x1f, 0x20, 0x03, 0xD5, // nop
];
/// The implementation of the FridaEdgeCoverageHelper
impl<'a> FridaEdgeCoverageHelper<'a> {
/// Constructor function to create a new FridaEdgeCoverageHelper, given a module_name.
pub fn new(gum: &'a Gum, module_name: &str) -> Self {
let mut helper = Self {
map: [0u8; MAP_SIZE],
previous_pc: RefCell::new(0x0),
base_address: Module::find_base_address(module_name).0 as u64,
size: get_module_size(module_name),
current_log_impl: 0,
transformer: None,
};
let transformer = Transformer::from_callback(gum, |basic_block, _output| {
let mut first = true;
for instruction in basic_block {
if first {
first = false;
let address = unsafe { (*instruction.instr()).address };
if address >= helper.base_address
&& address <= helper.base_address + helper.size as u64
{
let writer = _output.writer();
if helper.current_log_impl == 0
|| !writer.can_branch_directly_to(helper.current_log_impl)
|| !writer.can_branch_directly_between(
writer.pc() + 128,
helper.current_log_impl,
)
{
let after_log_impl = writer.code_offset() + 1;
#[cfg(target_arch = "x86_64")]
writer.put_jmp_near_label(after_log_impl);
#[cfg(target_arch = "aarch64")]
writer.put_b_label(after_log_impl);
helper.current_log_impl = writer.pc();
writer.put_bytes(&MAYBE_LOG_CODE);
let prev_loc_pointer = helper.previous_pc.as_ptr() as *mut _ as usize;
let map_pointer = helper.map.as_ptr() as usize;
writer.put_bytes(&prev_loc_pointer.to_ne_bytes());
writer.put_bytes(&map_pointer.to_ne_bytes());
writer.put_label(after_log_impl);
}
#[cfg(target_arch = "x86_64")]
{
println!("here");
writer.put_lea_reg_reg_offset(
X86Register::Rsp,
X86Register::Rsp,
-(frida_gum_sys::GUM_RED_ZONE_SIZE as i32),
);
writer.put_push_reg(X86Register::Rdi);
writer.put_mov_reg_address(X86Register::Rdi, address);
writer.put_call_address(helper.current_log_impl);
writer.put_pop_reg(X86Register::Rdi);
writer.put_lea_reg_reg_offset(
X86Register::Rsp,
X86Register::Rsp,
frida_gum_sys::GUM_RED_ZONE_SIZE as i32,
);
}
#[cfg(target_arch = "aarch64")]
{
writer.put_stp_reg_reg_reg_offset(
Aarch64Register::Lr,
Aarch64Register::X0,
Aarch64Register::Sp,
-(16 + frida_gum_sys::GUM_RED_ZONE_SIZE as i32) as i64,
IndexMode::PreAdjust,
);
writer.put_ldr_reg_u64(Aarch64Register::X0, address);
writer.put_bl_imm(helper.current_log_impl);
writer.put_ldp_reg_reg_reg_offset(
Aarch64Register::Lr,
Aarch64Register::X0,
Aarch64Register::Sp,
16 + frida_gum_sys::GUM_RED_ZONE_SIZE as i64,
IndexMode::PostAdjust,
);
}
}
}
instruction.keep()
}
});
helper.transformer = Some(transformer);
helper
}
}
struct FridaInProcessExecutor<'a, FH, H, I, OT>
where
FH: FridaHelper<'a>,
H: FnMut(&[u8]) -> ExitKind,
I: Input + HasTargetBytes,
OT: ObserversTuple,
{
base: InProcessExecutor<'a, H, I, OT>,
/// Frida's dynamic rewriting engine
stalker: Stalker<'a>,
/// User provided callback for instrumentation
helper: &'a FH,
followed: bool,
}
impl<'a, FH, H, I, OT> Executor<I> for FridaInProcessExecutor<'a, FH, H, I, OT>
where
FH: FridaHelper<'a>,
H: FnMut(&[u8]) -> ExitKind,
I: Input + HasTargetBytes,
OT: ObserversTuple,
{
/// Called right before exexution starts
#[inline]
fn pre_exec<EM, S>(&mut self, state: &mut S, event_mgr: &mut EM, input: &I) -> Result<(), Error>
where
EM: EventManager<I, S>,
{
if !self.followed {
self.followed = true;
self.stalker
.follow_me::<NoneEventSink>(self.helper.transformer(), None);
} else {
self.stalker.activate(NativePointer(
self.base.harness_mut() as *mut _ as *mut c_void
))
}
self.base.pre_exec(state, event_mgr, input)
}
/// Instruct the target about the input and run
#[inline]
fn run_target(&mut self, input: &I) -> Result<ExitKind, Error> {
self.base.run_target(input)
}
/// Called right after execution finished.
#[inline]
fn post_exec<EM, S>(
&mut self,
state: &mut S,
event_mgr: &mut EM,
input: &I,
) -> Result<(), Error>
where
EM: EventManager<I, S>,
{
self.stalker.deactivate();
self.base.post_exec(state, event_mgr, input)
}
}
impl<'a, FH, H, I, OT> HasObservers<OT> for FridaInProcessExecutor<'a, FH, H, I, OT>
where
FH: FridaHelper<'a>,
H: FnMut(&[u8]) -> ExitKind,
I: Input + HasTargetBytes,
OT: ObserversTuple,
{
#[inline]
fn observers(&self) -> &OT {
self.base.observers()
}
#[inline]
fn observers_mut(&mut self) -> &mut OT {
self.base.observers_mut()
}
}
impl<'a, FH, H, I, OT> Named for FridaInProcessExecutor<'a, FH, H, I, OT>
where
FH: FridaHelper<'a>,
H: FnMut(&[u8]) -> ExitKind,
I: Input + HasTargetBytes,
OT: ObserversTuple,
{
fn name(&self) -> &str {
self.base.name()
}
}
impl<'a, FH, H, I, OT> FridaInProcessExecutor<'a, FH, H, I, OT>
where
FH: FridaHelper<'a>,
H: FnMut(&[u8]) -> ExitKind,
I: Input + HasTargetBytes,
OT: ObserversTuple,
{
pub fn new(gum: &'a Gum, base: InProcessExecutor<'a, H, I, OT>, helper: &'a FH) -> Self {
let mut stalker = Stalker::new(gum);
// Let's exclude the main module and libc.so at least:
stalker.exclude(&MemoryRange::new(
Module::find_base_address(&env::args().next().unwrap()),
get_module_size(&env::args().next().unwrap()),
));
stalker.exclude(&MemoryRange::new(
Module::find_base_address("libc.so"),
get_module_size("libc.so"),
));
Self {
base: base,
stalker: stalker,
helper: helper,
followed: false,
}
}
}
/// 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>();
println!(
"Workdir: {:?}",
env::current_dir().unwrap().to_string_lossy().to_string()
);
unsafe {
fuzz(
&env::args().nth(1).expect("no module specified"),
&env::args().nth(2).expect("no symbol specified"),
vec![PathBuf::from("./corpus")],
PathBuf::from("./crashes"),
1337,
)
.expect("An error occurred while fuzzing");
}
}
/// Not supported on windows right now
#[cfg(windows)]
fn fuzz(
_module_name: &str,
_symbol_name: &str,
_corpus_dirs: Vec<PathBuf>,
_objective_dir: PathBuf,
_broker_port: u16,
) -> Result<(), ()> {
todo!("Example not supported on Windows");
}
/// The actual fuzzer
#[cfg(unix)]
unsafe fn fuzz(
module_name: &str,
symbol_name: &str,
corpus_dirs: Vec<PathBuf>,
objective_dir: PathBuf,
broker_port: u16,
) -> Result<(), Error> {
// '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::<_, _, UnixShMem, _>(stats, broker_port) {
Ok(res) => res,
Err(err) => match err {
Error::ShuttingDown => {
return Ok(());
}
_ => {
panic!("Failed to setup the restarter: {}", err);
}
},
};
let gum = Gum::obtain();
let lib = libloading::Library::new(module_name).unwrap();
let target_func: libloading::Symbol<unsafe extern "C" fn(data: *const u8, size: usize) -> i32> =
lib.get(symbol_name.as_bytes()).unwrap();
let mut frida_helper = FridaEdgeCoverageHelper::new(&gum, module_name);
// Create an observation channel using the coverage map
let edges_observer = HitcountsMapObserver::new(StdMapObserver::new_from_ptr(
"edges",
frida_helper.map.as_mut_ptr(),
MAP_SIZE,
));
let mut frida_harness = move |buf: &[u8]| {
(target_func)(buf.as_ptr(), buf.len());
ExitKind::Ok
};
// 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
)),
// 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()),
)
});
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 = FridaInProcessExecutor::new(
&gum,
InProcessExecutor::new(
"in-process(edges)",
&mut frida_harness,
tuple_list!(edges_observer),
&mut state,
&mut restarting_mgr,
)?,
&frida_helper,
);
// Let's exclude the main module and libc.so at least:
executor.stalker.exclude(&MemoryRange::new(
Module::find_base_address(&env::args().next().unwrap()),
get_module_size(&env::args().next().unwrap()),
));
executor.stalker.exclude(&MemoryRange::new(
Module::find_base_address("libc.so"),
get_module_size("libc.so"),
));
// 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(())
}

View File

@ -36,20 +36,6 @@ extern "C" {
static __lafl_max_edges_size: u32;
}
/// The wrapped harness function, calling out to the LLVM-style harness
#[cfg(unix)]
fn harness<E, I>(_executor: &E, buf: &[u8]) -> ExitKind
where
E: Executor<I>,
I: Input,
{
// println!("{:?}", buf);
unsafe {
LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len());
}
ExitKind::Ok
}
/// The main fn, usually parsing parameters, and starting the fuzzer
pub fn main() {
// Registry the metadata types used in this fuzzer
@ -121,10 +107,16 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// A fuzzer with just one stage and a random policy to get testcasess from the corpus
let fuzzer = StdFuzzer::new(RandCorpusScheduler::new(), tuple_list!(stage));
// 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
};
// Create the executor for an in-process function with just one observer for edge coverage
let mut executor = InProcessExecutor::new(
"in-process(edges)",
harness,
&mut harness,
tuple_list!(edges_observer),
&mut state,
&mut restarting_mgr,

View File

@ -12,10 +12,9 @@ use libafl::{
QueueCorpusScheduler,
},
events::setup_restarting_mgr,
executors::{inprocess::InProcessExecutor, Executor, ExitKind, TimeoutExecutor},
executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor},
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
inputs::Input,
mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens},
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
stages::mutational::StdMutationalStage,
@ -39,20 +38,6 @@ extern "C" {
static __lafl_max_edges_size: u32;
}
/// The wrapped harness function, calling out to the LLVM-style harness
#[cfg(unix)]
fn harness<E, I>(_executor: &E, buf: &[u8]) -> ExitKind
where
E: Executor<I>,
I: Input,
{
// println!("{:?}", buf);
unsafe {
LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len());
}
ExitKind::Ok
}
/// The main fn, usually parsing parameters, and starting the fuzzer
pub fn main() {
// Registry the metadata types used in this fuzzer
@ -143,11 +128,17 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage));
// 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
};
// Create the executor for an in-process function with just one observer for edge coverage
let mut executor = TimeoutExecutor::new(
InProcessExecutor::new(
"in-process(edges)",
harness,
&mut harness,
tuple_list!(edges_observer, TimeObserver::new("time")),
&mut state,
&mut restarting_mgr,

View File

@ -11,10 +11,9 @@ use libafl::{
QueueCorpusScheduler,
},
events::setup_restarting_mgr,
executors::{inprocess::InProcessExecutor, Executor, ExitKind},
executors::{inprocess::InProcessExecutor, ExitKind},
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
inputs::Input,
mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens},
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
stages::mutational::StdMutationalStage,
@ -41,20 +40,6 @@ extern "C" {
static __lafl_max_edges_size: u32;
}
/// The wrapped harness function, calling out to the LLVM-style harness
#[cfg(unix)]
fn harness<E, I>(_executor: &E, buf: &[u8]) -> ExitKind
where
E: Executor<I>,
I: Input,
{
// println!("{:?}", buf);
unsafe {
LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len());
}
ExitKind::Ok
}
/// The main fn, usually parsing parameters, and starting the fuzzer
pub fn main() {
// Registry the metadata types used in this fuzzer
@ -154,10 +139,16 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage));
// 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
};
// Create the executor for an in-process function with just one observer for edge coverage
let mut executor = InProcessExecutor::new(
"in-process(edges,cmps,allocs)",
harness,
&mut harness,
tuple_list!(
edges_observer,
cmps_observer,

View File

@ -25,26 +25,25 @@ use crate::{
Error,
};
/// The inmem executor harness
type HarnessFunction<E> = fn(&E, &[u8]) -> ExitKind;
/// The inmem executor simply calls a target function, then returns afterwards.
pub struct InProcessExecutor<I, OT>
pub struct InProcessExecutor<'a, H, I, OT>
where
H: FnMut(&[u8]) -> ExitKind,
I: Input + HasTargetBytes,
OT: ObserversTuple,
{
/// The name of this executor instance, to address it from other components
name: &'static str,
/// The harness function, being executed for each fuzzing loop execution
harness_fn: HarnessFunction<Self>,
harness_fn: &'a mut H,
/// The observers, observing each run
observers: OT,
phantom: PhantomData<I>,
}
impl<I, OT> Executor<I> for InProcessExecutor<I, OT>
impl<'a, H, I, OT> Executor<I> for InProcessExecutor<'a, H, I, OT>
where
H: FnMut(&[u8]) -> ExitKind,
I: Input + HasTargetBytes,
OT: ObserversTuple,
{
@ -95,7 +94,7 @@ where
#[inline]
fn run_target(&mut self, input: &I) -> Result<ExitKind, Error> {
let bytes = input.target_bytes();
let ret = (self.harness_fn)(self, bytes.as_slice());
let ret = (self.harness_fn)(bytes.as_slice());
Ok(ret)
}
@ -126,8 +125,9 @@ where
}
}
impl<I, OT> Named for InProcessExecutor<I, OT>
impl<'a, H, I, OT> Named for InProcessExecutor<'a, H, I, OT>
where
H: FnMut(&[u8]) -> ExitKind,
I: Input + HasTargetBytes,
OT: ObserversTuple,
{
@ -136,8 +136,9 @@ where
}
}
impl<I, OT> HasObservers<OT> for InProcessExecutor<I, OT>
impl<'a, H, I, OT> HasObservers<OT> for InProcessExecutor<'a, H, I, OT>
where
H: FnMut(&[u8]) -> ExitKind,
I: Input + HasTargetBytes,
OT: ObserversTuple,
{
@ -152,8 +153,9 @@ where
}
}
impl<I, OT> InProcessExecutor<I, OT>
impl<'a, H, I, OT> InProcessExecutor<'a, H, I, OT>
where
H: FnMut(&[u8]) -> ExitKind,
I: Input + HasTargetBytes,
OT: ObserversTuple,
{
@ -166,7 +168,7 @@ where
/// This may return an error on unix, if signal handler setup fails
pub fn new<EM, OC, OFT, S>(
name: &'static str,
harness_fn: HarnessFunction<Self>,
harness_fn: &'a mut H,
observers: OT,
_state: &mut S,
_event_mgr: &mut EM,
@ -215,6 +217,18 @@ where
phantom: PhantomData,
})
}
/// Retrieve the harness function.
#[inline]
pub fn harness(&self) -> &H {
self.harness_fn
}
/// Retrieve the harness function for a mutable reference.
#[inline]
pub fn harness_mut(&mut self) -> &mut H {
self.harness_fn
}
}
#[cfg(unix)]
@ -627,19 +641,15 @@ mod tests {
use crate::{
bolts::tuples::tuple_list,
executors::{Executor, ExitKind, InProcessExecutor},
inputs::Input,
inputs::NopInput,
};
fn test_harness_fn_nop<E: Executor<I>, I: Input>(_executor: &E, _buf: &[u8]) -> ExitKind {
ExitKind::Ok
}
#[test]
fn test_inmem_exec() {
use crate::inputs::NopInput;
let mut harness = |_buf: &[u8]| ExitKind::Ok;
let mut in_process_executor = InProcessExecutor::<NopInput, ()> {
harness_fn: test_harness_fn_nop,
let mut in_process_executor = InProcessExecutor::<_, NopInput, ()> {
harness_fn: &mut harness,
observers: tuple_list!(),
name: "main",
phantom: PhantomData,

View File

@ -137,8 +137,8 @@ mod tests {
use crate::{
bolts::tuples::tuple_list,
corpus::{Corpus, InMemoryCorpus, RandCorpusScheduler, Testcase},
executors::{Executor, ExitKind, InProcessExecutor},
inputs::{BytesInput, Input},
executors::{ExitKind, InProcessExecutor},
inputs::BytesInput,
mutators::{mutation_bitflip, ComposedByMutations, StdScheduledMutator},
stages::StdMutationalStage,
state::{HasCorpus, State},
@ -150,10 +150,6 @@ mod tests {
#[cfg(feature = "std")]
use crate::events::SimpleEventManager;
fn harness<E: Executor<I>, I: Input>(_executor: &E, _buf: &[u8]) -> ExitKind {
ExitKind::Ok
}
#[test]
fn test_fuzzer() {
let rand = StdRand::with_seed(0);
@ -175,9 +171,10 @@ mod tests {
});
let mut event_manager = SimpleEventManager::new(stats);
let mut harness = |_buf: &[u8]| ExitKind::Ok;
let mut executor = InProcessExecutor::new(
"main",
harness,
&mut harness,
tuple_list!(),
//Box::new(|_, _, _, _, _| ()),
&mut state,

View File

@ -126,6 +126,16 @@ where
}
}
/// Creates a new MapObserver with an owned map
pub fn new_owned(name: &'static str, map: Vec<T>) -> Self {
let initial = if map.is_empty() { T::default() } else { map[0] };
Self {
map: ArrayMut::Owned(map),
name: name.to_string(),
initial,
}
}
/// Creates a new MapObserver from a raw pointer
/// # Safety
/// Will dereference the map_ptr with up to len elements.