Compare commits
188 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
fc23782dc3 | ||
![]() |
96e24d1c8b | ||
![]() |
fd68c8a81f | ||
![]() |
fafa27a7e9 | ||
![]() |
c881dc996d | ||
![]() |
cdd3d8ace0 | ||
![]() |
863a6b8b7c | ||
![]() |
1b9ffcec74 | ||
![]() |
aa3f126100 | ||
![]() |
21ee8d2cae | ||
![]() |
b519d24981 | ||
![]() |
f73e2006fc | ||
![]() |
0f633962ff | ||
![]() |
6523341c4d | ||
![]() |
0d446bab20 | ||
![]() |
807a534121 | ||
![]() |
1bd21509af | ||
![]() |
ccd1211cd2 | ||
![]() |
590d3655cd | ||
![]() |
ea512f70f8 | ||
![]() |
a01863696e | ||
![]() |
702f163c13 | ||
![]() |
0c9933c3e4 | ||
![]() |
ae2caff990 | ||
![]() |
620b2861e3 | ||
![]() |
66b2867ba2 | ||
![]() |
a659dd821c | ||
![]() |
c9ea6ee6aa | ||
![]() |
76e4f6031d | ||
![]() |
02c6cab744 | ||
![]() |
d98384e582 | ||
![]() |
3f7d35bfdc | ||
![]() |
7c514c3669 | ||
![]() |
38ea17b426 | ||
![]() |
c9a78f154b | ||
![]() |
8f8e74d670 | ||
![]() |
f4f23de32b | ||
![]() |
bbe4e85768 | ||
![]() |
104c170ade | ||
![]() |
fd95560512 | ||
![]() |
d6ee2dbe12 | ||
![]() |
8245c7eda9 | ||
![]() |
306cdcd800 | ||
![]() |
b9970cbdac | ||
![]() |
a351e7a509 | ||
![]() |
08fe6ab791 | ||
![]() |
3c331e5a9b | ||
![]() |
51bc1d0328 | ||
![]() |
6c98945fc3 | ||
![]() |
0fa815f2b8 | ||
![]() |
c38405ef83 | ||
![]() |
8dfdee6fce | ||
![]() |
460787196a | ||
![]() |
6894a37ceb | ||
![]() |
44b798c07e | ||
![]() |
e8d99b9975 | ||
![]() |
786af9f6a9 | ||
![]() |
b72bf55555 | ||
![]() |
4d778dd64d | ||
![]() |
2ed6583041 | ||
![]() |
e8838ebebe | ||
![]() |
3ac439b345 | ||
![]() |
20c32316eb | ||
![]() |
3ffec79a17 | ||
![]() |
b96e194812 | ||
![]() |
c8254dbd0e | ||
![]() |
9df95bd936 | ||
![]() |
4f7b59aca4 | ||
![]() |
35e5b87188 | ||
![]() |
5b4ae61cdd | ||
![]() |
2a3f1d68f5 | ||
![]() |
672f4d1668 | ||
![]() |
2a926f6546 | ||
![]() |
452ca7a672 | ||
![]() |
df6271a0f3 | ||
![]() |
31357aa7e2 | ||
![]() |
3e7322e395 | ||
![]() |
fbe8cce1b8 | ||
![]() |
d36296c654 | ||
![]() |
dc800f0814 | ||
![]() |
59bf118a5a | ||
![]() |
0727c80347 | ||
![]() |
c0f229ec23 | ||
![]() |
cf02553ea7 | ||
![]() |
3dbea91a63 | ||
![]() |
95004aab7e | ||
![]() |
b3020d7296 | ||
![]() |
672d25e5ac | ||
![]() |
92842c8b04 | ||
![]() |
ff4e2f4192 | ||
![]() |
64a57ad3e3 | ||
![]() |
9e88e5734e | ||
![]() |
1b0cdab3e4 | ||
![]() |
b7296db406 | ||
![]() |
20958a979f | ||
![]() |
bdac876dd4 | ||
![]() |
46b75747ef | ||
![]() |
f454d17482 | ||
![]() |
8bffd28b4c | ||
![]() |
e7ef6ae8b7 | ||
![]() |
26aace6073 | ||
![]() |
e42cd9c12f | ||
![]() |
e61ac10656 | ||
![]() |
71d367af30 | ||
![]() |
fb0d3b07ea | ||
![]() |
cf0a0a0698 | ||
![]() |
44b69666da | ||
![]() |
f8a4a020e8 | ||
![]() |
53dba5f49d | ||
![]() |
30b51bb810 | ||
![]() |
bd2de16b4e | ||
![]() |
a74e5da268 | ||
![]() |
4d78878c02 | ||
![]() |
b7a0b823c6 | ||
![]() |
0173d722c6 | ||
![]() |
30e296968b | ||
![]() |
4e15be182e | ||
![]() |
4d5a759955 | ||
![]() |
e75f65080e | ||
![]() |
5d76707ede | ||
![]() |
48caffb802 | ||
![]() |
eaf5ff9de0 | ||
![]() |
86ab682e5a | ||
![]() |
db62c26eda | ||
![]() |
71f106be20 | ||
![]() |
d0b4c39acd | ||
![]() |
2a88a776bf | ||
![]() |
b77c0b78cc | ||
![]() |
f9dd67b59b | ||
![]() |
cc53da85fb | ||
![]() |
fdf579bcd5 | ||
![]() |
d73fb92ddf | ||
![]() |
fc8c92514f | ||
![]() |
7c4acb3b22 | ||
![]() |
2cd3fb8fea | ||
![]() |
ff9208f107 | ||
![]() |
afa506c0c8 | ||
![]() |
33ddce2cea | ||
![]() |
b927fc9b06 | ||
![]() |
e5c220519e | ||
![]() |
92c0c5eeab | ||
![]() |
00ec7e143c | ||
![]() |
fe51286586 | ||
![]() |
3b68399cc9 | ||
![]() |
7fd9ac0952 | ||
![]() |
5cdb7f7b05 | ||
![]() |
ebc886032f | ||
![]() |
333a51aeaa | ||
![]() |
3c8a00bc42 | ||
![]() |
1446692f02 | ||
![]() |
15c1c0fb5e | ||
![]() |
7cf7d545a6 | ||
![]() |
97e88af0c5 | ||
![]() |
28786c943a | ||
![]() |
ec84c71eae | ||
![]() |
3345727c94 | ||
![]() |
17cb317429 | ||
![]() |
159e6ea480 | ||
![]() |
266677bb88 | ||
![]() |
f27ca843e1 | ||
![]() |
1bb37e4b98 | ||
![]() |
7d412693c8 | ||
![]() |
d2985c5b2e | ||
![]() |
43425cf103 | ||
![]() |
9458549fef | ||
![]() |
3e38862837 | ||
![]() |
676a149497 | ||
![]() |
2b092f40fa | ||
![]() |
476cb7e7dc | ||
![]() |
75f12bd0eb | ||
![]() |
3a1e499d9d | ||
![]() |
de6ee8b161 | ||
![]() |
d77d9d5f31 | ||
![]() |
4d8b566a87 | ||
![]() |
3c7dcac41d | ||
![]() |
038732bd92 | ||
![]() |
f1b25fed65 | ||
![]() |
b0df0a26a1 | ||
![]() |
7ed1ac9c9b | ||
![]() |
e56d5318e4 | ||
![]() |
ccf6cc708a | ||
![]() |
016a4c3778 | ||
![]() |
d04346c870 | ||
![]() |
664e87809e | ||
![]() |
55e220f0e8 | ||
![]() |
162de0ceaf | ||
![]() |
9e4a0513c6 | ||
![]() |
08be5f732e |
94
.github/workflows/build_and_test.yml
vendored
94
.github/workflows/build_and_test.yml
vendored
@ -61,18 +61,14 @@ jobs:
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
- name: set mold linker as default linker
|
||||
uses: rui314/setup-mold@v1
|
||||
- name: Install and cache deps
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.1.0
|
||||
with:
|
||||
packages: llvm llvm-dev clang ninja-build clang-format-13 shellcheck libgtk-3-dev gcc-arm-linux-gnueabi g++-arm-linux-gnueabi libslirp-dev
|
||||
packages: llvm llvm-dev clang ninja-build clang-format-13 shellcheck libgtk-3-dev gcc-arm-linux-gnueabi g++-arm-linux-gnueabi libslirp-dev libz3-dev
|
||||
- name: get clang version
|
||||
run: command -v llvm-config && clang -v
|
||||
- name: Install cargo-hack
|
||||
run: curl -LsSf https://github.com/taiki-e/cargo-hack/releases/latest/download/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xzf - -C ~/.cargo/bin
|
||||
- name: Add nightly rustfmt and clippy
|
||||
run: rustup toolchain install nightly --component rustfmt --component clippy --allow-downgrade
|
||||
run: rustup toolchain install nightly --component rustfmt --component clippy --component miri --allow-downgrade
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
@ -82,6 +78,8 @@ jobs:
|
||||
run: cargo check --features=sancov_pcguard_edges
|
||||
- name: Format
|
||||
run: cargo fmt -- --check
|
||||
- name: Cleanup
|
||||
run: cargo clean
|
||||
- name: Run clang-format style check for C/C++ programs.
|
||||
run: clang-format-13 -n -Werror --style=file $(find . -type f \( -name '*.cpp' -o -iname '*.hpp' -o -name '*.cc' -o -name '*.cxx' -o -name '*.cc' -o -name '*.h' \) | grep -v '/target/' | grep -v 'libpng-1\.6\.37' | grep -v 'stb_image\.h' | grep -v 'dlmalloc\.c' | grep -v 'QEMU-Nyx')
|
||||
- name: run shellcheck
|
||||
@ -95,9 +93,39 @@ jobs:
|
||||
- name: Test Docs
|
||||
run: cargo +nightly test --doc --all-features
|
||||
|
||||
# ---- build and feature check ----
|
||||
# ---- build normal and examples ----
|
||||
- name: Run a normal build
|
||||
run: cargo build --verbose
|
||||
- name: Build examples
|
||||
run: cargo build --examples --verbose
|
||||
|
||||
# --- miri undefined behavior test --
|
||||
- name: Run miri tests
|
||||
run: RUST_BACKTRACE=1 MIRIFLAGS="-Zmiri-disable-isolation" cargo +nightly miri test
|
||||
|
||||
ubuntu-check:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Remove Dotnet & Haskell
|
||||
run: rm -rf /usr/share/dotnet && rm -rf /opt/ghc
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
- name: Install and cache deps
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.1.0
|
||||
with:
|
||||
packages: llvm llvm-dev clang ninja-build clang-format-13 shellcheck libgtk-3-dev gcc-arm-linux-gnueabi g++-arm-linux-gnueabi libslirp-dev libz3-dev
|
||||
- name: get clang version
|
||||
run: command -v llvm-config && clang -v
|
||||
- name: Install cargo-hack
|
||||
run: curl -LsSf https://github.com/taiki-e/cargo-hack/releases/latest/download/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xzf - -C ~/.cargo/bin
|
||||
- name: Add nightly
|
||||
run: rustup toolchain install nightly --allow-downgrade
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
# ---- build and feature check ----
|
||||
# cargo-hack's --feature-powerset would be nice here but libafl has a too many knobs
|
||||
- name: Check each feature
|
||||
# Skipping `python` as it has to be built with the `maturin` tool
|
||||
@ -106,8 +134,6 @@ jobs:
|
||||
run: cargo hack check --each-feature --clean-per-run --exclude-features=prelude,agpl,nautilus,python,sancov_pcguard_edges,arm,aarch64,i386,be,systemmode --no-dev-deps
|
||||
- name: Check nightly features
|
||||
run: cargo +nightly check --features=agpl && cargo +nightly check --features=nautilus
|
||||
- name: Build examples
|
||||
run: cargo build --examples --verbose
|
||||
|
||||
ubuntu-concolic:
|
||||
runs-on: ubuntu-latest
|
||||
@ -130,16 +156,16 @@ jobs:
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
- name: set mold linker as default linker
|
||||
uses: rui314/setup-mold@v1
|
||||
- name: Install deps
|
||||
run: sudo apt-get install -y llvm llvm-dev clang ninja-build python3-dev python3-pip python3-venv
|
||||
run: sudo apt-get install -y llvm llvm-dev clang ninja-build python3-dev python3-pip python3-venv libz3-dev
|
||||
- name: Install maturin
|
||||
run: python3 -m pip install maturin
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run a maturin build
|
||||
run: cd ./bindings/pylibafl && maturin build
|
||||
run: cd ./bindings/pylibafl && python3 -m venv .env && . .env/bin/activate && maturin develop && ./test.sh
|
||||
- name: Run python test
|
||||
run: . ./bindings/pylibafl/.env/bin/activate && cd ./fuzzers/baby_fuzzer && python3 baby_fuzzer.py 2>&1 | grep "Bye"
|
||||
|
||||
fuzzers:
|
||||
strategy:
|
||||
@ -151,24 +177,27 @@ jobs:
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
- name: set mold linker as default linker
|
||||
if: runner.os == 'Linux' # mold only support linux until now
|
||||
uses: rui314/setup-mold@v1
|
||||
- name: Add nightly rustfmt and clippy
|
||||
run: rustup toolchain install nightly --component rustfmt --component clippy --allow-downgrade
|
||||
- name: Add no_std toolchain
|
||||
run: rustup toolchain install nightly-x86_64-unknown-linux-gnu ; rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu
|
||||
- name: Install python
|
||||
- name: Add wasm target
|
||||
run: rustup target add wasm32-unknown-unknown
|
||||
- name: Install cxxbridge
|
||||
if: runner.os == 'macOS'
|
||||
run: brew install --force-bottle --overwrite python@3.11
|
||||
run: cargo install cxxbridge-cmd
|
||||
- name: Install python (macOS)
|
||||
# Removing macOS things already installed in CI against failed linking
|
||||
if: runner.os == 'macOS'
|
||||
run: rm /usr/local/bin/2to3* /usr/local/bin/idle3* /usr/local/bin/pydoc3* /usr/local/bin/python3*; brew install --force-bottle --overwrite python
|
||||
- uses: lyricwulf/abc@v1
|
||||
with:
|
||||
# todo: remove afl++-clang when nyx support samcov_pcguard
|
||||
linux: llvm llvm-dev clang nasm ninja-build gcc-arm-linux-gnueabi g++-arm-linux-gnueabi libgtk-3-dev afl++-clang pax-utils
|
||||
linux: llvm llvm-dev clang nasm ninja-build gcc-arm-linux-gnueabi g++-arm-linux-gnueabi libgtk-3-dev afl++-clang pax-utils libz3-dev
|
||||
# update bash for macos to support `declare -A` command`
|
||||
macos: llvm libpng nasm coreutils z3 bash
|
||||
macos: llvm libpng nasm coreutils z3 bash wget
|
||||
- name: pip install
|
||||
run: python3 -m pip install msgpack jinja2
|
||||
run: python3 -m pip install msgpack jinja2 find_libpython
|
||||
# Note that nproc needs to have coreutils installed on macOS, so the order of CI commands matters.
|
||||
- name: enable mult-thread for `make`
|
||||
run: export MAKEFLAGS="-j$(expr $(nproc) \+ 1)"
|
||||
@ -176,16 +205,25 @@ jobs:
|
||||
uses: baptiste0928/cargo-install@v1.3.0
|
||||
with:
|
||||
crate: cargo-make
|
||||
- name: install wasm-pack
|
||||
uses: baptiste0928/cargo-install@v1.3.0
|
||||
with:
|
||||
crate: wasm-pack
|
||||
- name: install chrome
|
||||
uses: browser-actions/setup-chrome@v1
|
||||
with:
|
||||
chrome-version: stable
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true # recursively checkout submodules
|
||||
fetch-depth: 0 # to diff with origin/main
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Build and run example fuzzers (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: ./scripts/test_all_fuzzers.sh
|
||||
run: RUN_ON_CI=1 ./scripts/test_all_fuzzers.sh
|
||||
- name: Build and run example fuzzers (macOS)
|
||||
if: runner.os == 'macOS' # use bash v4
|
||||
run: /usr/local/bin/bash ./scripts/test_all_fuzzers.sh
|
||||
run: /usr/local/bin/bash -c 'RUN_ON_CI=1 ./scripts/test_all_fuzzers.sh'
|
||||
|
||||
nostd-build:
|
||||
runs-on: ubuntu-latest
|
||||
@ -238,10 +276,14 @@ jobs:
|
||||
- name: install cargo-make
|
||||
run: cargo install --force cargo-make
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
- name: install cxx bridge
|
||||
run: cargo install cxxbridge-cmd
|
||||
- name: Build fuzzers/frida_libpng
|
||||
run: cd fuzzers/frida_libpng/ && cargo make test
|
||||
- name: Build fuzzers/frida_gdiplus
|
||||
run: cd fuzzers/frida_gdiplus/ && cargo make test
|
||||
- name: Build fuzzers/tinyinst_simple
|
||||
run: cd fuzzers/tinyinst_simple/ && cargo make test
|
||||
|
||||
macos:
|
||||
runs-on: macOS-latest
|
||||
@ -254,6 +296,8 @@ jobs:
|
||||
run: rustup toolchain install nightly --component rustfmt --component clippy --allow-downgrade
|
||||
- name: Install deps
|
||||
run: brew install z3 gtk+3
|
||||
- name: Install cxxbridge
|
||||
run: cargo install cxxbridge-cmd
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: MacOS Build
|
||||
@ -274,7 +318,7 @@ jobs:
|
||||
toolchain: stable
|
||||
- uses: nttld/setup-ndk@v1
|
||||
with:
|
||||
ndk-version: r21e
|
||||
ndk-version: r25b
|
||||
- name: install ios
|
||||
run: rustup target add aarch64-apple-ios
|
||||
- name: install android
|
||||
@ -284,7 +328,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Build iOS
|
||||
run: cargo build --target aarch64-apple-ios
|
||||
run: cargo build --target aarch64-apple-ios && cd libafl_frida && cargo build --target aarch64-apple-ios && cd ..
|
||||
- name: Build Android
|
||||
run: cargo ndk -t arm64-v8a build --release
|
||||
#run: cargo build --target aarch64-linux-android
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -21,6 +21,7 @@ vendor
|
||||
*.obj
|
||||
|
||||
.cur_input
|
||||
.cur_input_*
|
||||
.venv
|
||||
|
||||
crashes
|
||||
@ -46,6 +47,7 @@ a
|
||||
forkserver_test
|
||||
__pycache__
|
||||
*.lafl_lock
|
||||
*.metadata
|
||||
|
||||
*atomic_file_testfile*
|
||||
**/libxml2
|
||||
|
@ -16,6 +16,7 @@ members = [
|
||||
"utils/deexit",
|
||||
"utils/gramatron/construct_automata",
|
||||
"utils/libafl_benches",
|
||||
"utils/build_and_test_fuzzers",
|
||||
]
|
||||
default-members = [
|
||||
"libafl",
|
||||
@ -32,7 +33,7 @@ exclude = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.8.2"
|
||||
version = "0.10.0"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
@ -1,6 +1,6 @@
|
||||
# LibAFL, the fuzzer library.
|
||||
|
||||
<img align="right" src="https://github.com/AFLplusplus/Website/raw/master/static/logo_256x256.png" alt="AFL++ Logo">
|
||||
<img align="right" src="https://raw.githubusercontent.com/AFLplusplus/Website/main/static/libafl_logo.svg" alt="LibAFL logo" width="250" heigh="250">
|
||||
|
||||
Advanced Fuzzing Library - Slot your own fuzzers together and extend their features using Rust.
|
||||
|
||||
@ -10,6 +10,7 @@ LibAFL is written and maintained by
|
||||
* [Dominik Maier](https://twitter.com/domenuk) <dominik@aflplus.plus>
|
||||
* [s1341](https://twitter.com/srubenst1341) <github@shmarya.net>
|
||||
* [Dongjia Zhang](https://github.com/tokatoka) <toka@aflplus.plus>
|
||||
* [Addison Crump](https://github.com/addisoncrump) <me@addisoncrump.info>
|
||||
|
||||
## Why LibAFL?
|
||||
|
||||
@ -33,7 +34,7 @@ LibAFL offers integrations with popular instrumentation frameworks. At the momen
|
||||
|
||||
+ SanitizerCoverage, in [libafl_targets](./libafl_targets)
|
||||
+ Frida, in [libafl_frida](./libafl_frida)
|
||||
+ QEMU user-mode, in [libafl_qemu](./libafl_qemu)
|
||||
+ QEMU user-mode and system mode, including hooks for emulation, in [libafl_qemu](./libafl_qemu)
|
||||
+ TinyInst, in [libafl_tinyinst](./libafl_tinyinst) by [elbiazo](https://github.com/elbiazo)
|
||||
|
||||
## Getting started
|
||||
@ -44,7 +45,7 @@ We highly recommend *not* to use e.g. your Linux distribition package as this is
|
||||
Rust directly, instructions can be found [here](https://www.rust-lang.org/tools/install).
|
||||
|
||||
- LLVM tools
|
||||
The LLVM tools are needed (newer than LLVM 11.0.0 but older than LLVM 15.0.0)
|
||||
The LLVM tools (including clang, clang++) are needed (newer than LLVM 11.0.0 but older than LLVM 15.0.0)
|
||||
|
||||
- Cargo-make
|
||||
We use cargo-make to build the fuzzers in `fuzzers/` directory. You can install it with
|
||||
@ -71,7 +72,7 @@ cargo build --release
|
||||
cargo doc
|
||||
```
|
||||
|
||||
5. Browse the LibAFL book (WIP!) with (requires [mdbook](https://github.com/rust-lang/mdBook))
|
||||
5. Browse the LibAFL book (WIP!) with (requires [mdbook](https://rust-lang.github.io/mdBook/index.html))
|
||||
|
||||
```
|
||||
cd docs && mdbook serve
|
||||
|
@ -1,13 +1,14 @@
|
||||
[package]
|
||||
name = "pylibafl"
|
||||
version = "0.8.2"
|
||||
version = "0.10.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
pyo3 = { version = "0.17", features = ["extension-module"] }
|
||||
libafl_qemu = { path = "../../libafl_qemu", version = "0.8.2", features = ["python"] }
|
||||
libafl_sugar = { path = "../../libafl_sugar", version = "0.8.2", features = ["python"] }
|
||||
libafl = { path = "../../libafl", version = "0.8.2", features = ["python"] }
|
||||
pyo3-log = "0.8.1"
|
||||
libafl_qemu = { path = "../../libafl_qemu", version = "0.10.0", features = ["python"] }
|
||||
libafl_sugar = { path = "../../libafl_sugar", version = "0.10.0", features = ["python"] }
|
||||
libafl = { path = "../../libafl", version = "0.10.0", features = ["python"] }
|
||||
|
||||
[build-dependencies]
|
||||
pyo3-build-config = { version = "0.17" }
|
||||
|
@ -26,7 +26,7 @@ class BaseFeedback:
|
||||
pass
|
||||
def is_interesting(self, state, mgr, input, observers, exit_kind) -> bool:
|
||||
return False
|
||||
def append_metadata(self, state, testcase):
|
||||
def append_metadata(self, state, observers, testcase):
|
||||
pass
|
||||
def discard_metadata(self, state, input):
|
||||
pass
|
||||
@ -84,6 +84,8 @@ def feedback_or_fast(a, b):
|
||||
#[pymodule]
|
||||
#[pyo3(name = "pylibafl")]
|
||||
pub fn python_module(py: Python, m: &PyModule) -> PyResult<()> {
|
||||
pyo3_log::init();
|
||||
|
||||
let modules = py.import("sys")?.getattr("modules")?;
|
||||
|
||||
let sugar_module = PyModule::new(py, "sugar")?;
|
||||
|
@ -1,5 +1,8 @@
|
||||
from pylibafl.libafl import *
|
||||
import ctypes
|
||||
import platform
|
||||
|
||||
MAP_SIZE = 4096
|
||||
|
||||
|
||||
class FooObserver(BaseObserver):
|
||||
@ -33,11 +36,16 @@ class FooExecutor(BaseExecutor):
|
||||
return (self.h)(input)
|
||||
|
||||
|
||||
if platform.system() == "Darwin":
|
||||
libc = ctypes.cdll.LoadLibrary("libc.dylib")
|
||||
else:
|
||||
libc = ctypes.cdll.LoadLibrary("libc.so.6")
|
||||
|
||||
area_ptr = libc.calloc(1, 4096)
|
||||
# Get a buffer to use for our map observer
|
||||
libc.calloc.restype = ctypes.c_void_p
|
||||
area_ptr = libc.calloc(1, MAP_SIZE)
|
||||
|
||||
observer = StdMapObserverI8("mymap", area_ptr, 4096)
|
||||
observer = StdMapObserverI8("mymap", area_ptr, MAP_SIZE)
|
||||
|
||||
m = observer.as_map_observer()
|
||||
|
||||
@ -69,7 +77,12 @@ mgr = SimpleEventManager(monitor.as_monitor())
|
||||
|
||||
|
||||
def harness(buf) -> ExitKind:
|
||||
"""
|
||||
The harness fn that the fuzzer will execute in a loop
|
||||
"""
|
||||
# print(buf)
|
||||
|
||||
# set the observer map byte from python
|
||||
m[0] = 1
|
||||
if len(buf) > 0 and buf[0] == ord("a"):
|
||||
m[1] = 1
|
||||
@ -91,4 +104,6 @@ stage_tuple_list = StagesTuple([stage.as_stage()])
|
||||
|
||||
fuzzer.add_input(state, executor.as_executor(), mgr.as_manager(), b"\0\0")
|
||||
|
||||
print("Starting to fuzz from python!")
|
||||
|
||||
fuzzer.fuzz_loop(executor.as_executor(), state, mgr.as_manager(), stage_tuple_list)
|
||||
|
11
bindings/pylibafl/test.sh
Executable file
11
bindings/pylibafl/test.sh
Executable file
@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
timeout 10 python3 ./test.py
|
||||
export exit_code=$?
|
||||
if [ $exit_code -eq 124 ]; then
|
||||
# 124 = timeout happened. All good.
|
||||
exit 0
|
||||
else
|
||||
exit $exit_code
|
||||
fi
|
||||
|
@ -1,7 +1,8 @@
|
||||
# Concolic Tracing and Hybrid Fuzzing
|
||||
|
||||
LibAFL has support for concolic tracing based on the [SymCC](https://github.com/eurecom-s3/symcc) instrumenting compiler.
|
||||
|
||||
For those uninitiated, the following attempts to describe concolic tracing from the ground up using an example.
|
||||
For those uninitiated, the following text attempts to describe concolic tracing from the ground up using an example.
|
||||
Then, we'll go through the relationship of SymCC and LibAFL concolic tracing.
|
||||
Finally, we'll walk through building a basic hybrid fuzzer using LibAFL.
|
||||
|
||||
@ -92,18 +93,18 @@ In hybrid fuzzing, we combine this tracing + solving approach with more traditio
|
||||
The concolic tracing support in LibAFL is implemented using SymCC.
|
||||
SymCC is a compiler plugin for clang that can be used as a drop-in replacement for a normal C or C++ compiler.
|
||||
SymCC will instrument the compiled code with callbacks into a runtime that can be supplied by the user.
|
||||
These callbacks allow the runtime to construct a trace that similar to the previous example.
|
||||
These callbacks allow the runtime to construct a trace that is similar to the previous example.
|
||||
|
||||
### SymCC and its Runtimes
|
||||
|
||||
SymCC ships with 2 runtimes:
|
||||
|
||||
* a 'simple' runtime that attempts to solve any branches it comes across using [Z3](https://github.com/Z3Prover/z3/wiki) and
|
||||
* a [QSym](https://github.com/sslab-gatech/qsym)-based runtime, which does a bit more filtering on the expressions and also solves using Z3.
|
||||
* A 'simple' runtime that attempts to negate and analytically solve any branch conditions it comes across using [Z3](https://github.com/Z3Prover/z3/wiki) and
|
||||
* A [QSym](https://github.com/sslab-gatech/qsym)-based runtime, which does a bit more filtering on the expressions and also solves them using Z3.
|
||||
|
||||
The integration with LibAFL, however, requires you to **BYORT** (_bring your own runtime_) using the [`symcc_runtime`](https://docs.rs/symcc_runtime/0.1/symcc_runtime) crate.
|
||||
This crate allows you to easily build a custom runtime out of the built-in building blocks or create entirely new runtimes with full flexibility.
|
||||
Checkout out the `symcc_runtime` docs for more information on how to build your own runtime.
|
||||
Check out the `symcc_runtime` docs for more information on how to build your own runtime.
|
||||
|
||||
### SymQEMU
|
||||
|
||||
@ -123,7 +124,7 @@ There are three main steps involved with building a hybrid fuzzer using LibAFL:
|
||||
3. building the fuzzer.
|
||||
|
||||
Note that the order of these steps is important.
|
||||
For example, we need to have runtime ready before we can do instrumentation with SymCC.
|
||||
For example, we need to have a runtime ready before we can do instrumentation with SymCC.
|
||||
|
||||
### Building a Runtime
|
||||
|
||||
@ -134,10 +135,12 @@ Check out the [example hybrid fuzzer's runtime](https://github.com/AFLplusplus/L
|
||||
### Instrumentation
|
||||
|
||||
There are two main instrumentation methods to make use of concolic tracing in LibAFL:
|
||||
* Using an **compile-time** instrumented target with **SymCC**.
|
||||
|
||||
* Using a **compile-time** instrumented target with **SymCC**.
|
||||
This only works when the source is available for the target and the target is reasonably easy to build using the SymCC compiler wrapper.
|
||||
* Using **SymQEMU** to dynamically instrument the target at **runtime**.
|
||||
This avoids a separate instrumented target with concolic tracing instrumentation and does not require source code.
|
||||
This avoids building a separate instrumented target with concolic tracing instrumentation and so does not require source code.
|
||||
|
||||
It should be noted, however, that the 'quality' of the generated expressions can be significantly worse and SymQEMU generally produces significantly more and significantly more convoluted expressions than SymCC.
|
||||
Therefore, it is recommended to use SymCC over SymQEMU when possible.
|
||||
|
||||
@ -158,23 +161,23 @@ Make sure you satisfy the [build requirements](https://github.com/eurecom-s3/sym
|
||||
|
||||
Build SymQEMU according to its [build instructions](https://github.com/eurecom-s3/symqemu#readme).
|
||||
By default, SymQEMU looks for the runtime in a sibling directory.
|
||||
Since we don't have a runtime there, we need to let it know the path to your runtime by setting `--symcc-build` argument of the `configure` script to the path of your runtime.
|
||||
Since we don't have a runtime there, we need to explicitly set the `--symcc-build` argument of the `configure` script to the path of your runtime.
|
||||
|
||||
### Building the Fuzzer
|
||||
|
||||
No matter the instrumentation method, the interface between the fuzzer and the instrumented target should now be consistent.
|
||||
The only difference between using SymCC and SymQEMU should be the binary that represents the target:
|
||||
In the case of SymCC it will be the binary that was build with instrumentation and with SymQEMU it will be the emulator binary (eg. `x86_64-linux-user/symqemu-x86_64`), followed by your uninstrumented target binary and arguments.
|
||||
In the case of SymCC it will be the binary that was build with instrumentation and with SymQEMU it will be the emulator binary (eg. `x86_64-linux-user/symqemu-x86_64`), followed by your uninstrumented target binary and its arguments.
|
||||
|
||||
You can use the [`CommandExecutor`](https://docs.rs/libafl/0.6.0/libafl/executors/command/struct.CommandExecutor.html) to execute your target ([example](https://github.com/AFLplusplus/LibAFL/blob/main/fuzzers/libfuzzer_stb_image_concolic/fuzzer/src/main.rs#L244)).
|
||||
When configuring the command, make sure you pass the `SYMCC_INPUT_FILE` environment variable the input file path, if your target reads input from a file (instead of standard input).
|
||||
When configuring the command, make sure you pass the `SYMCC_INPUT_FILE` environment variable (set to the input file path), if your target reads input from a file (instead of standard input).
|
||||
|
||||
#### Serialization and Solving
|
||||
|
||||
While it is perfectly possible to build a custom runtime that also performs the solving step of hybrid fuzzing in the context of the target process, the intended use of the LibAFL concolic tracing support is to serialize the (filtered and pre-processed) branch conditions using the [`TracingRuntime`](https://docs.rs/symcc_runtime/0.1/symcc_runtime/tracing/struct.TracingRuntime.html).
|
||||
This serialized representation can be deserialized in the fuzzer process for solving using a [`ConcolicObserver`](https://docs.rs/libafl/0.6.0/libafl/observers/concolic/struct.ConcolicObserver.html) wrapped in a [`ConcolicTracingStage`](https://docs.rs/libafl/0.6.0/libafl/stages/concolic/struct.ConcolicTracingStage.html), which will attach a [`ConcolicMetadata`](https://docs.rs/libafl/0.6.0/libafl/observers/concolic/struct.ConcolicMetadata.html) to every [`TestCase`](https://docs.rs/libafl/0.6.0/libafl/corpus/testcase/struct.Testcase.html).
|
||||
|
||||
The `ConcolicMetadata` can be used to replay the concolic trace and solved using an SMT-Solver.
|
||||
The `ConcolicMetadata` can be used to replay the concolic trace and to solve the conditions using an SMT-Solver.
|
||||
Most use-cases involving concolic tracing, however, will need to define some policy around which branches they want to solve.
|
||||
The [`SimpleConcolicMutationalStage`](https://docs.rs/libafl/0.6.0//libafl/stages/concolic/struct.SimpleConcolicMutationalStage.html) can be used for testing purposes.
|
||||
It will attempt to solve all branches, like the original simple backend from SymCC, using Z3.
|
||||
|
@ -17,7 +17,7 @@ If you are on Windows, you'll need to install llvm tools.
|
||||
LibAFL uses Frida's [__Stalker__](https://frida.re/docs/stalker/) to trace the execution of your program and instrument your harness.
|
||||
Thus, you have to compile your harness to a dynamic library. Frida instruments your PUT after dynamically loading it.
|
||||
|
||||
For example in our `frida_libpng` example, we load the dynamic library and find the symbol to harness as follows:
|
||||
In our `frida_libpng` example, we load the dynamic library and find the symbol to harness as follows:
|
||||
|
||||
```rust,ignore
|
||||
let lib = libloading::Library::new(module_name).unwrap();
|
||||
@ -28,9 +28,9 @@ For example in our `frida_libpng` example, we load the dynamic library and find
|
||||
|
||||
## `FridaInstrumentationHelper` and Runtimes
|
||||
|
||||
To use functionalities that Frida offers, we'll first need to obtain `Gum` object by `Gum::obtain()`.
|
||||
To use functionalities that Frida offers, we'll first need to obtain a `Gum` object by `Gum::obtain()`.
|
||||
|
||||
In LibAFL, we use the `FridaInstrumentationHelper` struct to manage frida-related state. `FridaInstrumentationHelper` is a key component that sets up the [__Transformer__](https://frida.re/docs/stalker/#transformer) that is used to generate the instrumented code. It also initializes the `Runtimes` that offer various instrumentation.
|
||||
In LibAFL, we use the `FridaInstrumentationHelper` struct to manage frida-related state. `FridaInstrumentationHelper` is a key component that sets up the [__Transformer__](https://frida.re/docs/stalker/#transformer) that is used to generate the instrumented code. It also initializes the `Runtimes` that offer various instrumentations.
|
||||
|
||||
We have `CoverageRuntime` that can track the edge coverage, `AsanRuntime` for address sanitizer, `DrCovRuntime` that uses [__DrCov__](https://dynamorio.org/page_drcov.html) for coverage collection (to be imported in coverage tools like Lighthouse, bncov, dragondance,...), and `CmpLogRuntime` for cmplog instrumentation.
|
||||
All of these runtimes can be slotted into `FridaInstrumentationHelper` at build time.
|
||||
@ -53,12 +53,12 @@ Combined with any `Runtime` you'd like to use, you can initialize the `FridaInst
|
||||
|
||||
## Running the Fuzzer
|
||||
|
||||
After setting up the `FridaInstrumentationHelper`. You can obtain the pointer to the coverage map by calling `map_ptr_mut()`.
|
||||
After setting up the `FridaInstrumentationHelper` you can obtain the pointer to the coverage map by calling `map_mut_ptr()`.
|
||||
|
||||
```rust,ignore
|
||||
let edges_observer = HitcountsMapObserver::new(StdMapObserver::new_from_ptr(
|
||||
let edges_observer = HitcountsMapObserver::new(StdMapObserver::from_mut_ptr(
|
||||
"edges",
|
||||
frida_helper.map_ptr_mut().unwrap(),
|
||||
frida_helper.map_mut_ptr().unwrap(),
|
||||
MAP_SIZE,
|
||||
));
|
||||
```
|
||||
@ -83,5 +83,5 @@ You can then link this observer to `FridaInProcessExecutor` as follows:
|
||||
);
|
||||
```
|
||||
|
||||
And, finally you can run the fuzzer.
|
||||
And finally you can run the fuzzer.
|
||||
See the `frida_` examples in [`./fuzzers`](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers/) for more information and, for linux or full-system, play around with `libafl_qemu`, another binary-only tracer.
|
@ -1,6 +1,6 @@
|
||||
# Using LibAFL in `no_std` environments
|
||||
|
||||
It is possible to use LibAFL in `no_std` environments e.g. custom platforms like microcontrollers, kernels, hypervisors, and more.
|
||||
It is possible to use LibAFL in `no_std` environments e.g. on custom platforms like microcontrollers, kernels, hypervisors, and more.
|
||||
|
||||
You can simply add LibAFL to your `Cargo.toml` file:
|
||||
|
||||
@ -16,7 +16,7 @@ cargo build --no-default-features --target aarch64-unknown-none
|
||||
|
||||
## Use custom timing
|
||||
|
||||
The minimum amount of input LibAFL needs for `no_std` is a monotonically increasing timestamp.
|
||||
The minimum amount of support LibAFL needs for a `no_std` environment is a monotonically increasing timestamp.
|
||||
For this, anywhere in your project you need to implement the `external_current_millis` function, which returns the current time in milliseconds.
|
||||
|
||||
```c
|
||||
|
@ -2,12 +2,12 @@
|
||||
|
||||
NYX supports both source-based and binary-only fuzzing.
|
||||
|
||||
Currently, `libafl_nyx` only supports [afl++](https://github.com/AFLplusplus/AFLplusplus)'s instruction. To install it, you can use `sudo apt install aflplusplus`. Or compile from the source:
|
||||
Currently, `libafl_nyx` only supports [afl++](https://github.com/AFLplusplus/AFLplusplus)'s instruction type. To install it, you can use `sudo apt install aflplusplus`. Or compile from the source:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/AFLplusplus/AFLplusplus
|
||||
cd AFLplusplus
|
||||
make all # this will not compile afl's additional extension
|
||||
make all # this will not compile afl's additional extensions
|
||||
```
|
||||
|
||||
Then you should compile the target with the afl++ compiler wrapper:
|
||||
@ -20,9 +20,9 @@ export CXX=afl-clang-fast++
|
||||
make
|
||||
```
|
||||
|
||||
For binary-only fuzzing, Nyx uses intel-PT(Intel® Processor Trace). You can find the supported CPU at <https://www.intel.com/content/www/us/en/support/articles/000056730/processors.html>.
|
||||
For binary-only fuzzing, Nyx uses intel-PT(Intel® Processor Trace). You can find the list of supported CPUs at <https://www.intel.com/content/www/us/en/support/articles/000056730/processors.html>.
|
||||
|
||||
## Preparing Nyx working directory
|
||||
## Preparing the Nyx working directory
|
||||
|
||||
This step is used to pack the target into Nyx's kernel. Don't worry, we have a template shell script in our [example](https://github.com/AFLplusplus/LibAFL/blob/main/fuzzers/nyx_libxml2_parallel/setup_libxml2.sh):
|
||||
|
||||
@ -49,7 +49,7 @@ python3 ./packer/packer/nyx_config_gen.py /tmp/nyx_libxml2/ Kernel || exit
|
||||
|
||||
## Standalone fuzzing
|
||||
|
||||
In the [example fuzzer](https://github.com/AFLplusplus/LibAFL/blob/main/fuzzers/nyx_libxml2_standalone/src/main.rs). First you need to run `./setup_libxml2.sh`, It will prepare your target and create your nyx work directory in `/tmp/libxml2`. After that, you can start write your code.
|
||||
In the [example fuzzer](https://github.com/AFLplusplus/LibAFL/blob/main/fuzzers/nyx_libxml2_standalone/src/main.rs) you first need to run `./setup_libxml2.sh`. It will prepare your target and create your nyx work directory in `/tmp/libxml2`. After that, you can start to write your code.
|
||||
|
||||
First, to create `Nyxhelper`:
|
||||
|
||||
@ -57,22 +57,21 @@ First, to create `Nyxhelper`:
|
||||
let share_dir = Path::new("/tmp/nyx_libxml2/");
|
||||
let cpu_id = 0; // use first cpu
|
||||
let parallel_mode = false; // close parallel_mode
|
||||
let mut helper = NyxHelper::new(share_dir, cpu_id, true, parallel_mode, None).unwrap(); // we don't the set the last parameter in standalone mode, we just use None, here
|
||||
let mut helper = NyxHelper::new(share_dir, cpu_id, true, parallel_mode, None).unwrap(); // we don't need to set the last parameter in standalone mode, we just use None, here
|
||||
```
|
||||
|
||||
Then, fetch `trace_bits`, create an observer and the `NyxExecutor`:
|
||||
|
||||
```rust,ignore
|
||||
let trace_bits = unsafe { std::slice::from_raw_parts_mut(helper.trace_bits, helper.map_size) };
|
||||
let observer = StdMapObserver::new("trace", trace_bits);
|
||||
let observer = unsafe { StdMapObserver::from_mut_ptr("trace", helper.trace_bits, helper.map_size) };
|
||||
let mut executor = NyxExecutor::new(&mut helper, tuple_list!(observer)).unwrap();
|
||||
```
|
||||
|
||||
Finally, use them as normal and pass them into `fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)` to start fuzzing.
|
||||
Finally, use them normally and pass them into `fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)` to start fuzzing.
|
||||
|
||||
## Parallel fuzzing
|
||||
|
||||
In the [example fuzzer](https://github.com/AFLplusplus/LibAFL/blob/main/fuzzers/nyx_libxml2_parallel/src/main.rs). First you need to run `./setup_libxml2.sh` as described before.
|
||||
In the [example fuzzer](https://github.com/AFLplusplus/LibAFL/blob/main/fuzzers/nyx_libxml2_parallel/src/main.rs) you first need to run `./setup_libxml2.sh` as described before.
|
||||
|
||||
Parallel fuzzing relies on [`Launcher`](../message_passing/spawn_instances.md), so spawn logic should be written in the scoop of anonymous function `run_client`:
|
||||
|
||||
@ -91,7 +90,7 @@ let mut helper = NyxHelper::new(
|
||||
cpu_id, // current cpu id
|
||||
true, // open snap_mode
|
||||
parallel_mode, // open parallel mode
|
||||
Some(parent_cpu_id.id as u32), // the cpu-id of master instance, there is only one master instance, other instances will be treated as slaved
|
||||
Some(parent_cpu_id.id as u32), // the cpu-id of main instance, there is only one main instance, other instances will be treated as secondaries
|
||||
)
|
||||
.unwrap();
|
||||
```
|
||||
@ -99,13 +98,11 @@ let mut helper = NyxHelper::new(
|
||||
Then you can fetch the trace_bits and create an observer and `NyxExecutor`
|
||||
|
||||
```rust,ignore
|
||||
let trace_bits =
|
||||
unsafe { std::slice::from_raw_parts_mut(helper.trace_bits, helper.map_size) };
|
||||
let observer = StdMapObserver::new("trace", trace_bits);
|
||||
let observer = unsafe { StdMapObserver::from_mut_ptr("trace", helper.trace_bits, helper.map_size) }
|
||||
let mut executor = NyxExecutor::new(&mut helper, tuple_list!(observer)).unwrap();
|
||||
```
|
||||
|
||||
Finally, open a `Launcher` as normal to start fuzzing:
|
||||
Finally, open a `Launcher` as usual to start fuzzing:
|
||||
|
||||
```rust,ignore
|
||||
match Launcher::builder()
|
||||
@ -121,6 +118,6 @@ match Launcher::builder()
|
||||
{
|
||||
Ok(()) => (),
|
||||
Err(Error::ShuttingDown) => println!("Fuzzing stopped by user. Good bye."),
|
||||
Err(err) => panic!("Failed to run launcher: {:?}", err),
|
||||
Err(err) => panic!("Failed to run launcher: {err:?}"),
|
||||
}
|
||||
```
|
||||
|
@ -105,8 +105,8 @@ fn main(){
|
||||
|
||||
## Generating and running some tests
|
||||
|
||||
One of the main components that a LibAFL-based fuzzer uses is the State, a container of the data that is evolved during the fuzzing process.
|
||||
Includes all State, such as the Corpus of inputs, the current RNG state, and potential Metadata for the testcases and run.
|
||||
One of the main components that a LibAFL-based fuzzer uses is the State, a container of the data that will evolve during the fuzzing process.
|
||||
It includes all state, such as the Corpus of inputs, the current RNG state, and potential Metadata for the testcases and run.
|
||||
In our `main` we create a basic State instance like the following:
|
||||
|
||||
```rust,ignore
|
||||
@ -129,14 +129,14 @@ let mut state = StdState::new(
|
||||
|
||||
To avoid type annotation error, you can use `InMemoryCorpus::<BytesInput>::new()` to replace `InMemoryCorpus::new()`. If not, type annotation will be automatically inferred when adding `executor`.
|
||||
|
||||
- third parameter is another corpus that stores the "solution" testcases for the fuzzer. For our purpose, the solution is the input that triggers the panic. In this case, we want to store it to disk under the `crashes` directory, so we can inspect it.
|
||||
- last two parameters are feedback and objective, we will discuss them later.
|
||||
- The third parameter is another Corpus that stores the "solution" testcases for the fuzzer. For our purpose, the solution is the input that triggers the panic. In this case, we want to store it to disk under the `crashes` directory, so we can inspect it.
|
||||
- The last two parameters are feedback and objective, we will discuss them later.
|
||||
|
||||
Another required component is the **EventManager**. It handles some events such as the addition of a testcase to the corpus during the fuzzing process. For our purpose, we use the simplest one that just displays the information about these events to the user using a `Monitor` instance.
|
||||
|
||||
```rust,ignore
|
||||
// The Monitor trait defines how the fuzzer stats are displayed to the user
|
||||
let mon = SimpleMonitor::new(|s| println!("{}", s));
|
||||
let mon = SimpleMonitor::new(|s| println!("{s}"));
|
||||
|
||||
// The event manager handle the various events generated during the fuzzing loop
|
||||
// such as the notification of the addition of a new item to the corpus
|
||||
@ -225,7 +225,7 @@ Now we want to turn our simple fuzzer into a feedback-based one and increase the
|
||||
**Observer** can record the information about properties of a fuzzing run and then feeds the fuzzer. We use the `StdMapObserver`, the default observer that uses a map to keep track of covered elements. In our fuzzer, each condition is mapped to an entry of such map.
|
||||
|
||||
We represent such map as a `static mut` variable.
|
||||
As we don't rely on any instrumentation engine, we have to manually track the satisfied conditions by `singals_set` in our harness:
|
||||
As we don't rely on any instrumentation engine, we have to manually track the satisfied conditions by `signals_set` in our harness:
|
||||
|
||||
```rust
|
||||
extern crate libafl;
|
||||
@ -287,7 +287,7 @@ Now that the fuzzer can observe which condition is satisfied, we need a way to r
|
||||
|
||||
We use `MaxMapFeedback`, a feedback that implements a novelty search over the map of the MapObserver. Basically, if there is a value in the observer's map that is greater than the maximum value registered so far for the same entry, it rates the input as interesting and updates its state.
|
||||
|
||||
**Objective Feedback** is another kind of Feedback which decide if an input is a "solution". It will save input to solutions(`./crashes` in our case) other than corpus when the input is rated interesting. We use `CrashFeedback` to tell the fuzzer that if an input causes the program to crash it is a solution for us.
|
||||
**Objective Feedback** is another kind of Feedback which decides if an input is a "solution". It will save input to solutions(`./crashes` in our case) rather than corpus when the input is rated interesting. We use `CrashFeedback` to tell the fuzzer that if an input causes the program to crash it is a solution for us.
|
||||
|
||||
We need to update our State creation including the feedback state and the Fuzzer including the feedback and the objective:
|
||||
|
||||
@ -356,7 +356,7 @@ fuzzer
|
||||
|
||||
`fuzz_loop` will request a testcase for each iteration to the fuzzer using the scheduler and then it will invoke the stage.
|
||||
|
||||
After adding this code, we have a proper fuzzer, that can run a find the input that panics the function in less than a second.
|
||||
After adding this code, we have a proper fuzzer, that can run and find the input that panics the function in less than a second.
|
||||
|
||||
```text
|
||||
$ cargo run
|
||||
|
@ -9,4 +9,4 @@ Examples can be found under `./fuzzer`.
|
||||
| baby_fuzzer_nautilus | [nautilus](https://www.ndss-symposium.org/wp-content/uploads/2019/02/ndss2019_04A-3_Aschermann_paper.pdf) is a **coverage guided, grammar based** fuzzer|
|
||||
|baby_fuzzer_tokens| basic **token level** fuzzer with token level mutations|
|
||||
|baby_fuzzer_with_forkexecutor| example for **InProcessForkExecutor**|
|
||||
|baby_no_std|a minimalistic example how to create a libafl based fuzzer that works on **`no_std`** environments like TEEs, Kernels or on barew metal|
|
||||
|baby_no_std|a minimalistic example how to create a libafl based fuzzer that works on **`no_std`** environments like TEEs, Kernels or on bare metal|
|
||||
|
@ -4,8 +4,8 @@ The Corpus is where testcases are stored. We define a Testcase as an Input and a
|
||||
|
||||
A Corpus can store testcases in different ways, for example on disk, or in memory, or implement a cache to speedup on disk storage.
|
||||
|
||||
Usually, a testcase is added to the Corpus when it is considered as interesting, but a Corpus is used also to store testcases that fulfill an objective (like crashing the tested program for instance).
|
||||
Usually, a testcase is added to the Corpus when it is considered as interesting, but a Corpus is used also to store testcases that fulfill an objective (like crashing the program under test for instance).
|
||||
|
||||
Related to the Corpus, there is the way in which the fuzzer should ask for the next testcase to fuzz picking it from the Corpus. The taxonomy for this in LibAFL is CorpusScheduler, the entity representing the policy to pop testcases from the Corpus, FIFO for instance.
|
||||
Related to the Corpus is the way in which the next testcase (the fuzzer would ask for) is retrieved from the Corpus. The taxonomy for this handling in LibAFL is CorpusScheduler, the entity representing the policy to pop testcases from the Corpus, in a FIFO fashion for instance.
|
||||
|
||||
Speaking about the code, [`Corpus`](https://docs.rs/libafl/0/libafl/corpus/trait.Corpus.html) and [`CorpusScheduler`](https://docs.rs/libafl/0/libafl/corpus/trait.CorpusScheduler.html) are traits.
|
||||
|
@ -13,7 +13,7 @@ In Rust, we bind this concept to the [`Executor`](https://docs.rs/libafl/0/libaf
|
||||
|
||||
By default, we implement some commonly used Executors such as [`InProcessExecutor`](https://docs.rs/libafl/0/libafl/executors/inprocess/struct.InProcessExecutor.html) in which the target is a harness function providing in-process crash detection. Another Executor is the [`ForkserverExecutor`](https://docs.rs/libafl/0/libafl/executors/forkserver/struct.ForkserverExecutor.html) that implements an AFL-like mechanism to spawn child processes to fuzz.
|
||||
|
||||
A common pattern when creating an Executor is wrapping an existing one, for instance [`TimeoutExecutor`](https://docs.rs/libafl/0.6.1/libafl/executors/timeout/struct.TimeoutExecutor.html) wraps an executor and install a timeout callback before calling the original run function of the wrapped executor.
|
||||
A common pattern when creating an Executor is wrapping an existing one, for instance [`TimeoutExecutor`](https://docs.rs/libafl/0.6.1/libafl/executors/timeout/struct.TimeoutExecutor.html) wraps an executor and installs a timeout callback before calling the original `run` function of the wrapped executor.
|
||||
|
||||
## InProcessExecutor
|
||||
Let's begin with the base case; `InProcessExecutor`.
|
||||
@ -24,7 +24,7 @@ When you want to execute the harness as fast as possible, you will most probably
|
||||
One thing to note here is, when your harness is likely to have heap corruption bugs, you want to use another allocator so that corrupted heap does not affect the fuzzer itself. (For example, we adopt MiMalloc in some of our fuzzers.). Alternatively you can compile your harness with address sanitizer to make sure you can catch these heap bugs.
|
||||
|
||||
## ForkserverExecutor
|
||||
Next, we'll take a look at the `ForkserverExecutor`. In this case, it is `afl-cc` (from AFLplusplus/AFLplusplus) that compiles the harness code, and therefore, we can't use `EDGES_MAP` anymore. Hopefully, we have [_a way_](https://github.com/AFLplusplus/AFLplusplus/blob/2e15661f184c77ac1fbb6f868c894e946cbb7f17/instrumentation/afl-compiler-rt.o.c#L270) to tell the forkserver which map to record the coverage.
|
||||
Next, we'll take a look at the `ForkserverExecutor`. In this case, it is `afl-cc` (from AFLplusplus/AFLplusplus) that compiles the harness code, and therefore, we can't use `EDGES_MAP` anymore. Fortunately we have [_a way_](https://github.com/AFLplusplus/AFLplusplus/blob/2e15661f184c77ac1fbb6f868c894e946cbb7f17/instrumentation/afl-compiler-rt.o.c#L270) to tell the forkserver which map to record the coverage in.
|
||||
|
||||
As you can see from the forkserver example,
|
||||
|
||||
@ -48,7 +48,7 @@ See AFL++'s [_documentation_](https://github.com/AFLplusplus/AFLplusplus/blob/st
|
||||
Finally, we'll talk about the `InProcessForkExecutor`.
|
||||
`InProcessForkExecutor` has only one difference from `InprocessExecutor`; It forks before running the harness and that's it.
|
||||
|
||||
But why do we want to do so? well, under some circumstances, you may find your harness pretty unstable or your harness wreaks havoc on the global states. In this case, you want to fork it before executing the harness runs in the child process so that it doesn't break things.
|
||||
But why do we want to do so? Well, under some circumstances, you may find your harness pretty unstable or your harness wreaks havoc on the global states. In this case, you want to fork it before executing the harness runs in the child process so that it doesn't break things.
|
||||
|
||||
However, we have to take care of the shared memory, it's the child process that runs the harness code and writes the coverage to the map.
|
||||
|
||||
|
@ -11,16 +11,16 @@ The concept of "interestingness" is abstract, but typically it is related to a n
|
||||
As an example, given an Observer that reports all the sizes of memory allocations, a maximization Feedback can be used to maximize these sizes to sport pathological inputs in terms of memory consumption.
|
||||
|
||||
In terms of code, the library offers the [`Feedback`](https://docs.rs/libafl/0/libafl/feedbacks/trait.Feedback.html) and the [`FeedbackState`](https://docs.rs/libafl/0/libafl/feedbacks/trait.FeedbackState.html) traits.
|
||||
The first is used to implement functors that, given the state of the observers from the last execution, tells if the execution was interesting. The second is tied with `Feedback` and it is the state of the data that the feedback wants to persist in the fuzzers's state, for instance the cumulative map holding all the edges seen so far in the case of a feedback based on edge coverage.
|
||||
The first is used to implement functors that, given the state of the observers from the last execution, tells if the execution was interesting. The second is tied with `Feedback` and is the state of the data that the feedback wants to persist in the fuzzers's state, for instance the cumulative map holding all the edges seen so far in the case of a feedback based on edge coverage.
|
||||
|
||||
Multiple Feedbacks can be combined into boolean formula, considering for instance an execution as interesting if it triggers new code paths or execute in less time compared to the average execution time using [`feedback_or`](https://docs.rs/libafl/*/libafl/macro.feedback_or.html).
|
||||
Multiple Feedbacks can be combined into a boolean expression, considering for instance an execution as interesting if it triggers new code paths or execute in less time compared to the average execution time using [`feedback_or`](https://docs.rs/libafl/*/libafl/macro.feedback_or.html).
|
||||
|
||||
On top, logic operators like `feedback_or` and `feedback_and` have a `_fast` option (`feedback_or_fast` where the second feedback will not be evaluated, if the first part already answers the `interestingness` question, to save precious performance.
|
||||
On top, logic operators like `feedback_or` and `feedback_and` have a `_fast` variant (e.g. `feedback_or_fast`) where the second feedback will not be evaluated, if the value of the first feedback operand already answers the `interestingness` question so as to save precious performance.
|
||||
|
||||
Using `feedback_and_fast` in combination with [`ConstFeedback`](https://docs.rs/libafl/*/libafl/feedbacks/enum.ConstFeedback.html#method.new), certain feedbacks can be disabled dynamically.
|
||||
|
||||
## Objectives
|
||||
|
||||
While feedbacks are commonly used to decide if an [`Input`](https://docs.rs/libafl/*/libafl/inputs/trait.Input.html) should be kept for future mutations, they serve a double-purpose, as so-called `Objective Feedbacks`.
|
||||
In this case, the `interestingness` of a feedback indicates, if an `Objective` has been hit.
|
||||
Commonly, these would be a`crash or a timeout, but they can also be used to find specific parts of the program, for sanitization, or a differential fuzzing success.
|
||||
In this case, the `interestingness` of a feedback indicates if an `Objective` has been hit.
|
||||
Commonly, these objectives would be a crash or a timeout, but they can also be used to detect if specific parts of the program have been reached, for sanitization, or a differential fuzzing success.
|
||||
|
@ -6,10 +6,10 @@ In our model of an abstract fuzzer, we define the Input as the internal represen
|
||||
|
||||
In the straightforward case, the input of the program is a byte array and in fuzzers such as AFL we store and manipulate exactly these byte arrays.
|
||||
|
||||
But it is not always the case. A program can expect inputs that are not byte arrays (e.g. a sequence of syscalls) and the fuzzer does not represent the Input in the same way that the program consumes it.
|
||||
But it is not always the case. A program can expect inputs that are not linear byte arrays (e.g. a sequence of syscalls forming a use case or protocol) and the fuzzer does not represent the Input in the same way that the program consumes it.
|
||||
|
||||
In case of a grammar fuzzer for instance, the Input is generally an Abstract Syntax Tree because it is a data structure that can be easily manipulated while maintaining the validity, but the program expects a byte array as input, so just before the execution, the tree is serialized to a sequence of bytes.
|
||||
|
||||
In the Rust code, an [`Input`](https://docs.rs/libafl/*/libafl/inputs/trait.Input.html) is a trait that can be implemented only by structures that are serializable and have only owned data as fields.
|
||||
|
||||
While most fuzzer use a normal `BytesInput`], more advanced inputs like inputs include special inputs for grammar fuzzing ([GramatronInput](https://docs.rs/libafl/*/libafl/inputs/gramatron/struct.GramatronInput.html) or `NautilusInput` on nightly), as well as the token-level [EncodedInput](https://docs.rs/libafl/*/libafl/inputs/encoded/struct.EncodedInput.html).
|
||||
While most fuzzers use a normal `BytesInput`, more advanced ones use inputs that include special inputs for grammar fuzzing ([GramatronInput](https://docs.rs/libafl/*/libafl/inputs/gramatron/struct.GramatronInput.html) or `NautilusInput` on Rust nightly), as well as the token-level [EncodedInput](https://docs.rs/libafl/*/libafl/inputs/encoded/struct.EncodedInput.html).
|
||||
|
@ -1,9 +1,9 @@
|
||||
# Mutator
|
||||
|
||||
The Mutator is an entity that takes one or more Inputs and generates a new derived one.
|
||||
The Mutator is an entity that takes one or more Inputs and generates a new instance of Input derived by its inputs.
|
||||
|
||||
Mutators can be composed, and they are generally linked to a specific Input type.
|
||||
|
||||
There can be, for instance, a Mutator that applies more than a single type of mutation on the input. Consider a generic Mutator for a byte stream, bit flip is just one of the possible mutations but not the only one, there is also, for instance, the random replacement of a byte of the copy of a chunk.
|
||||
There can be, for instance, a Mutator that applies more than a single type of mutation to the input. Consider a generic Mutator for a byte stream, bit flip is just one of the possible mutations but not the only one, there is also, for instance, the random replacement of a byte of the copy of a chunk.
|
||||
|
||||
In LibAFL, [`Mutator`](https://docs.rs/libafl/*/libafl/mutators/trait.Mutator.html) is a trait.
|
||||
|
@ -4,8 +4,8 @@ An Observer is an entity that provides an information observed during the execut
|
||||
|
||||
The information contained in the Observer is not preserved across executions, but it may be serialized and passed on to other nodes if an `Input` is considered `interesting`, and added to the `Corpus`.
|
||||
|
||||
As an example, the coverage map, filled during the execution to report the executed edges used by fuzzers such as AFL and `HonggFuzz` can be considered an observation. Another `Observer` can be the time spent executing a run, the program output, or more advanced observation, like maximum stack depth at runtime.
|
||||
This information is not preserved across runs, and it is an observation of a dynamic property of the program.
|
||||
As an example, the coverage map, filled during the execution to report the executed edges used by fuzzers such as AFL and `HonggFuzz` can be considered an observation. Another `Observer` can collect the time spent executing a run, the program output, or a more advanced observation, like maximum stack depth at runtime.
|
||||
This information is an observation of a dynamic property of the program.
|
||||
|
||||
In terms of code, in the library this entity is described by the [`Observer`](https://docs.rs/libafl/0/libafl/observers/trait.Observer.html) trait.
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
# Stage
|
||||
|
||||
A Stage is an entity that operates on a single Input got from the Corpus.
|
||||
A Stage is an entity that operates on a single Input received from the Corpus.
|
||||
|
||||
For instance, a Mutational Stage, given an input of the corpus, applies a Mutator and executes the generated input one or more time. How many times this has to be done can be scheduled, AFL for instance uses a performance score of the input to choose how many times the havoc mutator should be invoked. This can depend also on other parameters, for instance, the length of the input if we want to just apply a sequential bitflip, or be a fixed value.
|
||||
For instance, a Mutational Stage, given an input of the corpus, applies a Mutator and executes the generated input one or more times. How many times this has to be done can be scheduled, AFL for instance uses a performance score of the input to choose how many times the havoc mutator should be invoked. This can depend also on other parameters, for instance, the length of the input if we want to just apply a sequential bitflip, or a fixed value.
|
||||
|
||||
A stage can also be an analysis stage, for instance, the Colorization stage of Redqueen that aims to introduce more entropy in a testcase or the Trimming stage of AFL that aims to reduce the size of a testcase.
|
||||
|
||||
|
@ -10,6 +10,6 @@ Thinking about similar fuzzers, you can observe that most of the time the data s
|
||||
|
||||
Beside the entities previously described, we introduce the [`Testcase`](https://docs.rs/libafl/0.6/libafl/corpus/testcase/struct.Testcase.html) and [`State`](https://docs.rs/libafl/0.6/libafl/state/struct.StdState.html) entities. The Testcase is a container for an Input stored in the Corpus and its metadata (so, in the implementation, the Corpus stores Testcases) and the State contains all the metadata that are evolved while running the fuzzer, Corpus included.
|
||||
|
||||
The State, in the implementation, contains only owned objects that are serializable, and it is serializable itself. Some fuzzers may want to serialize its state when pausing or just, when doing in-process fuzzing, serialize on crash and deserialize in the new process to continue to fuzz with all the metadata preserved.
|
||||
The State, in the implementation, contains only owned objects that are serializable, and it is serializable itself. Some fuzzers may want to serialize their state when pausing or just, when doing in-process fuzzing, serialize on crash and deserialize in the new process to continue to fuzz with all the metadata preserved.
|
||||
|
||||
Additionally, we group the entities that are "actions", like the `CorpusScheduler` and the `Feedbacks`, in a common place, the [`Fuzzer'](https://docs.rs/libafl/*/libafl/fuzzer/struct.StdFuzzer.html).
|
||||
|
@ -19,7 +19,7 @@ pub struct MyMetadata {
|
||||
|
||||
The struct must be static, so it cannot hold references to borrowed objects.
|
||||
|
||||
As an alternative to `derive(SerdeAny)` that is a proc-macro in `libafl_derive` the user can use `libafl::impl_serdeany!(MyMetadata);`.
|
||||
As an alternative to `derive(SerdeAny)` which is a proc-macro in `libafl_derive` the user can use `libafl::impl_serdeany!(MyMetadata);`.
|
||||
|
||||
## Usage
|
||||
|
||||
|
@ -75,7 +75,7 @@ where
|
||||
```
|
||||
|
||||
The executor is constrained to `EM` and `Z`, with each of their respective states being constrained to `E`'s state. It
|
||||
is no longer necessary to explicitly defined a generic for the input type, the state type, or the generic type, as these
|
||||
is no longer necessary to explicitly define a generic for the input type, the state type, or the generic type, as these
|
||||
are all present as associated types for `E`. Additionally, we don't even need to specify any details about the observers
|
||||
(`OT` in the previous version) as the type does not need to be constrained and is not shared by other types.
|
||||
|
||||
@ -101,7 +101,7 @@ See `fuzzers/` for examples of these changes.
|
||||
If you implemented a Mutator, Executor, State, or another kind of component, you must update your implementation. The
|
||||
main changes to the API are in the use of "Uses*" for associated types.
|
||||
|
||||
In many scenarios, Input, Observers, and State generics have been moved into traits with associated types (namely,
|
||||
In many scenarios, Input, Observer, and State generics have been moved into traits with associated types (namely,
|
||||
"UsesInput", "UsesObservers", and "UsesState". These traits are required for many existing traits now and are very
|
||||
straightforward to implement. In a majority of cases, you will have generics on your custom implementation or a fixed
|
||||
type to implement this with. Thankfully, Rust will let you know when you need to implement this type.
|
||||
@ -127,7 +127,7 @@ where
|
||||
}
|
||||
```
|
||||
|
||||
After 0.9, all `Corpus` implementations are required to implement `UsesInput` and `Corpus` no longer has a generic for
|
||||
After 0.9, all `Corpus` implementations are required to implement `UsesInput`. Also `Corpus` no longer has a generic for
|
||||
the input type (as it is now provided by the UsesInput impl). The migrated implementation is shown below:
|
||||
|
||||
```rust,ignore
|
||||
@ -160,3 +160,26 @@ Now, `Corpus` cannot be accidentally implemented for another type other than tha
|
||||
is fixed to the associated type for `UsesInput`.
|
||||
|
||||
A more complex example of migration can be found in the "Reasons for this change" section of this document.
|
||||
|
||||
## Observer Changes
|
||||
|
||||
Additionally, we changed the Observer API, as the API in 0.8 led to undefined behavior.
|
||||
At the same time, we used the change to simplify the common case: creating an `StdMapObserver`
|
||||
from libafl_target's `EDGES_MAP`.
|
||||
In the future, instead of using:
|
||||
|
||||
```rust,ignore
|
||||
let edges = unsafe { &mut EDGES_MAP[0..MAX_EDGES_NUM] };
|
||||
let edges_observer = StdMapObserver::new("edges", edges);
|
||||
```
|
||||
|
||||
creating the edges observer is as simple as using the new `std_edges_map_observer` function.
|
||||
|
||||
```rust,ignore
|
||||
let edges_observer = unsafe { std_edges_map_observer("edges") };
|
||||
```
|
||||
|
||||
Alternatively, `StdMapObserver::new` will still work, but now the whole method is marked as `unsafe`.
|
||||
The reason is that the caller has to make sure `EDGES_MAP` (or other maps) are not moved or freed in memory,
|
||||
for the lifetime of the `MapObserver`.
|
||||
This means that the buffer should either be `static` or `Pin`.
|
||||
|
@ -6,7 +6,7 @@ LibAFL, as most of the Rust projects, can be built using `cargo` from the root d
|
||||
$ cargo build --release
|
||||
```
|
||||
|
||||
Note that the `--release` flag is optional for development, but you needed to add it to fuzzing at a decent speed.
|
||||
Note that the `--release` flag is optional for development, but you need to add it to do fuzzing at a decent speed.
|
||||
Slowdowns of 10x or more are not uncommon for Debug builds.
|
||||
|
||||
The LibAFL repository is composed of multiple crates.
|
||||
|
@ -10,7 +10,7 @@ libafl = { version = "*" }
|
||||
|
||||
## Crate List
|
||||
|
||||
For LibAFL, each crate has its self-contained purpose, and the user may not need to use all of them in its project.
|
||||
For LibAFL, each crate has its self-contained purpose, and the user may not need to use all of them in their project.
|
||||
Following the naming convention of the folders in the project's root, they are:
|
||||
|
||||
### [`libafl`](https://github.com/AFLplusplus/LibAFL/tree/main/libafl)
|
||||
@ -52,13 +52,13 @@ To enable and disable features at compile-time, the features are enabled and dis
|
||||
Currently, the supported flags are:
|
||||
|
||||
- `pcguard_edges` defines the SanitizerCoverage trace-pc-guard hooks to track the executed edges in a map.
|
||||
- `pcguard_hitcounts defines the SanitizerCoverage trace-pc-guard hooks to track the executed edges with the hitcounts (like AFL) in a map.
|
||||
- `pcguard_hitcounts` defines the SanitizerCoverage trace-pc-guard hooks to track the executed edges with the hitcounts (like AFL) in a map.
|
||||
- `libfuzzer` exposes a compatibility layer with libFuzzer style harnesses.
|
||||
- `value_profile` defines the SanitizerCoverage trace-cmp hooks to track the matching bits of each comparison in a map.
|
||||
|
||||
### libafl_cc
|
||||
|
||||
This is a library that provides utils wrap compilers and create source-level fuzzers.
|
||||
This is a library that provides utils to wrap compilers and create source-level fuzzers.
|
||||
|
||||
At the moment, only the Clang compiler is supported.
|
||||
To understand it deeper, look through the tutorials and examples.
|
||||
|
@ -11,15 +11,15 @@ The first step is to download LibAFL and all dependencies that are not automatic
|
||||
> previous command. Additionally, PowerShell-specific examples will use `>`
|
||||
> rather than `$`.
|
||||
|
||||
While you technically do not need to install LibAFL, but can use the version from crates.io directly, we do recommend to download or clone the GitHub version.
|
||||
While technically you do not need to install LibAFL, but can use the version from crates.io directly, we do recommend to download or clone the GitHub version.
|
||||
This gets you the example fuzzers, additional utilities, and latest patches.
|
||||
The easiest way to do this is to use `git`.
|
||||
|
||||
```sh
|
||||
$ git clone git@github.com:AFLplusplus/LibAFL.git
|
||||
$ git clone https://github.com/AFLplusplus/LibAFL.git
|
||||
```
|
||||
|
||||
You can alternatively, on a UNIX-like machine, download a compressed archive and extract it with:
|
||||
Alternatively, on a UNIX-like machine, you can download a compressed archive and extract it with:
|
||||
|
||||
```sh
|
||||
wget https://github.com/AFLplusplus/LibAFL/archive/main.tar.gz
|
||||
@ -31,7 +31,7 @@ $ ls LibAFL-main # this is the extracted folder
|
||||
## Clang installation
|
||||
|
||||
One of the external dependencies of LibAFL is the Clang C/C++ compiler.
|
||||
While most of the code is in pure Rust, we still need a C compiler because stable Rust still does not support features that some parts of LibAFL may need, such as weak linking, and LLVM builtins linking.
|
||||
While most of the code is written in pure Rust, we still need a C compiler because stable Rust still does not support features that some parts of LibAFL may need, such as weak linking, and LLVM builtins linking.
|
||||
For these parts, we use C to expose the missing functionalities to our Rust codebase.
|
||||
|
||||
In addition, if you want to perform source-level fuzz testing of C/C++ applications,
|
||||
|
@ -4,7 +4,7 @@ Fuzzers are important tools for security researchers and developers alike.
|
||||
A wide range of state-of-the-art tools like [AFL++](https://github.com/AFLplusplus/AFLplusplus), [libFuzzer](https://llvm.org/docs/LibFuzzer.html) or [honggfuzz](https://github.com/google/honggfuzz) are available to users. They do their job in a very effective way, finding thousands of bugs.
|
||||
|
||||
From the perspective of a power user, however, these tools are limited.
|
||||
Their design does not treat extensibility as a first-class citizen.
|
||||
Their designs do not treat extensibility as a first-class citizen.
|
||||
Usually, a fuzzer developer can choose to either fork one of these existing tools, or to create a new fuzzer from scratch.
|
||||
In any case, researchers end up with tons of fuzzers, all of which are incompatible with each other.
|
||||
Their outstanding features cannot just be combined for new projects.
|
||||
@ -24,7 +24,7 @@ Some highlight features currently include:
|
||||
This means it does not require a specific OS-dependent runtime to function.
|
||||
Define an allocator and a way to map pages, and you are good to inject LibAFL in obscure targets like embedded devices, hypervisors, or maybe even WebAssembly?
|
||||
- `adaptable`: Given years of experience fine-tuning *AFLplusplus* and our academic fuzzing background, we could incorporate recent fuzzing trends into LibAFL's design and make it future-proof.
|
||||
To give an example, as opposed to old-skool fuzzers, a `BytesInput` is just one of the potential forms of inputs:
|
||||
To give an example, as opposed to old-school fuzzers, a `BytesInput` is just one of the potential forms of inputs:
|
||||
feel free to use and mutate an Abstract Syntax Tree instead, for structured fuzzing.
|
||||
- `scalable`: As part of LibAFL, we developed `Low Level Message Passing`, `LLMP` for short, which allows LibAFL to scale almost linearly over cores. That is, if you chose to use this feature - it is your fuzzer, after all.
|
||||
Scaling to multiple machines over TCP is also possible, using LLMP's `broker2broker` feature.
|
||||
|
@ -5,7 +5,7 @@
|
||||
*by Andrea Fioraldi and Dominik Maier*
|
||||
|
||||
Welcome to LibAFL, the Advanced Fuzzing Library.
|
||||
This book shall be a gentle introduction into the library.
|
||||
This book shall be a gentle introduction to the library.
|
||||
|
||||
This version of the LibAFL book is coupled with the release 1.0 beta of the library.
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
# Message Passing
|
||||
|
||||
LibAFL offers a standard mechanism for message passing over processes and machines with a low overhead.
|
||||
LibAFL offers a standard mechanism for message passing between processes and machines with a low overhead.
|
||||
We use message passing to inform the other connected clients/fuzzers/nodes about new testcases, metadata, and statistics about the current run.
|
||||
Depending on individual needs, LibAFL can also write testcase contents to disk, while still using events to notify other fuzzers, using an `OnDiskCorpus`.
|
||||
Depending on individual needs, LibAFL can also write testcase contents to disk, while still using events to notify other fuzzers, using the `CachedOnDiskCorpus` or similar.
|
||||
|
||||
In our tests, message passing scales very well to share new testcases and metadata between multiple running fuzzer instances for multi-core fuzzing.
|
||||
Specifically, it scales _a lot_ better than using memory locks on a shared corpus, and _a lot_ better than sharing the testcases via the filesystem, as AFL traditionally does.
|
||||
@ -12,7 +12,7 @@ The `EventManager` interface is used to send Events over the wire using `Low Lev
|
||||
|
||||
## Low Level Message Passing (LLMP)
|
||||
|
||||
LibAFL comes with a reasonably lock-free message passing mechanism that scales well across cores and, using its *broker2broker* mechanism, even to connected machines via TCP.
|
||||
LibAFL comes with a reasonably lock-free message passing mechanism that scales well across cores and, using its _broker2broker_ mechanism, even to connected machines via TCP.
|
||||
Most example fuzzers use this mechanism, and it is the best `EventManager` if you want to fuzz on more than a single core.
|
||||
In the following, we will describe the inner workings of `LLMP`.
|
||||
|
||||
@ -28,12 +28,12 @@ Shared maps, called shared memory for the sake of not colliding with Rust's `map
|
||||
Each client, usually a fuzzer trying to share stats and new testcases, maps an outgoing `ShMem` map.
|
||||
With very few exceptions, only this client writes to this map, therefore, we do not run in race conditions and can live without locks.
|
||||
The broker reads from all client's `ShMem` maps.
|
||||
It checks all incoming client maps periodically and then forwards new messages to its outgoing broadcast-`ShMem`, mapped by all connected clients.
|
||||
It periodically checks all incoming client maps and then forwards new messages to its outgoing broadcast-`ShMem`, mapped by all connected clients.
|
||||
|
||||
To send new messages, a client places a new message at the end of their shared memory and then updates a static field to notify the broker.
|
||||
Once the outgoing map is full, the sender allocates a new `ShMem` using the respective `ShMemProvider`.
|
||||
It then sends the information needed to map the newly-allocated page in connected processes to the old page, using an end of page (`EOP`) message.
|
||||
Once the receiver maps the new page, flags it as safe for unmapping from the sending process (to avoid race conditions if we have more than a single EOP in a short time), and then continues to read from the new `ShMem`.
|
||||
Once the receiver maps the new page, it flags it as safe for unmapping by the sending process (to avoid race conditions if we have more than a single EOP in a short time), and then continues to read from the new `ShMem`.
|
||||
|
||||
The schema for client's maps to the broker is as follows:
|
||||
|
||||
@ -54,10 +54,10 @@ After the broker received a new message from clientN, (`clientN_out->current_id
|
||||
|
||||
The clients periodically, for example after finishing `n` mutations, check for new incoming messages by checking if (`current_broadcast_map->current_id != last_message->message_id`).
|
||||
While the broker uses the same EOP mechanism to map new `ShMem`s for its outgoing map, it never unmaps old pages.
|
||||
This additional memory overhead serves a good purpose: by keeping all broadcast pages around, we make sure that new clients can join in on a fuzzing campaign at a later point in time
|
||||
This additional memory resources serve a good purpose: by keeping all broadcast pages around, we make sure that new clients can join in on a fuzzing campaign at a later point in time.
|
||||
They just need to re-read all broadcasted messages from start to finish.
|
||||
|
||||
So the outgoing messages flow like this over the outgoing broadcast `Shmem`:
|
||||
So the outgoing messages flow is like this over the outgoing broadcast `Shmem`:
|
||||
|
||||
```text
|
||||
[broker]
|
||||
|
@ -8,14 +8,14 @@ The straightforward way to do Multi-Threading is to use the `LlmpRestartingEvent
|
||||
It abstracts away all the pesky details about restarts on crash handling (for in-memory fuzzers) and multi-threading.
|
||||
With it, every instance you launch manually tries to connect to a TCP port on the local machine.
|
||||
|
||||
If the port is not yet bound, this instance becomes the broker, itself binding to the port to await new clients.
|
||||
If the port is not yet bound, this instance becomes the broker, binding itself to the port to await new clients.
|
||||
|
||||
If the port is already bound, the EventManager will try to connect to it.
|
||||
The instance becomes a client and can now communicate with all other nodes.
|
||||
|
||||
Launching nodes manually has the benefit that you can have multiple nodes with different configurations, such as clients fuzzing with and without ASAN.
|
||||
|
||||
While it's called "restarting" manager, it uses `fork` on Unix operating systems as optimization and only actually restarts from scratch on Windows.
|
||||
While it's called "restarting" manager, it uses `fork` on Unix-like operating systems as optimization and only actually restarts from scratch on Windows.
|
||||
|
||||
|
||||
## Automated, with Launcher
|
||||
@ -23,7 +23,7 @@ While it's called "restarting" manager, it uses `fork` on Unix operating systems
|
||||
The Launcher is the lazy way to do multiprocessing.
|
||||
You can use the Launcher builder to create a fuzzer that spawns multiple nodes with one click, all using restarting event managers and the same configuration.
|
||||
|
||||
To use launcher, first you need to write an anonymous function `let mut run_client = |state: Option<_>, mut mgr, _core_id|{}`, which uses three parameters to create individual fuzzer. Then you can specify the `shmem_provider`,`broker_port`,`monitor`,`cores` and other stuff through `Launcher::builder()`:
|
||||
To use launcher, first you need to write an anonymous function `let mut run_client = |state: Option<_>, mut mgr, _core_id|{}`, which uses three parameters to create an individual fuzzer. Then you can specify the `shmem_provider`,`broker_port`,`monitor`,`cores` and other stuff through `Launcher::builder()`:
|
||||
|
||||
```rust,ignore
|
||||
Launcher::builder()
|
||||
@ -42,12 +42,12 @@ To use launcher, first you need to write an anonymous function `let mut run_clie
|
||||
This first starts a broker, then spawns `n` clients, according to the value passed to `cores`.
|
||||
The value is a string indicating the cores to bind to, for example, `0,2,5` or `0-3`.
|
||||
For each client, `run_client` will be called.
|
||||
On Windows, the Launcher will restart each client, while on Unix, it will use `fork`.
|
||||
On Windows, the Launcher will restart each client, while on Unix-alikes, it will use `fork`.
|
||||
|
||||
Advanced use-cases:
|
||||
|
||||
1. To connect multiple nodes together via TCP, you can use the `remote_broker_addr`. this requires the `llmp_bind_public` compile-time feature for `LibAFL`.
|
||||
2. To use multiple launchers for individual configurations, you can set `spawn_broker` to `false` on all but one.
|
||||
2. To use multiple launchers for individual configurations, you can set `spawn_broker` to `false` on all instances but one.
|
||||
3. Launcher will not select the cores automatically, so you need to specify the `cores` that you want.
|
||||
|
||||
For more examples, you can check out `qemu_launcher` and `libfuzzer_libpng_launcher` in [`./fuzzers/`](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers).
|
||||
|
@ -2,4 +2,4 @@
|
||||
|
||||
In this chapter, we will build a custom fuzzer using the [Lain](https://github.com/microsoft/lain) mutator in Rust.
|
||||
|
||||
This tutorial will introduce you in writing extensions to LibAFL like Feedbacks and Testcase's metadata.
|
||||
This tutorial will introduce you to writing extensions to LibAFL like Feedbacks and Testcase's metadata.
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "baby_fuzzer"
|
||||
version = "0.7.1"
|
||||
version = "0.10.0"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
|
@ -6,3 +6,5 @@ It runs on a single core until a crash occurs and then exits.
|
||||
|
||||
The tested program is a simple Rust function without any instrumentation.
|
||||
For real fuzzing, you will want to add some sort to add coverage or other feedback.
|
||||
|
||||
You can run this example using `cargo run`, and you can enable the TUI feature by running `cargo run --features tui`.
|
@ -4,67 +4,86 @@ from pylibafl import libafl
|
||||
|
||||
def map_observer_wrapper(map_observer):
|
||||
if type(map_observer).__name__ == "OwnedMapObserverI32":
|
||||
return libafl.MapObserverI32.new_from_owned(map_observer)
|
||||
return libafl.MapObserverI32.new_owned(map_observer)
|
||||
|
||||
def executor_wrapper(executor):
|
||||
if type(executor).__name__ == "OwnedInProcessExecutorI32":
|
||||
return libafl.ExecutorI32.new_from_inprocess(executor)
|
||||
if type(executor).__name__ == "InProcessExecutor":
|
||||
return libafl.Executor.new_inprocess(executor)
|
||||
|
||||
def generator_wrapper(generator):
|
||||
if type(generator).__name__ == "RandPrintablesGenerator":
|
||||
return libafl.Generator.new_rand_printables(generator)
|
||||
|
||||
def monitor_wrapper(monitor):
|
||||
if type(monitor).__name__ == "SimpleMonitor":
|
||||
return libafl.Monitor.new_from_simple(monitor)
|
||||
return monitor.as_monitor()
|
||||
|
||||
def event_manager_wrapper(event_manager):
|
||||
if type(event_manager).__name__ == "SimpleEventManager":
|
||||
return libafl.EventManagerI32.new_from_simple(event_manager)
|
||||
return event_manager.as_manager()
|
||||
|
||||
def corpus_wrapper(corpus):
|
||||
if type(corpus).__name__ == "InMemoryCorpus":
|
||||
return libafl.Corpus.new_from_in_memory(corpus)
|
||||
return libafl.Corpus.new_in_memory(corpus)
|
||||
if type(corpus).__name__ == "OnDiskCorpus":
|
||||
return libafl.Corpus.new_from_on_disk(corpus)
|
||||
return libafl.Corpus.new_on_disk(corpus)
|
||||
|
||||
def rand_wrapper(rand):
|
||||
if type(rand).__name__ == "StdRand":
|
||||
return libafl.Rand.new_from_std(rand)
|
||||
return libafl.Rand.new_std(rand)
|
||||
|
||||
def mutator_wrapper(mutator):
|
||||
if type(mutator).__name__ == "StdHavocMutator":
|
||||
return libafl.Mutator.new_std_havoc(mutator)
|
||||
|
||||
def stage_wrapper(stage):
|
||||
if type(stage).__name__ == "StdScheduledHavocMutationsStageI32":
|
||||
return libafl.StageI32.new_from_std_scheduled(stage)
|
||||
if type(stage).__name__ == "StdMutationalStage":
|
||||
return libafl.Stage.new_std_mutational(stage)
|
||||
|
||||
# CODE WRITTEN BY USER
|
||||
|
||||
def harness(inp):
|
||||
if len(inp.hex()) >= 2 and inp.hex()[:2] == '61':
|
||||
raise Exception("NOOOOOO =)")
|
||||
import logging
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
map_observer = libafl.OwnedMapObserverI32("signals", [0] * 16)
|
||||
|
||||
feedback_state = libafl.MapFeedbackStateI32.with_observer(map_observer_wrapper(map_observer))
|
||||
def harness(inp):
|
||||
#print(inp)
|
||||
map_observer[0] = 1
|
||||
if len(inp) > 0 and inp[0] == ord('a'):
|
||||
map_observer[1] = 1
|
||||
if len(inp) > 1 and inp[1] == ord('b'):
|
||||
map_observer[2] = 1
|
||||
if len(inp) > 2 and inp[2] == ord('c'):
|
||||
map_observer[3] = 1
|
||||
raise Exception("NOOOOOO =)")
|
||||
|
||||
feedback = libafl.MaxMapFeedbackI32(feedback_state, map_observer_wrapper(map_observer))
|
||||
feedback = libafl.MaxMapFeedbackI32(map_observer_wrapper(map_observer))
|
||||
objective = libafl.CrashFeedback()
|
||||
|
||||
state = libafl.StdStateI32(
|
||||
state = libafl.StdState(
|
||||
rand_wrapper(libafl.StdRand.with_current_nanos()),
|
||||
corpus_wrapper(libafl.InMemoryCorpus()),
|
||||
corpus_wrapper(libafl.OnDiskCorpus("./crashes")),
|
||||
feedback_state
|
||||
feedback.as_feedback(),
|
||||
objective.as_feedback(),
|
||||
)
|
||||
|
||||
monitor = libafl.SimpleMonitor()
|
||||
monitor = libafl.SimpleMonitor(lambda x: print(x))
|
||||
|
||||
mgr = libafl.SimpleEventManager(monitor_wrapper(monitor))
|
||||
|
||||
fuzzer = libafl.StdFuzzerI32(feedback)
|
||||
fuzzer = libafl.StdFuzzer(feedback.as_feedback(), objective.as_feedback())
|
||||
|
||||
executor = libafl.OwnedInProcessExecutorI32(harness, map_observer_wrapper(map_observer), fuzzer, state, event_manager_wrapper(mgr))
|
||||
observers = libafl.ObserversTuple([libafl.Observer.new_map_i32(map_observer_wrapper(map_observer))])
|
||||
|
||||
generator = libafl.RandPrintablesGeneratorI32(32)
|
||||
executor = libafl.InProcessExecutor(harness, observers, fuzzer, state, event_manager_wrapper(mgr))
|
||||
|
||||
state.generate_initial_inputs(fuzzer, executor_wrapper(executor), generator, event_manager_wrapper(mgr), 8)
|
||||
generator = libafl.RandPrintablesGenerator(32)
|
||||
|
||||
stage = libafl.StdScheduledHavocMutationsStageI32.new_from_scheduled_havoc_mutations()
|
||||
state.generate_initial_inputs(fuzzer, executor_wrapper(executor), generator_wrapper(generator), event_manager_wrapper(mgr), 3)
|
||||
|
||||
stage_tuple_list = libafl.StagesOwnedListI32(stage_wrapper(stage))
|
||||
mutator = libafl.StdHavocMutator()
|
||||
|
||||
fuzzer.fuzz_loop(executor_wrapper(executor), state, event_manager_wrapper(mgr), stage_tuple_list)
|
||||
stage = libafl.StdMutationalStage(mutator_wrapper(mutator))
|
||||
|
||||
stages = libafl.StagesTuple([stage_wrapper(stage)])
|
||||
|
||||
fuzzer.fuzz_loop(executor_wrapper(executor), state, event_manager_wrapper(mgr), stages)
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::path::PathBuf;
|
||||
#[cfg(windows)]
|
||||
use std::ptr::write_volatile;
|
||||
use std::{path::PathBuf, ptr::write};
|
||||
|
||||
#[cfg(feature = "tui")]
|
||||
use libafl::monitors::tui::TuiMonitor;
|
||||
@ -24,13 +24,14 @@ use libafl::{
|
||||
|
||||
/// Coverage map with explicit assignments due to the lack of instrumentation
|
||||
static mut SIGNALS: [u8; 16] = [0; 16];
|
||||
static mut SIGNALS_PTR: *mut u8 = unsafe { SIGNALS.as_mut_ptr() };
|
||||
|
||||
/// Assign a signal to the signals map
|
||||
fn signals_set(idx: usize) {
|
||||
unsafe { SIGNALS[idx] = 1 };
|
||||
unsafe { write(SIGNALS_PTR.add(idx), 1) };
|
||||
}
|
||||
|
||||
#[allow(clippy::similar_names)]
|
||||
#[allow(clippy::similar_names, clippy::manual_assert)]
|
||||
pub fn main() {
|
||||
// The closure that we want to fuzz
|
||||
let mut harness = |input: &BytesInput| {
|
||||
@ -60,8 +61,7 @@ pub fn main() {
|
||||
};
|
||||
|
||||
// Create an observation channel using the signals map
|
||||
let observer =
|
||||
unsafe { StdMapObserver::new_from_ptr("signals", SIGNALS.as_mut_ptr(), SIGNALS.len()) };
|
||||
let observer = unsafe { StdMapObserver::from_mut_ptr("signals", SIGNALS_PTR, SIGNALS.len()) };
|
||||
|
||||
// Feedback to rate the interestingness of an input
|
||||
let mut feedback = MaxMapFeedback::new(&observer);
|
||||
@ -88,7 +88,7 @@ pub fn main() {
|
||||
|
||||
// The Monitor trait define how the fuzzer stats are displayed to the user
|
||||
#[cfg(not(feature = "tui"))]
|
||||
let mon = SimpleMonitor::new(|s| println!("{}", s));
|
||||
let mon = SimpleMonitor::new(|s| println!("{s}"));
|
||||
#[cfg(feature = "tui")]
|
||||
let mon = TuiMonitor::new(String::from("Baby Fuzzer"), false);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "baby_fuzzer_gramatron"
|
||||
version = "0.8.2"
|
||||
version = "0.10.0"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
|
@ -28,10 +28,11 @@ use libafl::{
|
||||
|
||||
/// Coverage map with explicit assignments due to the lack of instrumentation
|
||||
static mut SIGNALS: [u8; 16] = [0; 16];
|
||||
static mut SIGNALS_PTR: *mut u8 = unsafe { SIGNALS.as_mut_ptr() };
|
||||
/*
|
||||
/// Assign a signal to the signals map
|
||||
fn signals_set(idx: usize) {
|
||||
unsafe { SIGNALS[idx] = 1 };
|
||||
unsafe { std::ptr::write(SIGNALS_PTR.add(idx), 1) };
|
||||
}
|
||||
*/
|
||||
|
||||
@ -57,7 +58,7 @@ pub fn main() {
|
||||
};
|
||||
|
||||
// Create an observation channel using the signals map
|
||||
let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS });
|
||||
let observer = unsafe { StdMapObserver::from_mut_ptr("signals", SIGNALS_PTR, SIGNALS.len()) };
|
||||
|
||||
// Feedback to rate the interestingness of an input
|
||||
let mut feedback = MaxMapFeedback::new(&observer);
|
||||
@ -83,7 +84,7 @@ pub fn main() {
|
||||
.unwrap();
|
||||
|
||||
// The Monitor trait define how the fuzzer stats are reported to the user
|
||||
let monitor = SimpleMonitor::new(|s| println!("{}", s));
|
||||
let monitor = SimpleMonitor::new(|s| println!("{s}"));
|
||||
|
||||
// The event manager handle the various events generated during the fuzzing loop
|
||||
// such as the notification of the addition of a new item to the corpus
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "baby_fuzzer_grimoire"
|
||||
version = "0.8.2"
|
||||
version = "0.10.0"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
#[cfg(windows)]
|
||||
use std::ptr::write_volatile;
|
||||
use std::{fs, io::Read, path::PathBuf};
|
||||
use std::{fs, io::Read, path::PathBuf, ptr::write};
|
||||
|
||||
use libafl::{
|
||||
bolts::{current_nanos, rands::StdRand, tuples::tuple_list, AsSlice},
|
||||
@ -9,7 +9,7 @@ use libafl::{
|
||||
executors::{inprocess::InProcessExecutor, ExitKind},
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback},
|
||||
fuzzer::{Evaluator, Fuzzer, StdFuzzer},
|
||||
inputs::{GeneralizedInput, HasTargetBytes},
|
||||
inputs::{BytesInput, HasTargetBytes},
|
||||
monitors::SimpleMonitor,
|
||||
mutators::{
|
||||
havoc_mutations, scheduled::StdScheduledMutator, GrimoireExtensionMutator,
|
||||
@ -24,10 +24,10 @@ use libafl::{
|
||||
|
||||
/// Coverage map with explicit assignments due to the lack of instrumentation
|
||||
static mut SIGNALS: [u8; 16] = [0; 16];
|
||||
|
||||
static mut SIGNALS_PTR: *mut u8 = unsafe { SIGNALS.as_mut_ptr() };
|
||||
/// Assign a signal to the signals map
|
||||
fn signals_set(idx: usize) {
|
||||
unsafe { SIGNALS[idx] = 1 };
|
||||
unsafe { write(SIGNALS_PTR.add(idx), 1) };
|
||||
}
|
||||
|
||||
fn is_sub<T: PartialEq>(mut haystack: &[T], needle: &[T]) -> bool {
|
||||
@ -59,13 +59,13 @@ pub fn main() {
|
||||
let mut file = fs::File::open(path).expect("no file found");
|
||||
let mut buffer = vec![];
|
||||
file.read_to_end(&mut buffer).expect("buffer overflow");
|
||||
let input = GeneralizedInput::new(buffer);
|
||||
let input = BytesInput::new(buffer);
|
||||
initial_inputs.push(input);
|
||||
}
|
||||
}
|
||||
|
||||
// The closure that we want to fuzz
|
||||
let mut harness = |input: &GeneralizedInput| {
|
||||
let mut harness = |input: &BytesInput| {
|
||||
let target_bytes = input.target_bytes();
|
||||
let bytes = target_bytes.as_slice();
|
||||
|
||||
@ -77,21 +77,14 @@ pub fn main() {
|
||||
signals_set(3);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
if input.grimoire_mutated {
|
||||
// println!(">>> {:?}", input.generalized());
|
||||
println!(">>> {:?}", std::str::from_utf8_unchecked(bytes));
|
||||
}
|
||||
}
|
||||
signals_set(1);
|
||||
ExitKind::Ok
|
||||
};
|
||||
|
||||
// Create an observation channel using the signals map
|
||||
let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS });
|
||||
|
||||
let observer = unsafe { StdMapObserver::from_mut_ptr("signals", SIGNALS_PTR, SIGNALS.len()) };
|
||||
// Feedback to rate the interestingness of an input
|
||||
let mut feedback = MaxMapFeedback::new_tracking(&observer, false, true);
|
||||
let mut feedback = MaxMapFeedback::tracking(&observer, false, true);
|
||||
|
||||
// A feedback to choose if an input is a solution or not
|
||||
let mut objective = CrashFeedback::new();
|
||||
@ -113,12 +106,12 @@ pub fn main() {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if state.metadata().get::<Tokens>().is_none() {
|
||||
if state.metadata_map().get::<Tokens>().is_none() {
|
||||
state.add_metadata(Tokens::from([b"FOO".to_vec(), b"BAR".to_vec()]));
|
||||
}
|
||||
|
||||
// The Monitor trait define how the fuzzer stats are reported to the user
|
||||
let monitor = SimpleMonitor::new(|s| println!("{}", s));
|
||||
let monitor = SimpleMonitor::new(|s| println!("{s}"));
|
||||
|
||||
// The event manager handle the various events generated during the fuzzing loop
|
||||
// such as the notification of the addition of a new item to the corpus
|
||||
@ -158,7 +151,7 @@ pub fn main() {
|
||||
let mut stages = tuple_list!(
|
||||
generalization,
|
||||
StdMutationalStage::new(mutator),
|
||||
StdMutationalStage::new(grimoire_mutator)
|
||||
StdMutationalStage::transforming(grimoire_mutator)
|
||||
);
|
||||
|
||||
for input in initial_inputs {
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "baby_fuzzer_minimizing"
|
||||
version = "0.8.2"
|
||||
version = "0.10.0"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>", "Addison Crump <research@addisoncrump.info>"]
|
||||
edition = "2021"
|
||||
|
||||
|
@ -1,15 +1,16 @@
|
||||
use std::path::PathBuf;
|
||||
#[cfg(windows)]
|
||||
use std::ptr::write_volatile;
|
||||
use std::{path::PathBuf, ptr::write};
|
||||
|
||||
use libafl::prelude::*;
|
||||
|
||||
/// Coverage map with explicit assignments due to the lack of instrumentation
|
||||
static mut SIGNALS: [u8; 16] = [0; 16];
|
||||
static mut SIGNALS_PTR: *mut u8 = unsafe { SIGNALS.as_mut_ptr() };
|
||||
|
||||
/// Assign a signal to the signals map
|
||||
fn signals_set(idx: usize) {
|
||||
unsafe { SIGNALS[idx] = 1 };
|
||||
unsafe { write(SIGNALS_PTR.add(idx), 1) };
|
||||
}
|
||||
|
||||
#[allow(clippy::similar_names)]
|
||||
@ -32,10 +33,9 @@ pub fn main() -> Result<(), Error> {
|
||||
};
|
||||
|
||||
// Create an observation channel using the signals map
|
||||
let observer =
|
||||
unsafe { StdMapObserver::new_from_ptr("signals", SIGNALS.as_mut_ptr(), SIGNALS.len()) };
|
||||
let observer = unsafe { StdMapObserver::from_mut_ptr("signals", SIGNALS_PTR, SIGNALS.len()) };
|
||||
|
||||
let factory = MapEqualityFactory::new_from_observer(&observer);
|
||||
let factory = MapEqualityFactory::with_observer(&observer);
|
||||
|
||||
// Feedback to rate the interestingness of an input
|
||||
let mut feedback = MaxMapFeedback::new(&observer);
|
||||
@ -44,7 +44,7 @@ pub fn main() -> Result<(), Error> {
|
||||
let mut objective = CrashFeedback::new();
|
||||
|
||||
// The Monitor trait define how the fuzzer stats are displayed to the user
|
||||
let mon = SimpleMonitor::new(|s| println!("{}", s));
|
||||
let mon = SimpleMonitor::new(|s| println!("{s}"));
|
||||
|
||||
let mut mgr = SimpleEventManager::new(mon);
|
||||
|
||||
@ -56,7 +56,7 @@ pub fn main() -> Result<(), Error> {
|
||||
// RNG
|
||||
StdRand::with_seed(current_nanos()),
|
||||
// Corpus that will be evolved, we keep it in memory for performance
|
||||
OnDiskCorpus::new(&corpus_dir).unwrap(),
|
||||
InMemoryOnDiskCorpus::new(&corpus_dir).unwrap(),
|
||||
// Corpus in which we store solutions (crashes in this example),
|
||||
// on disk so the user can get them after stopping the fuzzer
|
||||
OnDiskCorpus::new(&solution_dir).unwrap(),
|
||||
@ -108,7 +108,7 @@ pub fn main() -> Result<(), Error> {
|
||||
|
||||
let mut state = StdState::new(
|
||||
StdRand::with_seed(current_nanos()),
|
||||
OnDiskCorpus::new(&minimized_dir).unwrap(),
|
||||
InMemoryOnDiskCorpus::new(&minimized_dir).unwrap(),
|
||||
InMemoryCorpus::new(),
|
||||
&mut (),
|
||||
&mut (),
|
||||
@ -116,7 +116,7 @@ pub fn main() -> Result<(), Error> {
|
||||
.unwrap();
|
||||
|
||||
// The Monitor trait define how the fuzzer stats are displayed to the user
|
||||
let mon = SimpleMonitor::new(|s| println!("{}", s));
|
||||
let mon = SimpleMonitor::new(|s| println!("{s}"));
|
||||
|
||||
let mut mgr = SimpleEventManager::new(mon);
|
||||
|
||||
@ -136,7 +136,13 @@ pub fn main() -> Result<(), Error> {
|
||||
let mut executor = InProcessExecutor::new(&mut harness, (), &mut fuzzer, &mut state, &mut mgr)?;
|
||||
|
||||
state.load_initial_inputs_forced(&mut fuzzer, &mut executor, &mut mgr, &[solution_dir])?;
|
||||
stages.perform_all(&mut fuzzer, &mut executor, &mut state, &mut mgr, 0)?;
|
||||
stages.perform_all(
|
||||
&mut fuzzer,
|
||||
&mut executor,
|
||||
&mut state,
|
||||
&mut mgr,
|
||||
CorpusId::from(0_usize),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "baby_fuzzer_nautilus"
|
||||
version = "0.8.2"
|
||||
version = "0.10.0"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
|
@ -24,10 +24,11 @@ use libafl::{
|
||||
|
||||
/// Coverage map with explicit assignments due to the lack of instrumentation
|
||||
static mut SIGNALS: [u8; 16] = [0; 16];
|
||||
static mut SIGNALS_PTR: *mut u8 = unsafe { SIGNALS.as_mut_ptr() };
|
||||
/*
|
||||
/// Assign a signal to the signals map
|
||||
fn signals_set(idx: usize) {
|
||||
unsafe { SIGNALS[idx] = 1 };
|
||||
unsafe { str::ptr::write(SIGNALS_PTR.add(idx), 1) };
|
||||
}
|
||||
*/
|
||||
|
||||
@ -46,7 +47,7 @@ pub fn main() {
|
||||
};
|
||||
|
||||
// Create an observation channel using the signals map
|
||||
let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS });
|
||||
let observer = unsafe { StdMapObserver::from_mut_ptr("signals", SIGNALS_PTR, SIGNALS.len()) };
|
||||
|
||||
// Feedback to rate the interestingness of an input
|
||||
let mut feedback = feedback_or!(
|
||||
@ -74,12 +75,16 @@ pub fn main() {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if state.metadata().get::<NautilusChunksMetadata>().is_none() {
|
||||
if state
|
||||
.metadata_map()
|
||||
.get::<NautilusChunksMetadata>()
|
||||
.is_none()
|
||||
{
|
||||
state.add_metadata(NautilusChunksMetadata::new("/tmp/".into()));
|
||||
}
|
||||
|
||||
// The Monitor trait define how the fuzzer stats are reported to the user
|
||||
let monitor = SimpleMonitor::new(|s| println!("{}", s));
|
||||
let monitor = SimpleMonitor::new(|s| println!("{s}"));
|
||||
|
||||
// The event manager handle the various events generated during the fuzzing loop
|
||||
// such as the notification of the addition of a new item to the corpus
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "baby_fuzzer_swap_differential"
|
||||
version = "0.7.1"
|
||||
version = "0.10.0"
|
||||
authors = ["Addison Crump <research@addisoncrump.info>"]
|
||||
edition = "2021"
|
||||
default-run = "fuzzer_sd"
|
||||
@ -21,7 +21,7 @@ debug = true
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = "1"
|
||||
bindgen = "0.61"
|
||||
bindgen = "0.63"
|
||||
cc = "1.0"
|
||||
|
||||
[dependencies]
|
||||
|
@ -1,10 +1,10 @@
|
||||
# Variables
|
||||
[env]
|
||||
FUZZER_NAME='fuzzer_sd'
|
||||
PROJECT_DIR = { script = ["pwd"] }
|
||||
CARGO_TARGET_DIR = { value = "target", condition = { env_not_set = ["CARGO_TARGET_DIR"] } }
|
||||
LIBAFL_CC = '${CARGO_TARGET_DIR}/release/libafl_cc'
|
||||
FUZZER = '${CARGO_TARGET_DIR}/release/${FUZZER_NAME}'
|
||||
PROJECT_DIR = { script = ["pwd"] }
|
||||
|
||||
# Compilers
|
||||
[tasks.cc]
|
||||
@ -31,7 +31,13 @@ windows_alias = "unsupported"
|
||||
[tasks.test_unix]
|
||||
script_runner = "@shell"
|
||||
script='''
|
||||
timeout 10s ${CARGO_TARGET_DIR}/release/${FUZZER_NAME}
|
||||
timeout 10s ${CARGO_TARGET_DIR}/release/${FUZZER_NAME} >fuzz_stdout.log || true
|
||||
if [ -z "$(grep "objectives: 1" fuzz_stdout.log)" ]; then
|
||||
echo "Fuzzer does not generate any testcases or any crashes"
|
||||
exit 1
|
||||
else
|
||||
echo "Fuzzer is working"
|
||||
fi
|
||||
'''
|
||||
dependencies = [ "fuzzer" ]
|
||||
|
||||
|
@ -27,9 +27,7 @@ fn main() -> anyhow::Result<()> {
|
||||
})
|
||||
.join("release/libafl_cc");
|
||||
println!("cargo:rerun-if-changed={}", compiler.to_str().unwrap());
|
||||
if !compiler.try_exists().unwrap_or(false) {
|
||||
println!("cargo:warning=Can't find libafl_cc; assuming that we're building it.");
|
||||
} else {
|
||||
if compiler.try_exists().unwrap_or(false) {
|
||||
cc::Build::new()
|
||||
.compiler(compiler)
|
||||
.file("first.c")
|
||||
@ -38,6 +36,8 @@ fn main() -> anyhow::Result<()> {
|
||||
.compile("diff-target");
|
||||
|
||||
println!("cargo:rustc-link-lib=diff-target");
|
||||
} else {
|
||||
println!("cargo:warning=Can't find libafl_cc; assuming that we're building it.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ pub fn main() {
|
||||
let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() {
|
||||
"cc" => false,
|
||||
"++" | "pp" | "xx" => true,
|
||||
_ => panic!("Could not figure out if c or c++ wrapper was called. Expected {:?} to end with c or cxx", dir),
|
||||
_ => panic!("Could not figure out if c or c++ wrapper was called. Expected {dir:?} to end with c or cxx"),
|
||||
};
|
||||
|
||||
dir.pop();
|
||||
|
@ -11,7 +11,7 @@ use libafl::monitors::tui::TuiMonitor;
|
||||
use libafl::monitors::SimpleMonitor;
|
||||
use libafl::{
|
||||
bolts::{current_nanos, rands::StdRand, tuples::tuple_list, AsSlice},
|
||||
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
||||
corpus::{Corpus, InMemoryCorpus, InMemoryOnDiskCorpus},
|
||||
events::SimpleEventManager,
|
||||
executors::{inprocess::InProcessExecutor, DiffExecutor, ExitKind},
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback},
|
||||
@ -24,18 +24,21 @@ use libafl::{
|
||||
stages::mutational::StdMutationalStage,
|
||||
state::{HasSolutions, StdState},
|
||||
};
|
||||
use libafl_targets::{DifferentialAFLMapSwapObserver, MAX_EDGES_NUM};
|
||||
use libafl_targets::{edges_max_num, DifferentialAFLMapSwapObserver};
|
||||
#[cfg(not(miri))]
|
||||
use mimalloc::MiMalloc;
|
||||
|
||||
#[global_allocator]
|
||||
#[cfg(not(miri))]
|
||||
static GLOBAL: MiMalloc = MiMalloc;
|
||||
|
||||
// bindings to the functions defined in the target
|
||||
mod bindings {
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(unused)]
|
||||
|
||||
#![allow(clippy::unreadable_literal)]
|
||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
||||
}
|
||||
|
||||
@ -43,25 +46,25 @@ use bindings::{inspect_first, inspect_second};
|
||||
|
||||
#[cfg(feature = "multimap")]
|
||||
mod multimap {
|
||||
pub use libafl::observers::{HitcountsIterableMapObserver, MultiMapObserver};
|
||||
|
||||
pub static mut FIRST_EDGES: &'static mut [u8] = &mut [];
|
||||
pub static mut SECOND_EDGES: &'static mut [u8] = &mut [];
|
||||
pub static mut COMBINED_EDGES: [&'static mut [u8]; 2] = [&mut [], &mut []];
|
||||
pub use libafl::{
|
||||
bolts::ownedref::OwnedMutSlice,
|
||||
observers::{HitcountsIterableMapObserver, MultiMapObserver},
|
||||
};
|
||||
}
|
||||
#[cfg(feature = "multimap")]
|
||||
use multimap::*;
|
||||
use multimap::{HitcountsIterableMapObserver, MultiMapObserver, OwnedMutSlice};
|
||||
|
||||
#[cfg(not(feature = "multimap"))]
|
||||
mod slicemap {
|
||||
pub use libafl::observers::HitcountsMapObserver;
|
||||
|
||||
pub static mut EDGES: &'static mut [u8] = &mut [];
|
||||
pub static mut EDGES: &mut [u8] = &mut [];
|
||||
}
|
||||
#[cfg(not(feature = "multimap"))]
|
||||
use slicemap::*;
|
||||
use slicemap::{HitcountsMapObserver, EDGES};
|
||||
|
||||
#[allow(clippy::similar_names)]
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn main() {
|
||||
// The closure that we want to fuzz
|
||||
let mut first_harness = |input: &BytesInput| {
|
||||
@ -83,19 +86,35 @@ pub fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
let num_edges: usize = edges_max_num();
|
||||
|
||||
#[cfg(feature = "multimap")]
|
||||
let (first_map_observer, second_map_observer, map_swapper, map_observer) = {
|
||||
let (
|
||||
first_map_observer,
|
||||
second_map_observer,
|
||||
map_swapper,
|
||||
map_observer,
|
||||
layout,
|
||||
first_edges,
|
||||
second_edges,
|
||||
) = {
|
||||
// initialize the maps
|
||||
unsafe {
|
||||
let layout = Layout::from_size_align(MAX_EDGES_NUM, 64).unwrap();
|
||||
FIRST_EDGES = core::slice::from_raw_parts_mut(alloc_zeroed(layout), MAX_EDGES_NUM);
|
||||
SECOND_EDGES = core::slice::from_raw_parts_mut(alloc_zeroed(layout), MAX_EDGES_NUM);
|
||||
COMBINED_EDGES = [&mut FIRST_EDGES, &mut SECOND_EDGES];
|
||||
}
|
||||
let layout = Layout::from_size_align(num_edges, 64).unwrap();
|
||||
let first_edges = unsafe { (alloc_zeroed(layout), num_edges) };
|
||||
let second_edges = unsafe { (alloc_zeroed(layout), num_edges) };
|
||||
|
||||
let combined_edges = unsafe {
|
||||
vec![
|
||||
OwnedMutSlice::from_raw_parts_mut(first_edges.0, first_edges.1),
|
||||
OwnedMutSlice::from_raw_parts_mut(second_edges.0, second_edges.1),
|
||||
]
|
||||
};
|
||||
|
||||
// create the base maps used to observe the different executors from two independent maps
|
||||
let mut first_map_observer = StdMapObserver::new("first-edges", unsafe { FIRST_EDGES });
|
||||
let mut second_map_observer = StdMapObserver::new("second-edges", unsafe { SECOND_EDGES });
|
||||
let mut first_map_observer =
|
||||
unsafe { StdMapObserver::from_mut_ptr("first-edges", first_edges.0, first_edges.1) };
|
||||
let mut second_map_observer =
|
||||
unsafe { StdMapObserver::from_mut_ptr("second-edges", second_edges.0, second_edges.1) };
|
||||
|
||||
// create a map swapper so that we can replace the coverage map pointer (requires feature pointer_maps!)
|
||||
let map_swapper =
|
||||
@ -106,28 +125,35 @@ pub fn main() {
|
||||
// differential mode
|
||||
let map_observer = HitcountsIterableMapObserver::new(MultiMapObserver::differential(
|
||||
"combined-edges",
|
||||
unsafe { &mut COMBINED_EDGES },
|
||||
combined_edges,
|
||||
));
|
||||
|
||||
(
|
||||
first_map_observer,
|
||||
second_map_observer,
|
||||
map_swapper,
|
||||
map_observer,
|
||||
layout,
|
||||
first_edges,
|
||||
second_edges,
|
||||
)
|
||||
};
|
||||
#[cfg(not(feature = "multimap"))]
|
||||
let (first_map_observer, second_map_observer, map_swapper, map_observer) = {
|
||||
// initialize the map
|
||||
unsafe {
|
||||
let layout = Layout::from_size_align(MAX_EDGES_NUM * 2, 64).unwrap();
|
||||
EDGES = core::slice::from_raw_parts_mut(alloc_zeroed(layout), MAX_EDGES_NUM * 2);
|
||||
let layout = Layout::from_size_align(num_edges * 2, 64).unwrap();
|
||||
EDGES = core::slice::from_raw_parts_mut(alloc_zeroed(layout), num_edges * 2);
|
||||
}
|
||||
|
||||
let edges_ptr = unsafe { EDGES.as_mut_ptr() };
|
||||
|
||||
// create the base maps used to observe the different executors by splitting a slice
|
||||
let mut first_map_observer =
|
||||
StdMapObserver::new("first-edges", unsafe { &mut EDGES[..MAX_EDGES_NUM] });
|
||||
let mut second_map_observer =
|
||||
StdMapObserver::new("second-edges", unsafe { &mut EDGES[MAX_EDGES_NUM..] });
|
||||
unsafe { StdMapObserver::from_mut_ptr("first-edges", edges_ptr, num_edges) };
|
||||
let mut second_map_observer = unsafe {
|
||||
StdMapObserver::from_mut_ptr("second-edges", edges_ptr.add(num_edges), num_edges)
|
||||
};
|
||||
|
||||
// create a map swapper so that we can replace the coverage map pointer (requires feature pointer_maps!)
|
||||
let map_swapper =
|
||||
@ -136,10 +162,14 @@ pub fn main() {
|
||||
// create a combined map observer, e.g. for calibration
|
||||
// we use StdMapObserver::differential to indicate that we want to use the observer in
|
||||
// differential mode
|
||||
let map_observer =
|
||||
HitcountsMapObserver::new(StdMapObserver::differential("combined-edges", unsafe {
|
||||
EDGES
|
||||
}));
|
||||
let map_observer = unsafe {
|
||||
HitcountsMapObserver::new(StdMapObserver::differential_from_mut_ptr(
|
||||
"combined-edges",
|
||||
edges_ptr,
|
||||
num_edges * 2,
|
||||
))
|
||||
};
|
||||
|
||||
(
|
||||
first_map_observer,
|
||||
second_map_observer,
|
||||
@ -163,7 +193,7 @@ pub fn main() {
|
||||
InMemoryCorpus::new(),
|
||||
// Corpus in which we store solutions (crashes in this example),
|
||||
// on disk so the user can get them after stopping the fuzzer
|
||||
OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(),
|
||||
InMemoryOnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(),
|
||||
// States of the feedbacks.
|
||||
// The feedbacks can report the data that should persist in the State.
|
||||
&mut feedback,
|
||||
@ -174,7 +204,7 @@ pub fn main() {
|
||||
|
||||
// The Monitor trait define how the fuzzer stats are displayed to the user
|
||||
#[cfg(not(feature = "tui"))]
|
||||
let mon = SimpleMonitor::new(|s| println!("{}", s));
|
||||
let mon = SimpleMonitor::new(|s| println!("{s}"));
|
||||
#[cfg(feature = "tui")]
|
||||
let mon = TuiMonitor::new(String::from("Baby Fuzzer"), false);
|
||||
|
||||
@ -243,4 +273,10 @@ pub fn main() {
|
||||
)
|
||||
.expect("Error in the fuzzing loop");
|
||||
}
|
||||
|
||||
#[cfg(feature = "multimap")]
|
||||
unsafe {
|
||||
std::alloc::dealloc(first_edges.0, layout);
|
||||
std::alloc::dealloc(second_edges.0, layout);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "baby_fuzzer_tokens"
|
||||
version = "0.8.2"
|
||||
version = "0.10.0"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
#[cfg(windows)]
|
||||
use std::ptr::write_volatile;
|
||||
use std::{fs, io::Read, path::PathBuf};
|
||||
use std::{fs, io::Read, path::PathBuf, ptr::write};
|
||||
|
||||
use libafl::{
|
||||
bolts::{current_nanos, rands::StdRand, tuples::tuple_list},
|
||||
@ -20,10 +20,12 @@ use libafl::{
|
||||
|
||||
/// Coverage map with explicit assignments due to the lack of instrumentation
|
||||
static mut SIGNALS: [u8; 16] = [0; 16];
|
||||
static mut SIGNALS_PTR: *mut u8 = unsafe { SIGNALS.as_mut_ptr() };
|
||||
|
||||
/*
|
||||
/// Assign a signal to the signals map
|
||||
fn signals_set(idx: usize) {
|
||||
unsafe { SIGNALS[idx] = 1 };
|
||||
unsafe { write(SIGNALS_PTR.add(idx), 1) };
|
||||
}
|
||||
*/
|
||||
|
||||
@ -65,7 +67,7 @@ pub fn main() {
|
||||
};
|
||||
|
||||
// Create an observation channel using the signals map
|
||||
let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS });
|
||||
let observer = unsafe { StdMapObserver::from_mut_ptr("signals", SIGNALS_PTR, SIGNALS.len()) };
|
||||
|
||||
// Feedback to rate the interestingness of an input
|
||||
let mut feedback = MaxMapFeedback::new(&observer);
|
||||
@ -91,7 +93,7 @@ pub fn main() {
|
||||
.unwrap();
|
||||
|
||||
// The Monitor trait define how the fuzzer stats are reported to the user
|
||||
let monitor = SimpleMonitor::new(|s| println!("{}", s));
|
||||
let monitor = SimpleMonitor::new(|s| println!("{s}"));
|
||||
|
||||
// The event manager handle the various events generated during the fuzzing loop
|
||||
// such as the notification of the addition of a new item to the corpus
|
||||
|
2
fuzzers/baby_fuzzer_wasm/.cargo/config.toml
Normal file
2
fuzzers/baby_fuzzer_wasm/.cargo/config.toml
Normal file
@ -0,0 +1,2 @@
|
||||
[build]
|
||||
target = "wasm32-unknown-unknown"
|
6
fuzzers/baby_fuzzer_wasm/.gitignore
vendored
Normal file
6
fuzzers/baby_fuzzer_wasm/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
bin/
|
||||
wasm-pack.log
|
||||
.idea/
|
35
fuzzers/baby_fuzzer_wasm/Cargo.toml
Normal file
35
fuzzers/baby_fuzzer_wasm/Cargo.toml
Normal file
@ -0,0 +1,35 @@
|
||||
[package]
|
||||
name = "baby_fuzzer_wasm"
|
||||
version = "0.1.0"
|
||||
authors = ["Addison Crump <research@addisoncrump.info>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[features]
|
||||
default = ["console_error_panic_hook"]
|
||||
|
||||
[dependencies]
|
||||
js-sys = "0.3"
|
||||
wasm-bindgen = "0.2.63"
|
||||
|
||||
libafl = { path = "../../libafl", default-features = false }
|
||||
|
||||
# The `console_error_panic_hook` crate provides better debugging of panics by
|
||||
# logging them with `console.error`. This is great for development, but requires
|
||||
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
|
||||
# code size when deploying.
|
||||
console_error_panic_hook = { version = "0.1.6", optional = true }
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3"
|
||||
features = ['console', 'Window', 'Performance', 'PerformanceTiming']
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.13"
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
lto = true
|
||||
codegen-units = 1
|
29
fuzzers/baby_fuzzer_wasm/Makefile.toml
Normal file
29
fuzzers/baby_fuzzer_wasm/Makefile.toml
Normal file
@ -0,0 +1,29 @@
|
||||
[env]
|
||||
FUZZER_NAME="fuzzer"
|
||||
PROJECT_DIR = { script = ["pwd"] }
|
||||
|
||||
[tasks.unsupported]
|
||||
script_runner="@shell"
|
||||
script='''
|
||||
echo "Cargo-make not integrated yet on this"
|
||||
'''
|
||||
|
||||
# Fuzzer
|
||||
[tasks.build]
|
||||
command = "wasm-pack"
|
||||
args = ["build", "--target", "web"]
|
||||
|
||||
# Test
|
||||
[tasks.test]
|
||||
linux_alias = "test_unix"
|
||||
mac_alias = "test_unix"
|
||||
windows_alias = "unsupported"
|
||||
|
||||
[tasks.test_unix]
|
||||
command = "wasm-pack"
|
||||
args = ["test", "--chrome", "--headless"]
|
||||
|
||||
# Clean
|
||||
[tasks.clean]
|
||||
command = "cargo"
|
||||
args = ["clean"]
|
12
fuzzers/baby_fuzzer_wasm/README.md
Normal file
12
fuzzers/baby_fuzzer_wasm/README.md
Normal file
@ -0,0 +1,12 @@
|
||||
# libafl-wasm
|
||||
|
||||
A brief demo demonstrating libafl's compatibility with WASM, and how to do it.
|
||||
|
||||
In this example, the entire LibAFL harness and target are present in a WASM binary, which is then loaded by [the example
|
||||
webpage](pkg/index.html). To run this example, do `cargo make build`, then open [the example webpage](pkg/index.html) in
|
||||
your browser (via something like `python3 -m http.server`). The fuzzer will execute until finding a solution and will
|
||||
write the fuzzer log to your console.
|
||||
|
||||
In a real fuzzing campaign, you would likely need to also create a LibAFL Corpus implementation which was backed by
|
||||
JavaScript, and restart the fuzzing campaign by re-invoking the fuzzer and providing the associated corpora. This is
|
||||
not demonstrated in this barebones example.
|
1
fuzzers/baby_fuzzer_wasm/pkg/.gitignore
vendored
Normal file
1
fuzzers/baby_fuzzer_wasm/pkg/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*
|
14
fuzzers/baby_fuzzer_wasm/pkg/index.html
Normal file
14
fuzzers/baby_fuzzer_wasm/pkg/index.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>libafl_wasm test</title>
|
||||
</head>
|
||||
<body>
|
||||
<script type="module">
|
||||
import libafl_wasm from './libafl_wasm.js'
|
||||
|
||||
libafl_wasm().then(wasm => wasm.fuzz())
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
15
fuzzers/baby_fuzzer_wasm/pkg/package.json
Normal file
15
fuzzers/baby_fuzzer_wasm/pkg/package.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "baby_fuzzer_wasm",
|
||||
"collaborators": [
|
||||
"Addison Crump <research@addisoncrump.info>"
|
||||
],
|
||||
"version": "0.1.0",
|
||||
"files": [
|
||||
"baby_fuzzer_wasm_bg.wasm",
|
||||
"baby_fuzzer_wasm.js",
|
||||
"baby_fuzzer_wasm.d.ts"
|
||||
],
|
||||
"module": "baby_fuzzer_wasm.js",
|
||||
"types": "baby_fuzzer_wasm.d.ts",
|
||||
"sideEffects": false
|
||||
}
|
135
fuzzers/baby_fuzzer_wasm/src/lib.rs
Normal file
135
fuzzers/baby_fuzzer_wasm/src/lib.rs
Normal file
@ -0,0 +1,135 @@
|
||||
mod utils;
|
||||
|
||||
use libafl::{
|
||||
bolts::{current_nanos, rands::StdRand, tuples::tuple_list, AsSlice},
|
||||
corpus::{Corpus, InMemoryCorpus},
|
||||
events::SimpleEventManager,
|
||||
executors::{ExitKind, InProcessExecutor},
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback},
|
||||
generators::RandPrintablesGenerator,
|
||||
inputs::{BytesInput, HasTargetBytes},
|
||||
monitors::SimpleMonitor,
|
||||
mutators::{havoc_mutations, StdScheduledMutator},
|
||||
observers::StdMapObserver,
|
||||
schedulers::QueueScheduler,
|
||||
stages::StdMutationalStage,
|
||||
state::{HasSolutions, StdState},
|
||||
Fuzzer, StdFuzzer,
|
||||
};
|
||||
use wasm_bindgen::prelude::*;
|
||||
use web_sys::{Performance, Window};
|
||||
|
||||
use crate::utils::set_panic_hook;
|
||||
|
||||
// defined for internal use by libafl
|
||||
#[no_mangle]
|
||||
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
||||
pub extern "C" fn external_current_millis() -> u64 {
|
||||
let window: Window = web_sys::window().expect("should be in browser to run this demo");
|
||||
let performance: Performance = window
|
||||
.performance()
|
||||
.expect("should be in browser to run this demo");
|
||||
performance.now() as u64
|
||||
}
|
||||
|
||||
#[allow(clippy::missing_panics_doc)]
|
||||
#[wasm_bindgen]
|
||||
pub fn fuzz() {
|
||||
set_panic_hook();
|
||||
|
||||
let mut signals = [0u8; 64];
|
||||
let signals_ptr = signals.as_mut_ptr();
|
||||
let signals_set = |i: usize| unsafe {
|
||||
*signals_ptr.add(i) += 1;
|
||||
};
|
||||
|
||||
// The closure that we want to fuzz
|
||||
let mut harness = |input: &BytesInput| {
|
||||
let target = input.target_bytes();
|
||||
let buf = target.as_slice();
|
||||
signals_set(0);
|
||||
if !buf.is_empty() && buf[0] == b'a' {
|
||||
signals_set(1);
|
||||
if buf.len() > 1 && buf[1] == b'b' {
|
||||
signals_set(2);
|
||||
#[allow(clippy::manual_assert)]
|
||||
if buf.len() > 2 && buf[2] == b'c' {
|
||||
// WASM cannot handle traps: https://webassembly.github.io/spec/core/intro/overview.html
|
||||
// in a "real" fuzzing campaign, you should prefer to setup trap handling in JS,
|
||||
// but we do not do this for demonstration purposes
|
||||
return ExitKind::Crash;
|
||||
}
|
||||
}
|
||||
}
|
||||
ExitKind::Ok
|
||||
};
|
||||
|
||||
// Create an observation channel using the signals map
|
||||
let observer =
|
||||
unsafe { StdMapObserver::from_mut_ptr("signals", signals.as_mut_ptr(), signals.len()) };
|
||||
|
||||
// Feedback to rate the interestingness of an input
|
||||
let mut feedback = MaxMapFeedback::new(&observer);
|
||||
|
||||
// A feedback to choose if an input is a solution or not
|
||||
let mut objective = CrashFeedback::new();
|
||||
|
||||
// create a State from scratch
|
||||
let mut state = StdState::new(
|
||||
// RNG
|
||||
StdRand::with_seed(current_nanos()),
|
||||
// Corpus that will be evolved, we keep it in memory for performance
|
||||
InMemoryCorpus::new(),
|
||||
// In a "real" fuzzing campaign, you should stash solutions in a JS array instead
|
||||
InMemoryCorpus::new(),
|
||||
// States of the feedbacks.
|
||||
// The feedbacks can report the data that should persist in the State.
|
||||
&mut feedback,
|
||||
// Same for objective feedbacks
|
||||
&mut objective,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// The Monitor trait define how the fuzzer stats are reported to the user
|
||||
let monitor = SimpleMonitor::new(|s| {
|
||||
web_sys::console::log_1(&s.into());
|
||||
});
|
||||
|
||||
// The event manager handle the various events generated during the fuzzing loop
|
||||
// such as the notification of the addition of a new item to the corpus
|
||||
let mut mgr = SimpleEventManager::new(monitor);
|
||||
|
||||
// A queue policy to get testcasess from the corpus
|
||||
let scheduler = QueueScheduler::new();
|
||||
|
||||
// A fuzzer with feedbacks and a corpus scheduler
|
||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||
|
||||
// Create the executor for an in-process function with just one observer
|
||||
let mut executor = InProcessExecutor::new(
|
||||
&mut harness,
|
||||
tuple_list!(observer),
|
||||
&mut fuzzer,
|
||||
&mut state,
|
||||
&mut mgr,
|
||||
)
|
||||
.expect("Failed to create the Executor");
|
||||
|
||||
// Generator of printable bytearrays of max size 32
|
||||
let mut generator = RandPrintablesGenerator::new(32);
|
||||
|
||||
// Generate 8 initial inputs
|
||||
state
|
||||
.generate_initial_inputs(&mut fuzzer, &mut executor, &mut generator, &mut mgr, 8)
|
||||
.expect("Failed to generate the initial corpus");
|
||||
|
||||
// Setup a mutational stage with a basic bytes mutator
|
||||
let mutator = StdScheduledMutator::new(havoc_mutations());
|
||||
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
||||
|
||||
while state.solutions().is_empty() {
|
||||
fuzzer
|
||||
.fuzz_one(&mut stages, &mut executor, &mut state, &mut mgr)
|
||||
.expect("Error in the fuzzing loop");
|
||||
}
|
||||
}
|
10
fuzzers/baby_fuzzer_wasm/src/utils.rs
Normal file
10
fuzzers/baby_fuzzer_wasm/src/utils.rs
Normal file
@ -0,0 +1,10 @@
|
||||
pub fn set_panic_hook() {
|
||||
// When the `console_error_panic_hook` feature is enabled, we can call the
|
||||
// `set_panic_hook` function at least once during initialization, and then
|
||||
// we will get better error messages if our code ever panics.
|
||||
//
|
||||
// For more details see
|
||||
// https://github.com/rustwasm/console_error_panic_hook#readme
|
||||
#[cfg(feature = "console_error_panic_hook")]
|
||||
console_error_panic_hook::set_once();
|
||||
}
|
14
fuzzers/baby_fuzzer_wasm/tests/web.rs
Normal file
14
fuzzers/baby_fuzzer_wasm/tests/web.rs
Normal file
@ -0,0 +1,14 @@
|
||||
//! Test suite for the Web and headless browsers.
|
||||
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
|
||||
extern crate wasm_bindgen_test;
|
||||
use baby_fuzzer_wasm::fuzz;
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_fuzz() {
|
||||
fuzz();
|
||||
}
|
15
fuzzers/baby_fuzzer_wasm/webdriver.json
Normal file
15
fuzzers/baby_fuzzer_wasm/webdriver.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"moz:firefoxOptions": {
|
||||
"prefs": {
|
||||
"media.navigator.streams.fake": true,
|
||||
"media.navigator.permission.disabled": true
|
||||
},
|
||||
"args": []
|
||||
},
|
||||
"goog:chromeOptions": {
|
||||
"args": [
|
||||
"--use-fake-device-for-media-stream",
|
||||
"--use-fake-ui-for-media-stream"
|
||||
]
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "baby_fuzzer_with_forkexecutor"
|
||||
version = "0.6.1"
|
||||
version = "0.10.0"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::path::PathBuf;
|
||||
#[cfg(windows)]
|
||||
use std::ptr::write_volatile;
|
||||
use std::{path::PathBuf, ptr::write};
|
||||
|
||||
use libafl::{
|
||||
bolts::{
|
||||
@ -29,11 +29,11 @@ use libafl::{
|
||||
pub fn main() {
|
||||
let mut shmem_provider = unix_shmem::UnixShMemProvider::new().unwrap();
|
||||
let mut signals = shmem_provider.new_shmem(16).unwrap();
|
||||
let mut signals_clone = signals.clone();
|
||||
let signals_len = signals.as_slice().len();
|
||||
let signals_ptr = signals.as_mut_slice().as_mut_ptr();
|
||||
|
||||
let mut signals_set = |idx: usize| {
|
||||
let a = signals.as_mut_slice();
|
||||
a[idx] = 1;
|
||||
let signals_set = |idx: usize| {
|
||||
unsafe { write(signals_ptr.add(idx), 1) };
|
||||
};
|
||||
|
||||
// The closure that we want to fuzz
|
||||
@ -64,7 +64,7 @@ pub fn main() {
|
||||
};
|
||||
|
||||
// Create an observation channel using the signals map
|
||||
let observer = StdMapObserver::new("signals", signals_clone.as_mut_slice());
|
||||
let observer = unsafe { StdMapObserver::from_mut_ptr("signals", signals_ptr, signals_len) };
|
||||
// Create a stacktrace observer to add the observers tuple
|
||||
|
||||
// Feedback to rate the interestingness of an input, obtained by ANDing the interestingness of both feedbacks
|
||||
@ -91,7 +91,7 @@ pub fn main() {
|
||||
.unwrap();
|
||||
|
||||
// The Monitor trait define how the fuzzer stats are displayed to the user
|
||||
let mon = SimpleMonitor::new(|s| println!("{}", s));
|
||||
let mon = SimpleMonitor::new(|s| println!("{s}"));
|
||||
|
||||
// The event manager handle the various events generated during the fuzzing loop
|
||||
// such as the notification of the addition of a new item to the corpus
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "baby_no_std"
|
||||
version = "0.8.2"
|
||||
version = "0.10.0"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
#![no_std]
|
||||
// Embedded targets: build with no_main
|
||||
#![cfg_attr(not(any(windows)), no_main)]
|
||||
// Embedded needs alloc error handlers which only work on nightly right now...
|
||||
#![cfg_attr(not(any(windows)), feature(default_alloc_error_handler))]
|
||||
|
||||
#[cfg(any(windows, unix))]
|
||||
extern crate alloc;
|
||||
@ -10,6 +8,7 @@ extern crate alloc;
|
||||
use alloc::ffi::CString;
|
||||
#[cfg(not(any(windows)))]
|
||||
use core::panic::PanicInfo;
|
||||
use core::ptr::write;
|
||||
|
||||
use libafl::{
|
||||
bolts::{current_nanos, rands::StdRand, tuples::tuple_list, AsSlice},
|
||||
@ -48,10 +47,11 @@ fn panic(_info: &PanicInfo) -> ! {
|
||||
|
||||
/// Coverage map with explicit assignments due to the lack of instrumentation
|
||||
static mut SIGNALS: [u8; 16] = [0; 16];
|
||||
static mut SIGNALS_PTR: *mut u8 = unsafe { SIGNALS.as_mut_ptr() };
|
||||
|
||||
/// Assign a signal to the signals map
|
||||
fn signals_set(idx: usize) {
|
||||
unsafe { SIGNALS[idx] = 1 };
|
||||
unsafe { write(SIGNALS_PTR.add(idx), 1) };
|
||||
}
|
||||
|
||||
/// Provide custom time in `no_std` environment
|
||||
@ -87,7 +87,7 @@ pub extern "C" fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
};
|
||||
|
||||
// Create an observation channel using the signals map
|
||||
let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS });
|
||||
let observer = unsafe { StdMapObserver::from_mut_ptr("signals", SIGNALS_PTR, SIGNALS.len()) };
|
||||
|
||||
// Feedback to rate the interestingness of an input
|
||||
let mut feedback = MaxMapFeedback::new(&observer);
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Backtrace baby fuzzers
|
||||
|
||||
The projects contained in this directory are simple fuzzers derived from the original baby_fuzzer examples, whose purpose is to show how to use a `BacktraceObserver` or an `ASANObserver` to dedupe crashes and other necessary components for this feature.
|
||||
The projects contained in this directory are simple fuzzers derived from the original baby_fuzzer examples, whose purpose is to show how to use a `BacktraceObserver` or an `ASANObserver` to dedupe crashes and other necessary components for this feature. To use `casr` deduplication for `BacktraceObserver` or `ASANObserver` build LibAFL with `casr` feature.
|
||||
|
||||
The examples cover:
|
||||
|
||||
|
@ -45,7 +45,7 @@ pub fn main() {
|
||||
libafl::executors::ExitKind::Ok
|
||||
};
|
||||
// Create an observation channel using the signals map
|
||||
let observer = unsafe { ConstMapObserver::<u8, 3>::new_from_ptr("signals", map_ptr) };
|
||||
let observer = unsafe { ConstMapObserver::<u8, 3>::from_mut_ptr("signals", map_ptr) };
|
||||
// Create a stacktrace observer
|
||||
let mut bt = shmem_provider.new_shmem_object::<Option<u64>>().unwrap();
|
||||
let bt_observer = BacktraceObserver::new(
|
||||
@ -78,7 +78,7 @@ pub fn main() {
|
||||
.unwrap();
|
||||
|
||||
// The Monitor trait define how the fuzzer stats are displayed to the user
|
||||
let mon = SimpleMonitor::new(|s| println!("{}", s));
|
||||
let mon = SimpleMonitor::new(|s| println!("{s}"));
|
||||
|
||||
// The event manager handle the various events generated during the fuzzing loop
|
||||
// such as the notification of the addition of a new item to the corpus
|
||||
|
@ -35,7 +35,7 @@ pub fn main() {
|
||||
libafl::executors::ExitKind::Ok
|
||||
};
|
||||
// Create an observation channel using the signals map
|
||||
let observer = unsafe { ConstMapObserver::<u8, 3>::new_from_ptr("signals", array_ptr) };
|
||||
let observer = unsafe { ConstMapObserver::<u8, 3>::from_mut_ptr("signals", array_ptr) };
|
||||
// Create a stacktrace observer
|
||||
let mut bt = None;
|
||||
let bt_observer = BacktraceObserver::new(
|
||||
@ -68,7 +68,7 @@ pub fn main() {
|
||||
.unwrap();
|
||||
|
||||
// The Monitor trait define how the fuzzer stats are displayed to the user
|
||||
let mon = SimpleMonitor::new(|s| println!("{}", s));
|
||||
let mon = SimpleMonitor::new(|s| println!("{s}"));
|
||||
|
||||
// The event manager handle the various events generated during the fuzzing loop
|
||||
// such as the notification of the addition of a new item to the corpus
|
||||
|
@ -18,6 +18,4 @@ cc = "*"
|
||||
|
||||
[dependencies]
|
||||
libafl = { path = "../../../libafl/" }
|
||||
ahash = { version = "0.7"} # another hash
|
||||
spawn-ptrace = {version= "0.1.2"}
|
||||
|
||||
|
@ -38,7 +38,7 @@ pub fn main() {
|
||||
let shmem_id = signals.id();
|
||||
|
||||
// Create an observation channel using the signals map
|
||||
let observer = StdMapObserver::new("signals", signals.as_mut_slice());
|
||||
let observer = unsafe { StdMapObserver::new("signals", signals.as_mut_slice()) };
|
||||
// Create a stacktrace observer
|
||||
let bt_observer = AsanBacktraceObserver::new("AsanBacktraceObserver");
|
||||
|
||||
@ -67,7 +67,7 @@ pub fn main() {
|
||||
.unwrap();
|
||||
|
||||
// The Monitor trait define how the fuzzer stats are displayed to the user
|
||||
let mon = SimpleMonitor::new(|s| println!("{}", s));
|
||||
let mon = SimpleMonitor::new(|s| println!("{s}"));
|
||||
|
||||
// The event manager handle the various events generated during the fuzzing loop
|
||||
// such as the notification of the addition of a new item to the corpus
|
||||
|
@ -15,4 +15,3 @@ opt-level = 3
|
||||
|
||||
[dependencies]
|
||||
libafl = { path = "../../../libafl/" }
|
||||
clap = { version = "3.2", features = ["default"] }
|
@ -54,7 +54,7 @@ pub fn main() {
|
||||
|
||||
// Feedback to rate the interestingness of an input
|
||||
// This one is composed by two Feedbacks in OR
|
||||
let mut feedback = MaxMapFeedback::new_tracking(&edges_observer, true, false);
|
||||
let mut feedback = MaxMapFeedback::tracking(&edges_observer, true, false);
|
||||
|
||||
// A feedback to choose if an input is a solution or not
|
||||
// We want to do the same crash deduplication that AFL does
|
||||
@ -78,7 +78,7 @@ pub fn main() {
|
||||
.unwrap();
|
||||
|
||||
// The Monitor trait define how the fuzzer stats are reported to the user
|
||||
let monitor = SimpleMonitor::new(|s| println!("{}", s));
|
||||
let monitor = SimpleMonitor::new(|s| println!("{s}"));
|
||||
|
||||
// The event manager handle the various events generated during the fuzzing loop
|
||||
// such as the notification of the addition of a new item to the corpus
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::path::PathBuf;
|
||||
#[cfg(windows)]
|
||||
use std::ptr::write_volatile;
|
||||
use std::{path::PathBuf, ptr::write};
|
||||
|
||||
use libafl::{
|
||||
bolts::{
|
||||
@ -30,12 +30,12 @@ use libafl::{
|
||||
pub fn main() {
|
||||
let mut shmem_provider = unix_shmem::UnixShMemProvider::new().unwrap();
|
||||
let mut signals = shmem_provider.new_shmem(16).unwrap();
|
||||
let mut signals_clone = signals.clone();
|
||||
let signals_len = signals.len();
|
||||
let signals_ptr = signals.as_mut_slice().as_mut_ptr();
|
||||
let mut bt = shmem_provider.new_shmem_object::<Option<u64>>().unwrap();
|
||||
|
||||
let mut signals_set = |idx: usize| {
|
||||
let a = signals.as_mut_slice();
|
||||
a[idx] = 1;
|
||||
let signals_set = |idx: usize| {
|
||||
unsafe { write(signals_ptr.add(idx), 1) };
|
||||
};
|
||||
|
||||
// The closure that we want to fuzz
|
||||
@ -65,7 +65,7 @@ pub fn main() {
|
||||
};
|
||||
|
||||
// Create an observation channel using the signals map
|
||||
let observer = StdMapObserver::new("signals", signals_clone.as_mut_slice());
|
||||
let observer = unsafe { StdMapObserver::from_mut_ptr("signals", signals_ptr, signals_len) };
|
||||
// Create a stacktrace observer
|
||||
let bt_observer = BacktraceObserver::new(
|
||||
"BacktraceObserver",
|
||||
@ -97,7 +97,7 @@ pub fn main() {
|
||||
.unwrap();
|
||||
|
||||
// The Monitor trait define how the fuzzer stats are displayed to the user
|
||||
let mon = SimpleMonitor::new(|s| println!("{}", s));
|
||||
let mon = SimpleMonitor::new(|s| println!("{s}"));
|
||||
|
||||
// The event manager handle the various events generated during the fuzzing loop
|
||||
// such as the notification of the addition of a new item to the corpus
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::path::PathBuf;
|
||||
#[cfg(windows)]
|
||||
use std::ptr::write_volatile;
|
||||
use std::{path::PathBuf, ptr::write};
|
||||
|
||||
use libafl::{
|
||||
bolts::{current_nanos, rands::StdRand, tuples::tuple_list, AsSlice},
|
||||
@ -22,10 +22,11 @@ use libafl::{
|
||||
|
||||
/// Coverage map with explicit assignments due to the lack of instrumentation
|
||||
static mut SIGNALS: [u8; 16] = [0; 16];
|
||||
static mut SIGNALS_PTR: *mut u8 = unsafe { SIGNALS.as_mut_ptr() };
|
||||
|
||||
/// Assign a signal to the signals map
|
||||
fn signals_set(idx: usize) {
|
||||
unsafe { SIGNALS[idx] = 1 };
|
||||
unsafe { write(SIGNALS_PTR.add(idx), 1) };
|
||||
}
|
||||
|
||||
#[allow(clippy::similar_names)]
|
||||
@ -58,7 +59,7 @@ pub fn main() {
|
||||
};
|
||||
|
||||
// Create an observation channel using the signals map
|
||||
let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS });
|
||||
let observer = unsafe { StdMapObserver::from_mut_ptr("signals", SIGNALS_PTR, SIGNALS.len()) };
|
||||
// Create a stacktrace observer to add the observers tuple
|
||||
let mut bt = None;
|
||||
let bt_observer = BacktraceObserver::new(
|
||||
@ -91,7 +92,7 @@ pub fn main() {
|
||||
.unwrap();
|
||||
|
||||
// The Monitor trait define how the fuzzer stats are displayed to the user
|
||||
let mon = SimpleMonitor::new(|s| println!("{}", s));
|
||||
let mon = SimpleMonitor::new(|s| println!("{s}"));
|
||||
|
||||
// The event manager handle the various events generated during the fuzzing loop
|
||||
// such as the notification of the addition of a new item to the corpus
|
||||
|
32
fuzzers/forkserver_libafl_cc/Cargo.toml
Normal file
32
fuzzers/forkserver_libafl_cc/Cargo.toml
Normal file
@ -0,0 +1,32 @@
|
||||
[package]
|
||||
name = "forkserver_libafl_cc"
|
||||
version = "0.8.2"
|
||||
authors = ["ergrelet <ergrelet@users.noreply.github.com>"]
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = []
|
||||
# Forces a crash
|
||||
crash = []
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
opt-level = 3
|
||||
debug = true
|
||||
|
||||
[build-dependencies]
|
||||
cc = { version = "1.0", features = ["parallel"] }
|
||||
which = { version = "4.0.2" }
|
||||
|
||||
[dependencies]
|
||||
libafl = { path = "../../libafl/", features = ["default"] }
|
||||
clap = { version = "4.0", features = ["derive"] }
|
||||
nix = "0.26"
|
||||
libafl_targets = { path = "../../libafl_targets/" }
|
||||
libafl_cc = { path = "../../libafl_cc/" }
|
||||
|
||||
[lib]
|
||||
name = "libforkserver_libafl_cc"
|
||||
crate-type = ["staticlib"]
|
115
fuzzers/forkserver_libafl_cc/Makefile.toml
Normal file
115
fuzzers/forkserver_libafl_cc/Makefile.toml
Normal file
@ -0,0 +1,115 @@
|
||||
# Variables
|
||||
[env]
|
||||
FUZZER_NAME='fuzzer_libafl_cc'
|
||||
CARGO_TARGET_DIR = { value = "${PROJECT_DIR}/target", condition = { env_not_set = ["CARGO_TARGET_DIR"] } }
|
||||
LIBAFL_CC = '${CARGO_TARGET_DIR}/release/libafl_cc'
|
||||
LIBAFL_CXX = '${CARGO_TARGET_DIR}/release/libafl_cxx'
|
||||
FUZZER = '${CARGO_TARGET_DIR}/release/${FUZZER_NAME}'
|
||||
PROJECT_DIR = { script = ["pwd"] }
|
||||
|
||||
[tasks.unsupported]
|
||||
script_runner="@shell"
|
||||
script='''
|
||||
echo "Cargo-make not integrated yet on this"
|
||||
'''
|
||||
|
||||
# Compilers
|
||||
[tasks.cxx]
|
||||
linux_alias = "cxx_unix"
|
||||
mac_alias = "cxx_unix"
|
||||
windows_alias = "unsupported"
|
||||
|
||||
[tasks.cxx_unix]
|
||||
command = "cargo"
|
||||
args = ["build" , "--release"]
|
||||
|
||||
[tasks.cc]
|
||||
linux_alias = "cc_unix"
|
||||
mac_alias = "cc_unix"
|
||||
windows_alias = "unsupported"
|
||||
|
||||
[tasks.cc_unix]
|
||||
command = "cargo"
|
||||
args = ["build" , "--release"]
|
||||
|
||||
[tasks.crash_cxx]
|
||||
linux_alias = "crash_cxx_unix"
|
||||
mac_alias = "crash_cxx_unix"
|
||||
windows_alias = "unsupported"
|
||||
|
||||
[tasks.crash_cxx_unix]
|
||||
command = "cargo"
|
||||
args = ["build" , "--release", "--features=crash"]
|
||||
|
||||
[tasks.crash_cc]
|
||||
linux_alias = "crash_cc_unix"
|
||||
mac_alias = "crash_cc_unix"
|
||||
windows_alias = "unsupported"
|
||||
|
||||
[tasks.crash_cc_unix]
|
||||
command = "cargo"
|
||||
args = ["build" , "--release", "--features=crash"]
|
||||
|
||||
# Harness
|
||||
[tasks.fuzzer]
|
||||
linux_alias = "fuzzer_unix"
|
||||
mac_alias = "fuzzer_unix"
|
||||
windows_alias = "unsupported"
|
||||
|
||||
[tasks.fuzzer_unix]
|
||||
command = "${CARGO_TARGET_DIR}/release/libafl_cc"
|
||||
args = ["${PROJECT_DIR}/src/program.c", "-o", "${FUZZER_NAME}", "-lm"]
|
||||
dependencies = [ "cxx", "cc" ]
|
||||
|
||||
# Crashing Harness
|
||||
[tasks.fuzzer_crash]
|
||||
linux_alias = "fuzzer_crash_unix"
|
||||
mac_alias = "fuzzer_crash_unix"
|
||||
windows_alias = "unsupported"
|
||||
|
||||
[tasks.fuzzer_crash_unix]
|
||||
command = "${CARGO_TARGET_DIR}/release/libafl_cc"
|
||||
args = ["${PROJECT_DIR}/src/program.c", "-o", "${FUZZER_NAME}_crash", "-lm"]
|
||||
dependencies = [ "crash_cxx", "crash_cc" ]
|
||||
|
||||
# Run the fuzzer
|
||||
[tasks.run]
|
||||
linux_alias = "run_unix"
|
||||
mac_alias = "run_unix"
|
||||
windows_alias = "unsupported"
|
||||
|
||||
[tasks.run_unix]
|
||||
script_runner = "@shell"
|
||||
script='''
|
||||
taskset -c 1 ${CARGO_TARGET_DIR}/release/${CARGO_MAKE_PROJECT_NAME} ./${FUZZER_NAME} ./corpus/ -t 1000
|
||||
'''
|
||||
dependencies = [ "fuzzer" ]
|
||||
|
||||
|
||||
# Run the fuzzer with a crash
|
||||
[tasks.crash]
|
||||
linux_alias = "crash_unix"
|
||||
mac_alias = "crash_unix"
|
||||
windows_alias = "unsupported"
|
||||
|
||||
[tasks.crash_unix]
|
||||
script_runner = "@shell"
|
||||
script='''
|
||||
taskset -c 1 ${CARGO_TARGET_DIR}/release/${CARGO_MAKE_PROJECT_NAME} ./${FUZZER_NAME}_crash ./corpus/ -t 1000
|
||||
'''
|
||||
dependencies = [ "fuzzer_crash" ]
|
||||
|
||||
# Clean up
|
||||
[tasks.clean]
|
||||
linux_alias = "clean_unix"
|
||||
mac_alias = "clean_unix"
|
||||
windows_alias = "unsupported"
|
||||
|
||||
[tasks.clean_unix]
|
||||
# Disable default `clean` definition
|
||||
clear = true
|
||||
script_runner="@shell"
|
||||
script='''
|
||||
rm -f ./${FUZZER_NAME}
|
||||
cargo clean
|
||||
'''
|
13
fuzzers/forkserver_libafl_cc/README.md
Normal file
13
fuzzers/forkserver_libafl_cc/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
# Simple Forkserver Fuzzer
|
||||
|
||||
This is a simple example fuzzer to fuzz an executable instrumented by libafl_cc.
|
||||
|
||||
## Usage
|
||||
|
||||
You can build this example by running `cargo make fuzzer`.
|
||||
This compiles, libafl_cc, the fuzzer and the example harness program in
|
||||
`src/program.c` with libafl_cc.
|
||||
|
||||
## Run
|
||||
|
||||
You can run this example by running `cargo make run`.
|
1
fuzzers/forkserver_libafl_cc/corpus/testfile
Normal file
1
fuzzers/forkserver_libafl_cc/corpus/testfile
Normal file
@ -0,0 +1 @@
|
||||
aaa
|
45
fuzzers/forkserver_libafl_cc/src/bin/libafl_cc.rs
Normal file
45
fuzzers/forkserver_libafl_cc/src/bin/libafl_cc.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use std::env;
|
||||
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses};
|
||||
|
||||
pub fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() > 1 {
|
||||
let mut dir = env::current_exe().unwrap();
|
||||
let wrapper_name = dir.file_name().unwrap().to_str().unwrap();
|
||||
|
||||
let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() {
|
||||
"cc" => false,
|
||||
"++" | "pp" | "xx" => true,
|
||||
_ => panic!("Could not figure out if c or c++ wrapper was called. Expected {dir:?} to end with c or cxx"),
|
||||
};
|
||||
|
||||
dir.pop();
|
||||
|
||||
let mut cc = ClangWrapper::new();
|
||||
if let Some(code) = cc
|
||||
.cpp(is_cpp)
|
||||
// silence the compiler wrapper output, needed for some configure scripts.
|
||||
.silence(true)
|
||||
.parse_args(&args)
|
||||
.expect("Failed to parse the command line")
|
||||
// Enable libafl's coverage instrumentation
|
||||
.add_pass(LLVMPasses::AFLCoverage)
|
||||
.add_arg("-mllvm")
|
||||
.add_arg("-ctx") // Context sensitive coverage
|
||||
// Imitate afl-cc's compile definitions
|
||||
.add_arg("-D__AFL_FUZZ_INIT()=int __afl_sharedmem_fuzzing = 1;extern unsigned int *__afl_fuzz_len;extern unsigned char *__afl_fuzz_ptr;unsigned char __afl_fuzz_alt[1048576];unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;void libafl_start_forkserver(void)")
|
||||
.add_arg("-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : __afl_fuzz_alt_ptr)")
|
||||
.add_arg("-D__AFL_FUZZ_TESTCASE_LEN=(__afl_fuzz_ptr ? *__afl_fuzz_len : (*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1048576)) == 0xffffffff ? 0 : *__afl_fuzz_len)")
|
||||
.add_arg("-D__AFL_INIT()=libafl_start_forkserver()")
|
||||
// Link with libafl's forkserver implementation
|
||||
.link_staticlib(&dir, "libforkserver_libafl_cc")
|
||||
.run()
|
||||
.expect("Failed to run the wrapped compiler")
|
||||
{
|
||||
std::process::exit(code);
|
||||
}
|
||||
} else {
|
||||
panic!("LibAFL CC: No Arguments given");
|
||||
}
|
||||
}
|
5
fuzzers/forkserver_libafl_cc/src/bin/libafl_cxx.rs
Normal file
5
fuzzers/forkserver_libafl_cc/src/bin/libafl_cxx.rs
Normal file
@ -0,0 +1,5 @@
|
||||
pub mod libafl_cc;
|
||||
|
||||
fn main() {
|
||||
libafl_cc::main();
|
||||
}
|
9
fuzzers/forkserver_libafl_cc/src/lib.rs
Normal file
9
fuzzers/forkserver_libafl_cc/src/lib.rs
Normal file
@ -0,0 +1,9 @@
|
||||
use libafl_targets::{map_shared_memory, start_forkserver};
|
||||
|
||||
#[no_mangle]
|
||||
pub fn libafl_start_forkserver() {
|
||||
// Map shared memory region for the edge coverage map
|
||||
map_shared_memory();
|
||||
// Start the forkserver
|
||||
start_forkserver();
|
||||
}
|
216
fuzzers/forkserver_libafl_cc/src/main.rs
Normal file
216
fuzzers/forkserver_libafl_cc/src/main.rs
Normal file
@ -0,0 +1,216 @@
|
||||
use core::time::Duration;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::{self, Parser};
|
||||
use libafl::{
|
||||
bolts::{
|
||||
current_nanos,
|
||||
rands::StdRand,
|
||||
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
||||
tuples::{tuple_list, MatchName, Merge},
|
||||
AsMutSlice, Truncate,
|
||||
},
|
||||
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
||||
events::SimpleEventManager,
|
||||
executors::{
|
||||
forkserver::{ForkserverExecutor, TimeoutForkserverExecutor},
|
||||
HasObservers,
|
||||
},
|
||||
feedback_and_fast, feedback_or,
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
|
||||
fuzzer::{Fuzzer, StdFuzzer},
|
||||
inputs::BytesInput,
|
||||
monitors::SimpleMonitor,
|
||||
mutators::{scheduled::havoc_mutations, tokens_mutations, StdScheduledMutator, Tokens},
|
||||
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
||||
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
|
||||
stages::mutational::StdMutationalStage,
|
||||
state::{HasCorpus, HasMetadata, StdState},
|
||||
};
|
||||
use nix::sys::signal::Signal;
|
||||
|
||||
/// The commandline args this fuzzer accepts
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(
|
||||
name = "forkserver_libafl_cc",
|
||||
about = "This is a simple example fuzzer to fuzz a executable instrumented by libafl_cc.",
|
||||
author = "ergrelet <ergrelet@users.noreply.github.com>"
|
||||
)]
|
||||
struct Opt {
|
||||
#[arg(
|
||||
help = "The instrumented binary we want to fuzz",
|
||||
name = "EXEC",
|
||||
required = true
|
||||
)]
|
||||
executable: String,
|
||||
|
||||
#[arg(
|
||||
help = "The directory to read initial inputs from ('seeds')",
|
||||
name = "INPUT_DIR",
|
||||
required = true
|
||||
)]
|
||||
in_dir: PathBuf,
|
||||
|
||||
#[arg(
|
||||
help = "Timeout for each individual execution, in milliseconds",
|
||||
short = 't',
|
||||
long = "timeout",
|
||||
default_value = "1200"
|
||||
)]
|
||||
timeout: u64,
|
||||
|
||||
#[arg(
|
||||
help = "If not set, the child's stdout and stderror will be redirected to /dev/null",
|
||||
short = 'd',
|
||||
long = "debug-child",
|
||||
default_value = "false"
|
||||
)]
|
||||
debug_child: bool,
|
||||
|
||||
#[arg(
|
||||
help = "Arguments passed to the target",
|
||||
name = "arguments",
|
||||
num_args(1..),
|
||||
allow_hyphen_values = true,
|
||||
)]
|
||||
arguments: Vec<String>,
|
||||
|
||||
#[arg(
|
||||
help = "Signal used to stop child",
|
||||
short = 's',
|
||||
long = "signal",
|
||||
value_parser = str::parse::<Signal>,
|
||||
default_value = "SIGKILL"
|
||||
)]
|
||||
signal: Signal,
|
||||
}
|
||||
|
||||
#[allow(clippy::similar_names)]
|
||||
pub fn main() {
|
||||
const MAP_SIZE: usize = 65536;
|
||||
|
||||
let opt = Opt::parse();
|
||||
|
||||
let corpus_dirs: Vec<PathBuf> = [opt.in_dir].to_vec();
|
||||
|
||||
// The unix shmem provider supported by LibAFL for shared memory
|
||||
let mut shmem_provider = UnixShMemProvider::new().unwrap();
|
||||
|
||||
// The coverage map shared between observer and executor
|
||||
let mut shmem = shmem_provider.new_shmem(MAP_SIZE).unwrap();
|
||||
// let the forkserver know the shmid
|
||||
shmem.write_to_env("__AFL_SHM_ID").unwrap();
|
||||
let shmem_buf = shmem.as_mut_slice();
|
||||
|
||||
// Create an observation channel using the signals map
|
||||
let edges_observer =
|
||||
unsafe { HitcountsMapObserver::new(StdMapObserver::new("shared_mem", shmem_buf)) };
|
||||
|
||||
// Create an observation channel to keep track of the execution time
|
||||
let time_observer = TimeObserver::new("time");
|
||||
|
||||
// Feedback to rate the interestingness of an input
|
||||
// This one is composed by two Feedbacks in OR
|
||||
let mut feedback = feedback_or!(
|
||||
// New maximization map feedback linked to the edges observer and the feedback state
|
||||
MaxMapFeedback::tracking(&edges_observer, true, false),
|
||||
// Time feedback, this one does not need a feedback state
|
||||
TimeFeedback::with_observer(&time_observer)
|
||||
);
|
||||
|
||||
// A feedback to choose if an input is a solution or not
|
||||
// We want to do the same crash deduplication that AFL does
|
||||
let mut objective = feedback_and_fast!(
|
||||
// Must be a crash
|
||||
CrashFeedback::new(),
|
||||
// Take it only if trigger new coverage over crashes
|
||||
// Uses `with_name` to create a different history from the `MaxMapFeedback` in `feedback` above
|
||||
MaxMapFeedback::with_name("mapfeedback_metadata_objective", &edges_observer)
|
||||
);
|
||||
|
||||
// create a State from scratch
|
||||
let mut state = StdState::new(
|
||||
// RNG
|
||||
StdRand::with_seed(current_nanos()),
|
||||
// Corpus that will be evolved, we keep it in memory for performance
|
||||
InMemoryCorpus::<BytesInput>::new(),
|
||||
// Corpus in which we store solutions (crashes in this example),
|
||||
// on disk so the user can get them after stopping the fuzzer
|
||||
OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(),
|
||||
// States of the feedbacks.
|
||||
// The feedbacks can report the data that should persist in the State.
|
||||
&mut feedback,
|
||||
// Same for objective feedbacks
|
||||
&mut objective,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// The Monitor trait define how the fuzzer stats are reported to the user
|
||||
let monitor = SimpleMonitor::new(|s| println!("{s}"));
|
||||
|
||||
// The event manager handle the various events generated during the fuzzing loop
|
||||
// such as the notification of the addition of a new item to the corpus
|
||||
let mut mgr = SimpleEventManager::new(monitor);
|
||||
|
||||
// A minimization+queue policy to get testcasess from the corpus
|
||||
let scheduler = IndexesLenTimeMinimizerScheduler::new(QueueScheduler::new());
|
||||
|
||||
// A fuzzer with feedbacks and a corpus scheduler
|
||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||
|
||||
// If we should debug the child
|
||||
let debug_child = opt.debug_child;
|
||||
|
||||
// Create the executor for the forkserver
|
||||
let args = opt.arguments;
|
||||
|
||||
let mut tokens = Tokens::new();
|
||||
let mut forkserver = ForkserverExecutor::builder()
|
||||
.program(opt.executable)
|
||||
.debug_child(debug_child)
|
||||
.shmem_provider(&mut shmem_provider)
|
||||
.autotokens(&mut tokens)
|
||||
.parse_afl_cmdline(args)
|
||||
.coverage_map_size(MAP_SIZE)
|
||||
.build(tuple_list!(time_observer, edges_observer))
|
||||
.unwrap();
|
||||
|
||||
if let Some(dynamic_map_size) = forkserver.coverage_map_size() {
|
||||
forkserver
|
||||
.observers_mut()
|
||||
.match_name_mut::<HitcountsMapObserver<StdMapObserver<'_, u8, false>>>("shared_mem")
|
||||
.unwrap()
|
||||
.truncate(dynamic_map_size);
|
||||
}
|
||||
|
||||
let mut executor = TimeoutForkserverExecutor::with_signal(
|
||||
forkserver,
|
||||
Duration::from_millis(opt.timeout),
|
||||
opt.signal,
|
||||
)
|
||||
.expect("Failed to create the executor.");
|
||||
|
||||
// In case the corpus is empty (on first run), reset
|
||||
if state.must_load_initial_inputs() {
|
||||
state
|
||||
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &corpus_dirs)
|
||||
.unwrap_or_else(|err| {
|
||||
panic!(
|
||||
"Failed to load initial corpus at {:?}: {:?}",
|
||||
&corpus_dirs, err
|
||||
)
|
||||
});
|
||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||
}
|
||||
|
||||
state.add_metadata(tokens);
|
||||
|
||||
// Setup a mutational stage with a basic bytes mutator
|
||||
let mutator =
|
||||
StdScheduledMutator::with_max_stack_pow(havoc_mutations().merge(tokens_mutations()), 6);
|
||||
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
||||
|
||||
fuzzer
|
||||
.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)
|
||||
.expect("Error in the fuzzing loop");
|
||||
}
|
37
fuzzers/forkserver_libafl_cc/src/program.c
Normal file
37
fuzzers/forkserver_libafl_cc/src/program.c
Normal file
@ -0,0 +1,37 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// The following line is needed for shared memory testcase fuzzing
|
||||
__AFL_FUZZ_INIT();
|
||||
|
||||
void vuln(char *buf) {
|
||||
if (strcmp(buf, "vuln") == 0) { abort(); }
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
// Start the forkserver at this point (i.e., forks will happen here)
|
||||
__AFL_INIT();
|
||||
|
||||
// The following five lines are for normal fuzzing.
|
||||
/*
|
||||
FILE *file = stdin;
|
||||
if (argc > 1) { file = fopen(argv[1], "rb"); }
|
||||
char buf[16];
|
||||
char *p = fgets(buf, 16, file);
|
||||
buf[15] = 0;
|
||||
*/
|
||||
|
||||
// The following line is also needed for shared memory testcase fuzzing
|
||||
unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; // must be after __AFL_INIT
|
||||
|
||||
// printf("input: %s\n", buf);
|
||||
if (buf[0] == 'b') {
|
||||
if (buf[1] == 'a') {
|
||||
if (buf[2] == 'd') { abort(); }
|
||||
}
|
||||
}
|
||||
vuln((char *)buf);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "forkserver_simple"
|
||||
version = "0.8.2"
|
||||
version = "0.10.0"
|
||||
authors = ["tokatoka <tokazerkje@outlook.com>"]
|
||||
edition = "2021"
|
||||
|
||||
@ -18,4 +18,4 @@ opt-level = 3
|
||||
[dependencies]
|
||||
libafl = { path = "../../libafl/", features = ["std", "derive"] }
|
||||
clap = { version = "4.0", features = ["derive"] }
|
||||
nix = "0.25"
|
||||
nix = "0.26"
|
||||
|
@ -8,7 +8,7 @@ use libafl::{
|
||||
rands::StdRand,
|
||||
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
||||
tuples::{tuple_list, MatchName, Merge},
|
||||
AsMutSlice,
|
||||
AsMutSlice, Truncate,
|
||||
},
|
||||
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
||||
events::SimpleEventManager,
|
||||
@ -22,7 +22,7 @@ use libafl::{
|
||||
inputs::BytesInput,
|
||||
monitors::SimpleMonitor,
|
||||
mutators::{scheduled::havoc_mutations, tokens_mutations, StdScheduledMutator, Tokens},
|
||||
observers::{HitcountsMapObserver, MapObserver, StdMapObserver, TimeObserver},
|
||||
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
||||
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
|
||||
stages::mutational::StdMutationalStage,
|
||||
state::{HasCorpus, HasMetadata, StdState},
|
||||
@ -103,7 +103,8 @@ pub fn main() {
|
||||
let shmem_buf = shmem.as_mut_slice();
|
||||
|
||||
// Create an observation channel using the signals map
|
||||
let edges_observer = HitcountsMapObserver::new(StdMapObserver::new("shared_mem", shmem_buf));
|
||||
let edges_observer =
|
||||
unsafe { HitcountsMapObserver::new(StdMapObserver::new("shared_mem", shmem_buf)) };
|
||||
|
||||
// Create an observation channel to keep track of the execution time
|
||||
let time_observer = TimeObserver::new("time");
|
||||
@ -112,9 +113,9 @@ pub fn main() {
|
||||
// This one is composed by two Feedbacks in OR
|
||||
let mut feedback = feedback_or!(
|
||||
// New maximization map feedback linked to the edges observer and the feedback state
|
||||
MaxMapFeedback::new_tracking(&edges_observer, true, false),
|
||||
MaxMapFeedback::tracking(&edges_observer, true, false),
|
||||
// Time feedback, this one does not need a feedback state
|
||||
TimeFeedback::new_with_observer(&time_observer)
|
||||
TimeFeedback::with_observer(&time_observer)
|
||||
);
|
||||
|
||||
// A feedback to choose if an input is a solution or not
|
||||
@ -122,8 +123,9 @@ pub fn main() {
|
||||
let mut objective = feedback_and_fast!(
|
||||
// Must be a crash
|
||||
CrashFeedback::new(),
|
||||
// Take it onlt if trigger new coverage over crashes
|
||||
MaxMapFeedback::new(&edges_observer)
|
||||
// Take it only if trigger new coverage over crashes
|
||||
// Uses `with_name` to create a different history from the `MaxMapFeedback` in `feedback` above
|
||||
MaxMapFeedback::with_name("mapfeedback_metadata_objective", &edges_observer)
|
||||
);
|
||||
|
||||
// create a State from scratch
|
||||
@ -144,7 +146,7 @@ pub fn main() {
|
||||
.unwrap();
|
||||
|
||||
// The Monitor trait define how the fuzzer stats are reported to the user
|
||||
let monitor = SimpleMonitor::new(|s| println!("{}", s));
|
||||
let monitor = SimpleMonitor::new(|s| println!("{s}"));
|
||||
|
||||
// The event manager handle the various events generated during the fuzzing loop
|
||||
// such as the notification of the addition of a new item to the corpus
|
||||
@ -178,7 +180,7 @@ pub fn main() {
|
||||
.observers_mut()
|
||||
.match_name_mut::<HitcountsMapObserver<StdMapObserver<'_, u8, false>>>("shared_mem")
|
||||
.unwrap()
|
||||
.downsize_map(dynamic_map_size);
|
||||
.truncate(dynamic_map_size);
|
||||
}
|
||||
|
||||
let mut executor = TimeoutForkserverExecutor::with_signal(
|
||||
@ -189,7 +191,7 @@ pub fn main() {
|
||||
.expect("Failed to create the executor.");
|
||||
|
||||
// In case the corpus is empty (on first run), reset
|
||||
if state.corpus().count() < 1 {
|
||||
if state.must_load_initial_inputs() {
|
||||
state
|
||||
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &corpus_dirs)
|
||||
.unwrap_or_else(|err| {
|
||||
|
5
fuzzers/frida_executable_libpng/.gitignore
vendored
Normal file
5
fuzzers/frida_executable_libpng/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
libpng-*
|
||||
corpus_discovered
|
||||
libafl_frida
|
||||
frida_libpng
|
||||
zlib*
|
46
fuzzers/frida_executable_libpng/Cargo.toml
Normal file
46
fuzzers/frida_executable_libpng/Cargo.toml
Normal file
@ -0,0 +1,46 @@
|
||||
[package]
|
||||
name = "frida_executable_fuzzer"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
name = "frida_executable_fuzzer"
|
||||
crate_type = ["cdylib"]
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = []
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
opt-level = 3
|
||||
debug = true
|
||||
|
||||
[build-dependencies]
|
||||
cc = { version = "1.0.42", features = ["parallel"] }
|
||||
which = "4.1"
|
||||
xz2 = "0.1.6"
|
||||
flate2 = "1.0.22"
|
||||
tar = "0.4.37"
|
||||
reqwest = { version = "0.11.4", features = ["blocking"] }
|
||||
|
||||
|
||||
|
||||
|
||||
[dependencies]
|
||||
libafl = { path = "../../libafl/", features = [ "std", "llmp_compression", "llmp_bind_public", "frida_cli" ] } #, "llmp_small_maps", "llmp_debug"]}
|
||||
capstone = "0.11.0"
|
||||
frida-gum = { version = "0.8.1", features = [ "auto-download", "event-sink", "invocation-listener"] }
|
||||
libafl_frida = { path = "../../libafl_frida", features = ["cmplog"] }
|
||||
libafl_targets = { path = "../../libafl_targets", features = ["sancov_cmplog"] }
|
||||
libc = "0.2"
|
||||
libloading = "0.7"
|
||||
num-traits = "0.2"
|
||||
rangemap = "1"
|
||||
clap = { version = "4.0", features = ["derive"] }
|
||||
serde = "1.0"
|
||||
mimalloc = { version = "*", default-features = false }
|
||||
|
||||
backtrace = "0.3"
|
||||
color-backtrace = "0.5"
|
115
fuzzers/frida_executable_libpng/Makefile.toml
Normal file
115
fuzzers/frida_executable_libpng/Makefile.toml
Normal file
@ -0,0 +1,115 @@
|
||||
# Variables
|
||||
[env]
|
||||
CARGO_TARGET_DIR = { value = "target", condition = { env_not_set = ["CARGO_TARGET_DIR"] } }
|
||||
|
||||
|
||||
[tasks.unsupported]
|
||||
script_runner="@shell"
|
||||
script='''
|
||||
echo "Cargo-make not integrated yet on this"
|
||||
'''
|
||||
|
||||
# libpng
|
||||
[tasks.libpng]
|
||||
linux_alias = "libpng_unix"
|
||||
mac_alias = "unsupported"
|
||||
windows_alias = "unsupported"
|
||||
|
||||
[tasks.libpng_unix]
|
||||
condition = { files_not_exist = ["./libpng-1.6.37"]}
|
||||
script_runner="@shell"
|
||||
script='''
|
||||
wget https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz
|
||||
tar -xvf libpng-1.6.37.tar.xz
|
||||
'''
|
||||
|
||||
# Library
|
||||
[tasks.lib]
|
||||
linux_alias = "lib_unix"
|
||||
mac_alias = "unsupported"
|
||||
windows_alias = "unsupported"
|
||||
|
||||
[tasks.lib_unix]
|
||||
script_runner="@shell"
|
||||
script='''
|
||||
cd libpng-1.6.37 && ./configure --enable-shared=no --with-pic=yes --enable-hardware-optimizations=yes
|
||||
cd ..
|
||||
make -C libpng-1.6.37
|
||||
'''
|
||||
dependencies = [ "libpng" ]
|
||||
|
||||
# Harness
|
||||
[tasks.harness]
|
||||
linux_alias = "harness_unix"
|
||||
mac_alias = "unsupported"
|
||||
windows_alias = "unsupported"
|
||||
|
||||
[tasks.harness_unix]
|
||||
script_runner="@shell"
|
||||
script='''
|
||||
clang++ -O0 -c -fPIC harness.cc -o harness.o
|
||||
clang++ -O0 harness.cc libpng-1.6.37/.libs/libpng16.a -lz -o libpng-harness -g
|
||||
'''
|
||||
dependencies = [ "lib" ]
|
||||
|
||||
# Fuzzer
|
||||
[tasks.fuzzer]
|
||||
linux_alias = "fuzzer_unix"
|
||||
mac_alias = "unsupported"
|
||||
windows_alias = "unsupported"
|
||||
|
||||
[tasks.fuzzer_unix]
|
||||
script_runner="@shell"
|
||||
script='''
|
||||
cargo build --release
|
||||
'''
|
||||
|
||||
# Run the fuzzer
|
||||
[tasks.run]
|
||||
linux_alias = "run_unix"
|
||||
mac_alias = "unsupported"
|
||||
windows_alias = "unsupported"
|
||||
|
||||
[tasks.run_unix]
|
||||
script_runner = "@shell"
|
||||
script='''
|
||||
LD_PRELOAD=$CARGO_TARGET_DIR/release/libfrida_executable_fuzzer.so ./libpng-harness -i corpus -o out -H ./libpng-harness
|
||||
'''
|
||||
dependencies = [ "fuzzer", "harness" ]
|
||||
|
||||
# Test
|
||||
[tasks.test]
|
||||
linux_alias = "test_unix"
|
||||
mac_alias = "unsupported"
|
||||
windows_alias = "unsupported"
|
||||
|
||||
[tasks.test_unix]
|
||||
script_runner = "@shell"
|
||||
script='''
|
||||
rm -rf libafl_unix_shmem_server || true
|
||||
LD_PRELOAD=$CARGO_TARGET_DIR/release/libfrida_executable_fuzzer.so ./libpng-harness -i corpus -o out -H ./libpng-harness > fuzz_stdout.log &
|
||||
sleep 10s && pkill libpng-harness
|
||||
if [ -z "$(grep "corpus: 30" fuzz_stdout.log)" ]; then
|
||||
echo "Fuzzer does not generate any testcases or any crashes"
|
||||
exit 1
|
||||
else
|
||||
echo "Fuzzer is working"
|
||||
fi
|
||||
'''
|
||||
dependencies = [ "fuzzer", "harness" ]
|
||||
|
||||
# Clean up
|
||||
[tasks.clean]
|
||||
linux_alias = "clean_unix"
|
||||
mac_alias = "unsupported"
|
||||
windows_alias = "unsupported"
|
||||
|
||||
[tasks.clean_unix]
|
||||
# Disable default `clean` definition
|
||||
clear = true
|
||||
script_runner="@shell"
|
||||
script='''
|
||||
rm -f ./libpng-harness
|
||||
make -C libpng-1.6.37 clean
|
||||
cargo clean
|
||||
'''
|
36
fuzzers/frida_executable_libpng/README.md
Normal file
36
fuzzers/frida_executable_libpng/README.md
Normal file
@ -0,0 +1,36 @@
|
||||
# Fuzzing libpng with frida as executale
|
||||
|
||||
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 --release` in this folder.
|
||||
This will call (the build.rs)[./build.rs], which in turn downloads a libpng archive from the web.
|
||||
Then, it will build (the C++ harness)[./harness.cc] and the instrumented `libpng`.
|
||||
Then, it will create frida fuzzer shared library in `./target/release/libfrida_fuzzer.so`.
|
||||
On unix platforms, you'll need [libc++](https://libcxx.llvm.org/) to build it.
|
||||
|
||||
Alternatively you can run `cargo make run` and this command will automatically build and run the fuzzer
|
||||
|
||||
### Build For Android
|
||||
When building for android using a cross-compiler, make sure you have a [_standalone toolchain_](https://developer.android.com/ndk/guides/standalone_toolchain), and then add the following:
|
||||
1. In the ~/.cargo/config file add a target with the correct cross-compiler toolchain name (in this case aarch64-linux-android, but names may vary)
|
||||
`[target.aarch64-linux-android]`
|
||||
`linker="aarch64-linux-android-clang"`
|
||||
2. add path to installed toolchain to PATH env variable.
|
||||
3. define CLANG_PATH and add target to the build command line:
|
||||
`CLANG_PATH=<path to installed toolchain>/bin/aarch64-linux-android-clang cargo -v build --release --target=aarch64-linux-android`
|
||||
|
||||
## Run
|
||||
|
||||
This example uses in-process-fuzzing, using the `launcher` feature, in combination with a Restarting Event Manager.
|
||||
This means running --cores each client will start itself again to listen for crashes and timeouts.
|
||||
By restarting the actual fuzzer, it can recover from these exit conditions.
|
||||
|
||||
After building the libpng-harness, you can run `find . -name libpng-harness` to find the location of your harness, then run
|
||||
|
||||
```
|
||||
LD_PRELOAD=./target/release/libfrida_fuzzer.so ./libpng-harness -i corpus -o out -l ./libpng-harness.so
|
||||
```
|
BIN
fuzzers/frida_executable_libpng/corpus/not_kitty.png
Normal file
BIN
fuzzers/frida_executable_libpng/corpus/not_kitty.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 218 B |
BIN
fuzzers/frida_executable_libpng/corpus/not_kitty_alpha.png
Normal file
BIN
fuzzers/frida_executable_libpng/corpus/not_kitty_alpha.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 376 B |
BIN
fuzzers/frida_executable_libpng/corpus/not_kitty_gamma.png
Normal file
BIN
fuzzers/frida_executable_libpng/corpus/not_kitty_gamma.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 228 B |
BIN
fuzzers/frida_executable_libpng/corpus/not_kitty_icc.png
Normal file
BIN
fuzzers/frida_executable_libpng/corpus/not_kitty_icc.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 427 B |
228
fuzzers/frida_executable_libpng/harness.cc
Normal file
228
fuzzers/frida_executable_libpng/harness.cc
Normal file
@ -0,0 +1,228 @@
|
||||
// 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 <stdlib.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#define PNG_INTERNAL
|
||||
#include "libpng-1.6.37/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;
|
||||
}
|
||||
|
||||
static char *allocation = NULL;
|
||||
|
||||
__attribute__((noinline)) void func3(char *alloc) {
|
||||
// printf("func3\n");
|
||||
if (random() == 0) {
|
||||
alloc[0x1ff] = 0xde;
|
||||
printf("alloc[0x200]: %d\n", alloc[0x200]);
|
||||
}
|
||||
}
|
||||
__attribute__((noinline)) void func2() {
|
||||
allocation = (char *)malloc(0xff);
|
||||
// printf("func2\n");
|
||||
func3(allocation);
|
||||
}
|
||||
__attribute__((noinline)) void func1() {
|
||||
// printf("func1\n");
|
||||
func2();
|
||||
}
|
||||
|
||||
// Export this symbol
|
||||
#define HARNESS_EXPORTS
|
||||
|
||||
// Entry point for LibFuzzer.
|
||||
// Roughly follows the libpng book example:
|
||||
// http://www.libpng.org/pub/png/book/chapter13.html
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc != 3) { abort(); }
|
||||
size_t size = atoi(argv[1]);
|
||||
uint8_t *data = (uint8_t *)argv[2];
|
||||
|
||||
if (size >= 8 && *(uint64_t *)data == 0xABCDEFAA8F1324AA) { abort(); }
|
||||
if (size < kPngHeaderSize) { return 0; }
|
||||
|
||||
#ifdef TEST_ASAN
|
||||
func1();
|
||||
#endif
|
||||
|
||||
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;
|
||||
}
|
483
fuzzers/frida_executable_libpng/src/fuzzer.rs
Normal file
483
fuzzers/frida_executable_libpng/src/fuzzer.rs
Normal file
@ -0,0 +1,483 @@
|
||||
//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts
|
||||
//! The example harness is built for libpng.
|
||||
use mimalloc::MiMalloc;
|
||||
#[global_allocator]
|
||||
static GLOBAL: MiMalloc = MiMalloc;
|
||||
|
||||
use std::{path::PathBuf, ptr::null};
|
||||
|
||||
use frida_gum::Gum;
|
||||
use libafl::{
|
||||
bolts::{
|
||||
cli::{parse_args, FuzzerOptions},
|
||||
current_nanos,
|
||||
launcher::Launcher,
|
||||
rands::StdRand,
|
||||
shmem::{ShMemProvider, StdShMemProvider},
|
||||
tuples::{tuple_list, Merge},
|
||||
AsSlice,
|
||||
},
|
||||
corpus::{CachedOnDiskCorpus, Corpus, OnDiskCorpus},
|
||||
events::{llmp::LlmpRestartingEventManager, EventConfig},
|
||||
executors::{inprocess::InProcessExecutor, ExitKind, ShadowExecutor},
|
||||
feedback_or, feedback_or_fast,
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||
fuzzer::{Fuzzer, StdFuzzer},
|
||||
inputs::{BytesInput, HasTargetBytes},
|
||||
monitors::MultiMonitor,
|
||||
mutators::{
|
||||
scheduled::{havoc_mutations, tokens_mutations, StdScheduledMutator},
|
||||
token_mutations::{I2SRandReplace, Tokens},
|
||||
},
|
||||
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
||||
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
|
||||
stages::{ShadowTracingStage, StdMutationalStage},
|
||||
state::{HasCorpus, HasMetadata, StdState},
|
||||
Error,
|
||||
};
|
||||
#[cfg(unix)]
|
||||
use libafl::{feedback_and_fast, feedbacks::ConstFeedback};
|
||||
#[cfg(unix)]
|
||||
use libafl_frida::asan::{
|
||||
asan_rt::AsanRuntime,
|
||||
errors::{AsanErrorsFeedback, AsanErrorsObserver, ASAN_ERRORS},
|
||||
};
|
||||
use libafl_frida::{
|
||||
cmplog_rt::CmpLogRuntime,
|
||||
coverage_rt::{CoverageRuntime, MAP_SIZE},
|
||||
executor::FridaInProcessExecutor,
|
||||
helper::FridaInstrumentationHelper,
|
||||
};
|
||||
use libafl_targets::cmplog::CmpLogObserver;
|
||||
|
||||
pub unsafe fn lib(main: extern "C" fn(i32, *const *const u8, *const *const u8) -> i32) {
|
||||
color_backtrace::install();
|
||||
|
||||
let options = parse_args();
|
||||
|
||||
let mut frida_harness = |input: &BytesInput| {
|
||||
let target = input.target_bytes();
|
||||
let buf = target.as_slice();
|
||||
let len = buf.len().to_string();
|
||||
|
||||
let argv: [*const u8; 3] = [
|
||||
null(), // dummy value
|
||||
len.as_ptr() as _,
|
||||
buf.as_ptr() as _,
|
||||
];
|
||||
|
||||
let env: [*const u8; 2] = [
|
||||
null(), // dummy value
|
||||
null(), // dummy value
|
||||
];
|
||||
|
||||
main(3, argv.as_ptr(), env.as_ptr());
|
||||
ExitKind::Ok
|
||||
};
|
||||
|
||||
unsafe {
|
||||
match fuzz(&options, &mut frida_harness) {
|
||||
Ok(()) | Err(Error::ShuttingDown) => println!("\nFinished fuzzing. Good bye."),
|
||||
Err(e) => panic!("Error during fuzzing: {e:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The actual fuzzer
|
||||
#[allow(clippy::too_many_lines, clippy::too_many_arguments)]
|
||||
unsafe fn fuzz(
|
||||
options: &FuzzerOptions,
|
||||
mut frida_harness: &dyn Fn(&BytesInput) -> ExitKind,
|
||||
) -> Result<(), Error> {
|
||||
// 'While the stats are state, they are usually used in the broker - which is likely never restarted
|
||||
let monitor = MultiMonitor::new(|s| println!("{s}"));
|
||||
|
||||
let shmem_provider = StdShMemProvider::new()?;
|
||||
|
||||
let mut run_client = |state: Option<_>, mgr: LlmpRestartingEventManager<_, _>, core_id| {
|
||||
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
|
||||
|
||||
// println!("{:?}", mgr.mgr_id());
|
||||
|
||||
if options.asan && options.asan_cores.contains(core_id) {
|
||||
(|state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _>, _core_id| {
|
||||
let gum = Gum::obtain();
|
||||
|
||||
let coverage = CoverageRuntime::new();
|
||||
#[cfg(unix)]
|
||||
let asan = AsanRuntime::new(options.clone());
|
||||
|
||||
#[cfg(unix)]
|
||||
let mut frida_helper =
|
||||
FridaInstrumentationHelper::new(&gum, options, tuple_list!(coverage, asan));
|
||||
#[cfg(windows)]
|
||||
let mut frida_helper =
|
||||
FridaInstrumentationHelper::new(&gum, &options, tuple_list!(coverage));
|
||||
|
||||
// Create an observation channel using the coverage map
|
||||
let edges_observer = HitcountsMapObserver::new(StdMapObserver::from_mut_ptr(
|
||||
"edges",
|
||||
frida_helper.map_mut_ptr().unwrap(),
|
||||
MAP_SIZE,
|
||||
));
|
||||
|
||||
// Create an observation channel to keep track of the execution time
|
||||
let time_observer = TimeObserver::new("time");
|
||||
|
||||
// Feedback to rate the interestingness of an input
|
||||
// This one is composed by two Feedbacks in OR
|
||||
let mut feedback = feedback_or!(
|
||||
// New maximization map feedback linked to the edges observer and the feedback state
|
||||
MaxMapFeedback::tracking(&edges_observer, true, false),
|
||||
// Time feedback, this one does not need a feedback state
|
||||
TimeFeedback::with_observer(&time_observer)
|
||||
);
|
||||
|
||||
// Feedbacks to recognize an input as solution
|
||||
#[cfg(unix)]
|
||||
let mut objective = feedback_or_fast!(
|
||||
CrashFeedback::new(),
|
||||
TimeoutFeedback::new(),
|
||||
// true enables the AsanErrorFeedback
|
||||
feedback_and_fast!(ConstFeedback::from(true), AsanErrorsFeedback::new())
|
||||
);
|
||||
#[cfg(windows)]
|
||||
let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());
|
||||
|
||||
// If not restarting, create a State from scratch
|
||||
let mut state = state.unwrap_or_else(|| {
|
||||
StdState::new(
|
||||
// RNG
|
||||
StdRand::with_seed(current_nanos()),
|
||||
// Corpus that will be evolved, we keep it in memory for performance
|
||||
CachedOnDiskCorpus::no_meta(PathBuf::from("./corpus_discovered"), 64)
|
||||
.unwrap(),
|
||||
// Corpus in which we store solutions (crashes in this example),
|
||||
// on disk so the user can get them after stopping the fuzzer
|
||||
OnDiskCorpus::new(options.output.clone()).unwrap(),
|
||||
&mut feedback,
|
||||
&mut objective,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
println!("We're a client, let's fuzz :)");
|
||||
|
||||
// Create a PNG dictionary if not existing
|
||||
if state.metadata_map().get::<Tokens>().is_none() {
|
||||
state.add_metadata(Tokens::from([
|
||||
vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header
|
||||
b"IHDR".to_vec(),
|
||||
b"IDAT".to_vec(),
|
||||
b"PLTE".to_vec(),
|
||||
b"IEND".to_vec(),
|
||||
]));
|
||||
}
|
||||
|
||||
// Setup a basic mutator with a mutational stage
|
||||
let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
|
||||
|
||||
// A minimization+queue policy to get testcasess from the corpus
|
||||
let scheduler = IndexesLenTimeMinimizerScheduler::new(QueueScheduler::new());
|
||||
|
||||
// A fuzzer with feedbacks and a corpus scheduler
|
||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||
|
||||
#[cfg(unix)]
|
||||
let observers = tuple_list!(
|
||||
edges_observer,
|
||||
time_observer,
|
||||
AsanErrorsObserver::new(&ASAN_ERRORS)
|
||||
);
|
||||
#[cfg(windows)]
|
||||
let observers = tuple_list!(edges_observer, time_observer);
|
||||
|
||||
// Create the executor for an in-process function with just one observer for edge coverage
|
||||
let mut executor = FridaInProcessExecutor::new(
|
||||
&gum,
|
||||
InProcessExecutor::new(
|
||||
&mut frida_harness,
|
||||
observers,
|
||||
&mut fuzzer,
|
||||
&mut state,
|
||||
&mut mgr,
|
||||
)?,
|
||||
&mut frida_helper,
|
||||
);
|
||||
|
||||
// In case the corpus is empty (on first run), reset
|
||||
if state.must_load_initial_inputs() {
|
||||
state
|
||||
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &options.input)
|
||||
.unwrap_or_else(|_| {
|
||||
panic!("Failed to load initial corpus at {:?}", &options.input)
|
||||
});
|
||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||
}
|
||||
|
||||
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
||||
|
||||
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
|
||||
|
||||
Ok(())
|
||||
})(state, mgr, core_id)
|
||||
} else if options.cmplog && options.cmplog_cores.contains(core_id) {
|
||||
(|state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _>, _core_id| {
|
||||
let gum = Gum::obtain();
|
||||
|
||||
let coverage = CoverageRuntime::new();
|
||||
let cmplog = CmpLogRuntime::new();
|
||||
|
||||
let mut frida_helper =
|
||||
FridaInstrumentationHelper::new(&gum, options, tuple_list!(coverage, cmplog));
|
||||
|
||||
// Create an observation channel using the coverage map
|
||||
let edges_observer = HitcountsMapObserver::new(StdMapObserver::from_mut_ptr(
|
||||
"edges",
|
||||
frida_helper.map_mut_ptr().unwrap(),
|
||||
MAP_SIZE,
|
||||
));
|
||||
|
||||
// Create an observation channel to keep track of the execution time
|
||||
let time_observer = TimeObserver::new("time");
|
||||
|
||||
// Feedback to rate the interestingness of an input
|
||||
// This one is composed by two Feedbacks in OR
|
||||
let mut feedback = feedback_or!(
|
||||
// New maximization map feedback linked to the edges observer and the feedback state
|
||||
MaxMapFeedback::tracking(&edges_observer, true, false),
|
||||
// Time feedback, this one does not need a feedback state
|
||||
TimeFeedback::with_observer(&time_observer)
|
||||
);
|
||||
|
||||
#[cfg(unix)]
|
||||
let mut objective = feedback_or_fast!(
|
||||
CrashFeedback::new(),
|
||||
TimeoutFeedback::new(),
|
||||
feedback_and_fast!(ConstFeedback::from(false), AsanErrorsFeedback::new())
|
||||
);
|
||||
#[cfg(windows)]
|
||||
let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());
|
||||
|
||||
// If not restarting, create a State from scratch
|
||||
let mut state = state.unwrap_or_else(|| {
|
||||
StdState::new(
|
||||
// RNG
|
||||
StdRand::with_seed(current_nanos()),
|
||||
// Corpus that will be evolved, we keep it in memory for performance
|
||||
CachedOnDiskCorpus::no_meta(PathBuf::from("./corpus_discovered"), 64)
|
||||
.unwrap(),
|
||||
// Corpus in which we store solutions (crashes in this example),
|
||||
// on disk so the user can get them after stopping the fuzzer
|
||||
OnDiskCorpus::new(options.output.clone()).unwrap(),
|
||||
&mut feedback,
|
||||
&mut objective,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
println!("We're a client, let's fuzz :)");
|
||||
|
||||
// Create a PNG dictionary if not existing
|
||||
if state.metadata_map().get::<Tokens>().is_none() {
|
||||
state.add_metadata(Tokens::from([
|
||||
vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header
|
||||
b"IHDR".to_vec(),
|
||||
b"IDAT".to_vec(),
|
||||
b"PLTE".to_vec(),
|
||||
b"IEND".to_vec(),
|
||||
]));
|
||||
}
|
||||
|
||||
// Setup a basic mutator with a mutational stage
|
||||
let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
|
||||
|
||||
// A minimization+queue policy to get testcasess from the corpus
|
||||
let scheduler = IndexesLenTimeMinimizerScheduler::new(QueueScheduler::new());
|
||||
|
||||
// A fuzzer with feedbacks and a corpus scheduler
|
||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||
|
||||
#[cfg(unix)]
|
||||
let observers = tuple_list!(
|
||||
edges_observer,
|
||||
time_observer,
|
||||
AsanErrorsObserver::new(&ASAN_ERRORS)
|
||||
);
|
||||
#[cfg(windows)]
|
||||
let observers = tuple_list!(edges_observer, time_observer,);
|
||||
|
||||
// Create the executor for an in-process function with just one observer for edge coverage
|
||||
let mut executor = FridaInProcessExecutor::new(
|
||||
&gum,
|
||||
InProcessExecutor::new(
|
||||
&mut frida_harness,
|
||||
observers,
|
||||
&mut fuzzer,
|
||||
&mut state,
|
||||
&mut mgr,
|
||||
)?,
|
||||
&mut frida_helper,
|
||||
);
|
||||
|
||||
// In case the corpus is empty (on first run), reset
|
||||
if state.must_load_initial_inputs() {
|
||||
state
|
||||
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &options.input)
|
||||
.unwrap_or_else(|_| {
|
||||
panic!("Failed to load initial corpus at {:?}", &options.input)
|
||||
});
|
||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||
}
|
||||
|
||||
// Create an observation channel using cmplog map
|
||||
let cmplog_observer = CmpLogObserver::new("cmplog", true);
|
||||
|
||||
let mut executor = ShadowExecutor::new(executor, tuple_list!(cmplog_observer));
|
||||
|
||||
let tracing = ShadowTracingStage::new(&mut executor);
|
||||
|
||||
// Setup a randomic Input2State stage
|
||||
let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(
|
||||
I2SRandReplace::new()
|
||||
)));
|
||||
|
||||
// Setup a basic mutator
|
||||
let mutational = StdMutationalStage::new(mutator);
|
||||
|
||||
// The order of the stages matter!
|
||||
let mut stages = tuple_list!(tracing, i2s, mutational);
|
||||
|
||||
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
|
||||
|
||||
Ok(())
|
||||
})(state, mgr, core_id)
|
||||
} else {
|
||||
(|state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _>, _core_id| {
|
||||
let gum = Gum::obtain();
|
||||
|
||||
let coverage = CoverageRuntime::new();
|
||||
|
||||
let mut frida_helper =
|
||||
FridaInstrumentationHelper::new(&gum, options, tuple_list!(coverage));
|
||||
|
||||
// Create an observation channel using the coverage map
|
||||
let edges_observer = HitcountsMapObserver::new(StdMapObserver::from_mut_ptr(
|
||||
"edges",
|
||||
frida_helper.map_mut_ptr().unwrap(),
|
||||
MAP_SIZE,
|
||||
));
|
||||
|
||||
// Create an observation channel to keep track of the execution time
|
||||
let time_observer = TimeObserver::new("time");
|
||||
|
||||
// Feedback to rate the interestingness of an input
|
||||
// This one is composed by two Feedbacks in OR
|
||||
let mut feedback = feedback_or!(
|
||||
// New maximization map feedback linked to the edges observer and the feedback state
|
||||
MaxMapFeedback::tracking(&edges_observer, true, false),
|
||||
// Time feedback, this one does not need a feedback state
|
||||
TimeFeedback::with_observer(&time_observer)
|
||||
);
|
||||
|
||||
#[cfg(unix)]
|
||||
let mut objective = feedback_or_fast!(
|
||||
CrashFeedback::new(),
|
||||
TimeoutFeedback::new(),
|
||||
feedback_and_fast!(ConstFeedback::from(false), AsanErrorsFeedback::new())
|
||||
);
|
||||
#[cfg(windows)]
|
||||
let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());
|
||||
|
||||
// If not restarting, create a State from scratch
|
||||
let mut state = state.unwrap_or_else(|| {
|
||||
StdState::new(
|
||||
// RNG
|
||||
StdRand::with_seed(current_nanos()),
|
||||
// Corpus that will be evolved, we keep it in memory for performance
|
||||
CachedOnDiskCorpus::no_meta(PathBuf::from("./corpus_discovered"), 64)
|
||||
.unwrap(),
|
||||
// Corpus in which we store solutions (crashes in this example),
|
||||
// on disk so the user can get them after stopping the fuzzer
|
||||
OnDiskCorpus::new(options.output.clone()).unwrap(),
|
||||
&mut feedback,
|
||||
&mut objective,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
println!("We're a client, let's fuzz :)");
|
||||
|
||||
// Create a PNG dictionary if not existing
|
||||
if state.metadata_map().get::<Tokens>().is_none() {
|
||||
state.add_metadata(Tokens::from([
|
||||
vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header
|
||||
b"IHDR".to_vec(),
|
||||
b"IDAT".to_vec(),
|
||||
b"PLTE".to_vec(),
|
||||
b"IEND".to_vec(),
|
||||
]));
|
||||
}
|
||||
|
||||
// Setup a basic mutator with a mutational stage
|
||||
let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
|
||||
|
||||
// A minimization+queue policy to get testcasess from the corpus
|
||||
let scheduler = IndexesLenTimeMinimizerScheduler::new(QueueScheduler::new());
|
||||
|
||||
// A fuzzer with feedbacks and a corpus scheduler
|
||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||
|
||||
#[cfg(unix)]
|
||||
let observers = tuple_list!(
|
||||
edges_observer,
|
||||
time_observer,
|
||||
AsanErrorsObserver::new(&ASAN_ERRORS)
|
||||
);
|
||||
#[cfg(windows)]
|
||||
let observers = tuple_list!(edges_observer, time_observer,);
|
||||
|
||||
// Create the executor for an in-process function with just one observer for edge coverage
|
||||
let mut executor = FridaInProcessExecutor::new(
|
||||
&gum,
|
||||
InProcessExecutor::new(
|
||||
&mut frida_harness,
|
||||
observers,
|
||||
&mut fuzzer,
|
||||
&mut state,
|
||||
&mut mgr,
|
||||
)?,
|
||||
&mut frida_helper,
|
||||
);
|
||||
|
||||
// In case the corpus is empty (on first run), reset
|
||||
if state.must_load_initial_inputs() {
|
||||
state
|
||||
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &options.input)
|
||||
.unwrap_or_else(|_| {
|
||||
panic!("Failed to load initial corpus at {:?}", &options.input)
|
||||
});
|
||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||
}
|
||||
|
||||
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
||||
|
||||
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
|
||||
|
||||
Ok(())
|
||||
})(state, mgr, core_id)
|
||||
}
|
||||
};
|
||||
|
||||
Launcher::builder()
|
||||
.configuration(EventConfig::AlwaysUnique)
|
||||
.shmem_provider(shmem_provider)
|
||||
.monitor(monitor)
|
||||
.run_client(&mut run_client)
|
||||
.cores(&options.cores)
|
||||
.broker_port(options.broker_port)
|
||||
.stdout_file(Some(&options.stdout))
|
||||
.remote_broker_addr(options.remote_broker_addr)
|
||||
.build()
|
||||
.launch()
|
||||
}
|
58
fuzzers/frida_executable_libpng/src/lib.rs
Normal file
58
fuzzers/frida_executable_libpng/src/lib.rs
Normal file
@ -0,0 +1,58 @@
|
||||
use std::mem::transmute;
|
||||
|
||||
use libc::{c_void, dlsym, RTLD_NEXT};
|
||||
|
||||
mod fuzzer;
|
||||
|
||||
type LibcStartMainFunc = fn(
|
||||
unsafe extern "C" fn(i32, *const *const u8, *const *const u8) -> i32,
|
||||
i32,
|
||||
*const *const char,
|
||||
extern "C" fn(i32, *const *const u8, *const *const u8) -> i32,
|
||||
extern "C" fn(),
|
||||
extern "C" fn(),
|
||||
*mut c_void,
|
||||
) -> i32;
|
||||
|
||||
type MainFunc = extern "C" fn(i32, *const *const u8, *const *const u8) -> i32;
|
||||
|
||||
extern "C" fn _dummy_main(_argc: i32, _argv: *const *const u8, _env: *const *const u8) -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
static mut ORIG_MAIN: MainFunc = _dummy_main;
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn main_hook(
|
||||
_argc: i32,
|
||||
_argv: *const *const u8,
|
||||
_env: *const *const u8,
|
||||
) -> i32 {
|
||||
fuzzer::lib(ORIG_MAIN);
|
||||
0
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn __libc_start_main(
|
||||
main: extern "C" fn(i32, *const *const u8, *const *const u8) -> i32,
|
||||
argc: i32,
|
||||
argv: *const *const char,
|
||||
init: extern "C" fn(i32, *const *const u8, *const *const u8) -> i32,
|
||||
fini: extern "C" fn(),
|
||||
rtld_fini: extern "C" fn(),
|
||||
stack_end: *mut c_void,
|
||||
) -> i32 {
|
||||
unsafe {
|
||||
ORIG_MAIN = main;
|
||||
|
||||
let orig_libc_start_main_addr: *mut c_void =
|
||||
dlsym(RTLD_NEXT, "__libc_start_main\0".as_ptr() as *const i8);
|
||||
|
||||
let orig_libc_start_main: LibcStartMainFunc = transmute(orig_libc_start_main_addr);
|
||||
|
||||
let exit_code =
|
||||
orig_libc_start_main(main_hook, argc, argv, init, fini, rtld_fini, stack_end);
|
||||
|
||||
return exit_code;
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "frida_gdiplus"
|
||||
version = "0.7.0"
|
||||
version = "0.10.0"
|
||||
authors = ["Richard Johnson <richinseattle@gmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
@ -15,27 +15,19 @@ opt-level = 3
|
||||
debug = true
|
||||
|
||||
[build-dependencies]
|
||||
cc = { version = "1.0", features = ["parallel"] }
|
||||
cc = { version = "1.0.42", features = ["parallel"] }
|
||||
num_cpus = "1.0"
|
||||
which = "4.1"
|
||||
xz = "0.1.0"
|
||||
xz2 = "0.1.6"
|
||||
flate2 = "1.0.22"
|
||||
tar = "0.4.37"
|
||||
reqwest = { version = "0.11.4", features = ["blocking"] }
|
||||
|
||||
[dependencies]
|
||||
libafl = { path = "../../libafl/", features = [ "std", "llmp_compression", "llmp_bind_public", "frida_cli" ] } #, "llmp_small_maps", "llmp_debug"]}
|
||||
capstone = "0.11.0"
|
||||
frida-gum = { version = "0.8.1", features = [ "auto-download", "event-sink", "invocation-listener"] }
|
||||
libafl_frida = { path = "../../libafl_frida", features = ["cmplog"] }
|
||||
libafl_targets = { path = "../../libafl_targets", features = ["sancov_cmplog"] }
|
||||
libc = "0.2"
|
||||
libloading = "0.7"
|
||||
num-traits = "0.2"
|
||||
rangemap = "0.1"
|
||||
clap = { version = "4.0", features = ["derive"] }
|
||||
serde = "1.0"
|
||||
mimalloc = { version = "*", default-features = false }
|
||||
|
||||
backtrace = "0.3"
|
||||
color-backtrace = "0.5"
|
||||
|
66
fuzzers/frida_gdiplus/Makefile.toml
Normal file
66
fuzzers/frida_gdiplus/Makefile.toml
Normal file
@ -0,0 +1,66 @@
|
||||
# Variables
|
||||
[env]
|
||||
CARGO_TARGET_DIR = { value = "target", condition = { env_not_set = ["CARGO_TARGET_DIR"] } }
|
||||
FUZZER_NAME={ source = "${CARGO_MAKE_RUST_TARGET_OS}", default_value = "frida_gdiplus", mapping = {"linux" = "frida_gdiplus", "macos" = "frida_gdiplus", "windows" = "frida_gdiplus.exe"} }
|
||||
|
||||
|
||||
[tasks.unsupported]
|
||||
script_runner="@shell"
|
||||
script='''
|
||||
echo "Cargo-make not integrated yet on this"
|
||||
'''
|
||||
|
||||
# Harness
|
||||
[tasks.harness]
|
||||
linux_alias = "unsupported"
|
||||
mac_alias = "unsupported"
|
||||
windows_alias = "harness_windows"
|
||||
|
||||
|
||||
[tasks.harness_windows]
|
||||
script_runner="@shell"
|
||||
script='''
|
||||
cl.exe /LD harness.cc /link /dll gdiplus.lib ole32.lib
|
||||
'''
|
||||
|
||||
# Fuzzer
|
||||
[tasks.fuzzer]
|
||||
linux_alias = "unsupported"
|
||||
mac_alias = "unsupported"
|
||||
windows_alias = "fuzzer_windows"
|
||||
|
||||
[tasks.fuzzer_windows]
|
||||
script_runner="@shell"
|
||||
script='''
|
||||
cargo build --release
|
||||
cp ./target/release/${FUZZER_NAME} .
|
||||
'''
|
||||
|
||||
# Run the fuzzer
|
||||
[tasks.run]
|
||||
linux_alias = "unsupported"
|
||||
mac_alias = "unsupported"
|
||||
windows_alias = "run_windows"
|
||||
|
||||
[tasks.run_windows]
|
||||
script_runner = "@shell"
|
||||
script='''
|
||||
./${FUZZER_NAME} -H harness.dll -i corpus -o output --libs-to-instrument gdi32.dll --libs-to-instrument gdi32full.dll --libs-to-instrument gdiplus.dll --libs-to-instrument WindowsCodecs.dll --disable-excludes
|
||||
'''
|
||||
dependencies = [ "fuzzer", "harness" ]
|
||||
|
||||
# Test
|
||||
[tasks.test]
|
||||
linux_alias = "unsupported"
|
||||
mac_alias = "unsupported"
|
||||
windows_alias = "test_windows"
|
||||
|
||||
[tasks.test_windows]
|
||||
script_runner = "@shell"
|
||||
script='''
|
||||
start "" "frida_gdiplus.exe" -H harness.dll -i corpus -o output --libs-to-instrument gdi32.dll --libs-to-instrument gdi32full.dll --libs-to-instrument gdiplus.dll --libs-to-instrument WindowsCodecs.dll --disable-excludes
|
||||
#ping is for timeout
|
||||
ping -n 10 127.0.0.1>NUL && taskkill /im frida_gdiplus.exe /F
|
||||
>nul 2>nul dir /a-d "corpus_discovered\*" && (echo Files exist) || (exit /b 1337)
|
||||
'''
|
||||
dependencies = [ "fuzzer", "harness" ]
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user