commit
bd22ea5268
28
README.md
28
README.md
@ -10,14 +10,12 @@ LibAFL is written and maintained by Andrea Fioraldi <andreafioraldi@gmail.com> a
|
|||||||
|
|
||||||
LibAFL gives you many of the benefits of an off-the-shelf fuzzer, while being completely customizable.
|
LibAFL gives you many of the benefits of an off-the-shelf fuzzer, while being completely customizable.
|
||||||
Some highlight features currently include:
|
Some highlight features currently include:
|
||||||
- `multi platform`: LibAFL was confirmed to work on *Windows*, *MacOS*, *Linux*, and *Android* on *x86_64* and *aarch64*.
|
- `fast`: We do everything we can at compile time, keeping runtime overhead minimal. Users reach 120k execs/sec in frida-mode on a phone (using all cores).
|
||||||
- `portable`: `LibAFL` can be built in `no_std` mode. Inject LibAFL in obscure targets like embedded devices and hypervisors.
|
- `scalable`: `Low Level Message Passing`, `LLMP` for short, allows LibAFL to scale almost linearly over cores, and via TCP to multiple machines soon!
|
||||||
- `adaptable`: You can replace each part of LibAFL. For example, `BytesInput` is just one potential form input:
|
- `adaptable`: You can replace each part of LibAFL. For example, `BytesInput` is just one potential form input:
|
||||||
feel free to add an AST-based input for structured fuzzing, and more.
|
feel free to add an AST-based input for structured fuzzing, and more.
|
||||||
- `scalable`: `Low Level Message Passing`, `LLMP` for short, allows LibAFL to scale almost linearly over cores, and via TCP to multiple machines!
|
- `multi platform`: LibAFL was confirmed to work on *Windows*, *MacOS*, *Linux*, and *Android* on *x86_64* and *aarch64*. `LibAFL` can be built in `no_std` mode to inject LibAFL into obscure targets like embedded devices and hypervisors.
|
||||||
- `fast`: We do everything we can at compile time, keeping runtime overhead minimal.
|
|
||||||
- `bring your own target`: We support binary-only modes, like Frida-Mode, as well as multiple compilation passes for sourced-based instrumentation. Of course it's easy to add custom instrumentation backends.
|
- `bring your own target`: We support binary-only modes, like Frida-Mode, as well as multiple compilation passes for sourced-based instrumentation. Of course it's easy to add custom instrumentation backends.
|
||||||
- `usable`: We hope. But we'll let you be the judge. Enjoy LibAFL.
|
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@ -26,26 +24,25 @@ It is fast, multi-platform, no_std compatible, and scales over cores and machine
|
|||||||
|
|
||||||
It offers a main crate that provide building blocks for custom fuzzers, [libafl](./libafl), a library containing common code that can be used for targets instrumentation, [libafl_targets](./libafl_targets), and a library providing facilities to wrap compilers, [libafl_cc](./libafl_cc).
|
It offers a main crate that provide building blocks for custom fuzzers, [libafl](./libafl), a library containing common code that can be used for targets instrumentation, [libafl_targets](./libafl_targets), and a library providing facilities to wrap compilers, [libafl_cc](./libafl_cc).
|
||||||
|
|
||||||
LibAFL offers integrations with popular instrumemntation frameworks. At the moment, the supported backends are:
|
LibAFL offers integrations with popular instrumentation frameworks. At the moment, the supported backends are:
|
||||||
|
|
||||||
+ SanitizerCoverage, in [libafl_targets](./libafl_targets)
|
+ SanitizerCoverage, in [libafl_targets](./libafl_targets)
|
||||||
+ Frida, in [libafl_frida](./libafl_frida), by s1341 <github@shmarya.net> (Windows support is broken atm, it relies on [this upstream issue](https://github.com/meme/frida-rust/issues/9) to be fixed.)
|
+ Frida, in [libafl_frida](./libafl_frida), by s1341 <github@shmarya.net> (Windows support is broken atm, it relies on [this upstream issue](https://github.com/meme/frida-rust/issues/9) to be fixed.)
|
||||||
+ More to come (QEMU-mode, ...)
|
+ More to come (QEMU-mode, ...)
|
||||||
|
|
||||||
LibAFL offers integrations with popular instrumemntation frameworks too. At the moment, the supported backends are:
|
|
||||||
|
|
||||||
+ SanitizerCoverage, in [libafl_targets](./libafl_targets)
|
|
||||||
+ Frida, in [libafl_frida](./libafl_frida), by s1341 <github@shmarya.net> (Windows support will be added soon)
|
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
Clone the LibAFL repository with
|
1. Install the Rust development language. We highly recommend *not* to use e.g.
|
||||||
|
your Linux distribution package as this is likely outdated. So rather install
|
||||||
|
Rust directly, instructions can be found [here](https://www.rust-lang.org/tools/install).
|
||||||
|
|
||||||
|
2. Clone the LibAFL repository with
|
||||||
|
|
||||||
```
|
```
|
||||||
git clone https://github.com/AFLplusplus/LibAFL
|
git clone https://github.com/AFLplusplus/LibAFL
|
||||||
```
|
```
|
||||||
|
|
||||||
To get the latest and greatest features,
|
If you want to get the latest and greatest features,
|
||||||
```
|
```
|
||||||
git checkout dev
|
git checkout dev
|
||||||
```
|
```
|
||||||
@ -56,18 +53,19 @@ Build the library using
|
|||||||
cargo build --release
|
cargo build --release
|
||||||
```
|
```
|
||||||
|
|
||||||
Build the API documentation with
|
4. Build the API documentation with
|
||||||
|
|
||||||
```
|
```
|
||||||
cargo doc
|
cargo doc
|
||||||
```
|
```
|
||||||
|
|
||||||
Browse the LibAFL book (WIP!) with (requires [mdbook](https://github.com/rust-lang/mdBook))
|
5. Browse the LibAFL book (WIP!) with (requires [mdbook](https://github.com/rust-lang/mdBook))
|
||||||
|
|
||||||
```
|
```
|
||||||
cd docs && mdbook serve
|
cd docs && mdbook serve
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
We collect all example fuzzers in [`./fuzzers`](./fuzzers/).
|
We collect all example fuzzers in [`./fuzzers`](./fuzzers/).
|
||||||
Be sure to read their documentation (and source), this is *the natural way to get started!*
|
Be sure to read their documentation (and source), this is *the natural way to get started!*
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
The LibAFL architecture is built around some entities to allow code reuse and low-cost abstractions.
|
The LibAFL architecture is built around some entities to allow code reuse and low-cost abstractions.
|
||||||
|
|
||||||
Initially, we started thinking to implement LibAFL in an Object Oriented language, such C++. When we landed to Rust, we immediately changed our idea as we realized that, while Rust allow a sort of OOP pattern, we can build the library using a more sane approach like the one described in [this blogpost](https://kyren.github.io/2018/09/14/rustconf-talk.html) about game design in Rust.
|
Initially, we started thinking to implement LibAFL in an Object Oriented language, such C++. When we landed to Rust, we immediately changed our idea as we realized that, while Rust allows a sort of OOP pattern, we can build the library using a more sane approach like the one described in [this blogpost](https://kyren.github.io/2018/09/14/rustconf-talk.html) about game design in Rust.
|
||||||
|
|
||||||
The LibAFL code reuse meachanism is so based on components rather than sub-classes, but there are still some OOP patterns in the library.
|
The LibAFL code reuse meachanism is so based on components rather than sub-classes, but there are still some OOP patterns in the library.
|
||||||
|
|
||||||
|
@ -37,17 +37,17 @@ In addition, if you want to perform source-level fuzz testing of C/C++ applicati
|
|||||||
you will likely need Clang with its instrumentation options to compile the programs
|
you will likely need Clang with its instrumentation options to compile the programs
|
||||||
under test.
|
under test.
|
||||||
|
|
||||||
You can download and build the LLVM source tree, Clang included, following the steps
|
On Linux you can use your distro's package manager to get Clang,
|
||||||
explained [here](https://clang.llvm.org/get_started.html).
|
|
||||||
|
|
||||||
Alternatively, on Linux, you can use your distro's package manager to get Clang,
|
|
||||||
but these packages are not always updated, so we suggest you to use the
|
but these packages are not always updated, so we suggest you to use the
|
||||||
Debian/Ubuntu prebuilt packages from LLVM that are available using their [official repository](https://apt.llvm.org/).
|
Debian/Ubuntu prebuilt packages from LLVM that are available using their [official repository](https://apt.llvm.org/).
|
||||||
|
|
||||||
For Miscrosoft Windows, you can download the [installer package](https://llvm.org/builds/) that LLVM generates periodically.
|
For Microsoft Windows, you can download the [installer package](https://llvm.org/builds/) that LLVM generates periodically.
|
||||||
|
|
||||||
Despite that Clang is the default C compiler on macOS, we discourage the use of the build shipped by Apple and encourage
|
Despite that Clang is the default C compiler on macOS, we discourage the use of the build shipped by Apple and encourage
|
||||||
the installation from `brew` or direclty a fresh build from the source code.
|
the installation from `brew` or directly a fresh build from the source code.
|
||||||
|
|
||||||
|
Alternatively you can download and build the LLVM source tree - Clang included - following the steps
|
||||||
|
explained [here](https://clang.llvm.org/get_started.html).
|
||||||
|
|
||||||
## Rust installation
|
## Rust installation
|
||||||
|
|
||||||
|
@ -14,6 +14,6 @@ pub struct MyMetadata {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The struct must be static, so it cannot holds references to borrowed objects.
|
The struct must be static, so it cannot hold references to borrowed objects.
|
||||||
|
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ static char * allocation = NULL;
|
|||||||
__attribute__((noinline))
|
__attribute__((noinline))
|
||||||
void func3( char * alloc) {
|
void func3( char * alloc) {
|
||||||
printf("func3\n");
|
printf("func3\n");
|
||||||
if (random() % 5 == 0) {
|
if (random() == 0) {
|
||||||
alloc[0xff] = 0xde;
|
alloc[0xff] = 0xde;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,9 +197,10 @@ pub fn main() {
|
|||||||
env::args()
|
env::args()
|
||||||
.nth(3)
|
.nth(3)
|
||||||
.expect("no modules to instrument specified")
|
.expect("no modules to instrument specified")
|
||||||
.split(":")
|
.split(':')
|
||||||
|
.map(|module_name| std::fs::canonicalize(module_name).unwrap())
|
||||||
.collect(),
|
.collect(),
|
||||||
vec![PathBuf::from("./corpus")],
|
&vec![PathBuf::from("./corpus")],
|
||||||
PathBuf::from("./crashes"),
|
PathBuf::from("./crashes"),
|
||||||
1337,
|
1337,
|
||||||
)
|
)
|
||||||
@ -224,8 +225,8 @@ fn fuzz(
|
|||||||
unsafe fn fuzz(
|
unsafe fn fuzz(
|
||||||
module_name: &str,
|
module_name: &str,
|
||||||
symbol_name: &str,
|
symbol_name: &str,
|
||||||
modules_to_instrument: Vec<&str>,
|
modules_to_instrument: Vec<PathBuf>,
|
||||||
corpus_dirs: Vec<PathBuf>,
|
corpus_dirs: &Vec<PathBuf>,
|
||||||
objective_dir: PathBuf,
|
objective_dir: PathBuf,
|
||||||
broker_port: u16,
|
broker_port: u16,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
@ -61,7 +61,6 @@ libafl_derive = { version = "*", optional = true, path = "../libafl_derive" }
|
|||||||
serde_json = { version = "1.0", optional = true, default-features = false, features = ["alloc"] } # an easy way to debug print SerdeAnyMap
|
serde_json = { version = "1.0", optional = true, default-features = false, features = ["alloc"] } # an easy way to debug print SerdeAnyMap
|
||||||
compression = { version = "0.1.5" }
|
compression = { version = "0.1.5" }
|
||||||
num_enum = "0.5.1"
|
num_enum = "0.5.1"
|
||||||
spin = "0.9.0"
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "android")'.dependencies]
|
[target.'cfg(target_os = "android")'.dependencies]
|
||||||
backtrace = { version = "0.3", optional = true, default-features = false, features = ["std", "libbacktrace"] } # for llmp_debug
|
backtrace = { version = "0.3", optional = true, default-features = false, features = ["std", "libbacktrace"] } # for llmp_debug
|
||||||
|
@ -27,7 +27,7 @@ termcolor = "1.1.2"
|
|||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
backtrace = { version = "0.3.58", default-features = false, features = ["std", "serde"] }
|
backtrace = { version = "0.3.58", default-features = false, features = ["std", "serde"] }
|
||||||
num-traits = "0.2.14"
|
num-traits = "0.2.14"
|
||||||
seahash = "4.1.0"
|
ahash = "0.7"
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
gothook = { version = "0.1" }
|
gothook = { version = "0.1" }
|
||||||
|
@ -31,6 +31,7 @@ use std::{
|
|||||||
cell::{RefCell, RefMut},
|
cell::{RefCell, RefMut},
|
||||||
ffi::c_void,
|
ffi::c_void,
|
||||||
io::{self, Write},
|
io::{self, Write},
|
||||||
|
path::PathBuf,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
use termcolor::{Color, ColorSpec, WriteColor};
|
use termcolor::{Color, ColorSpec, WriteColor};
|
||||||
@ -697,7 +698,7 @@ impl AsanRuntime {
|
|||||||
/// Initialize the runtime so that it is read for action. Take care not to move the runtime
|
/// Initialize the runtime so that it is read for action. Take care not to move the runtime
|
||||||
/// instance after this function has been called, as the generated blobs would become
|
/// instance after this function has been called, as the generated blobs would become
|
||||||
/// invalid!
|
/// invalid!
|
||||||
pub fn init(&mut self, modules_to_instrument: &[&str]) {
|
pub fn init(&mut self, modules_to_instrument: &[PathBuf]) {
|
||||||
// workaround frida's frida-gum-allocate-near bug:
|
// workaround frida's frida-gum-allocate-near bug:
|
||||||
unsafe {
|
unsafe {
|
||||||
for _ in 0..512 {
|
for _ in 0..512 {
|
||||||
@ -730,7 +731,7 @@ impl AsanRuntime {
|
|||||||
self.unpoison_all_existing_memory();
|
self.unpoison_all_existing_memory();
|
||||||
for module_name in modules_to_instrument {
|
for module_name in modules_to_instrument {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
self.hook_library(module_name);
|
self.hook_library(module_name.to_str().unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -786,15 +787,14 @@ impl AsanRuntime {
|
|||||||
pub fn register_thread(&self) {
|
pub fn register_thread(&self) {
|
||||||
let mut allocator = Allocator::get();
|
let mut allocator = Allocator::get();
|
||||||
let (stack_start, stack_end) = Self::current_stack();
|
let (stack_start, stack_end) = Self::current_stack();
|
||||||
println!("current stack: {:#016x}-{:#016x}", stack_start, stack_end);
|
|
||||||
allocator.map_shadow_for_region(stack_start, stack_end, true);
|
allocator.map_shadow_for_region(stack_start, stack_end, true);
|
||||||
|
|
||||||
//let (tls_start, tls_end) = Self::current_tls();
|
let (tls_start, tls_end) = Self::current_tls();
|
||||||
//allocator.map_shadow_for_region(tls_start, tls_end, true);
|
allocator.map_shadow_for_region(tls_start, tls_end, true);
|
||||||
//println!(
|
println!(
|
||||||
//"registering thread with stack {:x}:{:x} and tls {:x}:{:x}",
|
"registering thread with stack {:x}:{:x} and tls {:x}:{:x}",
|
||||||
//stack_start as usize, stack_end as usize, tls_start as usize, tls_end as usize
|
stack_start as usize, stack_end as usize, tls_start as usize, tls_end as usize
|
||||||
//);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine the stack start, end for the currently running thread
|
/// Determine the stack start, end for the currently running thread
|
||||||
@ -829,6 +829,9 @@ impl AsanRuntime {
|
|||||||
/// Determine the tls start, end for the currently running thread
|
/// Determine the tls start, end for the currently running thread
|
||||||
fn current_tls() -> (usize, usize) {
|
fn current_tls() -> (usize, usize) {
|
||||||
let tls_address = unsafe { get_tls_ptr() } as usize;
|
let tls_address = unsafe { get_tls_ptr() } as usize;
|
||||||
|
// we need to mask off the highest byte, due to 'High Byte Ignore"
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
let tls_address = tls_address & 0xffffffffffffff;
|
||||||
|
|
||||||
let (start, end, _, _) = find_mapping_for_address(tls_address).unwrap();
|
let (start, end, _, _) = find_mapping_for_address(tls_address).unwrap();
|
||||||
(start, end)
|
(start, end)
|
||||||
@ -1421,7 +1424,6 @@ impl AsanRuntime {
|
|||||||
; b >skip_report
|
; b >skip_report
|
||||||
|
|
||||||
; report:
|
; report:
|
||||||
; brk 0x11
|
|
||||||
; stp x29, x30, [sp, #-0x10]!
|
; stp x29, x30, [sp, #-0x10]!
|
||||||
; mov x29, sp
|
; mov x29, sp
|
||||||
|
|
||||||
@ -1541,7 +1543,6 @@ impl AsanRuntime {
|
|||||||
; b >skip_report
|
; b >skip_report
|
||||||
|
|
||||||
; report:
|
; report:
|
||||||
; brk 0x22
|
|
||||||
; stp x29, x30, [sp, #-0x10]!
|
; stp x29, x30, [sp, #-0x10]!
|
||||||
; mov x29, sp
|
; mov x29, sp
|
||||||
|
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
use ahash::AHasher;
|
||||||
|
use std::hash::Hasher;
|
||||||
|
|
||||||
use libafl::inputs::{HasTargetBytes, Input};
|
use libafl::inputs::{HasTargetBytes, Input};
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
@ -26,7 +29,7 @@ use frida_gum::{Gum, Module, PageProtection};
|
|||||||
use num_traits::cast::FromPrimitive;
|
use num_traits::cast::FromPrimitive;
|
||||||
|
|
||||||
use rangemap::RangeMap;
|
use rangemap::RangeMap;
|
||||||
use std::rc::Rc;
|
use std::{path::PathBuf, rc::Rc};
|
||||||
|
|
||||||
use crate::{asan_rt::AsanRuntime, FridaOptions};
|
use crate::{asan_rt::AsanRuntime, FridaOptions};
|
||||||
|
|
||||||
@ -82,9 +85,12 @@ impl<'a> FridaHelper<'a> for FridaInstrumentationHelper<'a> {
|
|||||||
|
|
||||||
fn post_exec<I: Input + HasTargetBytes>(&mut self, input: &I) {
|
fn post_exec<I: Input + HasTargetBytes>(&mut self, input: &I) {
|
||||||
if self.options.drcov_enabled() {
|
if self.options.drcov_enabled() {
|
||||||
|
let mut hasher = AHasher::new_with_keys(0, 0);
|
||||||
|
hasher.write(input.target_bytes().as_slice());
|
||||||
|
|
||||||
let filename = format!(
|
let filename = format!(
|
||||||
"./coverage/{:016x}.drcov",
|
"./coverage/{:016x}.drcov",
|
||||||
seahash::hash(input.target_bytes().as_slice())
|
hasher.finish(),
|
||||||
);
|
);
|
||||||
DrCovWriter::new(&filename, &self.ranges, &mut self.drcov_basic_blocks).write();
|
DrCovWriter::new(&filename, &self.ranges, &mut self.drcov_basic_blocks).write();
|
||||||
}
|
}
|
||||||
@ -193,7 +199,7 @@ impl<'a> FridaInstrumentationHelper<'a> {
|
|||||||
gum: &'a Gum,
|
gum: &'a Gum,
|
||||||
options: FridaOptions,
|
options: FridaOptions,
|
||||||
_harness_module_name: &str,
|
_harness_module_name: &str,
|
||||||
modules_to_instrument: &'a [&str],
|
modules_to_instrument: &'a [PathBuf],
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut helper = Self {
|
let mut helper = Self {
|
||||||
map: [0u8; MAP_SIZE],
|
map: [0u8; MAP_SIZE],
|
||||||
@ -214,11 +220,11 @@ impl<'a> FridaInstrumentationHelper<'a> {
|
|||||||
|
|
||||||
if options.stalker_enabled() {
|
if options.stalker_enabled() {
|
||||||
for (id, module_name) in modules_to_instrument.iter().enumerate() {
|
for (id, module_name) in modules_to_instrument.iter().enumerate() {
|
||||||
let (lib_start, lib_end) = find_mapping_for_path(module_name);
|
let (lib_start, lib_end) = find_mapping_for_path(module_name.to_str().unwrap());
|
||||||
println!("including range {:x}-{:x} for {}", lib_start, lib_end, module_name);
|
println!("including range {:x}-{:x} for {:?}", lib_start, lib_end, module_name);
|
||||||
helper
|
helper
|
||||||
.ranges
|
.ranges
|
||||||
.insert(lib_start..lib_end, (id as u16, module_name));
|
.insert(lib_start..lib_end, (id as u16, module_name.to_str().unwrap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if helper.options.drcov_enabled() {
|
if helper.options.drcov_enabled() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user