mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-24 14:43:22 +00:00
Compare commits
152 Commits
unusual_va
...
3.14c
Author | SHA1 | Date | |
---|---|---|---|
9321a24e68 | |||
1e23a8d678 | |||
4fa56f0696 | |||
766217e79e | |||
c55f7af657 | |||
939729e504 | |||
18fd97fc5f | |||
a705b1548f | |||
b13b8c7e55 | |||
212fe5b6f5 | |||
2c19750d08 | |||
9e8afcc615 | |||
6f03749c73 | |||
6e704e8a10 | |||
b5422c1a52 | |||
8d873357a3 | |||
8a2b140f40 | |||
c279750689 | |||
7fb2d90c49 | |||
ed2d474369 | |||
2d8050c397 | |||
50fcd60bc7 | |||
21215ac21c | |||
cd683ed253 | |||
3dd39fec90 | |||
b7cd6db08f | |||
4560ecc647 | |||
6df597213a | |||
6e818ed078 | |||
d346d07b63 | |||
bb627c7e58 | |||
9ec63d3f17 | |||
4fe572b80f | |||
3a3ef7b6b4 | |||
94999782f1 | |||
7cec158b0e | |||
b6a9e54c60 | |||
ec4ad161fc | |||
d16d8dbb85 | |||
fc3d7e821c | |||
37fff16a36 | |||
4ef12d7215 | |||
a09ab99534 | |||
ac565bfe51 | |||
6d878a375d | |||
f4b975d6ad | |||
161d763334 | |||
49df0af628 | |||
70312789fd | |||
8dbe87bdf6 | |||
458eb0813a | |||
f1bcd378a2 | |||
405382cbdd | |||
43db577dbb | |||
0662c5580b | |||
4a02118fda | |||
2a433f90c4 | |||
bf9a155418 | |||
6ec295db4e | |||
f7fb4495c4 | |||
dcf450ecba | |||
886e2ba770 | |||
7283205fe3 | |||
cca11b08b1 | |||
a8529de592 | |||
a6cf9bb336 | |||
5d5624b930 | |||
7da632065f | |||
000b16af16 | |||
046a9520f3 | |||
cda62bab08 | |||
7038e56da3 | |||
6a3877dcd3 | |||
c88b98d1c9 | |||
89ddd9998c | |||
28e6b96276 | |||
61e1c74d52 | |||
f348a35ec6 | |||
4057134d3c | |||
906bede108 | |||
1fcb52957e | |||
ec781af2c7 | |||
93b9832acd | |||
ae50a50677 | |||
d64cde8370 | |||
c6b77d2d05 | |||
600058aeab | |||
ff4d45eed2 | |||
56851fa499 | |||
cbac22d82b | |||
ba9323f14c | |||
98dc0d2649 | |||
58747f9f4f | |||
35153e9b49 | |||
c46f8c1f70 | |||
a6c0b5f766 | |||
7bcd4e2901 | |||
f3362007ed | |||
0c3feba3f6 | |||
ef5fd33120 | |||
74fcb365e9 | |||
ade8cdca55 | |||
35c23be973 | |||
2516324d3e | |||
8c1b0aba5f | |||
444e15cff0 | |||
dfff952f53 | |||
d2e256e73a | |||
581593ccab | |||
0978c08f4b | |||
b9f260452e | |||
8dd30947cb | |||
63504f7b7e | |||
a7340a1ac6 | |||
b8092c6227 | |||
706c2ac8e0 | |||
a4cb2414d5 | |||
f9a1e87635 | |||
e0aa411647 | |||
b9d2a87f03 | |||
3b9f4441e5 | |||
d57f0e3a1c | |||
48c878a76d | |||
c88f650bf8 | |||
fff8c5e0a8 | |||
c7b9171c10 | |||
cd95dfe1e7 | |||
d64dd7a952 | |||
63ee9df54f | |||
2d2d9b0b77 | |||
2449866f21 | |||
76c0940cee | |||
c69edc2b3c | |||
4bf08566ca | |||
c78a9e4b37 | |||
92fcef4520 | |||
a5ff9f1beb | |||
436f997d26 | |||
8f04269e17 | |||
280814c3a2 | |||
ddd9154e78 | |||
1474e2db23 | |||
2988dd206c | |||
fd07853550 | |||
0d50ee4947 | |||
43eca8203a | |||
36671ce799 | |||
f3b1c5c382 | |||
0897377b13 | |||
70a2077107 | |||
c5d899e0f5 | |||
a321d4102b |
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
@ -1,6 +1,7 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
# Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
github: AFLplusplus
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: AFLplusplusEU
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
|
11
GNUmakefile
11
GNUmakefile
@ -306,6 +306,7 @@ endif
|
||||
|
||||
.PHONY: all
|
||||
all: test_x86 test_shm test_python ready $(PROGS) afl-as llvm gcc_plugin test_build all_done
|
||||
-$(MAKE) -C utils/aflpp_driver
|
||||
|
||||
.PHONY: llvm
|
||||
llvm:
|
||||
@ -436,8 +437,8 @@ afl-showmap: src/afl-showmap.c src/afl-common.o src/afl-sharedmem.o src/afl-fork
|
||||
afl-tmin: src/afl-tmin.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(LDFLAGS)
|
||||
|
||||
afl-analyze: src/afl-analyze.c src/afl-common.o src/afl-sharedmem.o src/afl-performance.o $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-performance.o -o $@ $(LDFLAGS)
|
||||
afl-analyze: src/afl-analyze.c src/afl-common.o src/afl-sharedmem.o src/afl-performance.o src/afl-forkserver.o $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-performance.o src/afl-forkserver.o -o $@ $(LDFLAGS)
|
||||
|
||||
afl-gotcpu: src/afl-gotcpu.c src/afl-common.o $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o -o $@ $(LDFLAGS)
|
||||
@ -572,9 +573,9 @@ clean:
|
||||
$(MAKE) -C qemu_mode/unsigaction clean
|
||||
$(MAKE) -C qemu_mode/libcompcov clean
|
||||
$(MAKE) -C qemu_mode/libqasan clean
|
||||
$(MAKE) -C frida_mode clean
|
||||
-$(MAKE) -C frida_mode clean
|
||||
ifeq "$(IN_REPO)" "1"
|
||||
test -e qemu_mode/qemuafl/Makefile && $(MAKE) -C qemu_mode/qemuafl clean || true
|
||||
-test -e qemu_mode/qemuafl/Makefile && $(MAKE) -C qemu_mode/qemuafl clean || true
|
||||
test -e unicorn_mode/unicornafl/Makefile && $(MAKE) -C unicorn_mode/unicornafl clean || true
|
||||
else
|
||||
rm -rf qemu_mode/qemuafl
|
||||
@ -597,7 +598,6 @@ distrib: all
|
||||
-$(MAKE) -f GNUmakefile.gcc_plugin
|
||||
$(MAKE) -C utils/libdislocator
|
||||
$(MAKE) -C utils/libtokencap
|
||||
-$(MAKE) -C utils/aflpp_driver
|
||||
$(MAKE) -C utils/afl_network_proxy
|
||||
$(MAKE) -C utils/socket_fuzzing
|
||||
$(MAKE) -C utils/argv_fuzzing
|
||||
@ -622,7 +622,6 @@ source-only: all
|
||||
-$(MAKE) -f GNUmakefile.gcc_plugin
|
||||
$(MAKE) -C utils/libdislocator
|
||||
$(MAKE) -C utils/libtokencap
|
||||
-$(MAKE) -C utils/aflpp_driver
|
||||
|
||||
%.8: %
|
||||
@echo .TH $* 8 $(BUILD_DATE) "afl++" > $@
|
||||
|
@ -45,7 +45,7 @@ endif
|
||||
LLVMVER = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/git//' | sed 's/svn//' )
|
||||
LLVM_MAJOR = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/\..*//' )
|
||||
LLVM_MINOR = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/.*\.//' | sed 's/git//' | sed 's/svn//' | sed 's/ .*//' )
|
||||
LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^[0-5]\.' && echo 1 || echo 0 )
|
||||
LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^[0-2]\.|^3.[0-7]\.' && echo 1 || echo 0 )
|
||||
LLVM_TOO_NEW = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[3-9]' && echo 1 || echo 0 )
|
||||
LLVM_NEW_API = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[0-9]' && echo 1 || echo 0 )
|
||||
LLVM_10_OK = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[1-9]|^10\.[1-9]|^10\.0.[1-9]' && echo 1 || echo 0 )
|
||||
@ -57,11 +57,11 @@ LLVM_APPLE_XCODE = $(shell clang -v 2>&1 | grep -q Apple && echo 1 || echo 0)
|
||||
LLVM_LTO = 0
|
||||
|
||||
ifeq "$(LLVMVER)" ""
|
||||
$(warning [!] llvm_mode needs llvm-config, which was not found)
|
||||
$(warning [!] llvm_mode needs llvm-config, which was not found. Set LLVM_CONFIG to its path and retry.)
|
||||
endif
|
||||
|
||||
ifeq "$(LLVM_UNSUPPORTED)" "1"
|
||||
$(error llvm_mode only supports llvm from version 6.0 onwards)
|
||||
$(error llvm_mode only supports llvm from version 3.8 onwards)
|
||||
endif
|
||||
|
||||
ifeq "$(LLVM_TOO_NEW)" "1"
|
||||
@ -306,7 +306,7 @@ ifeq "$(TEST_MMAP)" "1"
|
||||
endif
|
||||
|
||||
PROGS_ALWAYS = ./afl-cc ./afl-compiler-rt.o ./afl-compiler-rt-32.o ./afl-compiler-rt-64.o
|
||||
PROGS = $(PROGS_ALWAYS) ./afl-llvm-pass.so ./SanitizerCoveragePCGUARD.so ./split-compares-pass.so ./split-switches-pass.so ./cmplog-routines-pass.so ./cmplog-instructions-pass.so ./afl-llvm-dict2file.so ./compare-transform-pass.so ./afl-ld-lto ./afl-llvm-lto-instrumentlist.so ./afl-llvm-lto-instrumentation.so ./SanitizerCoverageLTO.so
|
||||
PROGS = $(PROGS_ALWAYS) ./afl-llvm-pass.so ./SanitizerCoveragePCGUARD.so ./split-compares-pass.so ./split-switches-pass.so ./cmplog-routines-pass.so ./cmplog-instructions-pass.so ./cmplog-switches-pass.so ./afl-llvm-dict2file.so ./compare-transform-pass.so ./afl-ld-lto ./afl-llvm-lto-instrumentlist.so ./afl-llvm-lto-instrumentation.so ./SanitizerCoverageLTO.so
|
||||
|
||||
# If prerequisites are not given, warn, do not build anything, and exit with code 0
|
||||
ifeq "$(LLVMVER)" ""
|
||||
@ -433,6 +433,9 @@ endif
|
||||
./cmplog-instructions-pass.so: instrumentation/cmplog-instructions-pass.cc instrumentation/afl-llvm-common.o | test_deps
|
||||
$(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
|
||||
|
||||
./cmplog-switches-pass.so: instrumentation/cmplog-switches-pass.cc instrumentation/afl-llvm-common.o | test_deps
|
||||
$(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
|
||||
|
||||
afl-llvm-dict2file.so: instrumentation/afl-llvm-dict2file.so.cc instrumentation/afl-llvm-common.o | test_deps
|
||||
$(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
|
||||
|
||||
|
280
README.md
280
README.md
@ -2,9 +2,9 @@
|
||||
|
||||
<img align="right" src="https://raw.githubusercontent.com/andreafioraldi/AFLplusplus-website/master/static/logo_256x256.png" alt="AFL++ Logo">
|
||||
|
||||
Release Version: [3.13c](https://github.com/AFLplusplus/AFLplusplus/releases)
|
||||
Release Version: [3.14c](https://github.com/AFLplusplus/AFLplusplus/releases)
|
||||
|
||||
Github Version: 3.14a
|
||||
Github Version: 3.15a
|
||||
|
||||
Repository: [https://github.com/AFLplusplus/AFLplusplus](https://github.com/AFLplusplus/AFLplusplus)
|
||||
|
||||
@ -25,12 +25,22 @@
|
||||
For comparisons use the fuzzbench `aflplusplus` setup, or use `afl-clang-fast`
|
||||
with `AFL_LLVM_CMPLOG=1`.
|
||||
|
||||
## Major changes in afl++ 3.00 onwards:
|
||||
## Major behaviour changes in afl++ 3.00 onwards:
|
||||
|
||||
With afl++ 3.13-3.20 we introduce frida_mode (-O) to have an alternative for
|
||||
binary-only fuzzing. It is slower than Qemu mode but works on MacOS, Android,
|
||||
iOS etc.
|
||||
|
||||
With afl++ 3.15 we introduced the following changes from previous behaviours:
|
||||
* Also -M main mode does not do deterministic fuzzing by default anymore
|
||||
* afl-cmin and afl-showmap -Ci now descent into subdirectories like
|
||||
afl-fuzz -i does (but note that afl-cmin.bash does not)
|
||||
|
||||
With afl++ 3.14 we introduced the following changes from previous behaviours:
|
||||
* afl-fuzz: deterministic fuzzing it not a default for -M main anymore
|
||||
* afl-cmin/afl-showmap -i now descends into subdirectories (afl-cmin.bash
|
||||
however does not)
|
||||
|
||||
With afl++ 3.10 we introduced the following changes from previous behaviours:
|
||||
* The '+' feature of the '-t' option now means to auto-calculate the timeout
|
||||
with the value given being the maximum timeout. The original meaning of
|
||||
@ -38,7 +48,6 @@ With afl++ 3.10 we introduced the following changes from previous behaviours:
|
||||
|
||||
With afl++ 3.00 we introduced changes that break some previous afl and afl++
|
||||
behaviours and defaults:
|
||||
|
||||
* There are no llvm_mode and gcc_plugin subdirectories anymore and there is
|
||||
only one compiler: afl-cc. All previous compilers now symlink to this one.
|
||||
All instrumentation source code is now in the `instrumentation/` folder.
|
||||
@ -50,8 +59,8 @@ behaviours and defaults:
|
||||
shared libraries, etc. Additionally QEMU 5.1 supports more CPU targets so
|
||||
this is really worth it.
|
||||
* When instrumenting targets, afl-cc will not supersede optimizations anymore
|
||||
if any were given. This allows to fuzz targets as same as they are built
|
||||
for debug or release.
|
||||
if any were given. This allows to fuzz targets build regularly like those
|
||||
for debug or release versions.
|
||||
* afl-fuzz:
|
||||
* if neither -M or -S is specified, `-S default` is assumed, so more
|
||||
fuzzers can easily be added later
|
||||
@ -84,24 +93,24 @@ behaviours and defaults:
|
||||
|
||||
## Important features of afl++
|
||||
|
||||
afl++ supports llvm from 6.0 up to version 12, very fast binary fuzzing with QEMU 5.1
|
||||
afl++ supports llvm from 3.8 up to version 12, very fast binary fuzzing with QEMU 5.1
|
||||
with laf-intel and redqueen, frida mode, unicorn mode, gcc plugin, full *BSD,
|
||||
Mac OS, Solaris and Android support and much, much, much more.
|
||||
|
||||
| Feature/Instrumentation | afl-gcc | llvm | gcc_plugin | frida_mode | qemu_mode |unicorn_mode |
|
||||
| -------------------------|:-------:|:---------:|:----------:|:----------:|:----------------:|:------------:|
|
||||
| Threadsafe counters | | x(3) | | | | |
|
||||
| NeverZero | x86[_64]| x(1) | x | x | x | x |
|
||||
| Persistent Mode | | x | x | x86[_64] | x86[_64]/arm[64] | x |
|
||||
| LAF-Intel / CompCov | | x | | | x86[_64]/arm[64] | x86[_64]/arm |
|
||||
| CmpLog | | x | | x86[_64] | x86[_64]/arm[64] | |
|
||||
| Selective Instrumentation| | x | x | x | x | |
|
||||
| Non-Colliding Coverage | | x(4) | | | (x)(5) | |
|
||||
| Ngram prev_loc Coverage | | x(6) | | | | |
|
||||
| Context Coverage | | x(6) | | | | |
|
||||
| Auto Dictionary | | x(7) | | | | |
|
||||
| Snapshot LKM Support | | (x)(8) | (x)(8) | | (x)(5) | |
|
||||
| Shared Memory Testcases | | x | x | x | x | x |
|
||||
| Feature/Instrumentation | afl-gcc | llvm | gcc_plugin | frida_mode | qemu_mode |unicorn_mode |
|
||||
| -------------------------|:-------:|:---------:|:----------:|:----------------:|:----------------:|:----------------:|
|
||||
| Threadsafe counters | | x(3) | | | | |
|
||||
| NeverZero | x86[_64]| x(1) | x | x | x | x |
|
||||
| Persistent Mode | | x | x | x86[_64]/arm64 | x86[_64]/arm[64] | x |
|
||||
| LAF-Intel / CompCov | | x | | | x86[_64]/arm[64] | x86[_64]/arm[64] |
|
||||
| CmpLog | | x | | x86[_64]/arm64 | x86[_64]/arm[64] | |
|
||||
| Selective Instrumentation| | x | x | x | x | |
|
||||
| Non-Colliding Coverage | | x(4) | | | (x)(5) | |
|
||||
| Ngram prev_loc Coverage | | x(6) | | | | |
|
||||
| Context Coverage | | x(6) | | | | |
|
||||
| Auto Dictionary | | x(7) | | | | |
|
||||
| Snapshot LKM Support | | (x)(8) | (x)(8) | | (x)(5) | |
|
||||
| Shared Memory Testcases | | x | x | x86[_64]/arm64 | x | x |
|
||||
|
||||
1. default for LLVM >= 9.0, env var for older version due an efficiency bug in previous llvm versions
|
||||
2. GCC creates non-performant code, hence it is disabled in gcc_plugin
|
||||
@ -109,7 +118,7 @@ behaviours and defaults:
|
||||
4. with pcguard mode and LTO mode for LLVM 11 and newer
|
||||
5. upcoming, development in the branch
|
||||
6. not compatible with LTO instrumentation and needs at least LLVM v4.1
|
||||
7. automatic in LTO mode with LLVM 11 and newer, an extra pass for all LLVM version that writes to a file to use with afl-fuzz' `-x`
|
||||
7. automatic in LTO mode with LLVM 11 and newer, an extra pass for all LLVM versions that write to a file to use with afl-fuzz' `-x`
|
||||
8. the snapshot LKM is currently unmaintained due to too many kernel changes coming too fast :-(
|
||||
|
||||
Among others, the following features and patches have been integrated:
|
||||
@ -296,7 +305,7 @@ anything below 9 is not recommended.
|
||||
|
|
||||
v
|
||||
+---------------------------------+
|
||||
| clang/clang++ 6.0+ is available | --> use LLVM mode (afl-clang-fast/afl-clang-fast++)
|
||||
| clang/clang++ 3.8+ is available | --> use LLVM mode (afl-clang-fast/afl-clang-fast++)
|
||||
+---------------------------------+ see [instrumentation/README.llvm.md](instrumentation/README.llvm.md)
|
||||
|
|
||||
| if not, or if the target fails with LLVM afl-clang-fast/++
|
||||
@ -383,13 +392,62 @@ afl++ performs "never zero" counting in its bitmap. You can read more about this
|
||||
here:
|
||||
* [instrumentation/README.neverzero.md](instrumentation/README.neverzero.md)
|
||||
|
||||
#### c) Modify the target
|
||||
#### c) Sanitizers
|
||||
|
||||
It is possible to use sanitizers when instrumenting targets for fuzzing,
|
||||
which allows you to find bugs that would not necessarily result in a crash.
|
||||
|
||||
Note that sanitizers have a huge impact on CPU (= less executions per second)
|
||||
and RAM usage. Also you should only run one afl-fuzz instance per sanitizer type.
|
||||
This is enough because a use-after-free bug will be picked up, e.g. by
|
||||
ASAN (address sanitizer) anyway when syncing to other fuzzing instances,
|
||||
so not all fuzzing instances need to be instrumented with ASAN.
|
||||
|
||||
The following sanitizers have built-in support in afl++:
|
||||
* ASAN = Address SANitizer, finds memory corruption vulnerabilities like
|
||||
use-after-free, NULL pointer dereference, buffer overruns, etc.
|
||||
Enabled with `export AFL_USE_ASAN=1` before compiling.
|
||||
* MSAN = Memory SANitizer, finds read access to uninitialized memory, eg.
|
||||
a local variable that is defined and read before it is even set.
|
||||
Enabled with `export AFL_USE_MSAN=1` before compiling.
|
||||
* UBSAN = Undefined Behaviour SANitizer, finds instances where - by the
|
||||
C and C++ standards - undefined behaviour happens, e.g. adding two
|
||||
signed integers together where the result is larger than a signed integer
|
||||
can hold.
|
||||
Enabled with `export AFL_USE_UBSAN=1` before compiling.
|
||||
* CFISAN = Control Flow Integrity SANitizer, finds instances where the
|
||||
control flow is found to be illegal. Originally this was rather to
|
||||
prevent return oriented programming exploit chains from functioning,
|
||||
in fuzzing this is mostly reduced to detecting type confusion
|
||||
vulnerabilities - which is however one of the most important and dangerous
|
||||
C++ memory corruption classes!
|
||||
Enabled with `export AFL_USE_CFISAN=1` before compiling.
|
||||
* LSAN = Leak SANitizer, finds memory leaks in a program. This is not really
|
||||
a security issue, but for developers this can be very valuable.
|
||||
Note that unlike the other sanitizers above this needs
|
||||
`__AFL_LEAK_CHECK();` added to all areas of the target source code where you
|
||||
find a leak check necessary!
|
||||
Enabled with `export AFL_USE_LSAN=1` before compiling.
|
||||
|
||||
It is possible to further modify the behaviour of the sanitizers at run-time
|
||||
by setting `ASAN_OPTIONS=...`, `LSAN_OPTIONS` etc. - the available parameters
|
||||
can be looked up in the sanitizer documentation of llvm/clang.
|
||||
afl-fuzz however requires some specific parameters important for fuzzing to be
|
||||
set. If you want to set your own, it might bail and report what it is missing.
|
||||
|
||||
Note that some sanitizers cannot be used together, e.g. ASAN and MSAN, and
|
||||
others often cannot work together because of target weirdness, e.g. ASAN and
|
||||
CFISAN. You might need to experiment which sanitizers you can combine in a
|
||||
target (which means more instances can be run without a sanitized target,
|
||||
which is more effective).
|
||||
|
||||
#### d) Modify the target
|
||||
|
||||
If the target has features that make fuzzing more difficult, e.g.
|
||||
checksums, HMAC, etc. then modify the source code so that this is
|
||||
removed.
|
||||
This can even be done for operational source code by eliminating
|
||||
these checks within this specific defines:
|
||||
checksums, HMAC, etc. then modify the source code so that checks for these
|
||||
values are removed.
|
||||
This can even be done safely for source code used in operational products
|
||||
by eliminating these checks within these AFL specific blocks:
|
||||
|
||||
```
|
||||
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
||||
@ -401,7 +459,7 @@ these checks within this specific defines:
|
||||
|
||||
All afl++ compilers will set this preprocessor definition automatically.
|
||||
|
||||
#### d) Instrument the target
|
||||
#### e) Instrument the target
|
||||
|
||||
In this step the target source code is compiled so that it can be fuzzed.
|
||||
|
||||
@ -416,8 +474,8 @@ Then build the target. (Usually with `make`)
|
||||
|
||||
1. sometimes configure and build systems are fickle and do not like
|
||||
stderr output (and think this means a test failure) - which is something
|
||||
afl++ likes to do to show statistics. It is recommended to disable them via
|
||||
`export AFL_QUIET=1`.
|
||||
afl++ likes to do to show statistics. It is recommended to disable afl++
|
||||
instrumentation reporting via `export AFL_QUIET=1`.
|
||||
|
||||
2. sometimes configure and build systems error on warnings - these should be
|
||||
disabled (e.g. `--disable-werror` for some configure scripts).
|
||||
@ -458,20 +516,38 @@ non-standard way to set this, otherwise set up the build normally and edit the
|
||||
generated build environment afterwards manually to point it to the right compiler
|
||||
(and/or ranlib and ar).
|
||||
|
||||
#### d) Better instrumentation
|
||||
#### f) Better instrumentation
|
||||
|
||||
If you just fuzz a target program as-is you are wasting a great opportunity for
|
||||
much more fuzzing speed.
|
||||
|
||||
This requires the usage of afl-clang-lto or afl-clang-fast.
|
||||
This variant requires the usage of afl-clang-lto, afl-clang-fast or afl-gcc-fast.
|
||||
|
||||
This is the so-called `persistent mode`, which is much, much faster but
|
||||
It is the so-called `persistent mode`, which is much, much faster but
|
||||
requires that you code a source file that is specifically calling the target
|
||||
functions that you want to fuzz, plus a few specific afl++ functions around
|
||||
it. See [instrumentation/README.persistent_mode.md](instrumentation/README.persistent_mode.md) for details.
|
||||
|
||||
Basically if you do not fuzz a target in persistent mode then you are just
|
||||
doing it for a hobby and not professionally :-)
|
||||
doing it for a hobby and not professionally :-).
|
||||
|
||||
#### g) libfuzzer fuzzer harnesses with LLVMFuzzerTestOneInput()
|
||||
|
||||
libfuzzer `LLVMFuzzerTestOneInput()` harnesses are the defacto standard
|
||||
for fuzzing, and they can be used with afl++ (and honggfuzz) as well!
|
||||
Compiling them is as simple as:
|
||||
```
|
||||
afl-clang-fast++ -fsanitize=fuzzer -o harness harness.cpp targetlib.a
|
||||
```
|
||||
You can even use advanced libfuzzer features like `FuzzedDataProvider`,
|
||||
`LLVMFuzzerMutate()` etc. and they will work!
|
||||
|
||||
The generated binary is fuzzed with afl-fuzz like any other fuzz target.
|
||||
|
||||
Bonus: the target is already optimized for fuzzing due to persistent mode and
|
||||
shared-memory testcases and hence gives you the fastest speed possible.
|
||||
|
||||
For more information see [utils/aflpp_driver/README.md](utils/aflpp_driver/README.md)
|
||||
|
||||
### 2. Preparing the fuzzing campaign
|
||||
|
||||
@ -503,6 +579,8 @@ Note that the INPUTFILE argument that the target program would read from has to
|
||||
If the target reads from stdin instead, just omit the `@@` as this is the
|
||||
default.
|
||||
|
||||
This step is highly recommended!
|
||||
|
||||
#### c) Minimizing all corpus files
|
||||
|
||||
The shorter the input files that still traverse the same path
|
||||
@ -518,7 +596,8 @@ for i in *; do
|
||||
done
|
||||
```
|
||||
|
||||
This step can also be parallelized, e.g. with `parallel`
|
||||
This step can also be parallelized, e.g. with `parallel`.
|
||||
Note that this step is rather optional though.
|
||||
|
||||
#### Done!
|
||||
|
||||
@ -554,6 +633,16 @@ step [2a. Collect inputs](#a-collect-inputs):
|
||||
`afl-fuzz -i input -o output -- bin/target -d @@`
|
||||
Note that the directory specified with -o will be created if it does not exist.
|
||||
|
||||
It can be valuable to run afl-fuzz in a screen or tmux shell so you can log off,
|
||||
or afl-fuzz is not aborted if you are running it in a remote ssh session where
|
||||
the connection fails in between.
|
||||
Only do that though once you have verified that your fuzzing setup works!
|
||||
Simply run it like `screen -dmS afl-main -- afl-fuzz -M main-$HOSTNAME -i ...`
|
||||
and it will start away in a screen session. To enter this session simply type
|
||||
`screen -r afl-main`. You see - it makes sense to name the screen session
|
||||
same as the afl-fuzz -M/-S naming :-)
|
||||
For more information on screen or tmux please check their documentation.
|
||||
|
||||
If you need to stop and re-start the fuzzing, use the same command line options
|
||||
(or even change them by selecting a different power schedule or another
|
||||
mutation mode!) and switch the input directory with a dash (`-`):
|
||||
@ -572,8 +661,15 @@ to use afl-clang-lto as the compiler. You also have the option to generate
|
||||
a dictionary yourself, see [utils/libtokencap/README.md](utils/libtokencap/README.md).
|
||||
|
||||
afl-fuzz has a variety of options that help to workaround target quirks like
|
||||
specific locations for the input file (`-f`), not performing deterministic
|
||||
fuzzing (`-d`) and many more. Check out `afl-fuzz -h`.
|
||||
specific locations for the input file (`-f`), performing deterministic
|
||||
fuzzing (`-D`) and many more. Check out `afl-fuzz -h`.
|
||||
|
||||
We highly recommend that you set a memory limit for running the target with `-m`
|
||||
which defines the maximum memory in MB. This prevents a potential
|
||||
out-of-memory problem for your system plus helps you detect missing `malloc()`
|
||||
failure handling in the target.
|
||||
Play around with various -m values until you find one that safely works for all
|
||||
your input seeds (if you have good ones and then double or quadrouple that.
|
||||
|
||||
By default afl-fuzz never stops fuzzing. To terminate afl++ simply press Control-C
|
||||
or send a signal SIGINT. You can limit the number of executions or approximate runtime
|
||||
@ -600,7 +696,7 @@ of the testcases. Depending on the average testcase size (and those found
|
||||
during fuzzing) and their number, a value between 50-500MB is recommended.
|
||||
You can set the cache size (in MB) by setting the environment variable `AFL_TESTCACHE_SIZE`.
|
||||
|
||||
There should be one main fuzzer (`-M main` option) and as many secondary
|
||||
There should be one main fuzzer (`-M main-$HOSTNAME` option) and as many secondary
|
||||
fuzzers (eg `-S variant1`) as you have cores that you use.
|
||||
Every -M/-S entry needs a unique name (that can be whatever), however the same
|
||||
-o output directory location has to be used for all instances.
|
||||
@ -608,29 +704,35 @@ Every -M/-S entry needs a unique name (that can be whatever), however the same
|
||||
For every secondary fuzzer there should be a variation, e.g.:
|
||||
* one should fuzz the target that was compiled differently: with sanitizers
|
||||
activated (`export AFL_USE_ASAN=1 ; export AFL_USE_UBSAN=1 ;
|
||||
export AFL_USE_CFISAN=1 ; export AFL_USE_LSAN=1`)
|
||||
export AFL_USE_CFISAN=1`)
|
||||
* one or two should fuzz the target with CMPLOG/redqueen (see above), at
|
||||
least one cmplog instance should follow transformations (`-l AT`)
|
||||
* one to three fuzzers should fuzz a target compiled with laf-intel/COMPCOV
|
||||
(see above). Important note: If you run more than one laf-intel/COMPCOV
|
||||
fuzzer and you want them to share their intermediate results, the main
|
||||
fuzzer (`-M`) must be one of the them!
|
||||
fuzzer (`-M`) must be one of the them! (Although this is not really
|
||||
recommended.)
|
||||
|
||||
All other secondaries should be used like this:
|
||||
* A third to a half with the MOpt mutator enabled: `-L 0`
|
||||
* run with a different power schedule, available are:
|
||||
`fast (default), explore, coe, lin, quad, exploit, mmopt, rare, seek`
|
||||
which you can set with e.g. `-p seek`
|
||||
* A quarter to a third with the MOpt mutator enabled: `-L 0`
|
||||
* run with a different power schedule, recommended are:
|
||||
`fast (default), explore, coe, lin, quad, exploit and rare`
|
||||
which you can set with e.g. `-p explore`
|
||||
* a few instances should use the old queue cycling with `-Z`
|
||||
|
||||
Also it is recommended to set `export AFL_IMPORT_FIRST=1` to load testcases
|
||||
from other fuzzers in the campaign first.
|
||||
|
||||
If you have a large corpus, a corpus from a previous run or are fuzzing in
|
||||
a CI, then also set `export AFL_CMPLOG_ONLY_NEW=1` and `export AFL_FAST_CAL=1`.
|
||||
|
||||
You can also use different fuzzers.
|
||||
If you are using afl spinoffs or afl conforming fuzzers, then just use the
|
||||
same -o directory and give it a unique `-S` name.
|
||||
Examples are:
|
||||
* [Fuzzolic](https://github.com/season-lab/fuzzolic)
|
||||
* [symcc](https://github.com/eurecom-s/symcc/)
|
||||
* [Eclipser](https://github.com/SoftSec-KAIST/Eclipser/)
|
||||
* [Untracer](https://github.com/FoRTE-Research/UnTracer-AFL)
|
||||
* [AFLsmart](https://github.com/aflsmart/aflsmart)
|
||||
* [FairFuzz](https://github.com/carolemieux/afl-rb)
|
||||
* [Neuzz](https://github.com/Dongdongshe/neuzz)
|
||||
@ -638,11 +740,52 @@ Examples are:
|
||||
|
||||
A long list can be found at [https://github.com/Microsvuln/Awesome-AFL](https://github.com/Microsvuln/Awesome-AFL)
|
||||
|
||||
However you can also sync afl++ with honggfuzz, libfuzzer with -entropic, etc.
|
||||
However you can also sync afl++ with honggfuzz, libfuzzer with `-entropic=1`, etc.
|
||||
Just show the main fuzzer (-M) with the `-F` option where the queue/work
|
||||
directory of a different fuzzer is, e.g. `-F /src/target/honggfuzz`.
|
||||
Using honggfuzz (with `-n 1` or `-n 2`) and libfuzzer in parallel is highly
|
||||
recommended!
|
||||
|
||||
#### c) The status of the fuzz campaign
|
||||
#### c) Using multiple machines for fuzzing
|
||||
|
||||
Maybe you have more than one machine you want to fuzz the same target on.
|
||||
Simply start the `afl-fuzz` (and perhaps libfuzzer, honggfuzz, ...)
|
||||
orchestra as you like, just ensure that your have one and only one `-M`
|
||||
instance per server, and that its name is unique, hence the recommendation
|
||||
for `-M main-$HOSTNAME`.
|
||||
|
||||
Now there are three strategies on how you can sync between the servers:
|
||||
* never: sounds weird, but this makes every server an island and has the
|
||||
chance the each follow different paths into the target. You can make
|
||||
this even more interesting by even giving different seeds to each server.
|
||||
* regularly (~4h): this ensures that all fuzzing campaigns on the servers
|
||||
"see" the same thing. It is like fuzzing on a huge server.
|
||||
* in intervals of 1/10th of the overall expected runtime of the fuzzing you
|
||||
sync. This tries a bit to combine both. have some individuality of the
|
||||
paths each campaign on a server explores, on the other hand if one
|
||||
gets stuck where another found progress this is handed over making it
|
||||
unstuck.
|
||||
|
||||
The syncing process itself is very simple.
|
||||
As the `-M main-$HOSTNAME` instance syncs to all `-S` secondaries as well
|
||||
as to other fuzzers, you have to copy only this directory to the other
|
||||
machines.
|
||||
|
||||
Lets say all servers have the `-o out` directory in /target/foo/out, and
|
||||
you created a file `servers.txt` which contains the hostnames of all
|
||||
participating servers, plus you have an ssh key deployed to all of them,
|
||||
then run:
|
||||
```bash
|
||||
for FROM in `cat servers.txt`; do
|
||||
for TO in `cat servers.txt`; do
|
||||
rsync -rlpogtz --rsh=ssh $FROM:/target/foo/out/main-$FROM $TO:target/foo/out/
|
||||
done
|
||||
done
|
||||
```
|
||||
You can run this manually, per cron job - as you need it.
|
||||
There is a more complex and configurable script in `utils/distributed_fuzzing`.
|
||||
|
||||
#### d) The status of the fuzz campaign
|
||||
|
||||
afl++ comes with the `afl-whatsup` script to show the status of the fuzzing
|
||||
campaign.
|
||||
@ -651,11 +794,14 @@ Just supply the directory that afl-fuzz is given with the -o option and
|
||||
you will see a detailed status of every fuzzer in that campaign plus
|
||||
a summary.
|
||||
|
||||
To have only the summary use the `-s` switch e.g.: `afl-whatsup -s output/`
|
||||
To have only the summary use the `-s` switch e.g.: `afl-whatsup -s out/`
|
||||
|
||||
#### d) Checking the coverage of the fuzzing
|
||||
If you have multiple servers then use the command after a sync, or you have
|
||||
to execute this script per server.
|
||||
|
||||
The `paths found` value is a bad indicator how good the coverage is.
|
||||
#### e) Checking the coverage of the fuzzing
|
||||
|
||||
The `paths found` value is a bad indicator for checking how good the coverage is.
|
||||
|
||||
A better indicator - if you use default llvm instrumentation with at least
|
||||
version 9 - is to use `afl-showmap` with the collect coverage option `-C` on
|
||||
@ -683,12 +829,13 @@ then terminate it. The main node will pick it up and make it available to the
|
||||
other secondary nodes over time. Set `export AFL_NO_AFFINITY=1` or
|
||||
`export AFL_TRY_AFFINITY=1` if you have no free core.
|
||||
|
||||
Note that you in nearly all cases can never reach full coverage. A lot of
|
||||
functionality is usually behind options that were not activated or fuzz e.g.
|
||||
if you fuzz a library to convert image formats and your target is the png to
|
||||
tiff API then you will not touch any of the other library APIs and features.
|
||||
Note that in nearly all cases you can never reach full coverage. A lot of
|
||||
functionality is usually dependent on exclusive options that would need individual
|
||||
fuzzing campaigns each with one of these options set. E.g. if you fuzz a library to
|
||||
convert image formats and your target is the png to tiff API then you will not
|
||||
touch any of the other library APIs and features.
|
||||
|
||||
#### e) How long to fuzz a target?
|
||||
#### f) How long to fuzz a target?
|
||||
|
||||
This is a difficult question.
|
||||
Basically if no new path is found for a long time (e.g. for a day or a week)
|
||||
@ -700,7 +847,7 @@ Keep the queue/ directory (for future fuzzings of the same or similar targets)
|
||||
and use them to seed other good fuzzers like libfuzzer with the -entropic
|
||||
switch or honggfuzz.
|
||||
|
||||
#### f) Improve the speed!
|
||||
#### g) Improve the speed!
|
||||
|
||||
* Use [persistent mode](instrumentation/README.persistent_mode.md) (x2-x20 speed increase)
|
||||
* If you do not use shmem persistent mode, use `AFL_TMPDIR` to point the input file on a tempfs location, see [docs/env_variables.md](docs/env_variables.md)
|
||||
@ -767,25 +914,26 @@ campaigns as these are much shorter runnings.
|
||||
corpus needs to be loaded.
|
||||
* `AFL_CMPLOG_ONLY_NEW` - only perform cmplog on new found paths, not the
|
||||
initial corpus as this very likely has been done for them already.
|
||||
* Keep the generated corpus, use afl-cmin and reuse it everytime!
|
||||
* Keep the generated corpus, use afl-cmin and reuse it every time!
|
||||
|
||||
2. Additionally randomize the afl++ compilation options, e.g.
|
||||
* 40% for `AFL_LLVM_CMPLOG`
|
||||
* 10% for `AFL_LLVM_LAF_ALL`
|
||||
|
||||
3. Also randomize the afl-fuzz runtime options, e.g.
|
||||
* 60% for `AFL_DISABLE_TRIM`
|
||||
* 65% for `AFL_DISABLE_TRIM`
|
||||
* 50% use a dictionary generated by `AFL_LLVM_DICT2FILE`
|
||||
* 50% use MOpt (`-L 0`)
|
||||
* 40% use MOpt (`-L 0`)
|
||||
* 40% for `AFL_EXPAND_HAVOC_NOW`
|
||||
* 30% for old queue processing (`-Z`)
|
||||
* 20% for old queue processing (`-Z`)
|
||||
* for CMPLOG targets, 60% for `-l 2`, 40% for `-l 3`
|
||||
|
||||
4. Do *not* run any `-M` modes, just running `-S` modes is better for CI fuzzing.
|
||||
`-M` enables deterministic fuzzing, old queue handling etc. which is good for
|
||||
a fuzzing campaign but not good for short CI runs.
|
||||
`-M` enables old queue handling etc. which is good for a fuzzing campaign but
|
||||
not good for short CI runs.
|
||||
|
||||
How this can look like can e.g. be seen at afl++'s setup in Google's [oss-fuzz](https://github.com/google/oss-fuzz/blob/4bb61df7905c6005000f5766e966e6fe30ab4559/infra/base-images/base-builder/compile_afl#L69).
|
||||
How this can look like can e.g. be seen at afl++'s setup in Google's [oss-fuzz](https://github.com/google/oss-fuzz/blob/master/infra/base-images/base-builder/compile_afl)
|
||||
and [clusterfuzz](https://github.com/google/clusterfuzz/blob/master/src/python/bot/fuzzers/afl/launcher.py).
|
||||
|
||||
## Fuzzing binary-only targets
|
||||
|
||||
@ -796,12 +944,12 @@ If you do not have to use Unicorn the following setup is recommended to use
|
||||
qemu_mode:
|
||||
* run 1 afl-fuzz -Q instance with CMPLOG (`-c 0` + `AFL_COMPCOV_LEVEL=2`)
|
||||
* run 1 afl-fuzz -Q instance with QASAN (`AFL_USE_QASAN=1`)
|
||||
* run 1 afl-fuzz -Q instance with LAF (``AFL_PRELOAD=libcmpcov.so` + `AFL_COMPCOV_LEVEL=2`)
|
||||
* run 1 afl-fuzz -Q instance with LAF (`AFL_PRELOAD=libcmpcov.so` + `AFL_COMPCOV_LEVEL=2`)
|
||||
Alternatively you can use frida_mode, just switch `-Q` with `-O` and remove the
|
||||
LAF instance.
|
||||
|
||||
Then run as many instances as you have cores left with either -Q mode or - better -
|
||||
use a binary rewriter like afl-dyninst, retrowrite, zaflr, etc.
|
||||
use a binary rewriter like afl-dyninst, retrowrite, zafl, etc.
|
||||
|
||||
For Qemu and Frida mode, check out the persistent mode, it gives a huge speed
|
||||
improvement if it is possible to use.
|
||||
|
2
TODO.md
2
TODO.md
@ -2,13 +2,11 @@
|
||||
|
||||
## Roadmap 3.00+
|
||||
|
||||
- align map to 64 bytes but keep real IDs
|
||||
- Update afl->pending_not_fuzzed for MOpt
|
||||
- put fuzz target in top line of UI
|
||||
- afl-plot to support multiple plot_data
|
||||
- afl_custom_fuzz_splice_optin()
|
||||
- afl_custom_splice()
|
||||
- intel-pt tracer
|
||||
- better autodetection of shifting runtime timeout values
|
||||
- cmplog: use colorization input for havoc?
|
||||
- parallel builds for source-only targets
|
||||
|
43
afl-cmin
43
afl-cmin
@ -122,6 +122,7 @@ function usage() {
|
||||
"AFL_FORKSRV_INIT_TMOUT: time the fuzzer waits for the forkserver to come up\n" \
|
||||
"AFL_KEEP_TRACES: leave the temporary <out_dir>/.traces directory\n" \
|
||||
"AFL_KILL_SIGNAL: Signal delivered to child processes on timeout (default: SIGKILL)\n" \
|
||||
"AFL_NO_FORKSRV: run target via execve instead of using the forkserver\n" \
|
||||
"AFL_PATH: path for the afl-showmap binary if not found anywhere in PATH\n" \
|
||||
"AFL_PRINT_FILENAMES: If set, the filename currently processed will be " \
|
||||
"printed to stdout\n" \
|
||||
@ -296,13 +297,13 @@ BEGIN {
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (0 == system( "test -d "in_dir"/default" )) {
|
||||
in_dir = in_dir "/default"
|
||||
}
|
||||
|
||||
if (0 == system( "test -d "in_dir"/queue" )) {
|
||||
in_dir = in_dir "/queue"
|
||||
}
|
||||
#if (0 == system( "test -d "in_dir"/default" )) {
|
||||
# in_dir = in_dir "/default"
|
||||
#}
|
||||
#
|
||||
#if (0 == system( "test -d "in_dir"/queue" )) {
|
||||
# in_dir = in_dir "/queue"
|
||||
#}
|
||||
|
||||
system("rm -rf "trace_dir" 2>/dev/null");
|
||||
system("rm "out_dir"/id[:_]* 2>/dev/null")
|
||||
@ -355,30 +356,35 @@ BEGIN {
|
||||
} else {
|
||||
stat_format = "-f '%z %N'" # *BSD, MacOS
|
||||
}
|
||||
cmdline = "(cd "in_dir" && find . \\( ! -name . -a -type d -prune \\) -o -type f -exec stat "stat_format" \\{\\} + | sort -k1n -k2r)"
|
||||
cmdline = "(cd "in_dir" && find . \\( ! -name \".*\" -a -type d \\) -o -type f -exec stat "stat_format" \\{\\} + | sort -k1n -k2r)"
|
||||
#cmdline = "ls "in_dir" | (cd "in_dir" && xargs stat "stat_format" 2>/dev/null) | sort -k1n -k2r"
|
||||
#cmdline = "(cd "in_dir" && stat "stat_format" *) | sort -k1n -k2r"
|
||||
#cmdline = "(cd "in_dir" && ls | xargs stat "stat_format" ) | sort -k1n -k2r"
|
||||
while (cmdline | getline) {
|
||||
sub(/^[0-9]+ (\.\/)?/,"",$0)
|
||||
infilesSmallToBig[i++] = $0
|
||||
infilesSmallToBigFull[i] = $0
|
||||
sub(/.*\//, "", $0)
|
||||
infilesSmallToBig[i] = $0
|
||||
infilesSmallToBigMap[infilesSmallToBig[i]] = infilesSmallToBigFull[i]
|
||||
infilesSmallToBigFullMap[infilesSmallToBigFull[i]] = infilesSmallToBig[i]
|
||||
i++
|
||||
}
|
||||
in_count = i
|
||||
|
||||
first_file = infilesSmallToBig[0]
|
||||
first_file = infilesSmallToBigFull[0]
|
||||
|
||||
# Make sure that we're not dealing with a directory.
|
||||
#if (0 == system("test -d ""\""in_dir"/"first_file"\"")) {
|
||||
# print "[-] Error: The input directory is empty or contains subdirectories - please fix." > "/dev/stderr"
|
||||
# exit 1
|
||||
#}
|
||||
|
||||
if (0 == system("test -d ""\""in_dir"/"first_file"\"")) {
|
||||
print "[-] Error: The input directory is empty or contains subdirectories - please fix." > "/dev/stderr"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (0 == system("ln \""in_dir"/"first_file"\" "trace_dir"/.link_test")) {
|
||||
system(">\""in_dir"/.afl-cmin.test\"")
|
||||
if (0 == system("ln \""in_dir"/.afl-cmin.test\" "trace_dir"/.link_test")) {
|
||||
cp_tool = "ln"
|
||||
} else {
|
||||
cp_tool = "cp"
|
||||
}
|
||||
system("rm -f \""in_dir"/.afl-cmin.test\"")
|
||||
|
||||
if (!ENVIRON["AFL_SKIP_BIN_CHECK"]) {
|
||||
# Make sure that we can actually get anything out of afl-showmap before we
|
||||
@ -511,7 +517,8 @@ BEGIN {
|
||||
|
||||
# copy file unless already done
|
||||
if (! (fn in file_already_copied)) {
|
||||
system(cp_tool" \""in_dir"/"fn"\" \""out_dir"/"fn"\"")
|
||||
realfile = infilesSmallToBigMap[fn]
|
||||
system(cp_tool" \""in_dir"/"realfile"\" \""out_dir"/"fn"\"")
|
||||
file_already_copied[fn] = ""
|
||||
++out_count
|
||||
#printf "tuple nr %d (%d cnt=%d) -> %s\n",tcnt,key,key_count[key],fn > trace_dir"/.log"
|
||||
|
@ -135,6 +135,7 @@ For additional tips, please consult README.md.
|
||||
|
||||
Environment variables used:
|
||||
AFL_KEEP_TRACES: leave the temporary <out_dir>\.traces directory
|
||||
AFL_NO_FORKSRV: run target via execve instead of using the forkserver
|
||||
AFL_PATH: last resort location to find the afl-showmap binary
|
||||
AFL_SKIP_BIN_CHECK: skip check for target binary
|
||||
_EOF_
|
||||
|
2
afl-plot
2
afl-plot
@ -127,7 +127,7 @@ set key outside
|
||||
set autoscale xfixmin
|
||||
set autoscale xfixmax
|
||||
|
||||
#set xlabel "all times in UTC" font "small"
|
||||
set xlabel "relative time in seconds" font "small"
|
||||
|
||||
plot '$inputdir/plot_data' using 1:4 with filledcurve x1 title 'total paths' linecolor rgb '#000000' fillstyle transparent solid 0.2 noborder, \\
|
||||
'' using 1:3 with filledcurve x1 title 'current path' linecolor rgb '#f0f0f0' fillstyle transparent solid 0.5 noborder, \\
|
||||
|
@ -246,9 +246,9 @@ typedef struct {
|
||||
} timing;
|
||||
struct {
|
||||
struct {
|
||||
uint8_t val[256];
|
||||
uint8_t val[512];
|
||||
size_t len;
|
||||
} dictionary[1024];
|
||||
} dictionary[8192];
|
||||
size_t dictionaryCnt;
|
||||
const char* dictionaryFile;
|
||||
size_t mutationsMax;
|
||||
@ -263,6 +263,7 @@ typedef struct {
|
||||
struct {
|
||||
bool useVerifier;
|
||||
bool exitUponCrash;
|
||||
uint8_t exitCodeUponCrash;
|
||||
const char* reportFile;
|
||||
size_t dynFileIterExpire;
|
||||
bool only_printable;
|
||||
|
@ -1,342 +0,0 @@
|
||||
#ifndef CUSTOM_MUTATOR_HELPERS
|
||||
#define CUSTOM_MUTATOR_HELPERS
|
||||
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#define INITIAL_GROWTH_SIZE (64)
|
||||
|
||||
#define RAND_BELOW(limit) (rand() % (limit))
|
||||
|
||||
/* Use in a struct: creates a name_buf and a name_size variable. */
|
||||
#define BUF_VAR(type, name) \
|
||||
type * name##_buf; \
|
||||
size_t name##_size;
|
||||
/* this filles in `&structptr->something_buf, &structptr->something_size`. */
|
||||
#define BUF_PARAMS(struct, name) \
|
||||
(void **)&struct->name##_buf, &struct->name##_size
|
||||
|
||||
typedef struct {
|
||||
|
||||
} afl_t;
|
||||
|
||||
static void surgical_havoc_mutate(u8 *out_buf, s32 begin, s32 end) {
|
||||
|
||||
static s8 interesting_8[] = {INTERESTING_8};
|
||||
static s16 interesting_16[] = {INTERESTING_8, INTERESTING_16};
|
||||
static s32 interesting_32[] = {INTERESTING_8, INTERESTING_16, INTERESTING_32};
|
||||
|
||||
switch (RAND_BELOW(12)) {
|
||||
|
||||
case 0: {
|
||||
|
||||
/* Flip a single bit somewhere. Spooky! */
|
||||
|
||||
s32 bit_idx = ((RAND_BELOW(end - begin) + begin) << 3) + RAND_BELOW(8);
|
||||
|
||||
out_buf[bit_idx >> 3] ^= 128 >> (bit_idx & 7);
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 1: {
|
||||
|
||||
/* Set byte to interesting value. */
|
||||
|
||||
u8 val = interesting_8[RAND_BELOW(sizeof(interesting_8))];
|
||||
out_buf[(RAND_BELOW(end - begin) + begin)] = val;
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 2: {
|
||||
|
||||
/* Set word to interesting value, randomly choosing endian. */
|
||||
|
||||
if (end - begin < 2) break;
|
||||
|
||||
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
|
||||
|
||||
if (byte_idx >= end - 1) break;
|
||||
|
||||
switch (RAND_BELOW(2)) {
|
||||
|
||||
case 0:
|
||||
*(u16 *)(out_buf + byte_idx) =
|
||||
interesting_16[RAND_BELOW(sizeof(interesting_16) >> 1)];
|
||||
break;
|
||||
case 1:
|
||||
*(u16 *)(out_buf + byte_idx) =
|
||||
SWAP16(interesting_16[RAND_BELOW(sizeof(interesting_16) >> 1)]);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 3: {
|
||||
|
||||
/* Set dword to interesting value, randomly choosing endian. */
|
||||
|
||||
if (end - begin < 4) break;
|
||||
|
||||
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
|
||||
|
||||
if (byte_idx >= end - 3) break;
|
||||
|
||||
switch (RAND_BELOW(2)) {
|
||||
|
||||
case 0:
|
||||
*(u32 *)(out_buf + byte_idx) =
|
||||
interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)];
|
||||
break;
|
||||
case 1:
|
||||
*(u32 *)(out_buf + byte_idx) =
|
||||
SWAP32(interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 4: {
|
||||
|
||||
/* Set qword to interesting value, randomly choosing endian. */
|
||||
|
||||
if (end - begin < 8) break;
|
||||
|
||||
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
|
||||
|
||||
if (byte_idx >= end - 7) break;
|
||||
|
||||
switch (RAND_BELOW(2)) {
|
||||
|
||||
case 0:
|
||||
*(u64 *)(out_buf + byte_idx) =
|
||||
(s64)interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)];
|
||||
break;
|
||||
case 1:
|
||||
*(u64 *)(out_buf + byte_idx) = SWAP64(
|
||||
(s64)interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 5: {
|
||||
|
||||
/* Randomly subtract from byte. */
|
||||
|
||||
out_buf[(RAND_BELOW(end - begin) + begin)] -= 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 6: {
|
||||
|
||||
/* Randomly add to byte. */
|
||||
|
||||
out_buf[(RAND_BELOW(end - begin) + begin)] += 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 7: {
|
||||
|
||||
/* Randomly subtract from word, random endian. */
|
||||
|
||||
if (end - begin < 2) break;
|
||||
|
||||
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
|
||||
|
||||
if (byte_idx >= end - 1) break;
|
||||
|
||||
if (RAND_BELOW(2)) {
|
||||
|
||||
*(u16 *)(out_buf + byte_idx) -= 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
} else {
|
||||
|
||||
u16 num = 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
*(u16 *)(out_buf + byte_idx) =
|
||||
SWAP16(SWAP16(*(u16 *)(out_buf + byte_idx)) - num);
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 8: {
|
||||
|
||||
/* Randomly add to word, random endian. */
|
||||
|
||||
if (end - begin < 2) break;
|
||||
|
||||
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
|
||||
|
||||
if (byte_idx >= end - 1) break;
|
||||
|
||||
if (RAND_BELOW(2)) {
|
||||
|
||||
*(u16 *)(out_buf + byte_idx) += 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
} else {
|
||||
|
||||
u16 num = 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
*(u16 *)(out_buf + byte_idx) =
|
||||
SWAP16(SWAP16(*(u16 *)(out_buf + byte_idx)) + num);
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 9: {
|
||||
|
||||
/* Randomly subtract from dword, random endian. */
|
||||
|
||||
if (end - begin < 4) break;
|
||||
|
||||
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
|
||||
|
||||
if (byte_idx >= end - 3) break;
|
||||
|
||||
if (RAND_BELOW(2)) {
|
||||
|
||||
*(u32 *)(out_buf + byte_idx) -= 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
} else {
|
||||
|
||||
u32 num = 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
*(u32 *)(out_buf + byte_idx) =
|
||||
SWAP32(SWAP32(*(u32 *)(out_buf + byte_idx)) - num);
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 10: {
|
||||
|
||||
/* Randomly add to dword, random endian. */
|
||||
|
||||
if (end - begin < 4) break;
|
||||
|
||||
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
|
||||
|
||||
if (byte_idx >= end - 3) break;
|
||||
|
||||
if (RAND_BELOW(2)) {
|
||||
|
||||
*(u32 *)(out_buf + byte_idx) += 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
} else {
|
||||
|
||||
u32 num = 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
*(u32 *)(out_buf + byte_idx) =
|
||||
SWAP32(SWAP32(*(u32 *)(out_buf + byte_idx)) + num);
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 11: {
|
||||
|
||||
/* Just set a random byte to a random value. Because,
|
||||
why not. We use XOR with 1-255 to eliminate the
|
||||
possibility of a no-op. */
|
||||
|
||||
out_buf[(RAND_BELOW(end - begin) + begin)] ^= 1 + RAND_BELOW(255);
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* This function calculates the next power of 2 greater or equal its argument.
|
||||
@return The rounded up power of 2 (if no overflow) or 0 on overflow.
|
||||
*/
|
||||
static inline size_t next_pow2(size_t in) {
|
||||
|
||||
if (in == 0 || in > (size_t)-1)
|
||||
return 0; /* avoid undefined behaviour under-/overflow */
|
||||
size_t out = in - 1;
|
||||
out |= out >> 1;
|
||||
out |= out >> 2;
|
||||
out |= out >> 4;
|
||||
out |= out >> 8;
|
||||
out |= out >> 16;
|
||||
return out + 1;
|
||||
|
||||
}
|
||||
|
||||
/* This function makes sure *size is > size_needed after call.
|
||||
It will realloc *buf otherwise.
|
||||
*size will grow exponentially as per:
|
||||
https://blog.mozilla.org/nnethercote/2014/11/04/please-grow-your-buffers-exponentially/
|
||||
Will return NULL and free *buf if size_needed is <1 or realloc failed.
|
||||
@return For convenience, this function returns *buf.
|
||||
*/
|
||||
static inline void *maybe_grow(void **buf, size_t *size, size_t size_needed) {
|
||||
|
||||
/* No need to realloc */
|
||||
if (likely(size_needed && *size >= size_needed)) return *buf;
|
||||
|
||||
/* No initial size was set */
|
||||
if (size_needed < INITIAL_GROWTH_SIZE) size_needed = INITIAL_GROWTH_SIZE;
|
||||
|
||||
/* grow exponentially */
|
||||
size_t next_size = next_pow2(size_needed);
|
||||
|
||||
/* handle overflow */
|
||||
if (!next_size) { next_size = size_needed; }
|
||||
|
||||
/* alloc */
|
||||
*buf = realloc(*buf, next_size);
|
||||
*size = *buf ? next_size : 0;
|
||||
|
||||
return *buf;
|
||||
|
||||
}
|
||||
|
||||
/* Swaps buf1 ptr and buf2 ptr, as well as their sizes */
|
||||
static inline void afl_swap_bufs(void **buf1, size_t *size1, void **buf2,
|
||||
size_t *size2) {
|
||||
|
||||
void * scratch_buf = *buf1;
|
||||
size_t scratch_size = *size1;
|
||||
*buf1 = *buf2;
|
||||
*size1 = *size2;
|
||||
*buf2 = scratch_buf;
|
||||
*size2 = scratch_size;
|
||||
|
||||
}
|
||||
|
||||
#undef INITIAL_GROWTH_SIZE
|
||||
|
||||
#endif
|
||||
|
1
custom_mutators/radamsa/custom_mutator_helpers.h
Symbolic link
1
custom_mutators/radamsa/custom_mutator_helpers.h
Symbolic link
@ -0,0 +1 @@
|
||||
../examples/custom_mutator_helpers.h
|
@ -8,12 +8,42 @@
|
||||
Want to stay in the loop on major new features? Join our mailing list by
|
||||
sending a mail to <afl-users+subscribe@googlegroups.com>.
|
||||
|
||||
### Version ++3.14a (release)
|
||||
- Fix for llvm 13
|
||||
### Version ++3.14c (release)
|
||||
- afl-fuzz:
|
||||
- fix -F when a '/' was part of the parameter
|
||||
- fixed a crash for cmplog for very slow inputs
|
||||
- fix for AFLfast schedule counting
|
||||
- removed implied -D determinstic from -M main
|
||||
- if the target becomes unavailable check out out/default/error.txt
|
||||
for an indicator why
|
||||
- AFL_CAL_FAST was a dead env, now does the same as AFL_FAST_CAL
|
||||
- reverse read the queue on resumes (more effective)
|
||||
- fix custom mutator trimming
|
||||
- afl-cc:
|
||||
- Update to COMPCOV/laf-intel that speeds up the instrumentation
|
||||
process a lot - thanks to Michael Rodler/f0rki for the PR!
|
||||
- Fix for failures for some sized string instrumentations
|
||||
- Fix to instrument global namespace functions in c++
|
||||
- Fix for llvm 13
|
||||
- support partial linking
|
||||
- do honor AFL_LLVM_{ALLOW/DENY}LIST for LTO autodictionary and DICT2FILE
|
||||
- We do support llvm versions from 3.8 to 5.0 again
|
||||
- frida_mode:
|
||||
- several fixes for cmplog
|
||||
- remove need for AFL_FRIDA_PERSISTENT_RETADDR_OFFSET
|
||||
- less coverage collision
|
||||
- feature parity of aarch64 with intel now (persistent, cmplog,
|
||||
in-memory testcases, asan)
|
||||
- afl-cmin and afl-showmap -i do now descend into subdirectories
|
||||
(like afl-fuzz does) - note that afl-cmin.bash does not!
|
||||
- afl_analyze:
|
||||
- fix timeout handling
|
||||
- add forkserver support for better performance
|
||||
- ensure afl-compiler-rt is built for gcc_module
|
||||
|
||||
- always build aflpp_driver for libfuzzer harnesses
|
||||
- added `AFL_NO_FORKSRV` env variable support to
|
||||
afl-cmin, afl-tmin, and afl-showmap, by @jhertz
|
||||
- removed outdated documents, improved existing documentation
|
||||
|
||||
### Version ++3.13c (release)
|
||||
- Note: plot_data switched to relative time from unix time in 3.10
|
||||
|
37
docs/FAQ.md
37
docs/FAQ.md
@ -3,6 +3,7 @@
|
||||
## Contents
|
||||
|
||||
* [What is the difference between afl and afl++?](#what-is-the-difference-between-afl-and-afl)
|
||||
* [I got a weird compile error from clang](#i-got-a-weird-compile-error-from-clang)
|
||||
* [How to improve the fuzzing speed?](#how-to-improve-the-fuzzing-speed)
|
||||
* [How do I fuzz a network service?](#how-do-i-fuzz-a-network-service)
|
||||
* [How do I fuzz a GUI program?](#how-do-i-fuzz-a-gui-program)
|
||||
@ -35,6 +36,26 @@ flexible and feature rich guided fuzzer available as open source.
|
||||
And in independent fuzzing benchmarks it is one of the best fuzzers available,
|
||||
e.g. [Fuzzbench Report](https://www.fuzzbench.com/reports/2020-08-03/index.html)
|
||||
|
||||
## I got a weird compile error from clang
|
||||
|
||||
If you see this kind of error when trying to instrument a target with afl-cc/
|
||||
afl-clang-fast/afl-clang-lto:
|
||||
```
|
||||
/prg/tmp/llvm-project/build/bin/clang-13: symbol lookup error: /usr/local/bin/../lib/afl//cmplog-instructions-pass.so: undefined symbol: _ZNK4llvm8TypeSizecvmEv
|
||||
clang-13: error: unable to execute command: No such file or directory
|
||||
clang-13: error: clang frontend command failed due to signal (use -v to see invocation)
|
||||
clang version 13.0.0 (https://github.com/llvm/llvm-project 1d7cf550721c51030144f3cd295c5789d51c4aad)
|
||||
Target: x86_64-unknown-linux-gnu
|
||||
Thread model: posix
|
||||
InstalledDir: /prg/tmp/llvm-project/build/bin
|
||||
clang-13: note: diagnostic msg:
|
||||
********************
|
||||
```
|
||||
Then this means that your OS updated the clang installation from an upgrade
|
||||
package and because of that the afl++ llvm plugins do not match anymore.
|
||||
|
||||
Solution: `git pull ; make clean install` of afl++
|
||||
|
||||
## How to improve the fuzzing speed?
|
||||
|
||||
1. Use [llvm_mode](docs/llvm_mode/README.md): afl-clang-lto (llvm >= 11) or afl-clang-fast (llvm >= 9 recommended)
|
||||
@ -167,13 +188,7 @@ Four steps are required to do this and it also requires quite some knowledge
|
||||
of coding and/or disassembly and is effectively possible only with
|
||||
afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation.
|
||||
|
||||
1. First step: Identify which edge ID numbers are unstable
|
||||
|
||||
run the target with `export AFL_DEBUG=1` for a few minutes then terminate.
|
||||
The out/fuzzer_stats file will then show the edge IDs that were identified
|
||||
as unstable.
|
||||
|
||||
2. Second step: Find the responsible function(s).
|
||||
1. First step: Instrument to be able to find the responsible function(s).
|
||||
|
||||
a) For LTO instrumented binaries this can be documented during compile
|
||||
time, just set `export AFL_LLVM_DOCUMENT_IDS=/path/to/a/file`.
|
||||
@ -196,6 +211,14 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation.
|
||||
recompile with the two mentioned above. This is just for
|
||||
identifying the functions that have unstable edges.
|
||||
|
||||
2. Second step: Identify which edge ID numbers are unstable
|
||||
|
||||
run the target with `export AFL_DEBUG=1` for a few minutes then terminate.
|
||||
The out/fuzzer_stats file will then show the edge IDs that were identified
|
||||
as unstable in the `var_bytes` entry. You can match these numbers
|
||||
directly to the data you created in the first step.
|
||||
Now you know which functions are responsible for the instability
|
||||
|
||||
3. Third step: create a text file with the filenames/functions
|
||||
|
||||
Identify which source code files contain the functions that you need to
|
||||
|
@ -18,14 +18,12 @@ how to hit the ground running:
|
||||
custom SIGSEGV or SIGABRT handlers and background processes. For tips on
|
||||
detecting non-crashing flaws, see section 11 in [README.md](README.md) .
|
||||
|
||||
3) Compile the program / library to be fuzzed using afl-gcc. A common way to
|
||||
3) Compile the program / library to be fuzzed using afl-cc. A common way to
|
||||
do this would be:
|
||||
|
||||
CC=/path/to/afl-gcc CXX=/path/to/afl-g++ ./configure --disable-shared
|
||||
CC=/path/to/afl-cc CXX=/path/to/afl-c++ ./configure --disable-shared
|
||||
make clean all
|
||||
|
||||
If program build fails, ping <afl-users@googlegroups.com>.
|
||||
|
||||
4) Get a small but valid input file that makes sense to the program. When
|
||||
fuzzing verbose syntax (SQL, HTTP, etc), create a dictionary as described in
|
||||
dictionaries/README.md, too.
|
||||
@ -41,9 +39,6 @@ how to hit the ground running:
|
||||
6) Investigate anything shown in red in the fuzzer UI by promptly consulting
|
||||
[status_screen.md](status_screen.md).
|
||||
|
||||
7) compile and use llvm_mode (afl-clang-fast/afl-clang-fast++) as it is way
|
||||
faster and has a few cool features
|
||||
|
||||
8) There is a basic docker build with 'docker build -t aflplusplus .'
|
||||
|
||||
That's it. Sit back, relax, and - time permitting - try to skim through the
|
||||
|
@ -1,54 +0,0 @@
|
||||
# MOpt(imized) AFL by <puppet@zju.edu.cn>
|
||||
|
||||
### 1. Description
|
||||
MOpt-AFL is a AFL-based fuzzer that utilizes a customized Particle Swarm
|
||||
Optimization (PSO) algorithm to find the optimal selection probability
|
||||
distribution of operators with respect to fuzzing effectiveness.
|
||||
More details can be found in the technical report.
|
||||
|
||||
### 2. Cite Information
|
||||
Chenyang Lyu, Shouling Ji, Chao Zhang, Yuwei Li, Wei-Han Lee, Yu Song and
|
||||
Raheem Beyah, MOPT: Optimized Mutation Scheduling for Fuzzers,
|
||||
USENIX Security 2019.
|
||||
|
||||
### 3. Seed Sets
|
||||
We open source all the seed sets used in the paper
|
||||
"MOPT: Optimized Mutation Scheduling for Fuzzers".
|
||||
|
||||
### 4. Experiment Results
|
||||
The experiment results can be found in
|
||||
https://drive.google.com/drive/folders/184GOzkZGls1H2NuLuUfSp9gfqp1E2-lL?usp=sharing.
|
||||
We only open source the crash files since the space is limited.
|
||||
|
||||
### 5. Technical Report
|
||||
MOpt_TechReport.pdf is the technical report of the paper
|
||||
"MOPT: Optimized Mutation Scheduling for Fuzzers", which contains more deatails.
|
||||
|
||||
### 6. Parameter Introduction
|
||||
Most important, you must add the parameter `-L` (e.g., `-L 0`) to launch the
|
||||
MOpt scheme.
|
||||
|
||||
Option '-L' controls the time to move on to the pacemaker fuzzing mode.
|
||||
'-L t': when MOpt-AFL finishes the mutation of one input, if it has not
|
||||
discovered any new unique crash or path for more than t minutes, MOpt-AFL will
|
||||
enter the pacemaker fuzzing mode.
|
||||
|
||||
Setting 0 will enter the pacemaker fuzzing mode at first, which is
|
||||
recommended in a short time-scale evaluation.
|
||||
|
||||
Setting -1 will enable both pacemaker mode and normal aflmutation fuzzing in
|
||||
parallel.
|
||||
|
||||
Other important parameters can be found in afl-fuzz.c, for instance,
|
||||
|
||||
'swarm_num': the number of the PSO swarms used in the fuzzing process.
|
||||
'period_pilot': how many times MOpt-AFL will execute the target program
|
||||
in the pilot fuzzing module, then it will enter the core fuzzing module.
|
||||
'period_core': how many times MOpt-AFL will execute the target program in the
|
||||
core fuzzing module, then it will enter the PSO updating module.
|
||||
'limit_time_bound': control how many interesting test cases need to be found
|
||||
before MOpt-AFL quits the pacemaker fuzzing mode and reuses the deterministic stage.
|
||||
0 < 'limit_time_bound' < 1, MOpt-AFL-tmp.
|
||||
'limit_time_bound' >= 1, MOpt-AFL-ever.
|
||||
|
||||
Have fun with MOpt in AFL!
|
@ -122,7 +122,7 @@
|
||||
[https://github.com/vanhauser-thc/afl-dyninst](https://github.com/vanhauser-thc/afl-dyninst)
|
||||
|
||||
|
||||
## RETROWRITE
|
||||
## RETROWRITE, ZAFL, ... other binary rewriter
|
||||
|
||||
If you have an x86/x86_64 binary that still has its symbols, is compiled
|
||||
with position independant code (PIC/PIE) and does not use most of the C++
|
||||
@ -131,6 +131,7 @@
|
||||
|
||||
It is at about 80-85% performance.
|
||||
|
||||
[https://git.zephyr-software.com/opensrc/zafl](https://git.zephyr-software.com/opensrc/zafl)
|
||||
[https://github.com/HexHive/retrowrite](https://github.com/HexHive/retrowrite)
|
||||
|
||||
|
||||
|
@ -204,9 +204,7 @@ trimmed input. Here's a quick API description:
|
||||
arguments because we already have the initial buffer from `init_trim` and we
|
||||
can memorize the current state in the data variables. This can also save
|
||||
reparsing steps for each iteration. It should return the trimmed input
|
||||
buffer, where the returned data must not exceed the initial input data in
|
||||
length. Returning anything that is larger than the original data (passed to
|
||||
`init_trim`) will result in a fatal abort of AFL++.
|
||||
buffer.
|
||||
|
||||
- `post_trim` (optional)
|
||||
|
||||
|
@ -108,9 +108,6 @@ make fairly broad use of environmental variables instead:
|
||||
- Setting `AFL_QUIET` will prevent afl-cc and afl-as banners from being
|
||||
displayed during compilation, in case you find them distracting.
|
||||
|
||||
- Setting `AFL_CAL_FAST` will speed up the initial calibration, if the
|
||||
application is very slow.
|
||||
|
||||
## 2) Settings for LLVM and LTO: afl-clang-fast / afl-clang-fast++ / afl-clang-lto / afl-clang-lto++
|
||||
|
||||
The native instrumentation helpers (instrumentation and gcc_plugin) accept a subset
|
||||
@ -386,6 +383,7 @@ checks or alter some of the more exotic semantics of the tool:
|
||||
|
||||
- `AFL_FAST_CAL` keeps the calibration stage about 2.5x faster (albeit less
|
||||
precise), which can help when starting a session against a slow target.
|
||||
`AFL_CAL_FAST` works too.
|
||||
|
||||
- The CPU widget shown at the bottom of the screen is fairly simplistic and
|
||||
may complain of high load prematurely, especially on systems with low core
|
||||
|
@ -1,143 +0,0 @@
|
||||
# Historical notes
|
||||
|
||||
This doc talks about the rationale of some of the high-level design decisions
|
||||
for American Fuzzy Lop. It's adopted from a discussion with Rob Graham.
|
||||
See README.md for the general instruction manual, and technical_details.md for
|
||||
additional implementation-level insights.
|
||||
|
||||
## 1) Influences
|
||||
|
||||
In short, `afl-fuzz` is inspired chiefly by the work done by Tavis Ormandy back
|
||||
in 2007. Tavis did some very persuasive experiments using `gcov` block coverage
|
||||
to select optimal test cases out of a large corpus of data, and then using
|
||||
them as a starting point for traditional fuzzing workflows.
|
||||
|
||||
(By "persuasive", I mean: netting a significant number of interesting
|
||||
vulnerabilities.)
|
||||
|
||||
In parallel to this, both Tavis and I were interested in evolutionary fuzzing.
|
||||
Tavis had his experiments, and I was working on a tool called bunny-the-fuzzer,
|
||||
released somewhere in 2007.
|
||||
|
||||
Bunny used a generational algorithm not much different from `afl-fuzz`, but
|
||||
also tried to reason about the relationship between various input bits and
|
||||
the internal state of the program, with hopes of deriving some additional value
|
||||
from that. The reasoning / correlation part was probably in part inspired by
|
||||
other projects done around the same time by Will Drewry and Chris Evans.
|
||||
|
||||
The state correlation approach sounded very sexy on paper, but ultimately, made
|
||||
the fuzzer complicated, brittle, and cumbersome to use; every other target
|
||||
program would require a tweak or two. Because Bunny didn't fare a whole lot
|
||||
better than less sophisticated brute-force tools, I eventually decided to write
|
||||
it off. You can still find its original documentation at:
|
||||
|
||||
https://code.google.com/p/bunny-the-fuzzer/wiki/BunnyDoc
|
||||
|
||||
There has been a fair amount of independent work, too. Most notably, a few
|
||||
weeks earlier that year, Jared DeMott had a Defcon presentation about a
|
||||
coverage-driven fuzzer that relied on coverage as a fitness function.
|
||||
|
||||
Jared's approach was by no means identical to what afl-fuzz does, but it was in
|
||||
the same ballpark. His fuzzer tried to explicitly solve for the maximum coverage
|
||||
with a single input file; in comparison, afl simply selects for cases that do
|
||||
something new (which yields better results - see [technical_details.md](technical_details.md)).
|
||||
|
||||
A few years later, Gabriel Campana released fuzzgrind, a tool that relied purely
|
||||
on Valgrind and a constraint solver to maximize coverage without any brute-force
|
||||
bits; and Microsoft Research folks talked extensively about their still
|
||||
non-public, solver-based SAGE framework.
|
||||
|
||||
In the past six years or so, I've also seen a fair number of academic papers
|
||||
that dealt with smart fuzzing (focusing chiefly on symbolic execution) and a
|
||||
couple papers that discussed proof-of-concept applications of genetic
|
||||
algorithms with the same goals in mind. I'm unconvinced how practical most of
|
||||
these experiments were; I suspect that many of them suffer from the
|
||||
bunny-the-fuzzer's curse of being cool on paper and in carefully designed
|
||||
experiments, but failing the ultimate test of being able to find new,
|
||||
worthwhile security bugs in otherwise well-fuzzed, real-world software.
|
||||
|
||||
In some ways, the baseline that the "cool" solutions have to compete against is
|
||||
a lot more impressive than it may seem, making it difficult for competitors to
|
||||
stand out. For a singular example, check out the work by Gynvael and Mateusz
|
||||
Jurczyk, applying "dumb" fuzzing to ffmpeg, a prominent and security-critical
|
||||
component of modern browsers and media players:
|
||||
|
||||
http://googleonlinesecurity.blogspot.com/2014/01/ffmpeg-and-thousand-fixes.html
|
||||
|
||||
Effortlessly getting comparable results with state-of-the-art symbolic execution
|
||||
in equally complex software still seems fairly unlikely, and hasn't been
|
||||
demonstrated in practice so far.
|
||||
|
||||
But I digress; ultimately, attribution is hard, and glorying the fundamental
|
||||
concepts behind AFL is probably a waste of time. The devil is very much in the
|
||||
often-overlooked details, which brings us to...
|
||||
|
||||
## 2. Design goals for afl-fuzz
|
||||
|
||||
In short, I believe that the current implementation of afl-fuzz takes care of
|
||||
several itches that seemed impossible to scratch with other tools:
|
||||
|
||||
1) Speed. It's genuinely hard to compete with brute force when your "smart"
|
||||
approach is resource-intensive. If your instrumentation makes it 10x more
|
||||
likely to find a bug, but runs 100x slower, your users are getting a bad
|
||||
deal.
|
||||
|
||||
To avoid starting with a handicap, `afl-fuzz` is meant to let you fuzz most of
|
||||
the intended targets at roughly their native speed - so even if it doesn't
|
||||
add value, you do not lose much.
|
||||
|
||||
On top of this, the tool leverages instrumentation to actually reduce the
|
||||
amount of work in a couple of ways: for example, by carefully trimming the
|
||||
corpus or skipping non-functional but non-trimmable regions in the input
|
||||
files.
|
||||
|
||||
2) Rock-solid reliability. It's hard to compete with brute force if your
|
||||
approach is brittle and fails unexpectedly. Automated testing is attractive
|
||||
because it's simple to use and scalable; anything that goes against these
|
||||
principles is an unwelcome trade-off and means that your tool will be used
|
||||
less often and with less consistent results.
|
||||
|
||||
Most of the approaches based on symbolic execution, taint tracking, or
|
||||
complex syntax-aware instrumentation are currently fairly unreliable with
|
||||
real-world targets. Perhaps more importantly, their failure modes can render
|
||||
them strictly worse than "dumb" tools, and such degradation can be difficult
|
||||
for less experienced users to notice and correct.
|
||||
|
||||
In contrast, `afl-fuzz` is designed to be rock solid, chiefly by keeping it
|
||||
simple. In fact, at its core, it's designed to be just a very good
|
||||
traditional fuzzer with a wide range of interesting, well-researched
|
||||
strategies to go by. The fancy parts just help it focus the effort in
|
||||
places where it matters the most.
|
||||
|
||||
3) Simplicity. The author of a testing framework is probably the only person
|
||||
who truly understands the impact of all the settings offered by the tool -
|
||||
and who can dial them in just right. Yet, even the most rudimentary fuzzer
|
||||
frameworks often come with countless knobs and fuzzing ratios that need to
|
||||
be guessed by the operator ahead of the time. This can do more harm than
|
||||
good.
|
||||
|
||||
AFL is designed to avoid this as much as possible. The three knobs you
|
||||
can play with are the output file, the memory limit, and the ability to
|
||||
override the default, auto-calibrated timeout. The rest is just supposed to
|
||||
work. When it doesn't, user-friendly error messages outline the probable
|
||||
causes and workarounds, and get you back on track right away.
|
||||
|
||||
4) Chainability. Most general-purpose fuzzers can't be easily employed
|
||||
against resource-hungry or interaction-heavy tools, necessitating the
|
||||
creation of custom in-process fuzzers or the investment of massive CPU
|
||||
power (most of which is wasted on tasks not directly related to the code
|
||||
we actually want to test).
|
||||
|
||||
AFL tries to scratch this itch by allowing users to use more lightweight
|
||||
targets (e.g., standalone image parsing libraries) to create small
|
||||
corpora of interesting test cases that can be fed into a manual testing
|
||||
process or a UI harness later on.
|
||||
|
||||
As mentioned in [technical_details.md](technical_details.md), AFL does all this not by systematically
|
||||
applying a single overarching CS concept, but by experimenting with a variety
|
||||
of small, complementary methods that were shown to reliably yields results
|
||||
better than chance. The use of instrumentation is a part of that toolkit, but is
|
||||
far from being the most important one.
|
||||
|
||||
Ultimately, what matters is that `afl-fuzz` is designed to find cool bugs - and
|
||||
has a pretty robust track record of doing just that.
|
@ -34,6 +34,12 @@ Mentor: any
|
||||
Other programming languages also use llvm hence they could (easily?) supported
|
||||
for fuzzing, e.g. mono, swift, go, kotlin native, fortran, ...
|
||||
|
||||
GCC also supports: Objective-C, Fortran, Ada, Go, and D
|
||||
(according to [Gcc homepage](https://gcc.gnu.org/))
|
||||
|
||||
LLVM is also used by: Rust, LLGo (Go), kaleidoscope (Haskell), flang (Fortran), emscripten (JavaScript, WASM), ilwasm (CIL (C#))
|
||||
(according to [LLVM frontends](https://gist.github.com/axic/62d66fb9d8bccca6cc48fa9841db9241))
|
||||
|
||||
Mentor: vanhauser-thc
|
||||
|
||||
## Machine Learning
|
||||
|
@ -1,157 +0,0 @@
|
||||
# Notes for using ASAN with afl-fuzz
|
||||
|
||||
This file discusses some of the caveats for fuzzing under ASAN, and suggests
|
||||
a handful of alternatives. See README.md for the general instruction manual.
|
||||
|
||||
## 1) Short version
|
||||
|
||||
ASAN on 64-bit systems requests a lot of memory in a way that can't be easily
|
||||
distinguished from a misbehaving program bent on crashing your system.
|
||||
|
||||
Because of this, fuzzing with ASAN is recommended only in four scenarios:
|
||||
|
||||
- On 32-bit systems, where we can always enforce a reasonable memory limit
|
||||
(-m 800 or so is a good starting point),
|
||||
|
||||
- On 64-bit systems only if you can do one of the following:
|
||||
|
||||
- Compile the binary in 32-bit mode (gcc -m32),
|
||||
|
||||
- Precisely gauge memory needs using http://jwilk.net/software/recidivm .
|
||||
|
||||
- Limit the memory available to process using cgroups on Linux (see
|
||||
utils/asan_cgroups).
|
||||
|
||||
To compile with ASAN, set AFL_USE_ASAN=1 before calling 'make clean all'. The
|
||||
afl-gcc / afl-clang wrappers will pick that up and add the appropriate flags.
|
||||
Note that ASAN is incompatible with -static, so be mindful of that.
|
||||
|
||||
(You can also use AFL_USE_MSAN=1 to enable MSAN instead.)
|
||||
|
||||
When compiling with AFL_USE_LSAN, the leak sanitizer will normally run
|
||||
when the program exits. In order to utilize this check at different times,
|
||||
such as at the end of a loop, you may use the macro __AFL_LEAK_CHECK();.
|
||||
This macro will report a crash in afl-fuzz if any memory is left leaking
|
||||
at this stage. You can also use LSAN_OPTIONS and a supressions file
|
||||
for more fine-tuned checking, however make sure you keep exitcode=23.
|
||||
|
||||
NOTE: if you run several secondary instances, only one should run the target
|
||||
compiled with ASAN (and UBSAN, CFISAN), the others should run the target with
|
||||
no sanitizers compiled in.
|
||||
|
||||
There is also the option of generating a corpus using a non-ASAN binary, and
|
||||
then feeding it to an ASAN-instrumented one to check for bugs. This is faster,
|
||||
and can give you somewhat comparable results. You can also try using
|
||||
libdislocator (see [utils/libdislocator/README.dislocator.md](../utils/libdislocator/README.dislocator.md) in the parent directory) as a
|
||||
lightweight and hassle-free (but less thorough) alternative.
|
||||
|
||||
## 2) Long version
|
||||
|
||||
ASAN allocates a huge region of virtual address space for bookkeeping purposes.
|
||||
Most of this is never actually accessed, so the OS never has to allocate any
|
||||
real pages of memory for the process, and the VM grabbed by ASAN is essentially
|
||||
"free" - but the mapping counts against the standard OS-enforced limit
|
||||
(RLIMIT_AS, aka ulimit -v).
|
||||
|
||||
On our end, afl-fuzz tries to protect you from processes that go off-rails
|
||||
and start consuming all the available memory in a vain attempt to parse a
|
||||
malformed input file. This happens surprisingly often, so enforcing such a limit
|
||||
is important for almost any fuzzer: the alternative is for the kernel OOM
|
||||
handler to step in and start killing random processes to free up resources.
|
||||
Needless to say, that's not a very nice prospect to live with.
|
||||
|
||||
Unfortunately, un*x systems offer no portable way to limit the amount of
|
||||
pages actually given to a process in a way that distinguishes between that
|
||||
and the harmless "land grab" done by ASAN. In principle, there are three standard
|
||||
ways to limit the size of the heap:
|
||||
|
||||
- The RLIMIT_AS mechanism (ulimit -v) caps the size of the virtual space -
|
||||
but as noted, this pays no attention to the number of pages actually
|
||||
in use by the process, and doesn't help us here.
|
||||
|
||||
- The RLIMIT_DATA mechanism (ulimit -d) seems like a good fit, but it applies
|
||||
only to the traditional sbrk() / brk() methods of requesting heap space;
|
||||
modern allocators, including the one in glibc, routinely rely on mmap()
|
||||
instead, and circumvent this limit completely.
|
||||
|
||||
- Finally, the RLIMIT_RSS limit (ulimit -m) sounds like what we need, but
|
||||
doesn't work on Linux - mostly because nobody felt like implementing it.
|
||||
|
||||
There are also cgroups, but they are Linux-specific, not universally available
|
||||
even on Linux systems, and they require root permissions to set up; I'm a bit
|
||||
hesitant to make afl-fuzz require root permissions just for that. That said,
|
||||
if you are on Linux and want to use cgroups, check out the contributed script
|
||||
that ships in utils/asan_cgroups/.
|
||||
|
||||
In settings where cgroups aren't available, we have no nice, portable way to
|
||||
avoid counting the ASAN allocation toward the limit. On 32-bit systems, or for
|
||||
binaries compiled in 32-bit mode (-m32), this is not a big deal: ASAN needs
|
||||
around 600-800 MB or so, depending on the compiler - so all you need to do is
|
||||
to specify -m that is a bit higher than that.
|
||||
|
||||
On 64-bit systems, the situation is more murky, because the ASAN allocation
|
||||
is completely outlandish - around 17.5 TB in older versions, and closer to
|
||||
20 TB with newest ones. The actual amount of memory on your system is
|
||||
(probably!) just a tiny fraction of that - so unless you dial the limit
|
||||
with surgical precision, you will get no protection from OOM bugs.
|
||||
|
||||
On my system, the amount of memory grabbed by ASAN with a slightly older
|
||||
version of gcc is around 17,825,850 MB; for newest clang, it's 20,971,600.
|
||||
But there is no guarantee that these numbers are stable, and if you get them
|
||||
wrong by "just" a couple gigs or so, you will be at risk.
|
||||
|
||||
To get the precise number, you can use the recidivm tool developed by Jakub
|
||||
Wilk (http://jwilk.net/software/recidivm). In absence of this, ASAN is *not*
|
||||
recommended when fuzzing 64-bit binaries, unless you are confident that they
|
||||
are robust and enforce reasonable memory limits (in which case, you can
|
||||
specify '-m none' when calling afl-fuzz).
|
||||
|
||||
Using recidivm or running with no limits aside, there are two other decent
|
||||
alternatives: build a corpus of test cases using a non-ASAN binary, and then
|
||||
examine them with ASAN, Valgrind, or other heavy-duty tools in a more
|
||||
controlled setting; or compile the target program with -m32 (32-bit mode)
|
||||
if your system supports that.
|
||||
|
||||
## 3) Interactions with the QEMU mode
|
||||
|
||||
ASAN, MSAN, and other sanitizers appear to be incompatible with QEMU user
|
||||
emulation, so please do not try to use them with the -Q option; QEMU doesn't
|
||||
seem to appreciate the shadow VM trick used by these tools, and will likely
|
||||
just allocate all your physical memory, then crash.
|
||||
|
||||
You can, however, use QASan to run binaries that are not instrumented with ASan
|
||||
under QEMU with the AFL++ instrumentation.
|
||||
|
||||
https://github.com/andreafioraldi/qasan
|
||||
|
||||
## 4) ASAN and OOM crashes
|
||||
|
||||
By default, ASAN treats memory allocation failures as fatal errors, immediately
|
||||
causing the program to crash. Since this is a departure from normal POSIX
|
||||
semantics (and creates the appearance of security issues in otherwise
|
||||
properly-behaving programs), we try to disable this by specifying
|
||||
allocator_may_return_null=1 in ASAN_OPTIONS.
|
||||
|
||||
Unfortunately, it's been reported that this setting still causes ASAN to
|
||||
trigger phantom crashes in situations where the standard allocator would
|
||||
simply return NULL. If this is interfering with your fuzzing jobs, you may
|
||||
want to cc: yourself on this bug:
|
||||
|
||||
https://bugs.llvm.org/show_bug.cgi?id=22026
|
||||
|
||||
## 5) What about UBSAN?
|
||||
|
||||
New versions of UndefinedBehaviorSanitizer offers the
|
||||
-fsanitize=undefined-trap-on-error compiler flag that tells UBSan to insert an
|
||||
istruction that will cause SIGILL (ud2 on x86) when an undefined behaviour
|
||||
is detected. This is the option that you want to use when combining AFL++
|
||||
and UBSan.
|
||||
|
||||
AFL_USE_UBSAN=1 env var will add this compiler flag to afl-clang-fast,
|
||||
afl-gcc-fast and afl-gcc for you.
|
||||
|
||||
Old versions of UBSAN don't offer a consistent way
|
||||
to abort() on fault conditions or to terminate with a distinctive exit code
|
||||
but there are some versions of the library can be binary-patched to address this
|
||||
issue. You can also preload a shared library that substitute all the UBSan
|
||||
routines used to report errors with abort().
|
@ -1,7 +1,11 @@
|
||||
# Tips for parallel fuzzing
|
||||
|
||||
This document talks about synchronizing afl-fuzz jobs on a single machine
|
||||
or across a fleet of systems. See README.md for the general instruction manual.
|
||||
This document talks about synchronizing afl-fuzz jobs on a single machine
|
||||
or across a fleet of systems. See README.md for the general instruction manual.
|
||||
|
||||
Note that this document is rather outdated. please refer to the main document
|
||||
section on multiple core usage [../README.md#Using multiple cores](../README.md#b-using-multiple-coresthreads)
|
||||
for up to date strategies!
|
||||
|
||||
## 1) Introduction
|
||||
|
||||
|
@ -48,13 +48,9 @@ be then manually fed to a more resource-hungry program later on.
|
||||
Also note that reading the fuzzing input via stdin is faster than reading from
|
||||
a file.
|
||||
|
||||
## 3. Use LLVM instrumentation
|
||||
## 3. Use LLVM persistent instrumentation
|
||||
|
||||
When fuzzing slow targets, you can gain 20-100% performance improvement by
|
||||
using the LLVM-based instrumentation mode described in [the instrumentation README](../instrumentation/README.llvm.md).
|
||||
Note that this mode requires the use of clang and will not work with GCC.
|
||||
|
||||
The LLVM mode also offers a "persistent", in-process fuzzing mode that can
|
||||
The LLVM mode offers a "persistent", in-process fuzzing mode that can
|
||||
work well for certain types of self-contained libraries, and for fast targets,
|
||||
can offer performance gains up to 5-10x; and a "deferred fork server" mode
|
||||
that can offer huge benefits for programs with high startup overhead. Both
|
||||
@ -138,8 +134,7 @@ misses, or similar factors, but they are less likely to be a concern.)
|
||||
|
||||
## 7. Keep memory use and timeouts in check
|
||||
|
||||
If you have increased the `-m` or `-t` limits more than truly necessary, consider
|
||||
dialing them back down.
|
||||
Consider setting low values for `-m` and `-t`.
|
||||
|
||||
For programs that are nominally very fast, but get sluggish for some inputs,
|
||||
you can also try setting `-t` values that are more punishing than what `afl-fuzz`
|
||||
@ -164,6 +159,20 @@ There are several OS-level factors that may affect fuzzing speed:
|
||||
- Network filesystems, either used for fuzzer input / output, or accessed by
|
||||
the fuzzed binary to read configuration files (pay special attention to the
|
||||
home directory - many programs search it for dot-files).
|
||||
- Disable all the spectre, meltdown etc. security countermeasures in the
|
||||
kernel if your machine is properly separated:
|
||||
|
||||
```
|
||||
ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off
|
||||
no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable
|
||||
nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off
|
||||
spectre_v2=off stf_barrier=off
|
||||
```
|
||||
In most Linux distributions you can put this into a `/etc/default/grub`
|
||||
variable.
|
||||
|
||||
The following list of changes are made when executing `afl-system-config`:
|
||||
|
||||
- On-demand CPU scaling. The Linux `ondemand` governor performs its analysis
|
||||
on a particular schedule and is known to underestimate the needs of
|
||||
short-lived processes spawned by `afl-fuzz` (or any other fuzzer). On Linux,
|
||||
@ -196,26 +205,4 @@ There are several OS-level factors that may affect fuzzing speed:
|
||||
Setting a different scheduling policy for the fuzzer process - say
|
||||
`SCHED_RR` - can usually speed things up, too, but needs to be done with
|
||||
care.
|
||||
- Use the `afl-system-config` script to set all proc/sys settings above in one go.
|
||||
- Disable all the spectre, meltdown etc. security countermeasures in the
|
||||
kernel if your machine is properly separated:
|
||||
|
||||
```
|
||||
ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off
|
||||
no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable
|
||||
nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off
|
||||
spectre_v2=off stf_barrier=off
|
||||
```
|
||||
In most Linux distributions you can put this into a `/etc/default/grub`
|
||||
variable.
|
||||
|
||||
## 9. If all other options fail, use `-d`
|
||||
|
||||
For programs that are genuinely slow, in cases where you really can't escape
|
||||
using huge input files, or when you simply want to get quick and dirty results
|
||||
early on, you can always resort to the `-d` mode.
|
||||
|
||||
The mode causes `afl-fuzz` to skip all the deterministic fuzzing steps, which
|
||||
makes output a lot less neat and can ultimately make the testing a bit less
|
||||
in-depth, but it will give you an experience more familiar from other fuzzing
|
||||
tools.
|
||||
|
@ -1,32 +0,0 @@
|
||||
# afl++'s power schedules based on AFLfast
|
||||
|
||||
<a href="https://mboehme.github.io/paper/CCS16.pdf"><img src="https://mboehme.github.io/paper/CCS16.png" align="right" width="250"></a>
|
||||
Power schedules implemented by Marcel Böhme \<marcel.boehme@acm.org\>.
|
||||
AFLFast is an extension of AFL which is written and maintained by
|
||||
Michal Zalewski \<lcamtuf@google.com\>.
|
||||
|
||||
AFLfast has helped in the success of Team Codejitsu at the finals of the DARPA Cyber Grand Challenge where their bot Galactica took **2nd place** in terms of #POVs proven (see red bar at https://www.cybergrandchallenge.com/event#results). AFLFast exposed several previously unreported CVEs that could not be exposed by AFL in 24 hours and otherwise exposed vulnerabilities significantly faster than AFL while generating orders of magnitude more unique crashes.
|
||||
|
||||
Essentially, we observed that most generated inputs exercise the same few "high-frequency" paths and developed strategies to gravitate towards low-frequency paths, to stress significantly more program behavior in the same amount of time. We devised several **search strategies** that decide in which order the seeds should be fuzzed and **power schedules** that smartly regulate the number of inputs generated from a seed (i.e., the time spent fuzzing a seed). We call the number of inputs generated from a seed, the seed's **energy**.
|
||||
|
||||
We find that AFL's exploitation-based constant schedule assigns **too much energy to seeds exercising high-frequency paths** (e.g., paths that reject invalid inputs) and not enough energy to seeds exercising low-frequency paths (e.g., paths that stress interesting behaviors). Technically, we modified the computation of a seed's performance score (`calculate_score`), which seed is marked as favourite (`update_bitmap_score`), and which seed is chosen next from the circular queue (`main`). We implemented the following schedules (in the order of their effectiveness, best first):
|
||||
|
||||
| AFL flag | Power Schedule |
|
||||
| ------------- | -------------------------- |
|
||||
| `-p explore` |  |
|
||||
| `-p fast` (default)| =\\min\\left(\\frac{\\alpha(i)}{\\beta}\\cdot\\frac{2^{s(i)}}{f(i)},M\\right)) |
|
||||
| `-p coe` |  |
|
||||
| `-p quad` |  |
|
||||
| `-p lin` |  |
|
||||
| `-p exploit` (AFL) |  |
|
||||
| `-p mmopt` | Experimental: `explore` with no weighting to runtime and increased weighting on the last 5 queue entries |
|
||||
| `-p rare` | Experimental: `rare` puts focus on queue entries that hit rare edges |
|
||||
| `-p seek` | Experimental: `seek` is EXPLORE but ignoring the runtime of the queue input and less focus on the size |
|
||||
where *α(i)* is the performance score that AFL uses to compute for the seed input *i*, *β(i)>1* is a constant, *s(i)* is the number of times that seed *i* has been chosen from the queue, *f(i)* is the number of generated inputs that exercise the same path as seed *i*, and *μ* is the average number of generated inputs exercising a path.
|
||||
|
||||
More details can be found in the paper that was accepted at the [23rd ACM Conference on Computer and Communications Security (CCS'16)](https://www.sigsac.org/ccs/CCS2016/accepted-papers/).
|
||||
|
||||
PS: In parallel mode (several instances with shared queue), we suggest to run the main node using the exploit schedule (-p exploit) and the secondary nodes with a combination of cut-off-exponential (-p coe), exponential (-p fast; default), and explore (-p explore) schedules. In single mode, the default settings will do. **EDIT:** In parallel mode, AFLFast seems to perform poorly because the path probability estimates are incorrect for the imported seeds. Pull requests to fix this issue by syncing the estimates across instances are appreciated :)
|
||||
|
||||
Copyright 2013, 2014, 2015, 2016 Google Inc. All rights reserved.
|
||||
Released under terms and conditions of Apache License, Version 2.0.
|
@ -1,5 +1,9 @@
|
||||
# Technical "whitepaper" for afl-fuzz
|
||||
|
||||
|
||||
NOTE: this document is rather outdated!
|
||||
|
||||
|
||||
This document provides a quick overview of the guts of American Fuzzy Lop.
|
||||
See README.md for the general instruction manual; and for a discussion of
|
||||
motivations and design goals behind AFL, see historical_notes.md.
|
||||
|
2
frida_mode/.gitignore
vendored
2
frida_mode/.gitignore
vendored
@ -3,3 +3,5 @@ frida_test.dat
|
||||
qemu_test.dat
|
||||
frida_out/**
|
||||
qemu_out/**
|
||||
ts/dist/
|
||||
ts/node_modules/
|
||||
|
@ -6,6 +6,11 @@ INCLUDES:=$(wildcard $(INC_DIR)*.h)
|
||||
BUILD_DIR:=$(PWD)build/
|
||||
OBJ_DIR:=$(BUILD_DIR)obj/
|
||||
|
||||
JS_DIR:=$(SRC_DIR)js/
|
||||
JS_NAME:=api.js
|
||||
JS:=$(JS_DIR)$(JS_NAME)
|
||||
JS_SRC:=$(BUILD_DIR)api.c
|
||||
JS_OBJ:=$(BUILD_DIR)api.o
|
||||
SOURCES:=$(wildcard $(SRC_DIR)**/*.c) $(wildcard $(SRC_DIR)*.c)
|
||||
OBJS:=$(foreach src,$(SOURCES),$(OBJ_DIR)$(notdir $(patsubst %.c, %.o, $(src))))
|
||||
CFLAGS+=-fPIC \
|
||||
@ -14,18 +19,19 @@ CFLAGS+=-fPIC \
|
||||
-g \
|
||||
-O3 \
|
||||
-funroll-loops \
|
||||
-ffunction-sections \
|
||||
|
||||
RT_CFLAGS:=-Wno-unused-parameter \
|
||||
AFL_CFLAGS:=-Wno-unused-parameter \
|
||||
-Wno-sign-compare \
|
||||
-Wno-unused-function \
|
||||
-Wno-unused-result \
|
||||
-Wno-int-to-pointer-cast \
|
||||
-Wno-pointer-sign
|
||||
|
||||
LDFLAGS+=-shared \
|
||||
-lpthread \
|
||||
-lresolv \
|
||||
-ldl \
|
||||
-z noexecstack \
|
||||
-ldl
|
||||
|
||||
ifdef DEBUG
|
||||
CFLAGS+=-Werror \
|
||||
@ -47,6 +53,10 @@ ifeq "$(ARCH)" "aarch64"
|
||||
ARCH:=arm64
|
||||
endif
|
||||
|
||||
ifeq "$(ARCH)" "armv7l"
|
||||
ARCH:=armhf
|
||||
endif
|
||||
|
||||
ifeq "$(ARCH)" "i686"
|
||||
ARCH:=x86
|
||||
endif
|
||||
@ -54,11 +64,15 @@ endif
|
||||
|
||||
ifeq "$(shell uname)" "Darwin"
|
||||
OS:=macos
|
||||
RT_CFLAGS:=$(RT_CFLAGS) -Wno-deprecated-declarations
|
||||
AFL_CFLAGS:=$(AFL_CFLAGS) -Wno-deprecated-declarations
|
||||
else
|
||||
ifdef DEBUG
|
||||
RT_CFLAGS:=$(RT_CFLAGS) -Wno-prio-ctor-dtor
|
||||
AFL_CFLAGS:=$(AFL_CFLAGS) -Wno-prio-ctor-dtor
|
||||
endif
|
||||
LDFLAGS+= -z noexecstack \
|
||||
-Wl,--gc-sections \
|
||||
-Wl,--exclude-libs,ALL
|
||||
LDSCRIPT:=-Wl,--version-script=$(PWD)frida.map
|
||||
endif
|
||||
|
||||
ifeq "$(shell uname)" "Linux"
|
||||
@ -69,30 +83,52 @@ ifndef OS
|
||||
$(error "Operating system unsupported")
|
||||
endif
|
||||
|
||||
ifeq "$(ARCH)" "arm64"
|
||||
# 15.0.0 Not released for aarch64 yet
|
||||
GUM_DEVKIT_VERSION=14.2.18
|
||||
GUM_DEVKIT_FILENAME=frida-gum-devkit-$(GUM_DEVKIT_VERSION)-$(OS)-$(ARCH).tar.xz
|
||||
else
|
||||
ifeq "$(ARCH)" "armhf"
|
||||
GUM_DEVKIT_VERSION=14.2.18
|
||||
else
|
||||
GUM_DEVKIT_VERSION=15.0.0
|
||||
endif
|
||||
endif
|
||||
GUM_DEVKIT_FILENAME=frida-gumjs-devkit-$(GUM_DEVKIT_VERSION)-$(OS)-$(ARCH).tar.xz
|
||||
GUM_DEVKIT_URL="https://github.com/frida/frida/releases/download/$(GUM_DEVKIT_VERSION)/$(GUM_DEVKIT_FILENAME)"
|
||||
|
||||
GUM_DEVKIT_TARBALL:=$(FRIDA_BUILD_DIR)$(GUM_DEVKIT_FILENAME)
|
||||
GUM_DEVIT_LIBRARY=$(FRIDA_BUILD_DIR)libfrida-gum.a
|
||||
GUM_DEVIT_HEADER=$(FRIDA_BUILD_DIR)frida-gum.h
|
||||
GUM_DEVIT_LIBRARY=$(FRIDA_BUILD_DIR)libfrida-gumjs.a
|
||||
GUM_DEVIT_HEADER=$(FRIDA_BUILD_DIR)frida-gumjs.h
|
||||
|
||||
FRIDA_DIR:=$(PWD)build/frida-source/
|
||||
FRIDA_MAKEFILE:=$(FRIDA_DIR)Makefile
|
||||
FRIDA_GUM:=$(FRIDA_DIR)build/frida-linux-x86_64/lib/libfrida-gum-1.0.a
|
||||
FRIDA_GUM:=$(FRIDA_DIR)build/frida-linux-x86_64/lib/libfrida-gumjs-1.0.a
|
||||
FRIDA_GUM_DEVKIT_DIR:=$(FRIDA_DIR)build/gum-devkit/
|
||||
FRIDA_GUM_DEVKIT_HEADER:=$(FRIDA_GUM_DEVKIT_DIR)frida-gum.h
|
||||
FRIDA_GUM_DEVKIT_TARBALL:=$(FRIDA_DIR)build/frida-gum-devkit-$(GUM_DEVKIT_VERSION)-$(OS)-$(ARCH).tar
|
||||
FRIDA_GUM_DEVKIT_HEADER:=$(FRIDA_GUM_DEVKIT_DIR)frida-gumjs.h
|
||||
FRIDA_GUM_DEVKIT_TARBALL:=$(FRIDA_DIR)build/frida-gumjs-devkit-$(GUM_DEVKIT_VERSION)-$(OS)-$(ARCH).tar
|
||||
FRIDA_GUM_DEVKIT_COMPRESSED_TARBALL:=$(FRIDA_DIR)build/$(GUM_DEVKIT_FILENAME)
|
||||
|
||||
AFL_COMPILER_RT_SRC:=$(ROOT)instrumentation/afl-compiler-rt.o.c
|
||||
AFL_COMPILER_RT_OBJ:=$(OBJ_DIR)afl-compiler-rt.o
|
||||
|
||||
.PHONY: all 32 clean format $(FRIDA_GUM)
|
||||
AFL_PERFORMANCE_SRC:=$(ROOT)src/afl-performance.c
|
||||
AFL_PERFORMANCE_OBJ:=$(OBJ_DIR)afl-performance.o
|
||||
|
||||
HOOK_DIR:=$(PWD)hook/
|
||||
AFLPP_FRIDA_DRIVER_HOOK_SRC=$(HOOK_DIR)frida_hook.c
|
||||
AFLPP_FRIDA_DRIVER_HOOK_OBJ=$(BUILD_DIR)frida_hook.so
|
||||
|
||||
AFLPP_QEMU_DRIVER_HOOK_SRC:=$(HOOK_DIR)qemu_hook.c
|
||||
AFLPP_QEMU_DRIVER_HOOK_OBJ:=$(BUILD_DIR)qemu_hook.so
|
||||
|
||||
BIN2C:=$(BUILD_DIR)bin2c
|
||||
BIN2C_SRC:=$(PWD)util/bin2c.c
|
||||
|
||||
.PHONY: all 32 clean format hook $(FRIDA_GUM)
|
||||
|
||||
############################## ALL #############################################
|
||||
|
||||
all: $(FRIDA_TRACE)
|
||||
all: $(FRIDA_TRACE) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) $(AFLPP_QEMU_DRIVER_HOOK_OBJ)
|
||||
|
||||
32:
|
||||
CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
|
||||
@ -112,7 +148,7 @@ $(FRIDA_GUM): $(FRIDA_MAKEFILE)
|
||||
cd $(FRIDA_DIR) && make gum-linux-$(ARCH)
|
||||
|
||||
$(FRIDA_GUM_DEVKIT_HEADER): $(FRIDA_GUM)
|
||||
$(FRIDA_DIR)releng/devkit.py frida-gum linux-$(ARCH) $(FRIDA_DIR)build/gum-devkit/
|
||||
$(FRIDA_DIR)releng/devkit.py frida-gumjs linux-$(ARCH) $(FRIDA_DIR)build/gum-devkit/
|
||||
|
||||
$(FRIDA_GUM_DEVKIT_TARBALL): $(FRIDA_GUM_DEVKIT_HEADER)
|
||||
cd $(FRIDA_GUM_DEVKIT_DIR) && tar cvf $(FRIDA_GUM_DEVKIT_TARBALL) .
|
||||
@ -133,22 +169,48 @@ $(GUM_DEVKIT_TARBALL): | $(FRIDA_BUILD_DIR)
|
||||
wget -O $@ $(GUM_DEVKIT_URL)
|
||||
endif
|
||||
|
||||
$(GUM_DEVIT_LIBRARY): | $(GUM_DEVKIT_TARBALL)
|
||||
tar Jxvf $(GUM_DEVKIT_TARBALL) -C $(FRIDA_BUILD_DIR)
|
||||
$(GUM_DEVIT_LIBRARY): $(GUM_DEVKIT_TARBALL)
|
||||
tar Jxvfm $(GUM_DEVKIT_TARBALL) -C $(FRIDA_BUILD_DIR)
|
||||
|
||||
$(GUM_DEVIT_HEADER): | $(GUM_DEVKIT_TARBALL)
|
||||
tar Jxvf $(GUM_DEVKIT_TARBALL) -C $(FRIDA_BUILD_DIR)
|
||||
$(GUM_DEVIT_HEADER): $(GUM_DEVKIT_TARBALL)
|
||||
tar Jxvfm $(GUM_DEVKIT_TARBALL) -C $(FRIDA_BUILD_DIR)
|
||||
|
||||
############################## AFL #############################################
|
||||
$(AFL_COMPILER_RT_OBJ): $(AFL_COMPILER_RT_SRC)
|
||||
$(CC) \
|
||||
$(CFLAGS) \
|
||||
$(RT_CFLAGS) \
|
||||
$(AFL_CFLAGS) \
|
||||
-I $(ROOT) \
|
||||
-I $(ROOT)include \
|
||||
-o $@ \
|
||||
-c $<
|
||||
|
||||
$(AFL_PERFORMANCE_OBJ): $(AFL_PERFORMANCE_SRC)
|
||||
$(CC) \
|
||||
$(CFLAGS) \
|
||||
$(AFL_CFLAGS) \
|
||||
-I $(ROOT) \
|
||||
-I $(ROOT)include \
|
||||
-o $@ \
|
||||
-c $<
|
||||
|
||||
############################### JS #############################################
|
||||
|
||||
$(BIN2C): $(BIN2C_SRC)
|
||||
$(CC) -D_GNU_SOURCE -o $@ $<
|
||||
|
||||
$(JS_SRC): $(JS) $(BIN2C)| $(BUILD_DIR)
|
||||
cd $(JS_DIR) && $(BIN2C) api_js $(JS) $@
|
||||
|
||||
$(JS_OBJ): $(JS_SRC) GNUmakefile
|
||||
$(CC) \
|
||||
$(CFLAGS) \
|
||||
-I $(ROOT)include \
|
||||
-I $(FRIDA_BUILD_DIR) \
|
||||
-I $(INC_DIR) \
|
||||
-c $< \
|
||||
-o $@
|
||||
|
||||
############################# SOURCE ###########################################
|
||||
|
||||
define BUILD_SOURCE
|
||||
@ -166,23 +228,36 @@ $(foreach src,$(SOURCES),$(eval $(call BUILD_SOURCE,$(src),$(OBJ_DIR)$(notdir $(
|
||||
|
||||
######################## AFL-FRIDA-TRACE #######################################
|
||||
|
||||
$(FRIDA_TRACE): $(GUM_DEVIT_LIBRARY) $(GUM_DEVIT_HEADER) $(OBJS) $(AFL_COMPILER_RT_OBJ) GNUmakefile | $(BUILD_DIR)
|
||||
$(CC) \
|
||||
$(FRIDA_TRACE): $(GUM_DEVIT_LIBRARY) $(GUM_DEVIT_HEADER) $(OBJS) $(JS_OBJ) $(AFL_COMPILER_RT_OBJ) $(AFL_PERFORMANCE_OBJ) GNUmakefile | $(BUILD_DIR)
|
||||
$(CXX) \
|
||||
$(OBJS) \
|
||||
$(JS_OBJ) \
|
||||
$(GUM_DEVIT_LIBRARY) \
|
||||
$(AFL_COMPILER_RT_OBJ) \
|
||||
$(AFL_PERFORMANCE_OBJ) \
|
||||
$(LDFLAGS) \
|
||||
$(LDSCRIPT) \
|
||||
-o $@ \
|
||||
|
||||
cp -v $(FRIDA_TRACE) $(ROOT)
|
||||
|
||||
############################# HOOK #############################################
|
||||
|
||||
$(AFLPP_FRIDA_DRIVER_HOOK_OBJ): $(AFLPP_FRIDA_DRIVER_HOOK_SRC) $(GUM_DEVIT_HEADER) | $(BUILD_DIR)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -I $(FRIDA_BUILD_DIR) $< -o $@
|
||||
|
||||
$(AFLPP_QEMU_DRIVER_HOOK_OBJ): $(AFLPP_QEMU_DRIVER_HOOK_SRC) | $(BUILD_DIR)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
|
||||
|
||||
hook: $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) $(AFLPP_QEMU_DRIVER_HOOK_OBJ)
|
||||
|
||||
############################# CLEAN ############################################
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR)
|
||||
|
||||
############################# FORMAT ###########################################
|
||||
format:
|
||||
cd $(ROOT) && echo $(SOURCES) | xargs -L1 ./.custom-format.py -i
|
||||
cd $(ROOT) && echo $(SOURCES) $(AFLPP_FRIDA_DRIVER_HOOK_SRC) $(BIN2C_SRC) | xargs -L1 ./.custom-format.py -i
|
||||
cd $(ROOT) && echo $(INCLUDES) | xargs -L1 ./.custom-format.py -i
|
||||
|
||||
############################# RUN #############################################
|
||||
|
@ -11,3 +11,6 @@ clean:
|
||||
|
||||
format:
|
||||
@gmake format
|
||||
|
||||
hook:
|
||||
@gmake hook
|
||||
|
147
frida_mode/MapDensity.md
Normal file
147
frida_mode/MapDensity.md
Normal file
@ -0,0 +1,147 @@
|
||||
# Map Density
|
||||
|
||||
# How Coverage Works
|
||||
The coverage in AFL++ works by assigning each basic block of code a unique ID
|
||||
and during execution when transitioning between blocks (e.g. by calls or jumps)
|
||||
assigning each of these edges an ID based upon the source and destination block
|
||||
ID.
|
||||
|
||||
For each individual execution of the target, a single dimensional byte array
|
||||
indexed by the edge ID is used to count how many times each edge is traversed.
|
||||
|
||||
A single dimensional cumulative byte array is also constructed where each byte
|
||||
again represents an individual edge ID, but this time, the value of the byte
|
||||
represents a range of how many times that edge has been traversed.
|
||||
|
||||
```1, 2, 3, 4-7, 8-15, 16-31, 32-127, 128+```
|
||||
|
||||
The theory is that a new path isn't particularly interesting if an edge has been
|
||||
traversed `23` instead of `24` times for example, but is interesting if an edge
|
||||
has been traversed for the very first time, or the number of times fits within a different bucket.
|
||||
|
||||
After each run, the count of times each edge is hit is compared to the values in
|
||||
the cumulative map and if it is different, then the input is kept as a new seed
|
||||
and the cumulative map is updated.
|
||||
|
||||
This mechanism is described in greater detail in the seminal
|
||||
[paper](https://lcamtuf.coredump.cx/afl/technical_details.txt) on AFL by
|
||||
[lcamtuf](https://github.com/lcamtuf).
|
||||
|
||||
# Collisions
|
||||
In black-box fuzzing, we must assume that control may flow from any block to any
|
||||
other block, since we don't know any better. Thus for a target with `n` basic
|
||||
blocks of code, there are `n * n` potential edges. As we can see, even with a
|
||||
small number of edges, a very large map will be required so that we have space
|
||||
to fit them all. Even if our target only had `1024` blocks, this would require a
|
||||
map containing `1048576` entries (or 1Mb in size).
|
||||
|
||||
Whilst this may not seem like a lot of memory, it causes problems for two reasons. Firstly, the processing step after each execution must now process much more
|
||||
data, and secondly a map this size is unlikely to fit within the L2 cache of the processor. Since this is a very hot code path, we are likely to pay a very heavy
|
||||
performance cost.
|
||||
|
||||
Therefore, we must accept that not all edges can have a unique and that
|
||||
therefore there will be collisions. This means that if the fuzzer finds a new
|
||||
path by uncovering an edge which was not previously found, but that the same
|
||||
edge ID is used by another edge, then it may go completely unnoticed. This is
|
||||
obviously undesirable, but equally if our map is too large, then we will not be
|
||||
able to process as many potential inputs in the same time and hence not uncover
|
||||
edges for that reason. Thus a careful trade-off of map size must be made.
|
||||
|
||||
# Block & Edge Numbering
|
||||
Since the original AFL, blocks and edges have always been numbered in the same
|
||||
way as we can see from the following C snippet from the whitepaper.
|
||||
|
||||
```c
|
||||
cur_location = (block_address >> 4) ^ (block_address << 8);
|
||||
shared_mem[cur_location ^ prev_location]++;
|
||||
prev_location = cur_location >> 1;
|
||||
|
||||
```
|
||||
|
||||
Each block ID is generated by performing a shift and XOR on its address. Then
|
||||
the edge ID is calculated as `E = B ^ (B' >> 1)`. Here, we can make two
|
||||
observations. In fact, the edge ID is also masked to ensure it is less than the
|
||||
size of the map being used.
|
||||
|
||||
## Block IDs
|
||||
Firstly, the block ID doesn't have very good entropy. If we consider the address
|
||||
of the block, then whilst each block has a unique ID, it isn't necessarily very
|
||||
evenly distributed.
|
||||
|
||||
We start with a large address, and need to discard a large number of the bits to
|
||||
generate a block ID which is within range. But how do we choose the unique bits
|
||||
of the address verus those which are the same for every block? The high bits of
|
||||
the address may simply be all `0s` or all `1s` to make the address cannonical,
|
||||
the middle portion of the address may be the same for all blocks (since if they
|
||||
are all within the same binary, then they will all be adjacent in memory), and
|
||||
on some systems, even the low bits may have poor entropy as some use fixed
|
||||
length aligned instructions. Then we need to consider that a portion of each
|
||||
binary may contain the `.data` or `.bss` sections and so may not contain any
|
||||
blocks of code at all.
|
||||
|
||||
## Edge IDs
|
||||
Secondly, we can observe that when we generate an edge ID from the source and
|
||||
destination block IDs, we perform a right shift on the source block ID. Whilst
|
||||
there are good reasons as set out in the whitepaper why such a transform is
|
||||
applied, in so doing, we dispose of `1` bit of precious entropy in our source
|
||||
block ID.
|
||||
|
||||
All together, this means that some edge IDs may be more popular than others.
|
||||
This means that some portions of the map may be very densly populated with large
|
||||
numbers of edges, whilst others may be very sparsely populated, or not populated
|
||||
at all.
|
||||
|
||||
# Improvements
|
||||
One of the main reaons why this algorithm selected, is performance. All of the
|
||||
operations are very quick to perform and given we may be carrying this out for
|
||||
every block of code we execute, performance is critical.
|
||||
|
||||
However, the design of the binary instrumentation modes of AFL++ has moved on.
|
||||
Both QEMU and FRIDA modes use a two stage process when executing a target
|
||||
application. Each block is first compiled or instrumented, and then it is
|
||||
executed. The compiled blocks can be re-used each time the target executes them.
|
||||
|
||||
Since a blocks ID is based on its address, and this is known at compile time, we
|
||||
only need to generate this ID once per block and so this ID generation no longer
|
||||
needs to be as performant. We can therefore use a hash algorithm to generate
|
||||
this ID and therefore ensure that the block IDs are more evenly distributed.
|
||||
|
||||
Edge IDs however, can only be determined at run-time. Since we don't know which
|
||||
blocks a given input will traverse until we run it. However, given our block IDs
|
||||
are now evenly distributed, generating an evenly distributed edge ID becomes
|
||||
simple. Here, the only change we make is to use a rotate operation rather than
|
||||
a shift operation so we don't lose a bit of entropy from the source ID.
|
||||
|
||||
So our new algorithm becomes:
|
||||
```c
|
||||
cur_location = hash(block_address)
|
||||
shared_mem[cur_location ^ prev_location]++;
|
||||
prev_location = rotate(cur_location, 1);
|
||||
```
|
||||
|
||||
Lastly, in the original design, the `cur_location` was always set to `0`, at the
|
||||
beginning of a run, we instead set the value of `cur_location` to `hash(0)`.
|
||||
|
||||
# Parallel Fuzzing
|
||||
Another sub-optimal aspect of the original design is that no matter how many
|
||||
instances of the fuzzer you ran in parallel, each instance numbered each block
|
||||
and so each edge with the same ID. Each instance would therefore find the same
|
||||
subset of edges collide with each other. In the event of a collision, all
|
||||
instances will hit the same road block.
|
||||
|
||||
However, if we instead use a different seed for our hashing function for each
|
||||
instance, then each will ascribe each block a different ID and hence each edge
|
||||
will be given a different edge ID. This means that whilst one instance of the
|
||||
fuzzer may find a given pair of edges collide, it is very unlikely that another
|
||||
instance will find the same pair also collide.
|
||||
|
||||
Due to the collaborative nature of parallel fuzzing, this means that whilst one
|
||||
instance may struggle to find a particular new path because the new edge
|
||||
collides, another instance will likely not encounter the same collision and thus
|
||||
be able to differentiate this new path and share it with the other instances.
|
||||
|
||||
If only a single new edge is found, and the new path is shared with an instance
|
||||
for which that edge collides, that instance may disregard it as irrelevant. In
|
||||
practice, however, the discovery of a single new edge, likely leads to several
|
||||
more edges beneath it also being found and therefore the likelihood of all of
|
||||
these being collisions is very slim.
|
@ -21,16 +21,16 @@ perhaps leverage some of its design and implementation.
|
||||
| Feature/Instrumentation | frida-mode | Notes |
|
||||
| -------------------------|:----------:|:--------------------------------------------:|
|
||||
| NeverZero | x | |
|
||||
| Persistent Mode | x | (x86/x64 only)(Only on function boundaries) |
|
||||
| Persistent Mode | x | (x86/x64/aarch64 only) |
|
||||
| LAF-Intel / CompCov | - | (CMPLOG is better 90% of the time) |
|
||||
| CMPLOG | x | (x86/x64 only) |
|
||||
| CMPLOG | x | (x86/x64/aarch64 only) |
|
||||
| Selective Instrumentation| x | |
|
||||
| Non-Colliding Coverage | - | (Not possible in binary-only instrumentation |
|
||||
| Ngram prev_loc Coverage | - | |
|
||||
| Context Coverage | - | |
|
||||
| Auto Dictionary | - | |
|
||||
| Snapshot LKM Support | - | |
|
||||
| In-Memory Test Cases | x | (x86/x64 only) |
|
||||
| In-Memory Test Cases | x | (x86/x64/aarch64 only) |
|
||||
|
||||
## Compatibility
|
||||
Currently FRIDA mode supports Linux and macOS targets on both x86/x64
|
||||
@ -75,10 +75,13 @@ following options are currently supported:
|
||||
* `AFL_FRIDA_PERSISTENT_CNT` - See `AFL_QEMU_PERSISTENT_CNT`
|
||||
* `AFL_FRIDA_PERSISTENT_HOOK` - See `AFL_QEMU_PERSISTENT_HOOK`
|
||||
* `AFL_FRIDA_PERSISTENT_RET` - See `AFL_QEMU_PERSISTENT_RET`
|
||||
* `AFL_FRIDA_PERSISTENT_RETADDR_OFFSET` - See `AFL_QEMU_PERSISTENT_RETADDR_OFFSET`
|
||||
|
||||
To enable the powerful CMPLOG mechanism, set `-c 0` for `afl-fuzz`.
|
||||
|
||||
## Scripting
|
||||
|
||||
One of the more powerful features of FRIDA mode is it's support for configuration by JavaScript, rather than using environment variables. For details of how this works see [here](Scripting.md).
|
||||
|
||||
## Performance
|
||||
|
||||
Additionally, the intention is to be able to make a direct performance
|
||||
@ -150,22 +153,29 @@ Generated block 0x7ffff75e98e2
|
||||
|
||||
***
|
||||
```
|
||||
* `AFL_FRIDA_INST_JIT` - Enable the instrumentation of Just-In-Time compiled
|
||||
code. Code is considered to be JIT if the executable segment is not backed by a
|
||||
file.
|
||||
* `AFL_FRIDA_INST_NO_OPTIMIZE` - Don't use optimized inline assembly coverage
|
||||
instrumentation (the default where available). Required to use
|
||||
`AFL_FRIDA_INST_TRACE`.
|
||||
* `AFL_FRIDA_INST_NO_PREFETCH` - Disable prefetching. By default the child will
|
||||
report instrumented blocks back to the parent so that it can also instrument
|
||||
them and they be inherited by the next child on fork.
|
||||
* `AFL_FRIDA_INST_TRACE` - Log to stdout the address of executed blocks
|
||||
`AFL_FRIDA_INST_NO_OPTIMIZE`.
|
||||
* `AFL_FRIDA_INST_SEED` - Sets the initial seed for the hash function used to
|
||||
generate block (and hence edge) IDs. Setting this to a constant value may be
|
||||
useful for debugging purposes, e.g. investigating unstable edges.
|
||||
* `AFL_FRIDA_INST_TRACE` - Log to stdout the address of executed blocks,
|
||||
implies `AFL_FRIDA_INST_NO_OPTIMIZE`.
|
||||
* `AFL_FRIDA_INST_TRACE_UNIQUE` - As per `AFL_FRIDA_INST_TRACE`, but each edge
|
||||
is logged only once, requires `AFL_FRIDA_INST_NO_OPTIMIZE`.
|
||||
* `AFL_FRIDA_OUTPUT_STDOUT` - Redirect the standard output of the target
|
||||
application to the named file (supersedes the setting of `AFL_DEBUG_CHILD`)
|
||||
* `AFL_FRIDA_OUTPUT_STDERR` - Redirect the standard error of the target
|
||||
application to the named file (supersedes the setting of `AFL_DEBUG_CHILD`)
|
||||
* `AFL_FRIDA_PERSISTENT_DEBUG` - Insert a Breakpoint into the instrumented code
|
||||
at `AFL_FRIDA_PERSISTENT_HOOK` and `AFL_FRIDA_PERSISTENT_RET` to allow the user
|
||||
to determine the value of `AFL_FRIDA_PERSISTENT_RETADDR_OFFSET` using a
|
||||
debugger.
|
||||
to detect issues in the persistent loop using a debugger.
|
||||
|
||||
```
|
||||
|
||||
@ -286,9 +296,13 @@ FASAN then adds instrumentation for any instrucutions which use memory operands
|
||||
then calls into the `__asan_loadN` and `__asan_storeN` functions provided by the DSO
|
||||
to validate memory accesses against the shadow memory.
|
||||
|
||||
# Collisions
|
||||
FRIDA mode has also introduced some improvements to reduce collisions in the map.
|
||||
See [here](MapDensity.md) for details.
|
||||
|
||||
## TODO
|
||||
|
||||
The next features to be added are Aarch64 and Aarch32 support as well as looking at
|
||||
The next features to be added are Aarch32 support as well as looking at
|
||||
potential performance improvements. The intention is to achieve feature parity with
|
||||
QEMU mode in due course. Contributions are welcome, but please get in touch to
|
||||
ensure that efforts are deconflicted.
|
||||
|
863
frida_mode/Scripting.md
Normal file
863
frida_mode/Scripting.md
Normal file
@ -0,0 +1,863 @@
|
||||
# Scripting
|
||||
FRIDA now supports the ability to configure itself using JavaScript. This allows
|
||||
the user to make use of the convenience of FRIDA's scripting engine (along with
|
||||
it's support for debug symbols and exports) to configure all of the things which
|
||||
were traditionally configured using environment variables.
|
||||
|
||||
By default FRIDA mode will look for the file `afl.js` in the current working
|
||||
directory of the target. Alternatively, a script file can be configured using
|
||||
the environment variable `AFL_FRIDA_JS_SCRIPT`.
|
||||
|
||||
This script can make use of all of the standard [frida api functions](https://frida.re/docs/javascript-api/), but FRIDA mode adds some additional functions to allow
|
||||
you to interact with FRIDA mode itself. These can all be accessed via the global
|
||||
`Afl` parameter. e.g. `Afl.print("HELLO WORLD");`,
|
||||
|
||||
If you encounter a problem with your script, then you should set the environment
|
||||
variable `AFL_DEBUG_CHILD=1` to view any diagnostic information.
|
||||
|
||||
|
||||
# Example
|
||||
Most of the time, users will likely be wanting to call the functions which configure an address (e.g. for the entry point, or the persistent address).
|
||||
|
||||
The example below uses the API [`DebugSymbol.fromName()`](https://frida.re/docs/javascript-api/#debugsymbol). Another use API is [`Module.getExportByName()`](https://frida.re/docs/javascript-api/#module).
|
||||
|
||||
```js
|
||||
/* Use Afl.print instead of console.log */
|
||||
Afl.print('******************');
|
||||
Afl.print('* AFL FRIDA MODE *');
|
||||
Afl.print('******************');
|
||||
Afl.print('');
|
||||
|
||||
/* Print some useful diagnostics stuff */
|
||||
Afl.print(`PID: ${Process.id}`);
|
||||
|
||||
new ModuleMap().values().forEach(m => {
|
||||
Afl.print(`${m.base}-${m.base.add(m.size)} ${m.name}`);
|
||||
});
|
||||
|
||||
/*
|
||||
* Configure entry-point, persistence etc. This will be what most
|
||||
* people want to do.
|
||||
*/
|
||||
const persistent_addr = DebugSymbol.fromName('main');
|
||||
Afl.print(`persistent_addr: ${persistent_addr.address}`);
|
||||
|
||||
if (persistent_addr.address.equals(ptr(0))) {
|
||||
Afl.error('Cannot find symbol main');
|
||||
}
|
||||
|
||||
const persistent_ret = DebugSymbol.fromName('slow');
|
||||
Afl.print(`persistent_ret: ${persistent_ret.address}`);
|
||||
|
||||
if (persistent_ret.address.equals(ptr(0))) {
|
||||
Afl.error('Cannot find symbol slow');
|
||||
}
|
||||
|
||||
Afl.setPersistentAddress(persistent_addr.address);
|
||||
Afl.setPersistentReturn(persistent_ret.address);
|
||||
Afl.setPersistentCount(1000000);
|
||||
|
||||
/* Control instrumentation, you may want to do this too */
|
||||
Afl.setInstrumentLibraries();
|
||||
const mod = Process.findModuleByName("libc-2.31.so")
|
||||
Afl.addExcludedRange(mod.base, mod.size);
|
||||
|
||||
/* Some useful options to configure logging */
|
||||
Afl.setStdOut("/tmp/stdout.txt");
|
||||
Afl.setStdErr("/tmp/stderr.txt");
|
||||
|
||||
/* Show the address layout. Sometimes helpful */
|
||||
Afl.setDebugMaps();
|
||||
|
||||
/*
|
||||
* If you are using these options, then things aren't going
|
||||
* very well for you.
|
||||
*/
|
||||
Afl.setInstrumentDebugFile("/tmp/instr.log");
|
||||
Afl.setPrefetchDisable();
|
||||
Afl.setInstrumentNoOptimize();
|
||||
Afl.setInstrumentEnableTracing();
|
||||
Afl.setInstrumentTracingUnique();
|
||||
Afl.setStatsFile("/tmp/stats.txt");
|
||||
Afl.setStatsInterval(1);
|
||||
Afl.setStatsTransitions();
|
||||
|
||||
/* *ALWAYS* call this when you have finished all your configuration */
|
||||
Afl.done();
|
||||
Afl.print("done");
|
||||
```
|
||||
|
||||
# Stripped Binaries
|
||||
|
||||
Lastly, if the binary you attempting to fuzz has no symbol information, and no
|
||||
exports, then the following approach can be used.
|
||||
|
||||
```js
|
||||
const module = Process.getModuleByName('target.exe');
|
||||
/* Hardcoded offset within the target image */
|
||||
const address = module.base.add(0xdeadface);
|
||||
Afl.setPersistentAddress(address);
|
||||
```
|
||||
|
||||
# Persisent Hook
|
||||
A persistent hook can be implemented using a conventional shared object, sample
|
||||
source code for a hook suitable for the prototype of `LLVMFuzzerTestOneInput`
|
||||
can be found [here](hook/hook.c). This can be configured using code similar to
|
||||
the following.
|
||||
|
||||
```js
|
||||
const path = Afl.module.path;
|
||||
const dir = path.substring(0, path.lastIndexOf("/"));
|
||||
const mod = Module.load(`${dir}/frida_mode/build/hook.so`);
|
||||
const hook = mod.getExportByName('afl_persistent_hook');
|
||||
Afl.setPersistentHook(hook);
|
||||
```
|
||||
|
||||
Alternatively, the hook can be provided by using FRIDAs built in support for `CModule`, powered by TinyCC.
|
||||
|
||||
```js
|
||||
const cm = new CModule(`
|
||||
|
||||
#include <string.h>
|
||||
#include <gum/gumdefs.h>
|
||||
|
||||
void afl_persistent_hook(GumCpuContext *regs, uint8_t *input_buf,
|
||||
uint32_t input_buf_len) {
|
||||
|
||||
memcpy((void *)regs->rdi, input_buf, input_buf_len);
|
||||
regs->rsi = input_buf_len;
|
||||
|
||||
}
|
||||
`,
|
||||
{
|
||||
memcpy: Module.getExportByName(null, 'memcpy')
|
||||
});
|
||||
Afl.setPersistentHook(cm.afl_persistent_hook);
|
||||
```
|
||||
|
||||
# Advanced Persistence
|
||||
Consider the following target code...
|
||||
```c
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void LLVMFuzzerTestOneInput(char *buf, int len) {
|
||||
|
||||
if (len < 1) return;
|
||||
buf[len] = 0;
|
||||
|
||||
// we support three input cases
|
||||
if (buf[0] == '0')
|
||||
printf("Looks like a zero to me!\n");
|
||||
else if (buf[0] == '1')
|
||||
printf("Pretty sure that is a one!\n");
|
||||
else
|
||||
printf("Neither one or zero? How quaint!\n");
|
||||
|
||||
}
|
||||
|
||||
int run(char *file) {
|
||||
|
||||
int fd = -1;
|
||||
off_t len;
|
||||
char * buf = NULL;
|
||||
size_t n_read;
|
||||
int result = -1;
|
||||
|
||||
do {
|
||||
|
||||
dprintf(STDERR_FILENO, "Running: %s\n", file);
|
||||
|
||||
fd = open(file, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
|
||||
perror("open");
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
len = lseek(fd, 0, SEEK_END);
|
||||
if (len < 0) {
|
||||
|
||||
perror("lseek (SEEK_END)");
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (lseek(fd, 0, SEEK_SET) != 0) {
|
||||
|
||||
perror("lseek (SEEK_SET)");
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
buf = malloc(len);
|
||||
if (buf == NULL) {
|
||||
|
||||
perror("malloc");
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
n_read = read(fd, buf, len);
|
||||
if (n_read != len) {
|
||||
|
||||
perror("read");
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
dprintf(STDERR_FILENO, "Running: %s: (%zd bytes)\n", file, n_read);
|
||||
|
||||
LLVMFuzzerTestOneInput(buf, len);
|
||||
dprintf(STDERR_FILENO, "Done: %s: (%zd bytes)\n", file, n_read);
|
||||
|
||||
result = 0;
|
||||
|
||||
} while (false);
|
||||
|
||||
if (buf != NULL) { free(buf); }
|
||||
|
||||
if (fd != -1) { close(fd); }
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
void slow() {
|
||||
|
||||
usleep(100000);
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
if (argc != 2) { return 1; }
|
||||
slow();
|
||||
return run(argv[1]);
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
FRIDA mode supports the replacement of any function, with an implementation
|
||||
generated by CModule. This allows for a bespoke harness to be written as
|
||||
follows:
|
||||
|
||||
```
|
||||
const slow = DebugSymbol.fromName('slow').address;
|
||||
Afl.print(`slow: ${slow}`);
|
||||
|
||||
const LLVMFuzzerTestOneInput = DebugSymbol.fromName('LLVMFuzzerTestOneInput').address;
|
||||
Afl.print(`LLVMFuzzerTestOneInput: ${LLVMFuzzerTestOneInput}`);
|
||||
|
||||
const cm = new CModule(`
|
||||
|
||||
extern unsigned char * __afl_fuzz_ptr;
|
||||
extern unsigned int * __afl_fuzz_len;
|
||||
extern void LLVMFuzzerTestOneInput(char *buf, int len);
|
||||
|
||||
void slow(void) {
|
||||
|
||||
LLVMFuzzerTestOneInput(__afl_fuzz_ptr, *__afl_fuzz_len);
|
||||
}
|
||||
`,
|
||||
{
|
||||
LLVMFuzzerTestOneInput: LLVMFuzzerTestOneInput,
|
||||
__afl_fuzz_ptr: Afl.getAflFuzzPtr(),
|
||||
__afl_fuzz_len: Afl.getAflFuzzLen()
|
||||
});
|
||||
|
||||
Afl.setEntryPoint(cm.slow);
|
||||
Afl.setPersistentAddress(cm.slow);
|
||||
Afl.setInMemoryFuzzing();
|
||||
Interceptor.replace(slow, cm.slow);
|
||||
Afl.print("done");
|
||||
Afl.done();
|
||||
```
|
||||
|
||||
Here, we replace the function `slow` with our own code. This code is then
|
||||
selected as the entry point as well as the persistent loop address.
|
||||
|
||||
**WARNING** There are two key limitations in replacing a function in this way:
|
||||
- The function which is to be replaced must not be `main` this is because this
|
||||
is the point at which FRIDA mode is initialized and at the point the the JS has
|
||||
been run, the start of the `main` function has already been instrumented and
|
||||
cached.
|
||||
- The replacement function must not call itself. e.g. in this example we
|
||||
couldn't replace `LLVMFuzzerTestOneInput` and call itself.
|
||||
|
||||
# Patching
|
||||
Consider the [following](test/js/test2.c) test code...
|
||||
|
||||
```c
|
||||
/*
|
||||
american fuzzy lop++ - a trivial program to test the build
|
||||
--------------------------------------------------------
|
||||
Originally written by Michal Zalewski
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
Copyright 2019-2020 AFLplusplus Project. All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at:
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
const uint32_t crc32_tab[] = {
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
|
||||
|
||||
...
|
||||
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
||||
};
|
||||
|
||||
uint32_t
|
||||
crc32(const void *buf, size_t size)
|
||||
{
|
||||
const uint8_t *p = buf;
|
||||
uint32_t crc;
|
||||
crc = ~0U;
|
||||
while (size--)
|
||||
crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
|
||||
return crc ^ ~0U;
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't you hate those contrived examples which CRC their data. We can use
|
||||
* FRIDA to patch this function out and always return success. Otherwise, we
|
||||
* could change it to actually correct the checksum.
|
||||
*/
|
||||
int crc32_check (char * buf, int len) {
|
||||
if (len < sizeof(uint32_t)) { return 0; }
|
||||
uint32_t expected = *(uint32_t *)&buf[len - sizeof(uint32_t)];
|
||||
uint32_t calculated = crc32(buf, len - sizeof(uint32_t));
|
||||
return expected == calculated;
|
||||
}
|
||||
|
||||
/*
|
||||
* So you've found a really boring bug in an earlier campaign which results in
|
||||
* a NULL dereference or something like that. That bug can get in the way,
|
||||
* causing the persistent loop to exit whenever it is triggered, and can also
|
||||
* cloud your output unnecessarily. Again, we can use FRIDA to patch it out.
|
||||
*/
|
||||
void some_boring_bug(char c) {
|
||||
switch (c) {
|
||||
case 'A'...'Z':
|
||||
case 'a'...'z':
|
||||
__builtin_trap();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void LLVMFuzzerTestOneInput(char *buf, int len) {
|
||||
|
||||
if (!crc32_check(buf, len)) return;
|
||||
|
||||
some_boring_bug(buf[0]);
|
||||
|
||||
if (buf[0] == '0') {
|
||||
printf("Looks like a zero to me!\n");
|
||||
}
|
||||
else if (buf[0] == '1') {
|
||||
printf("Pretty sure that is a one!\n");
|
||||
}
|
||||
else if (buf[0] == '2') {
|
||||
if (buf[1] == '3') {
|
||||
if (buf[2] == '4') {
|
||||
printf("Oh we, weren't expecting that!");
|
||||
__builtin_trap();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
printf("Neither one or zero? How quaint!\n");
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
int fd = -1;
|
||||
off_t len;
|
||||
char * buf = NULL;
|
||||
size_t n_read;
|
||||
int result = -1;
|
||||
|
||||
if (argc != 2) { return 1; }
|
||||
|
||||
printf("Running: %s\n", argv[1]);
|
||||
|
||||
fd = open(argv[1], O_RDONLY);
|
||||
if (fd < 0) { return 1; }
|
||||
|
||||
len = lseek(fd, 0, SEEK_END);
|
||||
if (len < 0) { return 1; }
|
||||
|
||||
if (lseek(fd, 0, SEEK_SET) != 0) { return 1; }
|
||||
|
||||
buf = malloc(len);
|
||||
if (buf == NULL) { return 1; }
|
||||
|
||||
n_read = read(fd, buf, len);
|
||||
if (n_read != len) { return 1; }
|
||||
|
||||
printf("Running: %s: (%zd bytes)\n", argv[1], n_read);
|
||||
|
||||
LLVMFuzzerTestOneInput(buf, len);
|
||||
printf("Done: %s: (%zd bytes)\n", argv[1], n_read);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
There are a couple of obstacles with our target application. Unlike when fuzzing
|
||||
source code, though, we can't simply edit it and recompile it. The following
|
||||
script shows how we can use the normal functionality of FRIDA to modify any
|
||||
troublesome behaviour.
|
||||
|
||||
```js
|
||||
Afl.print('******************');
|
||||
Afl.print('* AFL FRIDA MODE *');
|
||||
Afl.print('******************');
|
||||
Afl.print('');
|
||||
|
||||
const main = DebugSymbol.fromName('main').address;
|
||||
Afl.print(`main: ${main}`);
|
||||
Afl.setEntryPoint(main);
|
||||
Afl.setPersistentAddress(main);
|
||||
Afl.setPersistentCount(10000000);
|
||||
|
||||
const crc32_check = DebugSymbol.fromName('crc32_check').address;
|
||||
const crc32_replacement = new NativeCallback(
|
||||
(buf, len) => {
|
||||
Afl.print(`len: ${len}`);
|
||||
if (len < 4) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
},
|
||||
'int',
|
||||
['pointer', 'int']);
|
||||
Interceptor.replace(crc32_check, crc32_replacement);
|
||||
|
||||
const some_boring_bug = DebugSymbol.fromName('some_boring_bug').address
|
||||
const boring_replacement = new NativeCallback(
|
||||
(c) => { },
|
||||
'void',
|
||||
['char']);
|
||||
Interceptor.replace(some_boring_bug, boring_replacement);
|
||||
|
||||
Afl.done();
|
||||
Afl.print("done");
|
||||
```
|
||||
|
||||
# Advanced Patching
|
||||
Consider the following code fragment...
|
||||
```c
|
||||
extern void some_boring_bug2(char c);
|
||||
|
||||
__asm__ (
|
||||
".text \n"
|
||||
"some_boring_bug2: \n"
|
||||
".global some_boring_bug2 \n"
|
||||
".type some_boring_bug2, @function \n"
|
||||
"mov %edi, %eax \n"
|
||||
"cmp $0xb4, %al \n"
|
||||
"jne ok \n"
|
||||
"ud2 \n"
|
||||
"ok: \n"
|
||||
"ret \n");
|
||||
|
||||
void LLVMFuzzerTestOneInput(char *buf, int len) {
|
||||
|
||||
...
|
||||
|
||||
some_boring_bug2(buf[0]);
|
||||
|
||||
...
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Rather than using FRIDAs `Interceptor.replace` or `Interceptor.attach` APIs, it
|
||||
is possible to apply much more fine grained modification to the target
|
||||
application by means of using the Stalker APIs.
|
||||
|
||||
The following code locates the function of interest and patches out the UD2
|
||||
instruction signifying a crash.
|
||||
|
||||
```js
|
||||
/* Modify the instructions */
|
||||
const some_boring_bug2 = DebugSymbol.fromName('some_boring_bug2').address
|
||||
const pid = Memory.alloc(4);
|
||||
pid.writeInt(Process.id);
|
||||
|
||||
const cm = new CModule(`
|
||||
#include <stdio.h>
|
||||
#include <gum/gumstalker.h>
|
||||
|
||||
typedef int pid_t;
|
||||
|
||||
#define STDERR_FILENO 2
|
||||
#define BORING2_LEN 10
|
||||
|
||||
extern int dprintf(int fd, const char *format, ...);
|
||||
extern void some_boring_bug2(char c);
|
||||
extern pid_t getpid(void);
|
||||
extern pid_t pid;
|
||||
|
||||
gboolean js_stalker_callback(const cs_insn *insn, gboolean begin,
|
||||
gboolean excluded, GumStalkerOutput *output)
|
||||
{
|
||||
pid_t my_pid = getpid();
|
||||
GumX86Writer *cw = output->writer.x86;
|
||||
|
||||
if (GUM_ADDRESS(insn->address) < GUM_ADDRESS(some_boring_bug2)) {
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
if (GUM_ADDRESS(insn->address) >=
|
||||
GUM_ADDRESS(some_boring_bug2) + BORING2_LEN) {
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
if (my_pid == pid) {
|
||||
|
||||
if (begin) {
|
||||
|
||||
dprintf(STDERR_FILENO, "\n> 0x%016lX: %s %s\n", insn->address,
|
||||
insn->mnemonic, insn->op_str);
|
||||
|
||||
} else {
|
||||
|
||||
dprintf(STDERR_FILENO, " 0x%016lX: %s %s\n", insn->address,
|
||||
insn->mnemonic, insn->op_str);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (insn->id == X86_INS_UD2) {
|
||||
|
||||
gum_x86_writer_put_nop(cw);
|
||||
return FALSE;
|
||||
|
||||
} else {
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
dprintf: Module.getExportByName(null, 'dprintf'),
|
||||
getpid: Module.getExportByName(null, 'getpid'),
|
||||
some_boring_bug2: some_boring_bug2,
|
||||
pid: pid
|
||||
});
|
||||
Afl.setStalkerCallback(cm.js_stalker_callback)
|
||||
Afl.setStdErr("/tmp/stderr.txt");
|
||||
```
|
||||
|
||||
Note that you will more likely want to find the
|
||||
patch address by using:
|
||||
|
||||
```js
|
||||
const module = Process.getModuleByName('target.exe');
|
||||
/* Hardcoded offset within the target image */
|
||||
const address = module.base.add(0xdeadface);
|
||||
```
|
||||
OR
|
||||
```
|
||||
const address = DebugSymbol.fromName("my_function").address.add(0xdeadface);
|
||||
```
|
||||
OR
|
||||
```
|
||||
const address = Module.getExportByName(null, "my_function").add(0xdeadface);
|
||||
```
|
||||
|
||||
The function `js_stalker_callback` should return `TRUE` if the original
|
||||
instruction should be emitted in the instrumented code, or `FALSE` otherwise.
|
||||
In the example above, we can see it is replaced with a `NOP`.
|
||||
|
||||
Lastly, note that the same callback will be called when compiling instrumented
|
||||
code both in the child of the forkserver (as it is executed) and also in the
|
||||
parent of the forserver (when prefetching is enabled) so that it can be
|
||||
inherited by the next forked child. It is **VERY** important that the same
|
||||
instructions be generated in both the parent and the child, or if prefetching is
|
||||
disabled that the same instructions are generated every time the block is
|
||||
compiled. Failure to do so will likely lead to bugs which are incredibly
|
||||
difficult to diagnose. The code above only prints the instructions when running
|
||||
in the parent process (the one provided by `Process.id` when the JS script is
|
||||
executed).
|
||||
|
||||
# OSX
|
||||
Note that the JavaScript debug symbol api for OSX makes use of the
|
||||
`CoreSymbolication` APIs and as such the `CoreFoundation` module must be loaded
|
||||
into the target to make use of it. This can be done by setting:
|
||||
|
||||
```
|
||||
AFL_PRELOAD=/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
|
||||
```
|
||||
|
||||
It should be noted that `CoreSymbolication` API may take a while to initialize
|
||||
and build its caches. For this reason, it may be nescessary to also increase the
|
||||
value of the `-t` flag passed to `afl-fuzz`.
|
||||
|
||||
# API
|
||||
```js
|
||||
class Afl {
|
||||
|
||||
/**
|
||||
* Field containing the `Module` object for `afl-frida-trace.so` (the FRIDA mode
|
||||
* implementation).
|
||||
*/
|
||||
public static module: Module = Process.getModuleByName("afl-frida-trace.so");
|
||||
|
||||
/**
|
||||
* This is equivalent to setting a value in `AFL_FRIDA_EXCLUDE_RANGES`,
|
||||
* it takes as arguments a `NativePointer` and a `number`. It can be
|
||||
* called multiple times to exclude several ranges.
|
||||
*/
|
||||
public static addExcludedRange(addressess: NativePointer, size: number): void {
|
||||
Afl.jsApiAddExcludeRange(addressess, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is equivalent to setting a value in `AFL_FRIDA_INST_RANGES`,
|
||||
* it takes as arguments a `NativePointer` and a `number`. It can be
|
||||
* called multiple times to include several ranges.
|
||||
*/
|
||||
public static addIncludedRange(addressess: NativePointer, size: number): void {
|
||||
Afl.jsApiAddIncludeRange(addressess, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* This must always be called at the end of your script. This lets
|
||||
* FRIDA mode know that your configuration is finished and that
|
||||
* execution has reached the end of your script. Failure to call
|
||||
* this will result in a fatal error.
|
||||
*/
|
||||
public static done(): void {
|
||||
Afl.jsApiDone();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function can be called within your script to cause FRIDA
|
||||
* mode to trigger a fatal error. This is useful if for example you
|
||||
* discover a problem you weren't expecting and want everything to
|
||||
* stop. The user will need to enable `AFL_DEBUG_CHILD=1` to view
|
||||
* this error message.
|
||||
*/
|
||||
public static error(msg: string): void {
|
||||
const buf = Memory.allocUtf8String(msg);
|
||||
Afl.jsApiError(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function used to provide access to `__afl_fuzz_ptr`, which contains the length of
|
||||
* fuzzing data when using in-memory test case fuzzing.
|
||||
*/
|
||||
public static getAflFuzzLen(): NativePointer {
|
||||
|
||||
return Afl.jsApiGetSymbol("__afl_fuzz_len");
|
||||
}
|
||||
|
||||
/**
|
||||
* Function used to provide access to `__afl_fuzz_ptr`, which contains the fuzzing
|
||||
* data when using in-memory test case fuzzing.
|
||||
*/
|
||||
public static getAflFuzzPtr(): NativePointer {
|
||||
|
||||
return Afl.jsApiGetSymbol("__afl_fuzz_ptr");
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a message to the STDOUT. This should be preferred to
|
||||
* FRIDA's `console.log` since FRIDA will queue it's log messages.
|
||||
* If `console.log` is used in a callback in particular, then there
|
||||
* may no longer be a thread running to service this queue.
|
||||
*/
|
||||
public static print(msg: string): void {
|
||||
const STDOUT_FILENO = 2;
|
||||
const log = `${msg}\n`;
|
||||
const buf = Memory.allocUtf8String(log);
|
||||
Afl.jsApiWrite(STDOUT_FILENO, buf, log.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_DEBUG_MAPS`.
|
||||
*/
|
||||
public static setDebugMaps(): void {
|
||||
Afl.jsApiSetDebugMaps();
|
||||
}
|
||||
|
||||
/**
|
||||
* This has the same effect as setting `AFL_ENTRYPOINT`, but has the
|
||||
* convenience of allowing you to use FRIDAs APIs to determine the
|
||||
* address you would like to configure, rather than having to grep
|
||||
* the output of `readelf` or something similarly ugly. This
|
||||
* function should be called with a `NativePointer` as its
|
||||
* argument.
|
||||
*/
|
||||
public static setEntryPoint(address: NativePointer): void {
|
||||
Afl.jsApiSetEntryPoint(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function used to enable in-memory test cases for fuzzing.
|
||||
*/
|
||||
public static setInMemoryFuzzing(): void {
|
||||
Afl.jsApiAflSharedMemFuzzing.writeInt(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_DEBUG_FILE`. This function takes a single `string` as
|
||||
* an argument.
|
||||
*/
|
||||
public static setInstrumentDebugFile(file: string): void {
|
||||
const buf = Memory.allocUtf8String(file);
|
||||
Afl.jsApiSetInstrumentDebugFile(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_TRACE`.
|
||||
*/
|
||||
public static setInstrumentEnableTracing(): void {
|
||||
Afl.jsApiSetInstrumentTrace();
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_INST_LIBS`.
|
||||
*/
|
||||
public static setInstrumentLibraries(): void {
|
||||
Afl.jsApiSetInstrumentLibraries();
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_NO_OPTIMIZE`
|
||||
*/
|
||||
public static setInstrumentNoOptimize(): void {
|
||||
Afl.jsApiSetInstrumentNoOptimize();
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_TRACE_UNIQUE`.
|
||||
*/
|
||||
public static setInstrumentTracingUnique(): void {
|
||||
Afl.jsApiSetInstrumentTraceUnique();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is equivalent to setting `AFL_FRIDA_PERSISTENT_ADDR`, again a
|
||||
* `NativePointer` should be provided as it's argument.
|
||||
*/
|
||||
public static setPersistentAddress(address: NativePointer): void {
|
||||
Afl.jsApiSetPersistentAddress(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is equivalent to setting `AFL_FRIDA_PERSISTENT_CNT`, a
|
||||
* `number` should be provided as it's argument.
|
||||
*/
|
||||
public static setPersistentCount(count: number): void {
|
||||
Afl.jsApiSetPersistentCount(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_PERSISTENT_DEBUG`.
|
||||
*/
|
||||
public static setPersistentDebug(): void {
|
||||
Afl.jsApiSetPersistentDebug();
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_PERSISTENT_ADDR`. This function takes a NativePointer as an
|
||||
* argument. See above for examples of use.
|
||||
*/
|
||||
public static setPersistentHook(address: NativePointer): void {
|
||||
Afl.jsApiSetPersistentHook(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is equivalent to setting `AFL_FRIDA_PERSISTENT_RET`, again a
|
||||
* `NativePointer` should be provided as it's argument.
|
||||
*/
|
||||
public static setPersistentReturn(address: NativePointer): void {
|
||||
Afl.jsApiSetPersistentReturn(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_NO_PREFETCH`.
|
||||
*/
|
||||
public static setPrefetchDisable(): void {
|
||||
Afl.jsApiSetPrefetchDisable();
|
||||
}
|
||||
|
||||
/*
|
||||
* Set a function to be called for each instruction which is instrumented
|
||||
* by AFL FRIDA mode.
|
||||
*/
|
||||
public static setStalkerCallback(callback: NativePointer): void {
|
||||
Afl.jsApiSetStalkerCallback(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_STATS_FILE`. This function takes a single `string` as
|
||||
* an argument.
|
||||
*/
|
||||
public static setStatsFile(file: string): void {
|
||||
const buf = Memory.allocUtf8String(file);
|
||||
Afl.jsApiSetStatsFile(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_STATS_INTERVAL`. This function takes a `number` as an
|
||||
* argument
|
||||
*/
|
||||
public static setStatsInterval(interval: number): void {
|
||||
Afl.jsApiSetStatsInterval(interval);
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_STATS_TRANSITIONS`
|
||||
*/
|
||||
public static setStatsTransitions(): void {
|
||||
Afl.jsApiSetStatsTransitions();
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_OUTPUT_STDERR`. This function takes a single `string` as
|
||||
* an argument.
|
||||
*/
|
||||
public static setStdErr(file: string): void {
|
||||
const buf = Memory.allocUtf8String(file);
|
||||
Afl.jsApiSetStdErr(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_OUTPUT_STDOUT`. This function takes a single `string` as
|
||||
* an argument.
|
||||
*/
|
||||
public static setStdOut(file: string): void {
|
||||
const buf = Memory.allocUtf8String(file);
|
||||
Afl.jsApiSetStdOut(buf);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
35
frida_mode/frida.map
Normal file
35
frida_mode/frida.map
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
global:
|
||||
__afl_fuzz_len;
|
||||
__afl_fuzz_ptr;
|
||||
__afl_sharedmem_fuzzing;
|
||||
afl_frida_start;
|
||||
js_api_add_exclude_range;
|
||||
js_api_add_include_range;
|
||||
js_api_done;
|
||||
js_api_error;
|
||||
js_api_set_debug_maps;
|
||||
js_api_set_entrypoint;
|
||||
js_api_set_instrument_debug_file;
|
||||
js_api_set_instrument_jit;
|
||||
js_api_set_instrument_libraries;
|
||||
js_api_set_instrument_no_optimize;
|
||||
js_api_set_instrument_seed;
|
||||
js_api_set_instrument_trace;
|
||||
js_api_set_instrument_trace_unique;
|
||||
js_api_set_persistent_address;
|
||||
js_api_set_persistent_count;
|
||||
js_api_set_persistent_debug;
|
||||
js_api_set_persistent_hook;
|
||||
js_api_set_persistent_return;
|
||||
js_api_set_prefetch_disable;
|
||||
js_api_set_stalker_callback;
|
||||
js_api_set_stats_file;
|
||||
js_api_set_stats_interval;
|
||||
js_api_set_stats_transitions;
|
||||
js_api_set_stderr;
|
||||
js_api_set_stdout;
|
||||
|
||||
local:
|
||||
*;
|
||||
};
|
64
frida_mode/hook/frida_hook.c
Normal file
64
frida_mode/hook/frida_hook.c
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
*
|
||||
* Modify this file to set the right registers with the fuzz input and length.
|
||||
* It is a good idea to check input_buf_len to be not larger than the
|
||||
* destination buffer!
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#if defined(__x86_64__)
|
||||
|
||||
__attribute__((visibility("default"))) void afl_persistent_hook(
|
||||
GumCpuContext *regs, uint8_t *input_buf, uint32_t input_buf_len) {
|
||||
|
||||
// do a length check matching the target!
|
||||
|
||||
memcpy((void *)regs->rdi, input_buf, input_buf_len);
|
||||
regs->rsi = input_buf_len;
|
||||
|
||||
}
|
||||
|
||||
#elif defined(__i386__)
|
||||
|
||||
__attribute__((visibility("default"))) void afl_persistent_hook(
|
||||
GumCpuContext *regs, uint8_t *input_buf, uint32_t input_buf_len) {
|
||||
|
||||
// do a length check matching the target!
|
||||
|
||||
void **esp = (void **)regs->esp;
|
||||
void * arg1 = esp[0];
|
||||
void **arg2 = &esp[1];
|
||||
memcpy(arg1, input_buf, input_buf_len);
|
||||
*arg2 = (void *)input_buf_len;
|
||||
|
||||
}
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
|
||||
__attribute__((visibility("default"))) void afl_persistent_hook(
|
||||
GumCpuContext *regs, uint8_t *input_buf, uint32_t input_buf_len) {
|
||||
|
||||
// do a length check matching the target!
|
||||
|
||||
memcpy((void *)regs->x[0], input_buf, input_buf_len);
|
||||
regs->x[1] = input_buf_len;
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
#pragma error "Unsupported architecture"
|
||||
#endif
|
||||
|
||||
__attribute__((visibility("default"))) int afl_persistent_hook_init(void) {
|
||||
|
||||
// 1 for shared memory input (faster), 0 for normal input (you have to use
|
||||
// read(), input_buf will be NULL)
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ struct x86_64_regs {
|
||||
void afl_persistent_hook(struct x86_64_regs *regs, uint64_t guest_base,
|
||||
uint8_t *input_buf, uint32_t input_buf_len) {
|
||||
|
||||
(void)guest_base; /* unused */
|
||||
memcpy((void *)regs->rdi, input_buf, input_buf_len);
|
||||
regs->rsi = input_buf_len;
|
||||
|
||||
@ -75,6 +76,7 @@ struct x86_regs {
|
||||
void afl_persistent_hook(struct x86_regs *regs, uint64_t guest_base,
|
||||
uint8_t *input_buf, uint32_t input_buf_len) {
|
||||
|
||||
(void)guest_base; /* unused */
|
||||
void **esp = (void **)regs->esp;
|
||||
void * arg1 = esp[1];
|
||||
void **arg2 = &esp[2];
|
||||
@ -82,6 +84,103 @@ void afl_persistent_hook(struct x86_regs *regs, uint64_t guest_base,
|
||||
*arg2 = (void *)input_buf_len;
|
||||
|
||||
}
|
||||
#elif defined(__aarch64__)
|
||||
|
||||
struct arm64_regs {
|
||||
|
||||
uint64_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10;
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x11;
|
||||
uint32_t fp_32;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x12;
|
||||
uint32_t ip_32;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x13;
|
||||
uint32_t sp_32;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x14;
|
||||
uint32_t lr_32;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x15;
|
||||
uint32_t pc_32;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x16;
|
||||
uint64_t ip0;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x17;
|
||||
uint64_t ip1;
|
||||
|
||||
};
|
||||
|
||||
uint64_t x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28;
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x29;
|
||||
uint64_t fp;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x30;
|
||||
uint64_t lr;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x31;
|
||||
uint64_t sp;
|
||||
|
||||
};
|
||||
|
||||
// the zero register is not saved here ofc
|
||||
|
||||
uint64_t pc;
|
||||
|
||||
uint32_t cpsr;
|
||||
|
||||
uint8_t vfp_zregs[32][16 * 16];
|
||||
uint8_t vfp_pregs[17][32];
|
||||
uint32_t vfp_xregs[16];
|
||||
|
||||
};
|
||||
|
||||
void afl_persistent_hook(struct arm64_regs *regs, uint64_t guest_base,
|
||||
uint8_t *input_buf, uint32_t input_buf_len) {
|
||||
|
||||
(void)guest_base; /* unused */
|
||||
memcpy((void *)regs->x0, input_buf, input_buf_len);
|
||||
regs->x1 = input_buf_len;
|
||||
}
|
||||
|
||||
#else
|
||||
#pragma error "Unsupported architecture"
|
||||
@ -94,4 +193,3 @@ int afl_persistent_hook_init(void) {
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
#ifndef _ASAN_H
|
||||
#define _ASAN_H
|
||||
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
extern gboolean asan_initialized;
|
||||
|
||||
void asan_config(void);
|
||||
void asan_init(void);
|
||||
void asan_arch_init(void);
|
||||
void asan_instrument(const cs_insn *instr, GumStalkerIterator *iterator);
|
||||
|
@ -1,10 +1,17 @@
|
||||
#ifndef _CTX_H
|
||||
#define _CTX_H
|
||||
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
gsize ctx_read_reg(GumCpuContext *ctx, x86_reg reg);
|
||||
#if defined(__x86_64__)
|
||||
gsize ctx_read_reg(GumX64CpuContext *ctx, x86_reg reg);
|
||||
#elif defined(__i386__)
|
||||
gsize ctx_read_reg(GumIA32CpuContext *ctx, x86_reg reg);
|
||||
#elif defined(__aarch64__)
|
||||
gsize ctx_read_reg(GumArm64CpuContext *ctx, arm64_reg reg);
|
||||
size_t ctx_get_size(const cs_insn *instr, cs_arm64_op *operand);
|
||||
#elif defined(__arm__)
|
||||
gsize ctx_read_reg(GumArmCpuContext *ctx, arm_reg reg);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -1,13 +1,16 @@
|
||||
#ifndef _ENTRY_H
|
||||
#define _ENTRY_H
|
||||
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
extern guint64 entry_start;
|
||||
extern guint64 entry_point;
|
||||
extern gboolean entry_reached;
|
||||
|
||||
void entry_config(void);
|
||||
|
||||
void entry_init(void);
|
||||
|
||||
void entry_run(void);
|
||||
void entry_start(void);
|
||||
|
||||
void entry_prologue(GumStalkerIterator *iterator, GumStalkerOutput *output);
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
extern struct cmp_map *__afl_cmp_map;
|
||||
|
||||
void cmplog_config(void);
|
||||
void cmplog_init(void);
|
||||
|
||||
/* Functions to be implemented by the different architectures */
|
||||
|
@ -1,13 +1,24 @@
|
||||
#ifndef _INSTRUMENT_H
|
||||
#define _INSTRUMENT_H
|
||||
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
extern __thread uint64_t previous_pc;
|
||||
extern uint8_t * __afl_area_ptr;
|
||||
extern uint32_t __afl_map_size;
|
||||
extern char * instrument_debug_filename;
|
||||
extern gboolean instrument_tracing;
|
||||
extern gboolean instrument_optimize;
|
||||
extern gboolean instrument_unique;
|
||||
extern __thread guint64 instrument_previous_pc;
|
||||
extern guint64 instrument_hash_zero;
|
||||
|
||||
extern gboolean instrument_use_fixed_seed;
|
||||
extern guint64 instrument_fixed_seed;
|
||||
|
||||
extern uint8_t *__afl_area_ptr;
|
||||
extern uint32_t __afl_map_size;
|
||||
|
||||
void instrument_config(void);
|
||||
|
||||
void instrument_init(void);
|
||||
|
||||
@ -19,9 +30,17 @@ gboolean instrument_is_coverage_optimize_supported(void);
|
||||
void instrument_coverage_optimize(const cs_insn * instr,
|
||||
GumStalkerOutput *output);
|
||||
|
||||
void instrument_debug_init(void);
|
||||
void instrument_debug_start(uint64_t address, GumStalkerOutput *output);
|
||||
void instrument_debug_instruction(uint64_t address, uint16_t size);
|
||||
void instrument_debug_end(GumStalkerOutput *output);
|
||||
void instrument_debug_config(void);
|
||||
void instrument_debug_init(void);
|
||||
void instrument_debug_start(uint64_t address, GumStalkerOutput *output);
|
||||
void instrument_debug_instruction(uint64_t address, uint16_t size);
|
||||
void instrument_debug_end(GumStalkerOutput *output);
|
||||
void instrument_flush(GumStalkerOutput *output);
|
||||
gpointer instrument_cur(GumStalkerOutput *output);
|
||||
|
||||
void instrument_on_fork();
|
||||
|
||||
guint64 instrument_get_offset_hash(GumAddress current_rip);
|
||||
|
||||
#endif
|
||||
|
||||
|
11
frida_mode/include/intercept.h
Normal file
11
frida_mode/include/intercept.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef _INTERCEPTOR_H
|
||||
#define _INTERCEPTOR_H
|
||||
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
void intercept_hook(void *address, gpointer replacement, gpointer user_data);
|
||||
void intercept_unhook(void *address);
|
||||
void intercept_unhook_self(void);
|
||||
|
||||
#endif
|
||||
|
@ -1,11 +0,0 @@
|
||||
#ifndef _INTERCEPTOR_H
|
||||
#define _INTERCEPTOR_H
|
||||
|
||||
#include "frida-gum.h"
|
||||
|
||||
void intercept(void *address, gpointer replacement, gpointer user_data);
|
||||
void unintercept(void *address);
|
||||
void unintercept_self(void);
|
||||
|
||||
#endif
|
||||
|
26
frida_mode/include/js.h
Normal file
26
frida_mode/include/js.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef _JS_H
|
||||
#define _JS_H
|
||||
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
typedef gboolean (*js_api_stalker_callback_t)(const cs_insn *insn,
|
||||
gboolean begin, gboolean excluded,
|
||||
GumStalkerOutput *output);
|
||||
|
||||
extern unsigned char api_js[];
|
||||
extern unsigned int api_js_len;
|
||||
|
||||
extern gboolean js_done;
|
||||
extern js_api_stalker_callback_t js_user_callback;
|
||||
|
||||
/* Frida Mode */
|
||||
|
||||
void js_config(void);
|
||||
|
||||
void js_start(void);
|
||||
|
||||
gboolean js_stalker_callback(const cs_insn *insn, gboolean begin,
|
||||
gboolean excluded, GumStalkerOutput *output);
|
||||
|
||||
#endif
|
||||
|
@ -1,7 +1,9 @@
|
||||
#ifndef _LIB_H
|
||||
#define _LIB_H
|
||||
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
void lib_config(void);
|
||||
|
||||
void lib_init(void);
|
||||
|
||||
|
@ -1,8 +1,12 @@
|
||||
#ifndef _OUTPUT_H
|
||||
#define _OUTPUT_H
|
||||
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
extern char *output_stdout;
|
||||
extern char *output_stderr;
|
||||
|
||||
void output_config(void);
|
||||
void output_init(void);
|
||||
|
||||
#endif
|
||||
|
@ -2,7 +2,7 @@
|
||||
#ifndef _PERSISTENT_H
|
||||
#define _PERSISTENT_H
|
||||
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
#include "config.h"
|
||||
|
||||
typedef struct arch_api_regs api_regs;
|
||||
@ -19,9 +19,10 @@ extern unsigned char *__afl_fuzz_ptr;
|
||||
extern guint64 persistent_start;
|
||||
extern guint64 persistent_count;
|
||||
extern guint64 persistent_ret;
|
||||
extern guint64 persistent_ret_offset;
|
||||
extern gboolean persistent_debug;
|
||||
extern afl_persistent_hook_fn hook;
|
||||
extern afl_persistent_hook_fn persistent_hook;
|
||||
|
||||
void persistent_config(void);
|
||||
|
||||
void persistent_init(void);
|
||||
|
||||
@ -29,7 +30,10 @@ void persistent_init(void);
|
||||
gboolean persistent_is_supported(void);
|
||||
|
||||
void persistent_prologue(GumStalkerOutput *output);
|
||||
void persistent_prologue_arch(GumStalkerOutput *output);
|
||||
|
||||
void persistent_epilogue(GumStalkerOutput *output);
|
||||
void persistent_epilogue_arch(GumStalkerOutput *output);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1,8 +1,11 @@
|
||||
#ifndef _PREFETCH_H
|
||||
#define _PREFETCH_H
|
||||
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
extern gboolean prefetch_enable;
|
||||
|
||||
void prefetch_config(void);
|
||||
void prefetch_init(void);
|
||||
void prefetch_write(void *addr);
|
||||
void prefetch_read(void);
|
||||
|
@ -1,13 +1,21 @@
|
||||
#ifndef _RANGES_H
|
||||
#define _RANGES_H
|
||||
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
extern gboolean ranges_debug_maps;
|
||||
extern gboolean ranges_inst_libs;
|
||||
extern gboolean ranges_inst_jit;
|
||||
|
||||
void ranges_config(void);
|
||||
void ranges_init(void);
|
||||
|
||||
gboolean range_is_excluded(gpointer address);
|
||||
|
||||
void ranges_exclude();
|
||||
|
||||
void ranges_add_include(GumMemoryRange *range);
|
||||
void ranges_add_exclude(GumMemoryRange *range);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1,11 +1,13 @@
|
||||
#ifndef _STALKER_H
|
||||
#define _STALKER_H
|
||||
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
void stalker_config(void);
|
||||
void stalker_init(void);
|
||||
GumStalker *stalker_get(void);
|
||||
void stalker_start(void);
|
||||
void stalker_trust(void);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
#ifndef _STATS_H
|
||||
#define _STATS_H
|
||||
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
typedef struct {
|
||||
|
||||
@ -15,6 +15,11 @@ typedef struct {
|
||||
|
||||
extern stats_data_header_t *stats_data;
|
||||
|
||||
extern char * stats_filename;
|
||||
extern guint64 stats_interval;
|
||||
extern gboolean stats_transitions;
|
||||
|
||||
void stats_config(void);
|
||||
void stats_init(void);
|
||||
void stats_collect(const cs_insn *instr, gboolean begin);
|
||||
void stats_print(char *format, ...);
|
||||
@ -23,6 +28,7 @@ gboolean stats_is_supported_arch(void);
|
||||
size_t stats_data_size_arch(void);
|
||||
void stats_collect_arch(const cs_insn *instr);
|
||||
void stats_write_arch(void);
|
||||
void stats_on_fork(void);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
#ifndef _UTIL_H
|
||||
#define _UTIL_H
|
||||
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#define UNUSED_PARAMETER(x) (void)(x)
|
||||
#define IGNORED_RETURN(x) (void)!(x)
|
||||
|
24
frida_mode/many-linux/Dockerfile
Normal file
24
frida_mode/many-linux/Dockerfile
Normal file
@ -0,0 +1,24 @@
|
||||
FROM fridadotre/manylinux-x86_64
|
||||
|
||||
COPY realpath /bin/realpath
|
||||
RUN chmod +x /bin/realpath
|
||||
|
||||
RUN yum -y install xz
|
||||
RUN yum -y install vim-common
|
||||
|
||||
WORKDIR /
|
||||
RUN git clone https://github.com/AFLplusplus/AFLplusplus.git
|
||||
|
||||
WORKDIR /AFLplusplus
|
||||
RUN mkdir -p /AFLplusplus/frida_mode/build/frida/
|
||||
RUN curl -L -o /AFLplusplus/frida_mode/build/frida/frida-gumjs-devkit-15.0.0-linux-x86_64.tar.xz "https://github.com/frida/frida/releases/download/15.0.0/frida-gumjs-devkit-15.0.0-linux-x86_64.tar.xz"
|
||||
|
||||
WORKDIR /AFLplusplus
|
||||
RUN git checkout dev
|
||||
WORKDIR /AFLplusplus/frida_mode
|
||||
ENV CFLAGS="\
|
||||
-DADDR_NO_RANDOMIZE=0x0040000 \
|
||||
-Wno-implicit-function-declaration \
|
||||
"
|
||||
ENV CXX=$CC
|
||||
RUN make
|
21
frida_mode/many-linux/GNUmakefile
Normal file
21
frida_mode/many-linux/GNUmakefile
Normal file
@ -0,0 +1,21 @@
|
||||
PWD:=$(shell pwd)/
|
||||
BUILD_DIR:=$(PWD)build/
|
||||
|
||||
.PHONY: all clean shell
|
||||
|
||||
all: | $(BUILD_DIR)
|
||||
docker build --tag many-afl-frida .
|
||||
docker run --rm \
|
||||
-v $(PWD)build/:/export \
|
||||
many-afl-frida \
|
||||
cp /AFLplusplus/afl-frida-trace.so /export
|
||||
|
||||
$(BUILD_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR)
|
||||
docker images --filter 'dangling=true' -q --no-trunc | xargs -L1 docker rmi --force
|
||||
|
||||
shell:
|
||||
docker run -ti --rm many-afl-frida /bin/bash
|
9
frida_mode/many-linux/Makefile
Normal file
9
frida_mode/many-linux/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
all:
|
||||
@echo trying to use GNU make...
|
||||
@gmake all || echo please install GNUmake
|
||||
|
||||
clean:
|
||||
@gmake clean
|
||||
|
||||
shell:
|
||||
@gmake shell
|
8
frida_mode/many-linux/README.md
Normal file
8
frida_mode/many-linux/README.md
Normal file
@ -0,0 +1,8 @@
|
||||
# many-linux
|
||||
|
||||
This folder contains a Docker image to allow the building of
|
||||
`afl-frida-trace.so` using the `many-linux` docker image. This docker image is
|
||||
based on CentOS Linux 5. By building `afl-frida-trace.so` for such an old
|
||||
version of Linux, given the strong backward compatibility of Linux, this should
|
||||
work on the majority of Linux environments. This may be useful for targetting
|
||||
Linux distributions other than your development environment.
|
2
frida_mode/many-linux/realpath
Normal file
2
frida_mode/many-linux/realpath
Normal file
@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
readlink -f -- "$@"
|
@ -1,18 +1,18 @@
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "asan.h"
|
||||
|
||||
gboolean asan_initialized = FALSE;
|
||||
static gboolean asan_enabled = FALSE;
|
||||
gboolean asan_initialized = FALSE;
|
||||
|
||||
void asan_init(void) {
|
||||
void asan_config(void) {
|
||||
|
||||
if (getenv("AFL_USE_FASAN") != NULL) {
|
||||
|
||||
OKF("Frida ASAN mode enabled");
|
||||
asan_arch_init();
|
||||
asan_initialized = TRUE;
|
||||
asan_enabled = TRUE;
|
||||
|
||||
} else {
|
||||
|
||||
@ -22,3 +22,14 @@ void asan_init(void) {
|
||||
|
||||
}
|
||||
|
||||
void asan_init(void) {
|
||||
|
||||
if (asan_enabled) {
|
||||
|
||||
asan_arch_init();
|
||||
asan_initialized = TRUE;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
@ -1,18 +1,80 @@
|
||||
#include "frida-gum.h"
|
||||
#include <dlfcn.h>
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "asan.h"
|
||||
#include "ctx.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined(__aarch64__)
|
||||
|
||||
typedef struct {
|
||||
|
||||
size_t size;
|
||||
cs_arm64_op operand;
|
||||
|
||||
} asan_ctx_t;
|
||||
|
||||
typedef void (*asan_loadN_t)(gsize address, uint8_t size);
|
||||
typedef void (*asan_storeN_t)(gsize address, uint8_t size);
|
||||
|
||||
asan_loadN_t asan_loadN = NULL;
|
||||
asan_storeN_t asan_storeN = NULL;
|
||||
|
||||
static void asan_callout(GumCpuContext *ctx, gpointer user_data) {
|
||||
|
||||
asan_ctx_t * asan_ctx = (asan_ctx_t *)user_data;
|
||||
cs_arm64_op * operand = &asan_ctx->operand;
|
||||
arm64_op_mem *mem = &operand->mem;
|
||||
gsize base = 0;
|
||||
gsize index = 0;
|
||||
gsize address;
|
||||
|
||||
if (mem->base != ARM64_REG_INVALID) { base = ctx_read_reg(ctx, mem->base); }
|
||||
|
||||
if (mem->index != ARM64_REG_INVALID) {
|
||||
|
||||
index = ctx_read_reg(ctx, mem->index);
|
||||
|
||||
}
|
||||
|
||||
address = base + index + mem->disp;
|
||||
|
||||
if ((operand->access & CS_AC_READ) == CS_AC_READ) {
|
||||
|
||||
asan_loadN(address, asan_ctx->size);
|
||||
|
||||
}
|
||||
|
||||
if ((operand->access & CS_AC_WRITE) == CS_AC_WRITE) {
|
||||
|
||||
asan_storeN(address, asan_ctx->size);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void asan_instrument(const cs_insn *instr, GumStalkerIterator *iterator) {
|
||||
|
||||
UNUSED_PARAMETER(instr);
|
||||
UNUSED_PARAMETER(iterator);
|
||||
if (asan_initialized) {
|
||||
|
||||
FATAL("ASAN mode not supported on this architecture");
|
||||
cs_arm64 arm64 = instr->detail->arm64;
|
||||
cs_arm64_op *operand;
|
||||
asan_ctx_t * ctx;
|
||||
|
||||
if (!asan_initialized) return;
|
||||
|
||||
for (uint8_t i = 0; i < arm64.op_count; i++) {
|
||||
|
||||
operand = &arm64.operands[i];
|
||||
|
||||
if (operand->type != ARM64_OP_MEM) { continue; }
|
||||
|
||||
ctx = g_malloc0(sizeof(asan_ctx_t));
|
||||
ctx->size = ctx_get_size(instr, &arm64.operands[0]);
|
||||
memcpy(&ctx->operand, operand, sizeof(cs_arm64_op));
|
||||
gum_stalker_iterator_put_callout(iterator, asan_callout, ctx, g_free);
|
||||
|
||||
}
|
||||
|
||||
@ -20,7 +82,13 @@ void asan_instrument(const cs_insn *instr, GumStalkerIterator *iterator) {
|
||||
|
||||
void asan_arch_init(void) {
|
||||
|
||||
FATAL("ASAN mode not supported on this architecture");
|
||||
asan_loadN = (asan_loadN_t)dlsym(RTLD_DEFAULT, "__asan_loadN");
|
||||
asan_storeN = (asan_loadN_t)dlsym(RTLD_DEFAULT, "__asan_storeN");
|
||||
if (asan_loadN == NULL || asan_storeN == NULL) {
|
||||
|
||||
FATAL("Frida ASAN failed to find '__asan_loadN' or '__asan_storeN'");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include <dlfcn.h>
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include <dlfcn.h>
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
|
@ -1,19 +1,32 @@
|
||||
#include "frida-gum.h"
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#define DEFAULT_MMAP_MIN_ADDR (32UL << 10)
|
||||
#define MAX_MEMFD_SIZE (64UL << 10)
|
||||
|
||||
extern struct cmp_map *__afl_cmp_map;
|
||||
static GArray * cmplog_ranges = NULL;
|
||||
static GHashTable * hash_yes = NULL;
|
||||
static GHashTable * hash_no = NULL;
|
||||
|
||||
static GArray *cmplog_ranges = NULL;
|
||||
static long page_size = 0;
|
||||
static long page_offset_mask = 0;
|
||||
static long page_mask = 0;
|
||||
|
||||
static gboolean cmplog_range(const GumRangeDetails *details,
|
||||
gpointer user_data) {
|
||||
|
||||
UNUSED_PARAMETER(user_data);
|
||||
GArray * cmplog_ranges = (GArray *)user_data;
|
||||
GumMemoryRange range = *details->range;
|
||||
g_array_append_val(cmplog_ranges, range);
|
||||
return TRUE;
|
||||
@ -27,20 +40,50 @@ static gint cmplog_sort(gconstpointer a, gconstpointer b) {
|
||||
|
||||
}
|
||||
|
||||
static void cmplog_get_ranges(void) {
|
||||
|
||||
OKF("CMPLOG - Collecting ranges");
|
||||
|
||||
cmplog_ranges = g_array_sized_new(false, false, sizeof(GumMemoryRange), 100);
|
||||
gum_process_enumerate_ranges(GUM_PAGE_READ, cmplog_range, cmplog_ranges);
|
||||
g_array_sort(cmplog_ranges, cmplog_sort);
|
||||
|
||||
}
|
||||
|
||||
void cmplog_config(void) {
|
||||
|
||||
}
|
||||
|
||||
void cmplog_init(void) {
|
||||
|
||||
if (__afl_cmp_map != NULL) { OKF("CMPLOG mode enabled"); }
|
||||
|
||||
cmplog_ranges = g_array_sized_new(false, false, sizeof(GumMemoryRange), 100);
|
||||
gum_process_enumerate_ranges(GUM_PAGE_READ, cmplog_range, NULL);
|
||||
g_array_sort(cmplog_ranges, cmplog_sort);
|
||||
cmplog_get_ranges();
|
||||
|
||||
for (guint i = 0; i < cmplog_ranges->len; i++) {
|
||||
|
||||
GumMemoryRange *range = &g_array_index(cmplog_ranges, GumMemoryRange, i);
|
||||
OKF("CMPLOG Range - 0x%016" G_GINT64_MODIFIER "X - 0x%016" G_GINT64_MODIFIER
|
||||
"X",
|
||||
range->base_address, range->base_address + range->size);
|
||||
OKF("CMPLOG Range - %3u: 0x%016" G_GINT64_MODIFIER
|
||||
"X - 0x%016" G_GINT64_MODIFIER "X",
|
||||
i, range->base_address, range->base_address + range->size);
|
||||
|
||||
}
|
||||
|
||||
page_size = sysconf(_SC_PAGE_SIZE);
|
||||
page_offset_mask = page_size - 1;
|
||||
page_mask = ~(page_offset_mask);
|
||||
|
||||
hash_yes = g_hash_table_new(g_direct_hash, g_direct_equal);
|
||||
if (hash_yes == NULL) {
|
||||
|
||||
FATAL("Failed to g_hash_table_new, errno: %d", errno);
|
||||
|
||||
}
|
||||
|
||||
hash_no = g_hash_table_new(g_direct_hash, g_direct_equal);
|
||||
if (hash_no == NULL) {
|
||||
|
||||
FATAL("Failed to g_hash_table_new, errno: %d", errno);
|
||||
|
||||
}
|
||||
|
||||
@ -53,6 +96,45 @@ static gboolean cmplog_contains(GumAddress inner_base, GumAddress inner_limit,
|
||||
|
||||
}
|
||||
|
||||
gboolean cmplog_test_addr(guint64 addr, size_t size) {
|
||||
|
||||
if (g_hash_table_contains(hash_yes, GSIZE_TO_POINTER(addr))) { return true; }
|
||||
if (g_hash_table_contains(hash_no, GSIZE_TO_POINTER(addr))) { return false; }
|
||||
|
||||
void * page_addr = GSIZE_TO_POINTER(addr & page_mask);
|
||||
size_t page_offset = addr & page_offset_mask;
|
||||
|
||||
/* If it spans a page, then bail */
|
||||
if (page_size - page_offset < size) { return false; }
|
||||
|
||||
/*
|
||||
* Our address map can change (e.g. stack growth), use msync as a fallback to
|
||||
* validate our address.
|
||||
*/
|
||||
if (msync(page_addr, page_offset + size, MS_ASYNC) < 0) {
|
||||
|
||||
if (!g_hash_table_add(hash_no, GSIZE_TO_POINTER(addr))) {
|
||||
|
||||
FATAL("Failed - g_hash_table_add");
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
} else {
|
||||
|
||||
if (!g_hash_table_add(hash_yes, GSIZE_TO_POINTER(addr))) {
|
||||
|
||||
FATAL("Failed - g_hash_table_add");
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
gboolean cmplog_is_readable(guint64 addr, size_t size) {
|
||||
|
||||
if (cmplog_ranges == NULL) FATAL("CMPLOG not initialized");
|
||||
@ -67,20 +149,26 @@ gboolean cmplog_is_readable(guint64 addr, size_t size) {
|
||||
*/
|
||||
if (addr < DEFAULT_MMAP_MIN_ADDR) { return false; }
|
||||
|
||||
/* Check our addres/length don't wrap around */
|
||||
if (SIZE_MAX - addr < size) { return false; }
|
||||
|
||||
GumAddress inner_base = addr;
|
||||
GumAddress inner_limit = inner_base + size;
|
||||
|
||||
for (guint i = 0; i < cmplog_ranges->len; i++) {
|
||||
|
||||
GumMemoryRange *range = &g_array_index(cmplog_ranges, GumMemoryRange, i);
|
||||
GumAddress outer_base = range->base_address;
|
||||
GumAddress outer_limit = outer_base + range->size;
|
||||
|
||||
GumAddress outer_base = range->base_address;
|
||||
GumAddress outer_limit = outer_base + range->size;
|
||||
|
||||
if (cmplog_contains(inner_base, inner_limit, outer_base, outer_limit))
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
if (cmplog_test_addr(addr, size)) { return true; }
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
@ -1,17 +1,304 @@
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "cmplog.h"
|
||||
|
||||
#include "ctx.h"
|
||||
#include "frida_cmplog.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined(__aarch64__)
|
||||
|
||||
typedef struct {
|
||||
|
||||
arm64_op_type type;
|
||||
uint8_t size;
|
||||
|
||||
union {
|
||||
|
||||
arm64_op_mem mem;
|
||||
arm64_reg reg;
|
||||
int64_t imm;
|
||||
|
||||
};
|
||||
|
||||
} cmplog_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
cmplog_ctx_t operand1;
|
||||
cmplog_ctx_t operand2;
|
||||
size_t size;
|
||||
|
||||
} cmplog_pair_ctx_t;
|
||||
|
||||
static gboolean cmplog_read_mem(GumCpuContext *ctx, uint8_t size,
|
||||
arm64_op_mem *mem, gsize *val) {
|
||||
|
||||
gsize base = 0;
|
||||
gsize index = 0;
|
||||
gsize address;
|
||||
|
||||
if (mem->base != ARM64_REG_INVALID) { base = ctx_read_reg(ctx, mem->base); }
|
||||
|
||||
if (mem->index != ARM64_REG_INVALID) {
|
||||
|
||||
index = ctx_read_reg(ctx, mem->index);
|
||||
|
||||
}
|
||||
|
||||
address = base + index + mem->disp;
|
||||
|
||||
if (!cmplog_is_readable(address, size)) { return FALSE; }
|
||||
|
||||
switch (size) {
|
||||
|
||||
case 1:
|
||||
*val = *((guint8 *)GSIZE_TO_POINTER(address));
|
||||
return TRUE;
|
||||
case 2:
|
||||
*val = *((guint16 *)GSIZE_TO_POINTER(address));
|
||||
return TRUE;
|
||||
case 4:
|
||||
*val = *((guint32 *)GSIZE_TO_POINTER(address));
|
||||
return TRUE;
|
||||
case 8:
|
||||
*val = *((guint64 *)GSIZE_TO_POINTER(address));
|
||||
return TRUE;
|
||||
default:
|
||||
FATAL("Invalid operand size: %d\n", size);
|
||||
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
|
||||
}
|
||||
|
||||
static gboolean cmplog_get_operand_value(GumCpuContext *context,
|
||||
cmplog_ctx_t *ctx, gsize *val) {
|
||||
|
||||
switch (ctx->type) {
|
||||
|
||||
case ARM64_OP_REG:
|
||||
*val = ctx_read_reg(context, ctx->reg);
|
||||
return TRUE;
|
||||
case ARM64_OP_IMM:
|
||||
*val = ctx->imm;
|
||||
return TRUE;
|
||||
case ARM64_OP_MEM:
|
||||
return cmplog_read_mem(context, ctx->size, &ctx->mem, val);
|
||||
default:
|
||||
FATAL("Invalid operand type: %d\n", ctx->type);
|
||||
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
|
||||
}
|
||||
|
||||
static void cmplog_call_callout(GumCpuContext *context, gpointer user_data) {
|
||||
|
||||
UNUSED_PARAMETER(user_data);
|
||||
|
||||
gsize address = context->pc;
|
||||
gsize x0 = ctx_read_reg(context, ARM64_REG_X0);
|
||||
gsize x1 = ctx_read_reg(context, ARM64_REG_X1);
|
||||
|
||||
if (((G_MAXULONG - x0) < 32) || ((G_MAXULONG - x1) < 32)) return;
|
||||
|
||||
if (!cmplog_is_readable(x0, 32) || !cmplog_is_readable(x1, 32)) return;
|
||||
|
||||
void *ptr1 = GSIZE_TO_POINTER(x0);
|
||||
void *ptr2 = GSIZE_TO_POINTER(x1);
|
||||
|
||||
uintptr_t k = address;
|
||||
|
||||
k = (k >> 4) ^ (k << 8);
|
||||
k &= CMP_MAP_W - 1;
|
||||
|
||||
__afl_cmp_map->headers[k].type = CMP_TYPE_RTN;
|
||||
|
||||
u32 hits = __afl_cmp_map->headers[k].hits;
|
||||
__afl_cmp_map->headers[k].hits = hits + 1;
|
||||
|
||||
__afl_cmp_map->headers[k].shape = 31;
|
||||
|
||||
hits &= CMP_MAP_RTN_H - 1;
|
||||
gum_memcpy(((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v0, ptr1,
|
||||
32);
|
||||
gum_memcpy(((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v1, ptr2,
|
||||
32);
|
||||
|
||||
}
|
||||
|
||||
static void cmplog_instrument_put_operand(cmplog_ctx_t *ctx,
|
||||
cs_arm64_op * operand) {
|
||||
|
||||
ctx->type = operand->type;
|
||||
switch (operand->type) {
|
||||
|
||||
case ARM64_OP_REG:
|
||||
gum_memcpy(&ctx->reg, &operand->reg, sizeof(arm64_reg));
|
||||
break;
|
||||
case ARM64_OP_IMM:
|
||||
gum_memcpy(&ctx->imm, &operand->imm, sizeof(int64_t));
|
||||
break;
|
||||
case ARM64_OP_MEM:
|
||||
gum_memcpy(&ctx->mem, &operand->mem, sizeof(arm64_op_mem));
|
||||
break;
|
||||
default:
|
||||
FATAL("Invalid operand type: %d\n", operand->type);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void cmplog_instrument_call(const cs_insn * instr,
|
||||
GumStalkerIterator *iterator) {
|
||||
|
||||
cs_arm64 arm64 = instr->detail->arm64;
|
||||
cs_arm64_op *operand;
|
||||
|
||||
switch (instr->id) {
|
||||
|
||||
case ARM64_INS_BL:
|
||||
case ARM64_INS_BLR:
|
||||
case ARM64_INS_BLRAA:
|
||||
case ARM64_INS_BLRAAZ:
|
||||
case ARM64_INS_BLRAB:
|
||||
case ARM64_INS_BLRABZ:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (arm64.op_count != 1) return;
|
||||
|
||||
operand = &arm64.operands[0];
|
||||
|
||||
if (operand->type == ARM64_OP_INVALID) return;
|
||||
|
||||
gum_stalker_iterator_put_callout(iterator, cmplog_call_callout, NULL, NULL);
|
||||
|
||||
}
|
||||
|
||||
static void cmplog_handle_cmp_sub(GumCpuContext *context, gsize operand1,
|
||||
gsize operand2, uint8_t size) {
|
||||
|
||||
gsize address = context->pc;
|
||||
|
||||
register uintptr_t k = (uintptr_t)address;
|
||||
|
||||
k = (k >> 4) ^ (k << 8);
|
||||
k &= CMP_MAP_W - 1;
|
||||
|
||||
__afl_cmp_map->headers[k].type = CMP_TYPE_INS;
|
||||
|
||||
u32 hits = __afl_cmp_map->headers[k].hits;
|
||||
__afl_cmp_map->headers[k].hits = hits + 1;
|
||||
|
||||
__afl_cmp_map->headers[k].shape = (size - 1);
|
||||
|
||||
hits &= CMP_MAP_H - 1;
|
||||
__afl_cmp_map->log[k][hits].v0 = operand1;
|
||||
__afl_cmp_map->log[k][hits].v1 = operand2;
|
||||
|
||||
}
|
||||
|
||||
static void cmplog_cmp_sub_callout(GumCpuContext *context, gpointer user_data) {
|
||||
|
||||
cmplog_pair_ctx_t *ctx = (cmplog_pair_ctx_t *)user_data;
|
||||
gsize operand1;
|
||||
gsize operand2;
|
||||
|
||||
if (!cmplog_get_operand_value(context, &ctx->operand1, &operand1)) { return; }
|
||||
if (!cmplog_get_operand_value(context, &ctx->operand2, &operand2)) { return; }
|
||||
|
||||
cmplog_handle_cmp_sub(context, operand1, operand2, ctx->size);
|
||||
|
||||
}
|
||||
|
||||
static void cmplog_instrument_cmp_sub_put_callout(GumStalkerIterator *iterator,
|
||||
cs_arm64_op * operand1,
|
||||
cs_arm64_op * operand2,
|
||||
size_t size) {
|
||||
|
||||
cmplog_pair_ctx_t *ctx = g_malloc(sizeof(cmplog_pair_ctx_t));
|
||||
if (ctx == NULL) return;
|
||||
|
||||
cmplog_instrument_put_operand(&ctx->operand1, operand1);
|
||||
cmplog_instrument_put_operand(&ctx->operand2, operand2);
|
||||
ctx->size = size;
|
||||
|
||||
gum_stalker_iterator_put_callout(iterator, cmplog_cmp_sub_callout, ctx,
|
||||
g_free);
|
||||
|
||||
}
|
||||
|
||||
static void cmplog_instrument_cmp_sub(const cs_insn * instr,
|
||||
GumStalkerIterator *iterator) {
|
||||
|
||||
cs_arm64 arm64 = instr->detail->arm64;
|
||||
cs_arm64_op *operand1;
|
||||
cs_arm64_op *operand2;
|
||||
size_t size;
|
||||
|
||||
switch (instr->id) {
|
||||
|
||||
case ARM64_INS_ADCS:
|
||||
case ARM64_INS_ADDS:
|
||||
case ARM64_INS_ANDS:
|
||||
case ARM64_INS_BICS:
|
||||
case ARM64_INS_CMN:
|
||||
case ARM64_INS_CMP:
|
||||
case ARM64_INS_CMPEQ:
|
||||
case ARM64_INS_CMPGE:
|
||||
case ARM64_INS_CMPGT:
|
||||
case ARM64_INS_CMPHI:
|
||||
case ARM64_INS_CMPHS:
|
||||
case ARM64_INS_CMPLE:
|
||||
case ARM64_INS_CMPLO:
|
||||
case ARM64_INS_CMPLS:
|
||||
case ARM64_INS_CMPLT:
|
||||
case ARM64_INS_CMPNE:
|
||||
case ARM64_INS_EORS:
|
||||
case ARM64_INS_NANDS:
|
||||
case ARM64_INS_NEGS:
|
||||
case ARM64_INS_NGCS:
|
||||
case ARM64_INS_NORS:
|
||||
case ARM64_INS_NOTS:
|
||||
case ARM64_INS_ORNS:
|
||||
case ARM64_INS_ORRS:
|
||||
case ARM64_INS_SBCS:
|
||||
case ARM64_INS_SUBS:
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (arm64.op_count != 2) return;
|
||||
|
||||
operand1 = &arm64.operands[0];
|
||||
operand2 = &arm64.operands[1];
|
||||
|
||||
if (operand1->type == ARM64_OP_INVALID) return;
|
||||
if (operand2->type == ARM64_OP_INVALID) return;
|
||||
|
||||
size = ctx_get_size(instr, &arm64.operands[0]);
|
||||
|
||||
cmplog_instrument_cmp_sub_put_callout(iterator, operand1, operand2, size);
|
||||
|
||||
}
|
||||
|
||||
void cmplog_instrument(const cs_insn *instr, GumStalkerIterator *iterator) {
|
||||
|
||||
UNUSED_PARAMETER(instr);
|
||||
UNUSED_PARAMETER(iterator);
|
||||
if (__afl_cmp_map == NULL) { return; }
|
||||
FATAL("CMPLOG mode not supported on this architecture");
|
||||
if (__afl_cmp_map == NULL) return;
|
||||
|
||||
cmplog_instrument_call(instr, iterator);
|
||||
cmplog_instrument_cmp_sub(instr, iterator);
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "cmplog.h"
|
||||
@ -177,7 +177,7 @@ static void cmplog_handle_cmp_sub(GumCpuContext *context, gsize operand1,
|
||||
register uintptr_t k = (uintptr_t)address;
|
||||
|
||||
k = (k >> 4) ^ (k << 8);
|
||||
k &= CMP_MAP_W - 1;
|
||||
k &= CMP_MAP_W - 7;
|
||||
|
||||
__afl_cmp_map->headers[k].type = CMP_TYPE_INS;
|
||||
|
||||
@ -198,8 +198,6 @@ static void cmplog_cmp_sub_callout(GumCpuContext *context, gpointer user_data) {
|
||||
gsize operand1;
|
||||
gsize operand2;
|
||||
|
||||
if (ctx->operand1.size != ctx->operand2.size) FATAL("Operand size mismatch");
|
||||
|
||||
if (!cmplog_get_operand_value(context, &ctx->operand1, &operand1)) { return; }
|
||||
if (!cmplog_get_operand_value(context, &ctx->operand2, &operand2)) { return; }
|
||||
|
||||
@ -233,6 +231,15 @@ static void cmplog_instrument_cmp_sub(const cs_insn * instr,
|
||||
|
||||
case X86_INS_CMP:
|
||||
case X86_INS_SUB:
|
||||
case X86_INS_SCASB:
|
||||
case X86_INS_SCASD:
|
||||
case X86_INS_SCASQ:
|
||||
case X86_INS_SCASW:
|
||||
case X86_INS_CMPSB:
|
||||
case X86_INS_CMPSD:
|
||||
case X86_INS_CMPSQ:
|
||||
case X86_INS_CMPSS:
|
||||
case X86_INS_CMPSW:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
@ -247,13 +254,8 @@ static void cmplog_instrument_cmp_sub(const cs_insn * instr,
|
||||
if (operand1->type == X86_OP_INVALID) return;
|
||||
if (operand2->type == X86_OP_INVALID) return;
|
||||
|
||||
if ((operand1->type == X86_OP_MEM) &&
|
||||
(operand1->mem.segment != X86_REG_INVALID))
|
||||
return;
|
||||
|
||||
if ((operand2->type == X86_OP_MEM) &&
|
||||
(operand2->mem.segment != X86_REG_INVALID))
|
||||
return;
|
||||
/* Both operands are the same size */
|
||||
if (operand1->size == 1) { return; }
|
||||
|
||||
cmplog_instrument_cmp_sub_put_callout(iterator, operand1, operand2);
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "cmplog.h"
|
||||
|
16
frida_mode/src/ctx/ctx_arm32.c
Normal file
16
frida_mode/src/ctx/ctx_arm32.c
Normal file
@ -0,0 +1,16 @@
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "ctx.h"
|
||||
|
||||
#if defined(__arm__)
|
||||
|
||||
gsize ctx_read_reg(GumArmCpuContext *ctx, arm_reg reg) {
|
||||
|
||||
FATAL("ctx_read_reg unimplemented for this architecture");
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
303
frida_mode/src/ctx/ctx_arm64.c
Normal file
303
frida_mode/src/ctx/ctx_arm64.c
Normal file
@ -0,0 +1,303 @@
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "ctx.h"
|
||||
|
||||
#if defined(__aarch64__)
|
||||
|
||||
#define ARM64_REG_8(LABEL, REG) \
|
||||
case LABEL: { \
|
||||
\
|
||||
return REG & GUM_INT8_MASK; \
|
||||
\
|
||||
}
|
||||
|
||||
#define ARM64_REG_16(LABEL, REG) \
|
||||
case LABEL: { \
|
||||
\
|
||||
return (REG & GUM_INT16_MASK); \
|
||||
\
|
||||
}
|
||||
|
||||
#define ARM64_REG_32(LABEL, REG) \
|
||||
case LABEL: { \
|
||||
\
|
||||
return (REG & GUM_INT32_MASK); \
|
||||
\
|
||||
}
|
||||
|
||||
#define ARM64_REG_64(LABEL, REG) \
|
||||
case LABEL: { \
|
||||
\
|
||||
return (REG); \
|
||||
\
|
||||
}
|
||||
|
||||
gsize ctx_read_reg(GumArm64CpuContext *ctx, arm64_reg reg) {
|
||||
|
||||
switch (reg) {
|
||||
|
||||
case ARM64_REG_WZR:
|
||||
case ARM64_REG_XZR:
|
||||
return 0;
|
||||
|
||||
ARM64_REG_8(ARM64_REG_B0, ctx->x[0])
|
||||
ARM64_REG_8(ARM64_REG_B1, ctx->x[1])
|
||||
ARM64_REG_8(ARM64_REG_B2, ctx->x[2])
|
||||
ARM64_REG_8(ARM64_REG_B3, ctx->x[3])
|
||||
ARM64_REG_8(ARM64_REG_B4, ctx->x[4])
|
||||
ARM64_REG_8(ARM64_REG_B5, ctx->x[5])
|
||||
ARM64_REG_8(ARM64_REG_B6, ctx->x[6])
|
||||
ARM64_REG_8(ARM64_REG_B7, ctx->x[7])
|
||||
ARM64_REG_8(ARM64_REG_B8, ctx->x[8])
|
||||
ARM64_REG_8(ARM64_REG_B9, ctx->x[9])
|
||||
ARM64_REG_8(ARM64_REG_B10, ctx->x[10])
|
||||
ARM64_REG_8(ARM64_REG_B11, ctx->x[11])
|
||||
ARM64_REG_8(ARM64_REG_B12, ctx->x[12])
|
||||
ARM64_REG_8(ARM64_REG_B13, ctx->x[13])
|
||||
ARM64_REG_8(ARM64_REG_B14, ctx->x[14])
|
||||
ARM64_REG_8(ARM64_REG_B15, ctx->x[15])
|
||||
ARM64_REG_8(ARM64_REG_B16, ctx->x[16])
|
||||
ARM64_REG_8(ARM64_REG_B17, ctx->x[17])
|
||||
ARM64_REG_8(ARM64_REG_B18, ctx->x[18])
|
||||
ARM64_REG_8(ARM64_REG_B19, ctx->x[19])
|
||||
ARM64_REG_8(ARM64_REG_B20, ctx->x[20])
|
||||
ARM64_REG_8(ARM64_REG_B21, ctx->x[21])
|
||||
ARM64_REG_8(ARM64_REG_B22, ctx->x[22])
|
||||
ARM64_REG_8(ARM64_REG_B23, ctx->x[23])
|
||||
ARM64_REG_8(ARM64_REG_B24, ctx->x[24])
|
||||
ARM64_REG_8(ARM64_REG_B25, ctx->x[25])
|
||||
ARM64_REG_8(ARM64_REG_B26, ctx->x[26])
|
||||
ARM64_REG_8(ARM64_REG_B27, ctx->x[27])
|
||||
ARM64_REG_8(ARM64_REG_B28, ctx->x[28])
|
||||
ARM64_REG_8(ARM64_REG_B29, ctx->fp)
|
||||
ARM64_REG_8(ARM64_REG_B30, ctx->lr)
|
||||
ARM64_REG_8(ARM64_REG_B31, ctx->sp)
|
||||
|
||||
ARM64_REG_16(ARM64_REG_H0, ctx->x[0])
|
||||
ARM64_REG_16(ARM64_REG_H1, ctx->x[1])
|
||||
ARM64_REG_16(ARM64_REG_H2, ctx->x[2])
|
||||
ARM64_REG_16(ARM64_REG_H3, ctx->x[3])
|
||||
ARM64_REG_16(ARM64_REG_H4, ctx->x[4])
|
||||
ARM64_REG_16(ARM64_REG_H5, ctx->x[5])
|
||||
ARM64_REG_16(ARM64_REG_H6, ctx->x[6])
|
||||
ARM64_REG_16(ARM64_REG_H7, ctx->x[7])
|
||||
ARM64_REG_16(ARM64_REG_H8, ctx->x[8])
|
||||
ARM64_REG_16(ARM64_REG_H9, ctx->x[9])
|
||||
ARM64_REG_16(ARM64_REG_H10, ctx->x[10])
|
||||
ARM64_REG_16(ARM64_REG_H11, ctx->x[11])
|
||||
ARM64_REG_16(ARM64_REG_H12, ctx->x[12])
|
||||
ARM64_REG_16(ARM64_REG_H13, ctx->x[13])
|
||||
ARM64_REG_16(ARM64_REG_H14, ctx->x[14])
|
||||
ARM64_REG_16(ARM64_REG_H15, ctx->x[15])
|
||||
ARM64_REG_16(ARM64_REG_H16, ctx->x[16])
|
||||
ARM64_REG_16(ARM64_REG_H17, ctx->x[17])
|
||||
ARM64_REG_16(ARM64_REG_H18, ctx->x[18])
|
||||
ARM64_REG_16(ARM64_REG_H19, ctx->x[19])
|
||||
ARM64_REG_16(ARM64_REG_H20, ctx->x[20])
|
||||
ARM64_REG_16(ARM64_REG_H21, ctx->x[21])
|
||||
ARM64_REG_16(ARM64_REG_H22, ctx->x[22])
|
||||
ARM64_REG_16(ARM64_REG_H23, ctx->x[23])
|
||||
ARM64_REG_16(ARM64_REG_H24, ctx->x[24])
|
||||
ARM64_REG_16(ARM64_REG_H25, ctx->x[25])
|
||||
ARM64_REG_16(ARM64_REG_H26, ctx->x[26])
|
||||
ARM64_REG_16(ARM64_REG_H27, ctx->x[27])
|
||||
ARM64_REG_16(ARM64_REG_H28, ctx->x[28])
|
||||
ARM64_REG_16(ARM64_REG_H29, ctx->fp)
|
||||
ARM64_REG_16(ARM64_REG_H30, ctx->lr)
|
||||
ARM64_REG_16(ARM64_REG_H31, ctx->sp)
|
||||
|
||||
ARM64_REG_32(ARM64_REG_W0, ctx->x[0])
|
||||
ARM64_REG_32(ARM64_REG_W1, ctx->x[1])
|
||||
ARM64_REG_32(ARM64_REG_W2, ctx->x[2])
|
||||
ARM64_REG_32(ARM64_REG_W3, ctx->x[3])
|
||||
ARM64_REG_32(ARM64_REG_W4, ctx->x[4])
|
||||
ARM64_REG_32(ARM64_REG_W5, ctx->x[5])
|
||||
ARM64_REG_32(ARM64_REG_W6, ctx->x[6])
|
||||
ARM64_REG_32(ARM64_REG_W7, ctx->x[7])
|
||||
ARM64_REG_32(ARM64_REG_W8, ctx->x[8])
|
||||
ARM64_REG_32(ARM64_REG_W9, ctx->x[9])
|
||||
ARM64_REG_32(ARM64_REG_W10, ctx->x[10])
|
||||
ARM64_REG_32(ARM64_REG_W11, ctx->x[11])
|
||||
ARM64_REG_32(ARM64_REG_W12, ctx->x[12])
|
||||
ARM64_REG_32(ARM64_REG_W13, ctx->x[13])
|
||||
ARM64_REG_32(ARM64_REG_W14, ctx->x[14])
|
||||
ARM64_REG_32(ARM64_REG_W15, ctx->x[15])
|
||||
ARM64_REG_32(ARM64_REG_W16, ctx->x[16])
|
||||
ARM64_REG_32(ARM64_REG_W17, ctx->x[17])
|
||||
ARM64_REG_32(ARM64_REG_W18, ctx->x[18])
|
||||
ARM64_REG_32(ARM64_REG_W19, ctx->x[19])
|
||||
ARM64_REG_32(ARM64_REG_W20, ctx->x[20])
|
||||
ARM64_REG_32(ARM64_REG_W21, ctx->x[21])
|
||||
ARM64_REG_32(ARM64_REG_W22, ctx->x[22])
|
||||
ARM64_REG_32(ARM64_REG_W23, ctx->x[23])
|
||||
ARM64_REG_32(ARM64_REG_W24, ctx->x[24])
|
||||
ARM64_REG_32(ARM64_REG_W25, ctx->x[25])
|
||||
ARM64_REG_32(ARM64_REG_W26, ctx->x[26])
|
||||
ARM64_REG_32(ARM64_REG_W27, ctx->x[27])
|
||||
ARM64_REG_32(ARM64_REG_W28, ctx->x[28])
|
||||
ARM64_REG_32(ARM64_REG_W29, ctx->fp)
|
||||
ARM64_REG_32(ARM64_REG_W30, ctx->lr)
|
||||
|
||||
ARM64_REG_64(ARM64_REG_X0, ctx->x[0])
|
||||
ARM64_REG_64(ARM64_REG_X1, ctx->x[1])
|
||||
ARM64_REG_64(ARM64_REG_X2, ctx->x[2])
|
||||
ARM64_REG_64(ARM64_REG_X3, ctx->x[3])
|
||||
ARM64_REG_64(ARM64_REG_X4, ctx->x[4])
|
||||
ARM64_REG_64(ARM64_REG_X5, ctx->x[5])
|
||||
ARM64_REG_64(ARM64_REG_X6, ctx->x[6])
|
||||
ARM64_REG_64(ARM64_REG_X7, ctx->x[7])
|
||||
ARM64_REG_64(ARM64_REG_X8, ctx->x[8])
|
||||
ARM64_REG_64(ARM64_REG_X9, ctx->x[9])
|
||||
ARM64_REG_64(ARM64_REG_X10, ctx->x[10])
|
||||
ARM64_REG_64(ARM64_REG_X11, ctx->x[11])
|
||||
ARM64_REG_64(ARM64_REG_X12, ctx->x[12])
|
||||
ARM64_REG_64(ARM64_REG_X13, ctx->x[13])
|
||||
ARM64_REG_64(ARM64_REG_X14, ctx->x[14])
|
||||
ARM64_REG_64(ARM64_REG_X15, ctx->x[15])
|
||||
ARM64_REG_64(ARM64_REG_X16, ctx->x[16])
|
||||
ARM64_REG_64(ARM64_REG_X17, ctx->x[17])
|
||||
ARM64_REG_64(ARM64_REG_X18, ctx->x[18])
|
||||
ARM64_REG_64(ARM64_REG_X19, ctx->x[19])
|
||||
ARM64_REG_64(ARM64_REG_X20, ctx->x[20])
|
||||
ARM64_REG_64(ARM64_REG_X21, ctx->x[21])
|
||||
ARM64_REG_64(ARM64_REG_X22, ctx->x[22])
|
||||
ARM64_REG_64(ARM64_REG_X23, ctx->x[23])
|
||||
ARM64_REG_64(ARM64_REG_X24, ctx->x[24])
|
||||
ARM64_REG_64(ARM64_REG_X25, ctx->x[25])
|
||||
ARM64_REG_64(ARM64_REG_X26, ctx->x[26])
|
||||
ARM64_REG_64(ARM64_REG_X27, ctx->x[27])
|
||||
ARM64_REG_64(ARM64_REG_X28, ctx->x[28])
|
||||
ARM64_REG_64(ARM64_REG_FP, ctx->fp)
|
||||
ARM64_REG_64(ARM64_REG_LR, ctx->lr)
|
||||
ARM64_REG_64(ARM64_REG_SP, ctx->sp)
|
||||
|
||||
default:
|
||||
FATAL("Failed to read register: %d", reg);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
size_t ctx_get_size(const cs_insn *instr, cs_arm64_op *operand) {
|
||||
|
||||
uint8_t num_registers;
|
||||
uint8_t count_byte;
|
||||
char vas_digit;
|
||||
size_t mnemonic_len;
|
||||
|
||||
switch (instr->id) {
|
||||
|
||||
case ARM64_INS_STP:
|
||||
case ARM64_INS_STXP:
|
||||
case ARM64_INS_STNP:
|
||||
case ARM64_INS_STLXP:
|
||||
case ARM64_INS_LDP:
|
||||
case ARM64_INS_LDXP:
|
||||
case ARM64_INS_LDNP:
|
||||
num_registers = 2;
|
||||
break;
|
||||
default:
|
||||
num_registers = 1;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
mnemonic_len = strlen(instr->mnemonic);
|
||||
if (mnemonic_len == 0) { FATAL("No mnemonic found"); };
|
||||
|
||||
char last = instr->mnemonic[mnemonic_len - 1];
|
||||
switch (last) {
|
||||
|
||||
case 'b':
|
||||
return 1;
|
||||
case 'h':
|
||||
return 2;
|
||||
case 'w':
|
||||
return 4 * num_registers;
|
||||
|
||||
}
|
||||
|
||||
if (operand->vas == ARM64_VAS_INVALID) {
|
||||
|
||||
if (operand->type == ARM64_OP_REG) {
|
||||
|
||||
switch (operand->reg) {
|
||||
|
||||
case ARM64_REG_WZR:
|
||||
case ARM64_REG_WSP:
|
||||
case ARM64_REG_W0 ... ARM64_REG_W30:
|
||||
case ARM64_REG_S0 ... ARM64_REG_S31:
|
||||
return 4 * num_registers;
|
||||
case ARM64_REG_D0 ... ARM64_REG_D31:
|
||||
return 8 * num_registers;
|
||||
case ARM64_REG_Q0 ... ARM64_REG_Q31:
|
||||
return 16;
|
||||
default:
|
||||
return 8 * num_registers;
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 8 * num_registers;
|
||||
|
||||
}
|
||||
|
||||
if (g_str_has_prefix(instr->mnemonic, "st") ||
|
||||
g_str_has_prefix(instr->mnemonic, "ld")) {
|
||||
|
||||
if (mnemonic_len < 3) {
|
||||
|
||||
FATAL("VAS Mnemonic too short: %s\n", instr->mnemonic);
|
||||
|
||||
}
|
||||
|
||||
vas_digit = instr->mnemonic[2];
|
||||
if (vas_digit < '0' || vas_digit > '9') {
|
||||
|
||||
FATAL("VAS Mnemonic digit out of range: %s\n", instr->mnemonic);
|
||||
|
||||
}
|
||||
|
||||
count_byte = vas_digit - '0';
|
||||
|
||||
} else {
|
||||
|
||||
count_byte = 1;
|
||||
|
||||
}
|
||||
|
||||
switch (operand->vas) {
|
||||
|
||||
case ARM64_VAS_1B:
|
||||
return 1 * count_byte;
|
||||
case ARM64_VAS_1H:
|
||||
return 2 * count_byte;
|
||||
case ARM64_VAS_4B:
|
||||
case ARM64_VAS_1S:
|
||||
case ARM64_VAS_1D:
|
||||
case ARM64_VAS_2H:
|
||||
return 4 * count_byte;
|
||||
case ARM64_VAS_8B:
|
||||
case ARM64_VAS_4H:
|
||||
case ARM64_VAS_2S:
|
||||
case ARM64_VAS_2D:
|
||||
case ARM64_VAS_1Q:
|
||||
return 8 * count_byte;
|
||||
case ARM64_VAS_8H:
|
||||
case ARM64_VAS_4S:
|
||||
case ARM64_VAS_16B:
|
||||
return 16 * count_byte;
|
||||
default:
|
||||
FATAL("Unexpected VAS type: %s %d", instr->mnemonic, operand->vas);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
@ -49,9 +49,18 @@ gsize ctx_read_reg(GumX64CpuContext *ctx, x86_reg reg) {
|
||||
X86_REG_8L(X86_REG_BL, ctx->rbx)
|
||||
X86_REG_8L(X86_REG_CL, ctx->rcx)
|
||||
X86_REG_8L(X86_REG_DL, ctx->rdx)
|
||||
X86_REG_8L(X86_REG_SPL, ctx->rsp)
|
||||
X86_REG_8L(X86_REG_BPL, ctx->rbp)
|
||||
X86_REG_8L(X86_REG_SIL, ctx->rsi)
|
||||
X86_REG_8L(X86_REG_DIL, ctx->rdi)
|
||||
X86_REG_8L(X86_REG_R8B, ctx->r8)
|
||||
X86_REG_8L(X86_REG_R9B, ctx->r9)
|
||||
X86_REG_8L(X86_REG_R10B, ctx->r10)
|
||||
X86_REG_8L(X86_REG_R11B, ctx->r11)
|
||||
X86_REG_8L(X86_REG_R12B, ctx->r12)
|
||||
X86_REG_8L(X86_REG_R13B, ctx->r13)
|
||||
X86_REG_8L(X86_REG_R14B, ctx->r14)
|
||||
X86_REG_8L(X86_REG_R15B, ctx->r15)
|
||||
|
||||
X86_REG_8H(X86_REG_AH, ctx->rax)
|
||||
X86_REG_8H(X86_REG_BH, ctx->rbx)
|
||||
@ -62,14 +71,23 @@ gsize ctx_read_reg(GumX64CpuContext *ctx, x86_reg reg) {
|
||||
X86_REG_16(X86_REG_BX, ctx->rbx)
|
||||
X86_REG_16(X86_REG_CX, ctx->rcx)
|
||||
X86_REG_16(X86_REG_DX, ctx->rdx)
|
||||
X86_REG_16(X86_REG_SP, ctx->rsp)
|
||||
X86_REG_16(X86_REG_BP, ctx->rbp)
|
||||
X86_REG_16(X86_REG_DI, ctx->rdi)
|
||||
X86_REG_16(X86_REG_SI, ctx->rsi)
|
||||
X86_REG_16(X86_REG_BP, ctx->rbp)
|
||||
X86_REG_16(X86_REG_R8W, ctx->r8)
|
||||
X86_REG_16(X86_REG_R9W, ctx->r9)
|
||||
X86_REG_16(X86_REG_R10W, ctx->r10)
|
||||
X86_REG_16(X86_REG_R11W, ctx->r11)
|
||||
X86_REG_16(X86_REG_R12W, ctx->r12)
|
||||
X86_REG_16(X86_REG_R13W, ctx->r13)
|
||||
X86_REG_16(X86_REG_R14W, ctx->r14)
|
||||
X86_REG_16(X86_REG_R15W, ctx->r15)
|
||||
|
||||
X86_REG_32(X86_REG_EAX, ctx->rax)
|
||||
X86_REG_32(X86_REG_EBX, ctx->rbx)
|
||||
X86_REG_32(X86_REG_ECX, ctx->rcx)
|
||||
X86_REG_32(X86_REG_EDX, ctx->rdx)
|
||||
X86_REG_32(X86_REG_EBX, ctx->rbx)
|
||||
X86_REG_32(X86_REG_ESP, ctx->rsp)
|
||||
X86_REG_32(X86_REG_EBP, ctx->rbp)
|
||||
X86_REG_32(X86_REG_ESI, ctx->rsi)
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
@ -42,6 +42,7 @@ gsize ctx_read_reg(GumIA32CpuContext *ctx, x86_reg reg) {
|
||||
X86_REG_8L(X86_REG_BL, ctx->ebx)
|
||||
X86_REG_8L(X86_REG_CL, ctx->ecx)
|
||||
X86_REG_8L(X86_REG_DL, ctx->edx)
|
||||
X86_REG_8L(X86_REG_SPL, ctx->esp)
|
||||
X86_REG_8L(X86_REG_BPL, ctx->ebp)
|
||||
X86_REG_8L(X86_REG_SIL, ctx->esi)
|
||||
X86_REG_8L(X86_REG_DIL, ctx->edi)
|
||||
@ -55,14 +56,15 @@ gsize ctx_read_reg(GumIA32CpuContext *ctx, x86_reg reg) {
|
||||
X86_REG_16(X86_REG_BX, ctx->ebx)
|
||||
X86_REG_16(X86_REG_CX, ctx->ecx)
|
||||
X86_REG_16(X86_REG_DX, ctx->edx)
|
||||
X86_REG_16(X86_REG_SP, ctx->esp)
|
||||
X86_REG_16(X86_REG_BP, ctx->ebp)
|
||||
X86_REG_16(X86_REG_DI, ctx->edi)
|
||||
X86_REG_16(X86_REG_SI, ctx->esi)
|
||||
X86_REG_16(X86_REG_BP, ctx->ebp)
|
||||
|
||||
X86_REG_32(X86_REG_EAX, ctx->eax)
|
||||
X86_REG_32(X86_REG_EBX, ctx->ebx)
|
||||
X86_REG_32(X86_REG_ECX, ctx->ecx)
|
||||
X86_REG_32(X86_REG_EDX, ctx->edx)
|
||||
X86_REG_32(X86_REG_EBX, ctx->ebx)
|
||||
X86_REG_32(X86_REG_ESP, ctx->esp)
|
||||
X86_REG_32(X86_REG_EBP, ctx->ebp)
|
||||
X86_REG_32(X86_REG_ESI, ctx->esi)
|
||||
|
@ -1,35 +1,46 @@
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "entry.h"
|
||||
#include "instrument.h"
|
||||
#include "persistent.h"
|
||||
#include "ranges.h"
|
||||
#include "stalker.h"
|
||||
#include "stats.h"
|
||||
#include "util.h"
|
||||
|
||||
extern void __afl_manual_init();
|
||||
|
||||
guint64 entry_start = 0;
|
||||
guint64 entry_point = 0;
|
||||
gboolean entry_reached = FALSE;
|
||||
|
||||
static void entry_launch(void) {
|
||||
|
||||
OKF("Entry point reached");
|
||||
__afl_manual_init();
|
||||
|
||||
/* Child here */
|
||||
previous_pc = 0;
|
||||
instrument_on_fork();
|
||||
stats_on_fork();
|
||||
|
||||
}
|
||||
|
||||
void entry_config(void) {
|
||||
|
||||
entry_point = util_read_address("AFL_ENTRYPOINT");
|
||||
|
||||
}
|
||||
|
||||
void entry_init(void) {
|
||||
|
||||
entry_start = util_read_address("AFL_ENTRYPOINT");
|
||||
OKF("entry_point: 0x%016" G_GINT64_MODIFIER "X", entry_start);
|
||||
OKF("entry_point: 0x%016" G_GINT64_MODIFIER "X", entry_point);
|
||||
|
||||
}
|
||||
|
||||
void entry_run(void) {
|
||||
void entry_start(void) {
|
||||
|
||||
if (entry_start == 0) { entry_launch(); }
|
||||
if (entry_point == 0) { entry_launch(); }
|
||||
|
||||
}
|
||||
|
||||
@ -44,6 +55,16 @@ static void entry_callout(GumCpuContext *cpu_context, gpointer user_data) {
|
||||
void entry_prologue(GumStalkerIterator *iterator, GumStalkerOutput *output) {
|
||||
|
||||
UNUSED_PARAMETER(output);
|
||||
OKF("AFL_ENTRYPOINT reached");
|
||||
|
||||
if (persistent_start == 0) {
|
||||
|
||||
entry_reached = TRUE;
|
||||
ranges_exclude();
|
||||
stalker_trust();
|
||||
|
||||
}
|
||||
|
||||
gum_stalker_iterator_put_callout(iterator, entry_callout, NULL, NULL);
|
||||
|
||||
}
|
||||
|
@ -1,14 +1,19 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
#include "hash.h"
|
||||
|
||||
#include "asan.h"
|
||||
#include "entry.h"
|
||||
#include "frida_cmplog.h"
|
||||
#include "instrument.h"
|
||||
#include "js.h"
|
||||
#include "persistent.h"
|
||||
#include "prefetch.h"
|
||||
#include "ranges.h"
|
||||
@ -16,46 +21,55 @@
|
||||
#include "stats.h"
|
||||
#include "util.h"
|
||||
|
||||
static gboolean tracing = false;
|
||||
static gboolean optimize = false;
|
||||
gboolean instrument_tracing = false;
|
||||
gboolean instrument_optimize = false;
|
||||
gboolean instrument_unique = false;
|
||||
guint64 instrument_hash_zero = 0;
|
||||
guint64 instrument_hash_seed = 0;
|
||||
|
||||
gboolean instrument_use_fixed_seed = FALSE;
|
||||
guint64 instrument_fixed_seed = 0;
|
||||
|
||||
static GumStalkerTransformer *transformer = NULL;
|
||||
|
||||
__thread uint64_t previous_pc = 0;
|
||||
__thread guint64 instrument_previous_pc = 0;
|
||||
|
||||
__attribute__((hot)) static void on_basic_block(GumCpuContext *context,
|
||||
gpointer user_data) {
|
||||
static GumAddress previous_rip = 0;
|
||||
static u8 * edges_notified = NULL;
|
||||
|
||||
UNUSED_PARAMETER(context);
|
||||
/*
|
||||
* This function is performance critical as it is called to instrument every
|
||||
* basic block. By moving our print buffer to a global, we avoid it affecting
|
||||
* the critical path with additional stack adjustments if tracing is not
|
||||
* enabled. If tracing is enabled, then we're printing a load of diagnostic
|
||||
* information so this overhead is unlikely to be noticeable.
|
||||
*/
|
||||
static char buffer[200];
|
||||
int len;
|
||||
GumAddress current_pc = GUM_ADDRESS(user_data);
|
||||
uint8_t * cursor;
|
||||
uint64_t value;
|
||||
if (unlikely(tracing)) {
|
||||
static void trace_debug(char *format, ...) {
|
||||
|
||||
/* Avoid any functions which may cause an allocation since the target app
|
||||
* may already be running inside malloc and it isn't designed to be
|
||||
* re-entrant on a single thread */
|
||||
len = snprintf(buffer, sizeof(buffer),
|
||||
"current_pc: 0x%016" G_GINT64_MODIFIER
|
||||
"x, previous_pc: 0x%016" G_GINT64_MODIFIER "x\n",
|
||||
current_pc, previous_pc);
|
||||
va_list ap;
|
||||
char buffer[4096] = {0};
|
||||
int ret;
|
||||
int len;
|
||||
|
||||
IGNORED_RETURN(write(STDOUT_FILENO, buffer, len + 1));
|
||||
va_start(ap, format);
|
||||
ret = vsnprintf(buffer, sizeof(buffer) - 1, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
}
|
||||
if (ret < 0) { return; }
|
||||
|
||||
current_pc = (current_pc >> 4) ^ (current_pc << 8);
|
||||
current_pc &= MAP_SIZE - 1;
|
||||
len = strnlen(buffer, sizeof(buffer));
|
||||
|
||||
cursor = &__afl_area_ptr[current_pc ^ previous_pc];
|
||||
IGNORED_RETURN(write(STDOUT_FILENO, buffer, len));
|
||||
|
||||
}
|
||||
|
||||
guint64 instrument_get_offset_hash(GumAddress current_rip) {
|
||||
|
||||
guint64 area_offset = hash64((unsigned char *)¤t_rip,
|
||||
sizeof(GumAddress), instrument_hash_seed);
|
||||
return area_offset &= MAP_SIZE - 1;
|
||||
|
||||
}
|
||||
|
||||
__attribute__((hot)) static void instrument_increment_map(GumAddress edge) {
|
||||
|
||||
uint8_t *cursor;
|
||||
uint64_t value;
|
||||
|
||||
cursor = &__afl_area_ptr[edge];
|
||||
value = *cursor;
|
||||
|
||||
if (value == 0xff) {
|
||||
@ -69,12 +83,47 @@ __attribute__((hot)) static void on_basic_block(GumCpuContext *context,
|
||||
}
|
||||
|
||||
*cursor = value;
|
||||
previous_pc = current_pc >> 1;
|
||||
|
||||
}
|
||||
|
||||
static void instr_basic_block(GumStalkerIterator *iterator,
|
||||
GumStalkerOutput *output, gpointer user_data) {
|
||||
__attribute__((hot)) static void on_basic_block(GumCpuContext *context,
|
||||
gpointer user_data) {
|
||||
|
||||
UNUSED_PARAMETER(context);
|
||||
|
||||
GumAddress current_rip = GUM_ADDRESS(user_data);
|
||||
guint64 current_pc = instrument_get_offset_hash(current_rip);
|
||||
guint64 edge;
|
||||
|
||||
edge = current_pc ^ instrument_previous_pc;
|
||||
|
||||
instrument_increment_map(edge);
|
||||
|
||||
if (unlikely(instrument_tracing)) {
|
||||
|
||||
if (!instrument_unique || edges_notified[edge] == 0) {
|
||||
|
||||
trace_debug("TRACE: edge: %10" G_GINT64_MODIFIER
|
||||
"d, current_rip: 0x%016" G_GINT64_MODIFIER
|
||||
"x, previous_rip: 0x%016" G_GINT64_MODIFIER "x\n",
|
||||
edge, current_rip, previous_rip);
|
||||
|
||||
}
|
||||
|
||||
if (instrument_unique) { edges_notified[edge] = 1; }
|
||||
|
||||
previous_rip = current_rip;
|
||||
|
||||
}
|
||||
|
||||
instrument_previous_pc =
|
||||
((current_pc & (MAP_SIZE - 1) >> 1)) | ((current_pc & 0x1) << 15);
|
||||
|
||||
}
|
||||
|
||||
static void instrument_basic_block(GumStalkerIterator *iterator,
|
||||
GumStalkerOutput * output,
|
||||
gpointer user_data) {
|
||||
|
||||
UNUSED_PARAMETER(user_data);
|
||||
|
||||
@ -84,7 +133,9 @@ static void instr_basic_block(GumStalkerIterator *iterator,
|
||||
|
||||
while (gum_stalker_iterator_next(iterator, &instr)) {
|
||||
|
||||
if (instr->address == entry_start) { entry_prologue(iterator, output); }
|
||||
if (unlikely(begin)) { instrument_debug_start(instr->address, output); }
|
||||
|
||||
if (instr->address == entry_point) { entry_prologue(iterator, output); }
|
||||
if (instr->address == persistent_start) { persistent_prologue(output); }
|
||||
if (instr->address == persistent_ret) { persistent_epilogue(output); }
|
||||
|
||||
@ -121,11 +172,15 @@ static void instr_basic_block(GumStalkerIterator *iterator,
|
||||
|
||||
instrument_debug_start(instr->address, output);
|
||||
|
||||
prefetch_write(GSIZE_TO_POINTER(instr->address));
|
||||
if (likely(entry_reached)) {
|
||||
|
||||
prefetch_write(GSIZE_TO_POINTER(instr->address));
|
||||
|
||||
}
|
||||
|
||||
if (likely(!excluded)) {
|
||||
|
||||
if (likely(optimize)) {
|
||||
if (likely(instrument_optimize)) {
|
||||
|
||||
instrument_coverage_optimize(instr, output);
|
||||
|
||||
@ -138,8 +193,6 @@ static void instr_basic_block(GumStalkerIterator *iterator,
|
||||
|
||||
}
|
||||
|
||||
begin = FALSE;
|
||||
|
||||
}
|
||||
|
||||
instrument_debug_instruction(instr->address, instr->size);
|
||||
@ -151,38 +204,117 @@ static void instr_basic_block(GumStalkerIterator *iterator,
|
||||
|
||||
}
|
||||
|
||||
gum_stalker_iterator_keep(iterator);
|
||||
if (js_stalker_callback(instr, begin, excluded, output)) {
|
||||
|
||||
gum_stalker_iterator_keep(iterator);
|
||||
|
||||
}
|
||||
|
||||
begin = FALSE;
|
||||
|
||||
}
|
||||
|
||||
instrument_flush(output);
|
||||
instrument_debug_end(output);
|
||||
|
||||
}
|
||||
|
||||
void instrument_config(void) {
|
||||
|
||||
instrument_optimize = (getenv("AFL_FRIDA_INST_NO_OPTIMIZE") == NULL);
|
||||
instrument_tracing = (getenv("AFL_FRIDA_INST_TRACE") != NULL);
|
||||
instrument_unique = (getenv("AFL_FRIDA_INST_TRACE_UNIQUE") != NULL);
|
||||
instrument_use_fixed_seed = (getenv("AFL_FRIDA_INST_SEED") != NULL);
|
||||
instrument_fixed_seed = util_read_num("AFL_FRIDA_INST_SEED");
|
||||
|
||||
instrument_debug_config();
|
||||
asan_config();
|
||||
cmplog_config();
|
||||
|
||||
}
|
||||
|
||||
void instrument_init(void) {
|
||||
|
||||
optimize = (getenv("AFL_FRIDA_INST_NO_OPTIMIZE") == NULL);
|
||||
tracing = (getenv("AFL_FRIDA_INST_TRACE") != NULL);
|
||||
if (!instrument_is_coverage_optimize_supported()) instrument_optimize = false;
|
||||
|
||||
if (!instrument_is_coverage_optimize_supported()) optimize = false;
|
||||
OKF("Instrumentation - optimize [%c]", instrument_optimize ? 'X' : ' ');
|
||||
OKF("Instrumentation - tracing [%c]", instrument_tracing ? 'X' : ' ');
|
||||
OKF("Instrumentation - unique [%c]", instrument_unique ? 'X' : ' ');
|
||||
OKF("Instrumentation - fixed seed [%c] [0x%016" G_GINT64_MODIFIER "x]",
|
||||
instrument_use_fixed_seed ? 'X' : ' ', instrument_fixed_seed);
|
||||
|
||||
OKF("Instrumentation - optimize [%c]", optimize ? 'X' : ' ');
|
||||
OKF("Instrumentation - tracing [%c]", tracing ? 'X' : ' ');
|
||||
if (instrument_tracing && instrument_optimize) {
|
||||
|
||||
if (tracing && optimize) {
|
||||
|
||||
FATAL("AFL_FRIDA_INST_OPTIMIZE and AFL_FRIDA_INST_TRACE are incompatible");
|
||||
WARNF("AFL_FRIDA_INST_TRACE implies AFL_FRIDA_INST_NO_OPTIMIZE");
|
||||
instrument_optimize = FALSE;
|
||||
|
||||
}
|
||||
|
||||
if (instrument_unique && instrument_optimize) {
|
||||
|
||||
WARNF("AFL_FRIDA_INST_TRACE_UNIQUE implies AFL_FRIDA_INST_NO_OPTIMIZE");
|
||||
instrument_optimize = FALSE;
|
||||
|
||||
}
|
||||
|
||||
if (instrument_unique) { instrument_tracing = TRUE; }
|
||||
|
||||
if (__afl_map_size != 0x10000) {
|
||||
|
||||
FATAL("Bad map size: 0x%08x", __afl_map_size);
|
||||
|
||||
}
|
||||
|
||||
transformer =
|
||||
gum_stalker_transformer_make_from_callback(instr_basic_block, NULL, NULL);
|
||||
transformer = gum_stalker_transformer_make_from_callback(
|
||||
instrument_basic_block, NULL, NULL);
|
||||
|
||||
if (instrument_unique) {
|
||||
|
||||
int shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600);
|
||||
if (shm_id < 0) { FATAL("shm_id < 0 - errno: %d\n", errno); }
|
||||
|
||||
edges_notified = shmat(shm_id, NULL, 0);
|
||||
g_assert(edges_notified != MAP_FAILED);
|
||||
|
||||
/*
|
||||
* Configure the shared memory region to be removed once the process
|
||||
* dies.
|
||||
*/
|
||||
if (shmctl(shm_id, IPC_RMID, NULL) < 0) {
|
||||
|
||||
FATAL("shmctl (IPC_RMID) < 0 - errno: %d\n", errno);
|
||||
|
||||
}
|
||||
|
||||
/* Clear it, not sure it's necessary, just seems like good practice */
|
||||
memset(edges_notified, '\0', MAP_SIZE);
|
||||
|
||||
}
|
||||
|
||||
if (instrument_use_fixed_seed) {
|
||||
|
||||
/*
|
||||
* This configuration option may be useful for diagnostics or
|
||||
* debugging.
|
||||
*/
|
||||
instrument_hash_seed = instrument_fixed_seed;
|
||||
|
||||
} else {
|
||||
|
||||
/*
|
||||
* By using a different seed value for the hash, we can make different
|
||||
* instances have edge collisions in different places when carrying out
|
||||
* parallel fuzzing. The seed itself, doesn't have to be random, it
|
||||
* just needs to be different for each instance.
|
||||
*/
|
||||
instrument_hash_seed = g_get_monotonic_time() ^
|
||||
(((guint64)getpid()) << 32) ^ syscall(SYS_gettid);
|
||||
|
||||
}
|
||||
|
||||
OKF("Instrumentation - seed [0x%016" G_GINT64_MODIFIER "x]",
|
||||
instrument_hash_seed);
|
||||
instrument_hash_zero = instrument_get_offset_hash(0);
|
||||
|
||||
instrument_debug_init();
|
||||
asan_init();
|
||||
@ -197,3 +329,9 @@ GumStalkerTransformer *instrument_get_transformer(void) {
|
||||
|
||||
}
|
||||
|
||||
void instrument_on_fork() {
|
||||
|
||||
instrument_previous_pc = instrument_hash_zero;
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
@ -22,5 +22,17 @@ void instrument_coverage_optimize(const cs_insn * instr,
|
||||
|
||||
}
|
||||
|
||||
void instrument_flush(GumStalkerOutput *output) {
|
||||
|
||||
gum_arm_writer_flush(output->writer.arm);
|
||||
|
||||
}
|
||||
|
||||
gpointer instrument_cur(GumStalkerOutput *output) {
|
||||
|
||||
return gum_arm_writer_cur(output->writer.arm);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
@ -12,15 +12,15 @@ static GumAddress current_log_impl = GUM_ADDRESS(0);
|
||||
static const guint8 afl_log_code[] = {
|
||||
|
||||
// __afl_area_ptr[current_pc ^ previous_pc]++;
|
||||
// previous_pc = current_pc >> 1;
|
||||
// previous_pc = current_pc ROR 1;
|
||||
0xE1, 0x0B, 0xBF, 0xA9, // stp x1, x2, [sp, -0x10]!
|
||||
0xE3, 0x13, 0xBF, 0xA9, // stp x3, x4, [sp, -0x10]!
|
||||
|
||||
// x0 = current_pc
|
||||
0xe1, 0x01, 0x00, 0x58, // ldr x1, #0x3c, =&__afl_area_ptr
|
||||
0x21, 0x02, 0x00, 0x58, // ldr x1, #0x44, =&__afl_area_ptr
|
||||
0x21, 0x00, 0x40, 0xf9, // ldr x1, [x1] (=__afl_area_ptr)
|
||||
|
||||
0xe2, 0x01, 0x00, 0x58, // ldr x2, #0x3c, =&previous_pc
|
||||
0x22, 0x02, 0x00, 0x58, // ldr x2, #0x44, =&previous_pc
|
||||
0x42, 0x00, 0x40, 0xf9, // ldr x2, [x2] (=previous_pc)
|
||||
|
||||
// __afl_area_ptr[current_pc ^ previous_pc]++;
|
||||
@ -30,8 +30,11 @@ static const guint8 afl_log_code[] = {
|
||||
0x63, 0x00, 0x1f, 0x9a, // adc x3, x3, xzr
|
||||
0x23, 0x68, 0x22, 0xf8, // str x3, [x1, x2]
|
||||
|
||||
// previous_pc = current_pc >> 1;
|
||||
0xe0, 0x07, 0x40, 0x8b, // add x0, xzr, x0, LSR #1
|
||||
// previous_pc = current_pc ROR 1;
|
||||
0xe4, 0x07, 0x40, 0x8b, // add x4, xzr, x0, LSR #1
|
||||
0xe0, 0xff, 0x00, 0x8b, // add x0, xzr, x0, LSL #63
|
||||
0x80, 0xc0, 0x40, 0x8b, // add x0, x4, x0, LSR #48
|
||||
|
||||
0xe2, 0x00, 0x00, 0x58, // ldr x2, #0x1c, =&previous_pc
|
||||
0x40, 0x00, 0x00, 0xf9, // str x0, [x2]
|
||||
|
||||
@ -54,8 +57,7 @@ void instrument_coverage_optimize(const cs_insn * instr,
|
||||
GumStalkerOutput *output) {
|
||||
|
||||
guint64 current_pc = instr->address;
|
||||
guint64 area_offset = (current_pc >> 4) ^ (current_pc << 8);
|
||||
area_offset &= MAP_SIZE - 1;
|
||||
guint64 area_offset = instrument_get_offset_hash(GUM_ADDRESS(instr->address));
|
||||
GumArm64Writer *cw = output->writer.arm64;
|
||||
|
||||
if (current_log_impl == 0 ||
|
||||
@ -72,7 +74,7 @@ void instrument_coverage_optimize(const cs_insn * instr,
|
||||
gum_arm64_writer_put_bytes(cw, afl_log_code, sizeof(afl_log_code));
|
||||
|
||||
uint8_t **afl_area_ptr_ptr = &__afl_area_ptr;
|
||||
uint64_t *afl_prev_loc_ptr = &previous_pc;
|
||||
uint64_t *afl_prev_loc_ptr = &instrument_previous_pc;
|
||||
gum_arm64_writer_put_bytes(cw, (const guint8 *)&afl_area_ptr_ptr,
|
||||
sizeof(afl_area_ptr_ptr));
|
||||
gum_arm64_writer_put_bytes(cw, (const guint8 *)&afl_prev_loc_ptr,
|
||||
@ -93,5 +95,17 @@ void instrument_coverage_optimize(const cs_insn * instr,
|
||||
|
||||
}
|
||||
|
||||
void instrument_flush(GumStalkerOutput *output) {
|
||||
|
||||
gum_arm64_writer_flush(output->writer.arm64);
|
||||
|
||||
}
|
||||
|
||||
gpointer instrument_cur(GumStalkerOutput *output) {
|
||||
|
||||
return gum_arm64_writer_cur(output->writer.arm64);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -3,15 +3,18 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "instrument.h"
|
||||
#include "util.h"
|
||||
|
||||
static int debugging_fd = -1;
|
||||
static gpointer instrument_gen_start = NULL;
|
||||
|
||||
char *instrument_debug_filename = NULL;
|
||||
|
||||
static void instrument_debug(char *format, ...) {
|
||||
|
||||
va_list ap;
|
||||
@ -31,24 +34,44 @@ static void instrument_debug(char *format, ...) {
|
||||
|
||||
}
|
||||
|
||||
static void instrument_disasm(guint8 *code, guint size) {
|
||||
static void instrument_disasm(guint8 *start, guint8 *end) {
|
||||
|
||||
csh capstone;
|
||||
cs_err err;
|
||||
uint16_t size;
|
||||
cs_insn *insn;
|
||||
size_t count, i;
|
||||
size_t count = 0;
|
||||
size_t i;
|
||||
uint16_t len;
|
||||
|
||||
err = cs_open(GUM_DEFAULT_CS_ARCH,
|
||||
GUM_DEFAULT_CS_MODE | GUM_DEFAULT_CS_ENDIAN, &capstone);
|
||||
g_assert(err == CS_ERR_OK);
|
||||
|
||||
count = cs_disasm(capstone, code, size, GPOINTER_TO_SIZE(code), 0, &insn);
|
||||
g_assert(insn != NULL);
|
||||
size = GPOINTER_TO_SIZE(end) - GPOINTER_TO_SIZE(start);
|
||||
|
||||
for (i = 0; i != count; i++) {
|
||||
for (guint8 *curr = start; curr < end; curr += len, size -= len, len = 0) {
|
||||
|
||||
instrument_debug("\t0x%" G_GINT64_MODIFIER "x\t%s %s\n", insn[i].address,
|
||||
insn[i].mnemonic, insn[i].op_str);
|
||||
count = cs_disasm(capstone, curr, size, GPOINTER_TO_SIZE(curr), 0, &insn);
|
||||
if (insn == NULL) {
|
||||
|
||||
instrument_debug("\t0x%" G_GINT64_MODIFIER "x\t* 0x%016" G_GSIZE_MODIFIER
|
||||
"x\n",
|
||||
curr, *(size_t *)curr);
|
||||
|
||||
len += sizeof(size_t);
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
for (i = 0; i != count; i++) {
|
||||
|
||||
instrument_debug("\t0x%" G_GINT64_MODIFIER "x\t%s %s\n", insn[i].address,
|
||||
insn[i].mnemonic, insn[i].op_str);
|
||||
|
||||
len += insn[i].size;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -58,32 +81,25 @@ static void instrument_disasm(guint8 *code, guint size) {
|
||||
|
||||
}
|
||||
|
||||
static gpointer instrument_cur(GumStalkerOutput *output) {
|
||||
void instrument_debug_config(void) {
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
return gum_x86_writer_cur(output->writer.x86);
|
||||
#elif defined(__aarch64__)
|
||||
return gum_arm64_writer_cur(output->writer.arm64);
|
||||
#elif defined(__arm__)
|
||||
return gum_arm_writer_cur(output->writer.arm);
|
||||
#else
|
||||
#error "Unsupported architecture"
|
||||
#endif
|
||||
instrument_debug_filename = getenv("AFL_FRIDA_INST_DEBUG_FILE");
|
||||
|
||||
}
|
||||
|
||||
void instrument_debug_init(void) {
|
||||
|
||||
char *filename = getenv("AFL_FRIDA_INST_DEBUG_FILE");
|
||||
OKF("Instrumentation debugging - enabled [%c]", filename == NULL ? ' ' : 'X');
|
||||
OKF("Instrumentation debugging - enabled [%c]",
|
||||
instrument_debug_filename == NULL ? ' ' : 'X');
|
||||
|
||||
if (filename == NULL) { return; }
|
||||
if (instrument_debug_filename == NULL) { return; }
|
||||
|
||||
OKF("Instrumentation debugging - file [%s]", filename);
|
||||
OKF("Instrumentation debugging - file [%s]", instrument_debug_filename);
|
||||
|
||||
if (filename == NULL) { return; }
|
||||
if (instrument_debug_filename == NULL) { return; }
|
||||
|
||||
char *path = g_canonicalize_filename(filename, g_get_current_dir());
|
||||
char *path =
|
||||
g_canonicalize_filename(instrument_debug_filename, g_get_current_dir());
|
||||
|
||||
OKF("Instrumentation debugging - path [%s]", path);
|
||||
|
||||
@ -111,7 +127,7 @@ void instrument_debug_instruction(uint64_t address, uint16_t size) {
|
||||
|
||||
if (likely(debugging_fd < 0)) { return; }
|
||||
uint8_t *start = (uint8_t *)GSIZE_TO_POINTER(address);
|
||||
instrument_disasm(start, size);
|
||||
instrument_disasm(start, start + size);
|
||||
|
||||
}
|
||||
|
||||
@ -119,11 +135,10 @@ void instrument_debug_end(GumStalkerOutput *output) {
|
||||
|
||||
if (likely(debugging_fd < 0)) { return; }
|
||||
gpointer instrument_gen_end = instrument_cur(output);
|
||||
uint16_t size = GPOINTER_TO_SIZE(instrument_gen_end) -
|
||||
GPOINTER_TO_SIZE(instrument_gen_start);
|
||||
|
||||
instrument_debug("\nGenerated block %p\n", instrument_gen_start);
|
||||
instrument_disasm(instrument_gen_start, size);
|
||||
instrument_debug("\nGenerated block %p-%p\n", instrument_gen_start,
|
||||
instrument_gen_end);
|
||||
instrument_disasm(instrument_gen_start, instrument_gen_end);
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
@ -10,23 +10,21 @@ static GumAddress current_log_impl = GUM_ADDRESS(0);
|
||||
|
||||
static const guint8 afl_log_code[] = {
|
||||
|
||||
// 0xcc,
|
||||
|
||||
0x9c, /* pushfq */
|
||||
0x51, /* push rcx */
|
||||
0x52, /* push rdx */
|
||||
|
||||
0x48, 0x8b, 0x0d, 0x28,
|
||||
0x48, 0x8b, 0x0d, 0x26,
|
||||
0x00, 0x00, 0x00, /* mov rcx, sym.&previous_pc */
|
||||
0x48, 0x8b, 0x11, /* mov rdx, qword [rcx] */
|
||||
0x48, 0x31, 0xfa, /* xor rdx, rdi */
|
||||
|
||||
0x48, 0x03, 0x15, 0x13,
|
||||
0x48, 0x03, 0x15, 0x11,
|
||||
0x00, 0x00, 0x00, /* add rdx, sym._afl_area_ptr_ptr */
|
||||
|
||||
0x80, 0x02, 0x01, /* add byte ptr [rdx], 1 */
|
||||
0x80, 0x12, 0x00, /* adc byte ptr [rdx], 0 */
|
||||
0x48, 0xd1, 0xef, /* shr rdi, 1 */
|
||||
0x66, 0xd1, 0xcf, /* ror di, 1 */
|
||||
0x48, 0x89, 0x39, /* mov qword [rcx], rdi */
|
||||
|
||||
0x5a, /* pop rdx */
|
||||
@ -34,7 +32,8 @@ static const guint8 afl_log_code[] = {
|
||||
0x9d, /* popfq */
|
||||
|
||||
0xc3, /* ret */
|
||||
0x90, 0x90, 0x90 /* nop pad */
|
||||
|
||||
0x90
|
||||
|
||||
/* Read-only data goes here: */
|
||||
/* uint8_t* __afl_area_ptr */
|
||||
@ -48,12 +47,11 @@ gboolean instrument_is_coverage_optimize_supported(void) {
|
||||
|
||||
}
|
||||
|
||||
void instrument_coverage_optimize(const cs_insn * instr,
|
||||
GumStalkerOutput *output) {
|
||||
static guint8 align_pad[] = {0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
|
||||
|
||||
guint64 current_pc = instr->address;
|
||||
guint64 area_offset = (current_pc >> 4) ^ (current_pc << 8);
|
||||
area_offset &= MAP_SIZE - 1;
|
||||
static void instrument_coverate_write_function(GumStalkerOutput *output) {
|
||||
|
||||
guint64 misalign = 0;
|
||||
GumX86Writer *cw = output->writer.x86;
|
||||
|
||||
if (current_log_impl == 0 ||
|
||||
@ -65,10 +63,17 @@ void instrument_coverage_optimize(const cs_insn * instr,
|
||||
|
||||
gum_x86_writer_put_jmp_near_label(cw, after_log_impl);
|
||||
|
||||
misalign = (cw->pc & 0x7);
|
||||
if (misalign != 0) {
|
||||
|
||||
gum_x86_writer_put_bytes(cw, align_pad, 8 - misalign);
|
||||
|
||||
}
|
||||
|
||||
current_log_impl = cw->pc;
|
||||
gum_x86_writer_put_bytes(cw, afl_log_code, sizeof(afl_log_code));
|
||||
|
||||
uint64_t *afl_prev_loc_ptr = &previous_pc;
|
||||
uint64_t *afl_prev_loc_ptr = &instrument_previous_pc;
|
||||
gum_x86_writer_put_bytes(cw, (const guint8 *)&__afl_area_ptr,
|
||||
sizeof(__afl_area_ptr));
|
||||
gum_x86_writer_put_bytes(cw, (const guint8 *)&afl_prev_loc_ptr,
|
||||
@ -78,6 +83,15 @@ void instrument_coverage_optimize(const cs_insn * instr,
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void instrument_coverage_optimize(const cs_insn * instr,
|
||||
GumStalkerOutput *output) {
|
||||
|
||||
GumX86Writer *cw = output->writer.x86;
|
||||
guint64 area_offset = instrument_get_offset_hash(GUM_ADDRESS(instr->address));
|
||||
instrument_coverate_write_function(output);
|
||||
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
-GUM_RED_ZONE_SIZE);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_RDI);
|
||||
@ -89,5 +103,17 @@ void instrument_coverage_optimize(const cs_insn * instr,
|
||||
|
||||
}
|
||||
|
||||
void instrument_flush(GumStalkerOutput *output) {
|
||||
|
||||
gum_x86_writer_flush(output->writer.x86);
|
||||
|
||||
}
|
||||
|
||||
gpointer instrument_cur(GumStalkerOutput *output) {
|
||||
|
||||
return gum_x86_writer_cur(output->writer.x86);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
@ -16,7 +16,7 @@ static void instrument_coverage_function(GumX86Writer *cw) {
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_EDX);
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_ECX,
|
||||
GUM_ADDRESS(&previous_pc));
|
||||
GUM_ADDRESS(&instrument_previous_pc));
|
||||
gum_x86_writer_put_mov_reg_reg_ptr(cw, GUM_REG_EDX, GUM_REG_ECX);
|
||||
gum_x86_writer_put_xor_reg_reg(cw, GUM_REG_EDX, GUM_REG_EDI);
|
||||
|
||||
@ -30,7 +30,8 @@ static void instrument_coverage_function(GumX86Writer *cw) {
|
||||
uint8_t adc_byte_ptr_edx_0[] = {0x80, 0x12, 0x00};
|
||||
gum_x86_writer_put_bytes(cw, adc_byte_ptr_edx_0, sizeof(adc_byte_ptr_edx_0));
|
||||
|
||||
gum_x86_writer_put_shr_reg_u8(cw, GUM_REG_EDI, 1);
|
||||
uint8_t ror_di_1[] = {0x66, 0xd1, 0xcf};
|
||||
gum_x86_writer_put_bytes(cw, ror_di_1, sizeof(ror_di_1));
|
||||
gum_x86_writer_put_mov_reg_ptr_reg(cw, GUM_REG_ECX, GUM_REG_EDI);
|
||||
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_REG_EDX);
|
||||
@ -46,15 +47,8 @@ gboolean instrument_is_coverage_optimize_supported(void) {
|
||||
|
||||
}
|
||||
|
||||
void instrument_coverage_optimize(const cs_insn * instr,
|
||||
GumStalkerOutput *output) {
|
||||
static void instrument_coverate_write_function(GumStalkerOutput *output) {
|
||||
|
||||
UNUSED_PARAMETER(instr);
|
||||
UNUSED_PARAMETER(output);
|
||||
|
||||
guint64 current_pc = instr->address;
|
||||
guint64 area_offset = (current_pc >> 4) ^ (current_pc << 8);
|
||||
area_offset &= MAP_SIZE - 1;
|
||||
GumX86Writer *cw = output->writer.x86;
|
||||
|
||||
if (current_log_impl == 0 ||
|
||||
@ -73,7 +67,15 @@ void instrument_coverage_optimize(const cs_insn * instr,
|
||||
|
||||
}
|
||||
|
||||
// gum_x86_writer_put_breakpoint(cw);
|
||||
}
|
||||
|
||||
void instrument_coverage_optimize(const cs_insn * instr,
|
||||
GumStalkerOutput *output) {
|
||||
|
||||
GumX86Writer *cw = output->writer.x86;
|
||||
guint64 area_offset = instrument_get_offset_hash(GUM_ADDRESS(instr->address));
|
||||
instrument_coverate_write_function(output);
|
||||
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_EDI);
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EDI, area_offset);
|
||||
gum_x86_writer_put_call_address(cw, current_log_impl);
|
||||
@ -81,5 +83,17 @@ void instrument_coverage_optimize(const cs_insn * instr,
|
||||
|
||||
}
|
||||
|
||||
void instrument_flush(GumStalkerOutput *output) {
|
||||
|
||||
gum_x86_writer_flush(output->writer.x86);
|
||||
|
||||
}
|
||||
|
||||
gpointer instrument_cur(GumStalkerOutput *output) {
|
||||
|
||||
return gum_x86_writer_cur(output->writer.x86);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "interceptor.h"
|
||||
#include "intercept.h"
|
||||
|
||||
void intercept(void *address, gpointer replacement, gpointer user_data) {
|
||||
void intercept_hook(void *address, gpointer replacement, gpointer user_data) {
|
||||
|
||||
GumInterceptor *interceptor = gum_interceptor_obtain();
|
||||
gum_interceptor_begin_transaction(interceptor);
|
||||
@ -15,7 +15,7 @@ void intercept(void *address, gpointer replacement, gpointer user_data) {
|
||||
|
||||
}
|
||||
|
||||
void unintercept(void *address) {
|
||||
void intercept_unhook(void *address) {
|
||||
|
||||
GumInterceptor *interceptor = gum_interceptor_obtain();
|
||||
|
||||
@ -26,10 +26,10 @@ void unintercept(void *address) {
|
||||
|
||||
}
|
||||
|
||||
void unintercept_self(void) {
|
||||
void intercept_unhook_self(void) {
|
||||
|
||||
GumInvocationContext *ctx = gum_interceptor_get_current_invocation();
|
||||
unintercept(ctx->function);
|
||||
intercept_unhook(ctx->function);
|
||||
|
||||
}
|
||||
|
257
frida_mode/src/js/api.js
Normal file
257
frida_mode/src/js/api.js
Normal file
@ -0,0 +1,257 @@
|
||||
"use strict";
|
||||
class Afl {
|
||||
/**
|
||||
* This is equivalent to setting a value in `AFL_FRIDA_EXCLUDE_RANGES`,
|
||||
* it takes as arguments a `NativePointer` and a `number`. It can be
|
||||
* called multiple times to exclude several ranges.
|
||||
*/
|
||||
static addExcludedRange(addressess, size) {
|
||||
Afl.jsApiAddExcludeRange(addressess, size);
|
||||
}
|
||||
/**
|
||||
* This is equivalent to setting a value in `AFL_FRIDA_INST_RANGES`,
|
||||
* it takes as arguments a `NativePointer` and a `number`. It can be
|
||||
* called multiple times to include several ranges.
|
||||
*/
|
||||
static addIncludedRange(addressess, size) {
|
||||
Afl.jsApiAddIncludeRange(addressess, size);
|
||||
}
|
||||
/**
|
||||
* This must always be called at the end of your script. This lets
|
||||
* FRIDA mode know that your configuration is finished and that
|
||||
* execution has reached the end of your script. Failure to call
|
||||
* this will result in a fatal error.
|
||||
*/
|
||||
static done() {
|
||||
Afl.jsApiDone();
|
||||
}
|
||||
/**
|
||||
* This function can be called within your script to cause FRIDA
|
||||
* mode to trigger a fatal error. This is useful if for example you
|
||||
* discover a problem you weren't expecting and want everything to
|
||||
* stop. The user will need to enable `AFL_DEBUG_CHILD=1` to view
|
||||
* this error message.
|
||||
*/
|
||||
static error(msg) {
|
||||
const buf = Memory.allocUtf8String(msg);
|
||||
Afl.jsApiError(buf);
|
||||
}
|
||||
/**
|
||||
* Function used to provide access to `__afl_fuzz_ptr`, which contains the length of
|
||||
* fuzzing data when using in-memory test case fuzzing.
|
||||
*/
|
||||
static getAflFuzzLen() {
|
||||
return Afl.jsApiGetSymbol("__afl_fuzz_len");
|
||||
}
|
||||
/**
|
||||
* Function used to provide access to `__afl_fuzz_ptr`, which contains the fuzzing
|
||||
* data when using in-memory test case fuzzing.
|
||||
*/
|
||||
static getAflFuzzPtr() {
|
||||
return Afl.jsApiGetSymbol("__afl_fuzz_ptr");
|
||||
}
|
||||
/**
|
||||
* Print a message to the STDOUT. This should be preferred to
|
||||
* FRIDA's `console.log` since FRIDA will queue it's log messages.
|
||||
* If `console.log` is used in a callback in particular, then there
|
||||
* may no longer be a thread running to service this queue.
|
||||
*/
|
||||
static print(msg) {
|
||||
const STDOUT_FILENO = 2;
|
||||
const log = `${msg}\n`;
|
||||
const buf = Memory.allocUtf8String(log);
|
||||
Afl.jsApiWrite(STDOUT_FILENO, buf, log.length);
|
||||
}
|
||||
/**
|
||||
* See `AFL_FRIDA_DEBUG_MAPS`.
|
||||
*/
|
||||
static setDebugMaps() {
|
||||
Afl.jsApiSetDebugMaps();
|
||||
}
|
||||
/**
|
||||
* This has the same effect as setting `AFL_ENTRYPOINT`, but has the
|
||||
* convenience of allowing you to use FRIDAs APIs to determine the
|
||||
* address you would like to configure, rather than having to grep
|
||||
* the output of `readelf` or something similarly ugly. This
|
||||
* function should be called with a `NativePointer` as its
|
||||
* argument.
|
||||
*/
|
||||
static setEntryPoint(address) {
|
||||
Afl.jsApiSetEntryPoint(address);
|
||||
}
|
||||
/**
|
||||
* Function used to enable in-memory test cases for fuzzing.
|
||||
*/
|
||||
static setInMemoryFuzzing() {
|
||||
Afl.jsApiAflSharedMemFuzzing.writeInt(1);
|
||||
}
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_DEBUG_FILE`. This function takes a single `string` as
|
||||
* an argument.
|
||||
*/
|
||||
static setInstrumentDebugFile(file) {
|
||||
const buf = Memory.allocUtf8String(file);
|
||||
Afl.jsApiSetInstrumentDebugFile(buf);
|
||||
}
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_TRACE`.
|
||||
*/
|
||||
static setInstrumentEnableTracing() {
|
||||
Afl.jsApiSetInstrumentTrace();
|
||||
}
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_JIT`.
|
||||
*/
|
||||
static setInstrumentJit() {
|
||||
Afl.jsApiSetInstrumentJit();
|
||||
}
|
||||
/**
|
||||
* See `AFL_INST_LIBS`.
|
||||
*/
|
||||
static setInstrumentLibraries() {
|
||||
Afl.jsApiSetInstrumentLibraries();
|
||||
}
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_NO_OPTIMIZE`
|
||||
*/
|
||||
static setInstrumentNoOptimize() {
|
||||
Afl.jsApiSetInstrumentNoOptimize();
|
||||
}
|
||||
/*
|
||||
* See `AFL_FRIDA_INST_SEED`
|
||||
*/
|
||||
static setInstrumentSeed(seed) {
|
||||
Afl.jsApiSetInstrumentSeed(seed);
|
||||
}
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_TRACE_UNIQUE`.
|
||||
*/
|
||||
static setInstrumentTracingUnique() {
|
||||
Afl.jsApiSetInstrumentTraceUnique();
|
||||
}
|
||||
/**
|
||||
* This is equivalent to setting `AFL_FRIDA_PERSISTENT_ADDR`, again a
|
||||
* `NativePointer` should be provided as it's argument.
|
||||
*/
|
||||
static setPersistentAddress(address) {
|
||||
Afl.jsApiSetPersistentAddress(address);
|
||||
}
|
||||
/**
|
||||
* This is equivalent to setting `AFL_FRIDA_PERSISTENT_CNT`, a
|
||||
* `number` should be provided as it's argument.
|
||||
*/
|
||||
static setPersistentCount(count) {
|
||||
Afl.jsApiSetPersistentCount(count);
|
||||
}
|
||||
/**
|
||||
* See `AFL_FRIDA_PERSISTENT_DEBUG`.
|
||||
*/
|
||||
static setPersistentDebug() {
|
||||
Afl.jsApiSetPersistentDebug();
|
||||
}
|
||||
/**
|
||||
* See `AFL_FRIDA_PERSISTENT_ADDR`. This function takes a NativePointer as an
|
||||
* argument. See above for examples of use.
|
||||
*/
|
||||
static setPersistentHook(address) {
|
||||
Afl.jsApiSetPersistentHook(address);
|
||||
}
|
||||
/**
|
||||
* This is equivalent to setting `AFL_FRIDA_PERSISTENT_RET`, again a
|
||||
* `NativePointer` should be provided as it's argument.
|
||||
*/
|
||||
static setPersistentReturn(address) {
|
||||
Afl.jsApiSetPersistentReturn(address);
|
||||
}
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_NO_PREFETCH`.
|
||||
*/
|
||||
static setPrefetchDisable() {
|
||||
Afl.jsApiSetPrefetchDisable();
|
||||
}
|
||||
/*
|
||||
* Set a function to be called for each instruction which is instrumented
|
||||
* by AFL FRIDA mode.
|
||||
*/
|
||||
static setStalkerCallback(callback) {
|
||||
Afl.jsApiSetStalkerCallback(callback);
|
||||
}
|
||||
/**
|
||||
* See `AFL_FRIDA_STATS_FILE`. This function takes a single `string` as
|
||||
* an argument.
|
||||
*/
|
||||
static setStatsFile(file) {
|
||||
const buf = Memory.allocUtf8String(file);
|
||||
Afl.jsApiSetStatsFile(buf);
|
||||
}
|
||||
/**
|
||||
* See `AFL_FRIDA_STATS_INTERVAL`. This function takes a `number` as an
|
||||
* argument
|
||||
*/
|
||||
static setStatsInterval(interval) {
|
||||
Afl.jsApiSetStatsInterval(interval);
|
||||
}
|
||||
/**
|
||||
* See `AFL_FRIDA_STATS_TRANSITIONS`
|
||||
*/
|
||||
static setStatsTransitions() {
|
||||
Afl.jsApiSetStatsTransitions();
|
||||
}
|
||||
/**
|
||||
* See `AFL_FRIDA_OUTPUT_STDERR`. This function takes a single `string` as
|
||||
* an argument.
|
||||
*/
|
||||
static setStdErr(file) {
|
||||
const buf = Memory.allocUtf8String(file);
|
||||
Afl.jsApiSetStdErr(buf);
|
||||
}
|
||||
/**
|
||||
* See `AFL_FRIDA_OUTPUT_STDOUT`. This function takes a single `string` as
|
||||
* an argument.
|
||||
*/
|
||||
static setStdOut(file) {
|
||||
const buf = Memory.allocUtf8String(file);
|
||||
Afl.jsApiSetStdOut(buf);
|
||||
}
|
||||
static jsApiGetFunction(name, retType, argTypes) {
|
||||
const addr = Afl.module.getExportByName(name);
|
||||
return new NativeFunction(addr, retType, argTypes);
|
||||
}
|
||||
static jsApiGetSymbol(name) {
|
||||
return Afl.module.getExportByName(name);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Field containing the `Module` object for `afl-frida-trace.so` (the FRIDA mode
|
||||
* implementation).
|
||||
*/
|
||||
Afl.module = Process.getModuleByName("afl-frida-trace.so");
|
||||
Afl.jsApiAddExcludeRange = Afl.jsApiGetFunction("js_api_add_exclude_range", "void", ["pointer", "size_t"]);
|
||||
Afl.jsApiAddIncludeRange = Afl.jsApiGetFunction("js_api_add_include_range", "void", ["pointer", "size_t"]);
|
||||
Afl.jsApiAflSharedMemFuzzing = Afl.jsApiGetSymbol("__afl_sharedmem_fuzzing");
|
||||
Afl.jsApiDone = Afl.jsApiGetFunction("js_api_done", "void", []);
|
||||
Afl.jsApiError = Afl.jsApiGetFunction("js_api_error", "void", ["pointer"]);
|
||||
Afl.jsApiSetDebugMaps = Afl.jsApiGetFunction("js_api_set_debug_maps", "void", []);
|
||||
Afl.jsApiSetEntryPoint = Afl.jsApiGetFunction("js_api_set_entrypoint", "void", ["pointer"]);
|
||||
Afl.jsApiSetInstrumentDebugFile = Afl.jsApiGetFunction("js_api_set_instrument_debug_file", "void", ["pointer"]);
|
||||
Afl.jsApiSetInstrumentJit = Afl.jsApiGetFunction("js_api_set_instrument_jit", "void", []);
|
||||
Afl.jsApiSetInstrumentLibraries = Afl.jsApiGetFunction("js_api_set_instrument_libraries", "void", []);
|
||||
Afl.jsApiSetInstrumentNoOptimize = Afl.jsApiGetFunction("js_api_set_instrument_no_optimize", "void", []);
|
||||
Afl.jsApiSetInstrumentSeed = Afl.jsApiGetFunction("js_api_set_instrument_seed", "void", ["uint64"]);
|
||||
Afl.jsApiSetInstrumentTrace = Afl.jsApiGetFunction("js_api_set_instrument_trace", "void", []);
|
||||
Afl.jsApiSetInstrumentTraceUnique = Afl.jsApiGetFunction("js_api_set_instrument_trace_unique", "void", []);
|
||||
Afl.jsApiSetPersistentAddress = Afl.jsApiGetFunction("js_api_set_persistent_address", "void", ["pointer"]);
|
||||
Afl.jsApiSetPersistentCount = Afl.jsApiGetFunction("js_api_set_persistent_count", "void", ["uint64"]);
|
||||
Afl.jsApiSetPersistentDebug = Afl.jsApiGetFunction("js_api_set_persistent_debug", "void", []);
|
||||
Afl.jsApiSetPersistentHook = Afl.jsApiGetFunction("js_api_set_persistent_hook", "void", ["pointer"]);
|
||||
Afl.jsApiSetPersistentReturn = Afl.jsApiGetFunction("js_api_set_persistent_return", "void", ["pointer"]);
|
||||
Afl.jsApiSetPrefetchDisable = Afl.jsApiGetFunction("js_api_set_prefetch_disable", "void", []);
|
||||
Afl.jsApiSetStalkerCallback = Afl.jsApiGetFunction("js_api_set_stalker_callback", "void", ["pointer"]);
|
||||
Afl.jsApiSetStatsFile = Afl.jsApiGetFunction("js_api_set_stats_file", "void", ["pointer"]);
|
||||
Afl.jsApiSetStatsInterval = Afl.jsApiGetFunction("js_api_set_stats_interval", "void", ["uint64"]);
|
||||
Afl.jsApiSetStatsTransitions = Afl.jsApiGetFunction("js_api_set_stats_transitions", "void", []);
|
||||
Afl.jsApiSetStdErr = Afl.jsApiGetFunction("js_api_set_stderr", "void", ["pointer"]);
|
||||
Afl.jsApiSetStdOut = Afl.jsApiGetFunction("js_api_set_stdout", "void", ["pointer"]);
|
||||
Afl.jsApiWrite = new NativeFunction(
|
||||
/* tslint:disable-next-line:no-null-keyword */
|
||||
Module.getExportByName(null, "write"), "int", ["int", "pointer", "int"]);
|
142
frida_mode/src/js/js.c
Normal file
142
frida_mode/src/js/js.c
Normal file
@ -0,0 +1,142 @@
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "js.h"
|
||||
#include "util.h"
|
||||
|
||||
static char * js_script = NULL;
|
||||
gboolean js_done = FALSE;
|
||||
js_api_stalker_callback_t js_user_callback = NULL;
|
||||
|
||||
static gchar * filename = "afl.js";
|
||||
static gchar * contents;
|
||||
static GumScriptBackend * backend;
|
||||
static GCancellable * cancellable = NULL;
|
||||
static GError * error = NULL;
|
||||
static GumScript * script;
|
||||
static GumScriptScheduler *scheduler;
|
||||
static GMainContext * context;
|
||||
static GMainLoop * main_loop;
|
||||
|
||||
static void js_msg(GumScript *script, const gchar *message, GBytes *data,
|
||||
gpointer user_data) {
|
||||
|
||||
UNUSED_PARAMETER(script);
|
||||
UNUSED_PARAMETER(data);
|
||||
UNUSED_PARAMETER(user_data);
|
||||
OKF("%s", message);
|
||||
|
||||
}
|
||||
|
||||
void js_config(void) {
|
||||
|
||||
js_script = getenv("AFL_FRIDA_JS_SCRIPT");
|
||||
|
||||
}
|
||||
|
||||
static gchar *js_get_script() {
|
||||
|
||||
gsize length;
|
||||
if (js_script != NULL) { filename = js_script; }
|
||||
|
||||
filename = g_canonicalize_filename(filename, g_get_current_dir());
|
||||
|
||||
if (!g_file_get_contents(filename, &contents, &length, NULL)) {
|
||||
|
||||
if (js_script == NULL) {
|
||||
|
||||
return NULL;
|
||||
|
||||
} else {
|
||||
|
||||
FATAL("Could not load script file: %s", filename);
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
OKF("Loaded AFL script: %s, %" G_GSIZE_MODIFIER "d bytes", filename,
|
||||
length);
|
||||
|
||||
gchar *source = g_malloc0(api_js_len + length + 1);
|
||||
memcpy(source, api_js, api_js_len);
|
||||
memcpy(&source[api_js_len], contents, length);
|
||||
|
||||
return source;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void js_print_script(gchar *source) {
|
||||
|
||||
gchar **split = g_strsplit(source, "\n", 0);
|
||||
|
||||
for (size_t i = 0; split[i] != NULL; i++) {
|
||||
|
||||
OKF("%3" G_GSIZE_MODIFIER "d. %s", i + 1, split[i]);
|
||||
|
||||
}
|
||||
|
||||
g_strfreev(split);
|
||||
|
||||
}
|
||||
|
||||
static void load_cb(GObject *source_object, GAsyncResult *result,
|
||||
gpointer user_data) {
|
||||
|
||||
UNUSED_PARAMETER(source_object);
|
||||
UNUSED_PARAMETER(user_data);
|
||||
gum_script_load_finish(script, result);
|
||||
if (error != NULL) { FATAL("Failed to load script - %s", error->message); }
|
||||
|
||||
}
|
||||
|
||||
static void create_cb(GObject *source_object, GAsyncResult *result,
|
||||
gpointer user_data) {
|
||||
|
||||
UNUSED_PARAMETER(source_object);
|
||||
UNUSED_PARAMETER(user_data);
|
||||
script = gum_script_backend_create_finish(backend, result, &error);
|
||||
if (error != NULL) { FATAL("Failed to create script: %s", error->message); }
|
||||
|
||||
gum_script_set_message_handler(script, js_msg, NULL, NULL);
|
||||
|
||||
gum_script_load(script, cancellable, load_cb, NULL);
|
||||
|
||||
}
|
||||
|
||||
void js_start(void) {
|
||||
|
||||
gchar *source = js_get_script();
|
||||
if (source == NULL) { return; }
|
||||
js_print_script(source);
|
||||
|
||||
scheduler = gum_script_backend_get_scheduler();
|
||||
gum_script_scheduler_disable_background_thread(scheduler);
|
||||
|
||||
backend = gum_script_backend_obtain_qjs();
|
||||
|
||||
context = gum_script_scheduler_get_js_context(scheduler);
|
||||
main_loop = g_main_loop_new(context, true);
|
||||
g_main_context_push_thread_default(context);
|
||||
|
||||
gum_script_backend_create(backend, "example", source, cancellable, create_cb,
|
||||
&error);
|
||||
|
||||
while (g_main_context_pending(context))
|
||||
g_main_context_iteration(context, FALSE);
|
||||
|
||||
if (!js_done) { FATAL("Script didn't call Afl.done()"); }
|
||||
|
||||
}
|
||||
|
||||
gboolean js_stalker_callback(const cs_insn *insn, gboolean begin,
|
||||
gboolean excluded, GumStalkerOutput *output) {
|
||||
|
||||
if (js_user_callback == NULL) { return TRUE; }
|
||||
return js_user_callback(insn, begin, excluded, output);
|
||||
|
||||
}
|
||||
|
201
frida_mode/src/js/js_api.c
Normal file
201
frida_mode/src/js/js_api.c
Normal file
@ -0,0 +1,201 @@
|
||||
#include "debug.h"
|
||||
|
||||
#include "entry.h"
|
||||
#include "instrument.h"
|
||||
#include "js.h"
|
||||
#include "output.h"
|
||||
#include "persistent.h"
|
||||
#include "prefetch.h"
|
||||
#include "ranges.h"
|
||||
#include "stats.h"
|
||||
#include "util.h"
|
||||
__attribute__((visibility("default"))) void js_api_done() {
|
||||
|
||||
js_done = TRUE;
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_error(char *msg) {
|
||||
|
||||
FATAL("%s", msg);
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_set_entrypoint(
|
||||
void *address) {
|
||||
|
||||
if (address == NULL) {
|
||||
|
||||
js_api_error("js_api_set_entrypoint called with NULL");
|
||||
|
||||
}
|
||||
|
||||
entry_point = GPOINTER_TO_SIZE(address);
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_set_persistent_address(
|
||||
void *address) {
|
||||
|
||||
if (address == NULL) {
|
||||
|
||||
js_api_error("js_api_set_persistent_address called with NULL");
|
||||
|
||||
}
|
||||
|
||||
persistent_start = GPOINTER_TO_SIZE(address);
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_set_persistent_return(
|
||||
void *address) {
|
||||
|
||||
if (address == NULL) {
|
||||
|
||||
js_api_error("js_api_set_persistent_return called with NULL");
|
||||
|
||||
}
|
||||
|
||||
persistent_ret = GPOINTER_TO_SIZE(address);
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_set_persistent_count(
|
||||
uint64_t count) {
|
||||
|
||||
persistent_count = count;
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_set_persistent_debug() {
|
||||
|
||||
persistent_debug = TRUE;
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_set_debug_maps() {
|
||||
|
||||
ranges_debug_maps = TRUE;
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_add_include_range(
|
||||
void *address, gsize size) {
|
||||
|
||||
GumMemoryRange range = {.base_address = GUM_ADDRESS(address), .size = size};
|
||||
ranges_add_include(&range);
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_add_exclude_range(
|
||||
void *address, gsize size) {
|
||||
|
||||
GumMemoryRange range = {.base_address = GUM_ADDRESS(address), .size = size};
|
||||
ranges_add_exclude(&range);
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_set_instrument_jit() {
|
||||
|
||||
ranges_inst_jit = TRUE;
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_set_instrument_libraries() {
|
||||
|
||||
ranges_inst_libs = TRUE;
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_set_instrument_debug_file(
|
||||
char *path) {
|
||||
|
||||
instrument_debug_filename = g_strdup(path);
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_set_prefetch_disable(void) {
|
||||
|
||||
prefetch_enable = FALSE;
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_set_instrument_no_optimize(
|
||||
void) {
|
||||
|
||||
instrument_optimize = FALSE;
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_set_instrument_seed(
|
||||
guint64 seed) {
|
||||
|
||||
instrument_use_fixed_seed = TRUE;
|
||||
instrument_fixed_seed = seed;
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_set_instrument_trace(void) {
|
||||
|
||||
instrument_tracing = TRUE;
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_set_instrument_trace_unique(
|
||||
void) {
|
||||
|
||||
instrument_unique = TRUE;
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_set_stdout(char *file) {
|
||||
|
||||
output_stdout = g_strdup(file);
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_set_stderr(char *file) {
|
||||
|
||||
output_stderr = g_strdup(file);
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_set_stats_file(char *file) {
|
||||
|
||||
stats_filename = g_strdup(file);
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_set_stats_interval(
|
||||
uint64_t interval) {
|
||||
|
||||
stats_interval = interval;
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_set_stats_transitions() {
|
||||
|
||||
stats_transitions = TRUE;
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_set_persistent_hook(
|
||||
void *address) {
|
||||
|
||||
if (address == NULL) {
|
||||
|
||||
js_api_error("js_api_set_persistent_hook called with NULL");
|
||||
|
||||
}
|
||||
|
||||
persistent_hook = address;
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_set_stalker_callback(
|
||||
const js_api_stalker_callback_t callback) {
|
||||
|
||||
js_user_callback = callback;
|
||||
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
@ -151,6 +151,10 @@ static void lib_get_text_section(lib_details_t *details) {
|
||||
|
||||
}
|
||||
|
||||
void lib_config(void) {
|
||||
|
||||
}
|
||||
|
||||
void lib_init(void) {
|
||||
|
||||
lib_details_t lib_details;
|
||||
|
@ -1,5 +1,5 @@
|
||||
#ifdef __APPLE__
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
@ -56,6 +56,10 @@ gboolean lib_get_text_section(const GumDarwinSectionDetails *details,
|
||||
|
||||
}
|
||||
|
||||
void lib_config(void) {
|
||||
|
||||
}
|
||||
|
||||
void lib_init(void) {
|
||||
|
||||
GumDarwinModule *module = NULL;
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
@ -10,14 +11,15 @@
|
||||
#include <sys/personality.h>
|
||||
#endif
|
||||
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "entry.h"
|
||||
#include "instrument.h"
|
||||
#include "interceptor.h"
|
||||
#include "intercept.h"
|
||||
#include "js.h"
|
||||
#include "lib.h"
|
||||
#include "output.h"
|
||||
#include "persistent.h"
|
||||
@ -27,6 +29,8 @@
|
||||
#include "stats.h"
|
||||
#include "util.h"
|
||||
|
||||
#define PROC_MAX 65536
|
||||
|
||||
#ifdef __APPLE__
|
||||
extern mach_port_t mach_task_self();
|
||||
extern GumAddress gum_darwin_find_entrypoint(mach_port_t task);
|
||||
@ -41,13 +45,6 @@ typedef int *(*main_fn_t)(int argc, char **argv, char **envp);
|
||||
|
||||
static main_fn_t main_fn = NULL;
|
||||
|
||||
static int on_fork(void) {
|
||||
|
||||
prefetch_read();
|
||||
return fork();
|
||||
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
static void on_main_os(int argc, char **argv, char **envp) {
|
||||
|
||||
@ -78,7 +75,7 @@ static void on_main_os(int argc, char **argv, char **envp) {
|
||||
|
||||
#endif
|
||||
|
||||
static void embedded_init() {
|
||||
static void embedded_init(void) {
|
||||
|
||||
static gboolean initialized = false;
|
||||
if (!initialized) {
|
||||
@ -90,25 +87,117 @@ static void embedded_init() {
|
||||
|
||||
}
|
||||
|
||||
void afl_frida_start() {
|
||||
static void afl_print_cmdline(void) {
|
||||
|
||||
char * buffer = g_malloc0(PROC_MAX);
|
||||
gchar *fname = g_strdup_printf("/proc/%d/cmdline", getppid());
|
||||
int fd = open(fname, O_RDONLY);
|
||||
|
||||
if (fd < 0) {
|
||||
|
||||
WARNF("Failed to open /proc/self/cmdline, errno: (%d)", errno);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
ssize_t bytes_read = read(fd, buffer, PROC_MAX - 1);
|
||||
if (bytes_read < 0) {
|
||||
|
||||
FATAL("Failed to read /proc/self/cmdline, errno: (%d)", errno);
|
||||
|
||||
}
|
||||
|
||||
int idx = 0;
|
||||
|
||||
for (ssize_t i = 0; i < bytes_read; i++) {
|
||||
|
||||
if (i == 0 || buffer[i - 1] == '\0') {
|
||||
|
||||
OKF("AFL - COMMANDLINE: argv[%d] = %s", idx++, &buffer[i]);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
close(fd);
|
||||
g_free(fname);
|
||||
g_free(buffer);
|
||||
|
||||
}
|
||||
|
||||
static void afl_print_env(void) {
|
||||
|
||||
char * buffer = g_malloc0(PROC_MAX);
|
||||
gchar *fname = g_strdup_printf("/proc/%d/environ", getppid());
|
||||
int fd = open(fname, O_RDONLY);
|
||||
|
||||
if (fd < 0) {
|
||||
|
||||
WARNF("Failed to open /proc/self/cmdline, errno: (%d)", errno);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
ssize_t bytes_read = read(fd, buffer, PROC_MAX - 1);
|
||||
if (bytes_read < 0) {
|
||||
|
||||
FATAL("Failed to read /proc/self/cmdline, errno: (%d)", errno);
|
||||
|
||||
}
|
||||
|
||||
int idx = 0;
|
||||
|
||||
for (ssize_t i = 0; i < bytes_read; i++) {
|
||||
|
||||
if (i == 0 || buffer[i - 1] == '\0') {
|
||||
|
||||
OKF("AFL - ENVIRONMENT %3d: %s", idx++, &buffer[i]);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
close(fd);
|
||||
g_free(fname);
|
||||
g_free(buffer);
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void afl_frida_start(void) {
|
||||
|
||||
afl_print_cmdline();
|
||||
afl_print_env();
|
||||
|
||||
/* Configure */
|
||||
entry_config();
|
||||
instrument_config();
|
||||
js_config();
|
||||
lib_config();
|
||||
output_config();
|
||||
persistent_config();
|
||||
prefetch_config();
|
||||
ranges_config();
|
||||
stalker_config();
|
||||
stats_config();
|
||||
|
||||
js_start();
|
||||
|
||||
/* Initialize */
|
||||
output_init();
|
||||
|
||||
embedded_init();
|
||||
stalker_init();
|
||||
lib_init();
|
||||
entry_init();
|
||||
instrument_init();
|
||||
output_init();
|
||||
lib_init();
|
||||
persistent_init();
|
||||
prefetch_init();
|
||||
stalker_init();
|
||||
ranges_init();
|
||||
stats_init();
|
||||
|
||||
void *fork_addr =
|
||||
GSIZE_TO_POINTER(gum_module_find_export_by_name(NULL, "fork"));
|
||||
intercept(fork_addr, on_fork, NULL);
|
||||
|
||||
/* Start */
|
||||
stalker_start();
|
||||
entry_run();
|
||||
entry_start();
|
||||
|
||||
}
|
||||
|
||||
@ -116,7 +205,7 @@ static int *on_main(int argc, char **argv, char **envp) {
|
||||
|
||||
on_main_os(argc, argv, envp);
|
||||
|
||||
unintercept_self();
|
||||
intercept_unhook_self();
|
||||
|
||||
afl_frida_start();
|
||||
|
||||
@ -130,7 +219,7 @@ extern int *main(int argc, char **argv, char **envp);
|
||||
static void intercept_main(void) {
|
||||
|
||||
main_fn = main;
|
||||
intercept(main, on_main, NULL);
|
||||
intercept_hook(main, on_main, NULL);
|
||||
|
||||
}
|
||||
|
||||
@ -143,7 +232,7 @@ static void intercept_main(void) {
|
||||
OKF("Entry Point: 0x%016" G_GINT64_MODIFIER "x", entry);
|
||||
void *main = GSIZE_TO_POINTER(entry);
|
||||
main_fn = main;
|
||||
intercept(main, on_main, NULL);
|
||||
intercept_hook(main, on_main, NULL);
|
||||
|
||||
}
|
||||
|
||||
@ -154,8 +243,8 @@ static int on_libc_start_main(int *(main)(int, char **, char **), int argc,
|
||||
void(*stack_end)) {
|
||||
|
||||
main_fn = main;
|
||||
unintercept_self();
|
||||
intercept(main, on_main, NULL);
|
||||
intercept_unhook_self();
|
||||
intercept_hook(main, on_main, NULL);
|
||||
return __libc_start_main(main, argc, ubp_av, init, fini, rtld_fini,
|
||||
stack_end);
|
||||
|
||||
@ -163,7 +252,7 @@ static int on_libc_start_main(int *(main)(int, char **, char **), int argc,
|
||||
|
||||
static void intercept_main(void) {
|
||||
|
||||
intercept(__libc_start_main, on_libc_start_main, NULL);
|
||||
intercept_hook(__libc_start_main, on_libc_start_main, NULL);
|
||||
|
||||
}
|
||||
|
||||
|
@ -2,17 +2,17 @@
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "output.h"
|
||||
|
||||
static int output_fd = -1;
|
||||
char *output_stdout = NULL;
|
||||
char *output_stderr = NULL;
|
||||
|
||||
static void output_redirect(int fd, char *variable) {
|
||||
static void output_redirect(int fd, char *filename) {
|
||||
|
||||
char *filename = getenv(variable);
|
||||
char *path = NULL;
|
||||
|
||||
if (filename == NULL) { return; }
|
||||
@ -21,8 +21,8 @@ static void output_redirect(int fd, char *variable) {
|
||||
|
||||
OKF("Redirect %d -> '%s'", fd, path);
|
||||
|
||||
output_fd = open(path, O_RDWR | O_CREAT | O_TRUNC,
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
|
||||
int output_fd = open(path, O_RDWR | O_CREAT | O_TRUNC,
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
|
||||
|
||||
g_free(path);
|
||||
|
||||
@ -34,12 +34,24 @@ static void output_redirect(int fd, char *variable) {
|
||||
|
||||
}
|
||||
|
||||
close(output_fd);
|
||||
|
||||
}
|
||||
|
||||
void output_config(void) {
|
||||
|
||||
output_stdout = getenv("AFL_FRIDA_OUTPUT_STDOUT");
|
||||
output_stderr = getenv("AFL_FRIDA_OUTPUT_STDERR");
|
||||
|
||||
}
|
||||
|
||||
void output_init(void) {
|
||||
|
||||
output_redirect(STDOUT_FILENO, "AFL_FRIDA_OUTPUT_STDOUT");
|
||||
output_redirect(STDERR_FILENO, "AFL_FRIDA_OUTPUT_STDERR");
|
||||
OKF("Output - StdOut: %s", output_stdout);
|
||||
OKF("Output - StdErr: %s", output_stderr);
|
||||
|
||||
output_redirect(STDOUT_FILENO, output_stdout);
|
||||
output_redirect(STDERR_FILENO, output_stderr);
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,30 +1,31 @@
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "entry.h"
|
||||
#include "persistent.h"
|
||||
#include "ranges.h"
|
||||
#include "stalker.h"
|
||||
#include "util.h"
|
||||
|
||||
int __afl_sharedmem_fuzzing = 0;
|
||||
afl_persistent_hook_fn hook = NULL;
|
||||
int __afl_sharedmem_fuzzing = 0;
|
||||
static char *hook_name = NULL;
|
||||
|
||||
afl_persistent_hook_fn persistent_hook = NULL;
|
||||
guint64 persistent_start = 0;
|
||||
guint64 persistent_count = 0;
|
||||
guint64 persistent_ret = 0;
|
||||
guint64 persistent_ret_offset = 0;
|
||||
gboolean persistent_debug = FALSE;
|
||||
|
||||
void persistent_init(void) {
|
||||
|
||||
char *hook_name = getenv("AFL_FRIDA_PERSISTENT_HOOK");
|
||||
void persistent_config(void) {
|
||||
|
||||
hook_name = getenv("AFL_FRIDA_PERSISTENT_HOOK");
|
||||
persistent_start = util_read_address("AFL_FRIDA_PERSISTENT_ADDR");
|
||||
persistent_count = util_read_num("AFL_FRIDA_PERSISTENT_CNT");
|
||||
persistent_ret = util_read_address("AFL_FRIDA_PERSISTENT_RET");
|
||||
persistent_ret_offset =
|
||||
util_read_address("AFL_FRIDA_PERSISTENT_RETADDR_OFFSET");
|
||||
|
||||
if (getenv("AFL_FRIDA_PERSISTENT_DEBUG") != NULL) { persistent_debug = TRUE; }
|
||||
|
||||
@ -36,6 +37,11 @@ void persistent_init(void) {
|
||||
|
||||
}
|
||||
|
||||
if (persistent_start != 0 && persistent_count == 0) persistent_count = 1000;
|
||||
|
||||
if (persistent_start != 0 && !persistent_is_supported())
|
||||
FATAL("Persistent mode not supported on this architecture");
|
||||
|
||||
if (persistent_ret != 0 && persistent_start == 0) {
|
||||
|
||||
FATAL(
|
||||
@ -44,21 +50,28 @@ void persistent_init(void) {
|
||||
|
||||
}
|
||||
|
||||
if (persistent_ret_offset != 0 && persistent_ret == 0) {
|
||||
if (hook_name == NULL) { return; }
|
||||
|
||||
FATAL(
|
||||
"AFL_FRIDA_PERSISTENT_RET must be specified if "
|
||||
"AFL_FRIDA_PERSISTENT_RETADDR_OFFSET is");
|
||||
void *hook_obj = dlopen(hook_name, RTLD_NOW);
|
||||
if (hook_obj == NULL)
|
||||
FATAL("Failed to load AFL_FRIDA_PERSISTENT_HOOK (%s)", hook_name);
|
||||
|
||||
}
|
||||
int (*afl_persistent_hook_init_ptr)(void) =
|
||||
dlsym(hook_obj, "afl_persistent_hook_init");
|
||||
if (afl_persistent_hook_init_ptr == NULL)
|
||||
FATAL("Failed to find afl_persistent_hook_init in %s", hook_name);
|
||||
|
||||
if (persistent_start != 0 && persistent_count == 0) persistent_count = 1000;
|
||||
if (afl_persistent_hook_init_ptr() == 0)
|
||||
FATAL("afl_persistent_hook_init returned a failure");
|
||||
|
||||
if (persistent_count != 0 && persistent_count < 100)
|
||||
WARNF("Persistent count out of recommended range (<100)");
|
||||
persistent_hook =
|
||||
(afl_persistent_hook_fn)dlsym(hook_obj, "afl_persistent_hook");
|
||||
if (persistent_hook == NULL)
|
||||
FATAL("Failed to find afl_persistent_hook in %s", hook_name);
|
||||
|
||||
if (persistent_start != 0 && !persistent_is_supported())
|
||||
FATAL("Persistent mode not supported on this architecture");
|
||||
}
|
||||
|
||||
void persistent_init(void) {
|
||||
|
||||
OKF("Instrumentation - persistent mode [%c] (0x%016" G_GINT64_MODIFIER "X)",
|
||||
persistent_start == 0 ? ' ' : 'X', persistent_start);
|
||||
@ -68,30 +81,25 @@ void persistent_init(void) {
|
||||
|
||||
OKF("Instrumentation - persistent ret [%c] (0x%016" G_GINT64_MODIFIER "X)",
|
||||
persistent_ret == 0 ? ' ' : 'X', persistent_ret);
|
||||
OKF("Instrumentation - persistent ret offset [%c] (%" G_GINT64_MODIFIER "d)",
|
||||
persistent_ret_offset == 0 ? ' ' : 'X', persistent_ret_offset);
|
||||
|
||||
if (hook_name != NULL) {
|
||||
|
||||
void *hook_obj = dlopen(hook_name, RTLD_NOW);
|
||||
if (hook_obj == NULL)
|
||||
FATAL("Failed to load AFL_FRIDA_PERSISTENT_HOOK (%s)", hook_name);
|
||||
|
||||
int (*afl_persistent_hook_init_ptr)(void) =
|
||||
dlsym(hook_obj, "afl_persistent_hook_init");
|
||||
if (afl_persistent_hook_init_ptr == NULL)
|
||||
FATAL("Failed to find afl_persistent_hook_init in %s", hook_name);
|
||||
|
||||
if (afl_persistent_hook_init_ptr() == 0)
|
||||
FATAL("afl_persistent_hook_init returned a failure");
|
||||
|
||||
hook = (afl_persistent_hook_fn)dlsym(hook_obj, "afl_persistent_hook");
|
||||
if (hook == NULL)
|
||||
FATAL("Failed to find afl_persistent_hook in %s", hook_name);
|
||||
|
||||
__afl_sharedmem_fuzzing = 1;
|
||||
|
||||
}
|
||||
if (persistent_hook != NULL) { __afl_sharedmem_fuzzing = 1; }
|
||||
|
||||
}
|
||||
|
||||
void persistent_prologue(GumStalkerOutput *output) {
|
||||
|
||||
OKF("AFL_FRIDA_PERSISTENT_ADDR reached");
|
||||
entry_reached = TRUE;
|
||||
ranges_exclude();
|
||||
stalker_trust();
|
||||
persistent_prologue_arch(output);
|
||||
|
||||
}
|
||||
|
||||
void persistent_epilogue(GumStalkerOutput *output) {
|
||||
|
||||
OKF("AFL_FRIDA_PERSISTENT_RET reached");
|
||||
persistent_epilogue_arch(output);
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
@ -61,14 +61,14 @@ gboolean persistent_is_supported(void) {
|
||||
|
||||
}
|
||||
|
||||
void persistent_prologue(GumStalkerOutput *output) {
|
||||
void persistent_prologue_arch(GumStalkerOutput *output) {
|
||||
|
||||
UNUSED_PARAMETER(output);
|
||||
FATAL("Persistent mode not supported on this architecture");
|
||||
|
||||
}
|
||||
|
||||
void persistent_epilogue(GumStalkerOutput *output) {
|
||||
void persistent_epilogue_arch(GumStalkerOutput *output) {
|
||||
|
||||
UNUSED_PARAMETER(output);
|
||||
FATAL("Persistent mode not supported on this architecture");
|
||||
|
@ -1,120 +1,383 @@
|
||||
#include "frida-gum.h"
|
||||
#include <unistd.h>
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "instrument.h"
|
||||
#include "persistent.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined(__aarch64__)
|
||||
typedef struct {
|
||||
|
||||
struct arm64_regs {
|
||||
GumCpuContext ctx;
|
||||
uint64_t rflags;
|
||||
|
||||
uint64_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10;
|
||||
} persistent_ctx_t;
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x11;
|
||||
uint32_t fp_32;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x12;
|
||||
uint32_t ip_32;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x13;
|
||||
uint32_t sp_32;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x14;
|
||||
uint32_t lr_32;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x15;
|
||||
uint32_t pc_32;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x16;
|
||||
uint64_t ip0;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x17;
|
||||
uint64_t ip1;
|
||||
|
||||
};
|
||||
|
||||
uint64_t x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28;
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x29;
|
||||
uint64_t fp;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x30;
|
||||
uint64_t lr;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x31;
|
||||
uint64_t sp;
|
||||
|
||||
};
|
||||
|
||||
// the zero register is not saved here ofc
|
||||
|
||||
uint64_t pc;
|
||||
|
||||
uint32_t cpsr;
|
||||
|
||||
uint8_t vfp_zregs[32][16 * 16];
|
||||
uint8_t vfp_pregs[17][32];
|
||||
uint32_t vfp_xregs[16];
|
||||
|
||||
};
|
||||
|
||||
typedef struct arm64_regs arch_api_regs;
|
||||
static persistent_ctx_t saved_regs = {0};
|
||||
static gpointer saved_lr = NULL;
|
||||
|
||||
gboolean persistent_is_supported(void) {
|
||||
|
||||
return false;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
void persistent_prologue(GumStalkerOutput *output) {
|
||||
static void instrument_persitent_save_regs(GumArm64Writer * cw,
|
||||
persistent_ctx_t *regs) {
|
||||
|
||||
UNUSED_PARAMETER(output);
|
||||
FATAL("Persistent mode not supported on this architecture");
|
||||
GumAddress regs_address = GUM_ADDRESS(regs);
|
||||
const guint32 mrs_x1_nzcv = 0xd53b4201;
|
||||
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X0, ARM64_REG_X1, ARM64_REG_SP, -(16 + GUM_RED_ZONE_SIZE),
|
||||
GUM_INDEX_PRE_ADJUST);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(cw, ARM64_REG_X2, ARM64_REG_X3,
|
||||
ARM64_REG_SP, -(16),
|
||||
GUM_INDEX_PRE_ADJUST);
|
||||
|
||||
gum_arm64_writer_put_instruction(cw, mrs_x1_nzcv);
|
||||
|
||||
gum_arm64_writer_put_ldr_reg_address(cw, ARM64_REG_X0,
|
||||
GUM_ADDRESS(regs_address));
|
||||
|
||||
/* Skip x0 & x1 we'll do that later */
|
||||
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X2, ARM64_REG_X3, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[2]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X4, ARM64_REG_X5, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[4]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X6, ARM64_REG_X7, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[6]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X8, ARM64_REG_X9, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[8]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X10, ARM64_REG_X11, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[10]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X12, ARM64_REG_X13, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[12]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X14, ARM64_REG_X15, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[14]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X16, ARM64_REG_X17, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[16]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X18, ARM64_REG_X19, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[18]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X20, ARM64_REG_X21, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[20]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X22, ARM64_REG_X23, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[22]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X24, ARM64_REG_X25, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[24]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X26, ARM64_REG_X27, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[26]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X28, ARM64_REG_X29, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[28]), GUM_INDEX_SIGNED_OFFSET);
|
||||
|
||||
/* LR (x30) */
|
||||
gum_arm64_writer_put_str_reg_reg_offset(cw, ARM64_REG_X30, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[30]));
|
||||
|
||||
/* PC & Adjusted SP (31) */
|
||||
gum_arm64_writer_put_ldr_reg_address(cw, ARM64_REG_X2,
|
||||
GUM_ADDRESS(persistent_start));
|
||||
gum_arm64_writer_put_add_reg_reg_imm(cw, ARM64_REG_X3, ARM64_REG_SP,
|
||||
(GUM_RED_ZONE_SIZE + 32));
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X2, ARM64_REG_X3, ARM64_REG_X0, offsetof(GumCpuContext, pc),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
|
||||
/* CPSR */
|
||||
gum_arm64_writer_put_str_reg_reg_offset(cw, ARM64_REG_X1, ARM64_REG_X0,
|
||||
offsetof(persistent_ctx_t, rflags));
|
||||
|
||||
/* Q */
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_Q0, ARM64_REG_Q1, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, q[0]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_Q2, ARM64_REG_Q3, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, q[16]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_Q4, ARM64_REG_Q5, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, q[32]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_Q6, ARM64_REG_Q7, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, q[48]), GUM_INDEX_SIGNED_OFFSET);
|
||||
|
||||
/* x0 & x1 */
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_X2, ARM64_REG_X3,
|
||||
ARM64_REG_SP, 16,
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X2, ARM64_REG_X3, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[0]), GUM_INDEX_SIGNED_OFFSET);
|
||||
|
||||
/* Pop the saved values */
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X2, ARM64_REG_X3, ARM64_REG_SP, 16, GUM_INDEX_POST_ADJUST);
|
||||
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X0, ARM64_REG_X1, ARM64_REG_SP, 16 + GUM_RED_ZONE_SIZE,
|
||||
GUM_INDEX_POST_ADJUST);
|
||||
|
||||
}
|
||||
|
||||
void persistent_epilogue(GumStalkerOutput *output) {
|
||||
static void instrument_persitent_restore_regs(GumArm64Writer * cw,
|
||||
persistent_ctx_t *regs) {
|
||||
|
||||
UNUSED_PARAMETER(output);
|
||||
FATAL("Persistent mode not supported on this architecture");
|
||||
GumAddress regs_address = GUM_ADDRESS(regs);
|
||||
const guint32 msr_nzcv_x1 = 0xd51b4201;
|
||||
|
||||
gum_arm64_writer_put_ldr_reg_address(cw, ARM64_REG_X0,
|
||||
GUM_ADDRESS(regs_address));
|
||||
|
||||
/* Skip x0 - x3 we'll do that last */
|
||||
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X4, ARM64_REG_X5, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[4]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X6, ARM64_REG_X7, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[6]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X8, ARM64_REG_X9, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[8]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X10, ARM64_REG_X11, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[10]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X12, ARM64_REG_X13, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[12]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X14, ARM64_REG_X15, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[14]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X16, ARM64_REG_X17, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[16]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X18, ARM64_REG_X19, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[18]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X20, ARM64_REG_X21, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[20]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X22, ARM64_REG_X23, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[22]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X24, ARM64_REG_X25, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[24]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X26, ARM64_REG_X27, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[26]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X28, ARM64_REG_X29, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[28]), GUM_INDEX_SIGNED_OFFSET);
|
||||
|
||||
/* LR (x30) */
|
||||
gum_arm64_writer_put_ldr_reg_reg_offset(cw, ARM64_REG_X30, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[30]));
|
||||
|
||||
/* Adjusted SP (31) (use x1 as clobber)*/
|
||||
gum_arm64_writer_put_ldr_reg_reg_offset(cw, ARM64_REG_X1, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, sp));
|
||||
gum_arm64_writer_put_mov_reg_reg(cw, ARM64_REG_SP, ARM64_REG_X1);
|
||||
|
||||
/* CPSR */
|
||||
gum_arm64_writer_put_ldr_reg_reg_offset(cw, ARM64_REG_X1, ARM64_REG_X0,
|
||||
offsetof(persistent_ctx_t, rflags));
|
||||
gum_arm64_writer_put_instruction(cw, msr_nzcv_x1);
|
||||
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_Q0, ARM64_REG_Q1, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, q[0]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_Q2, ARM64_REG_Q3, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, q[16]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_Q4, ARM64_REG_Q5, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, q[32]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_Q6, ARM64_REG_Q7, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, q[48]), GUM_INDEX_SIGNED_OFFSET);
|
||||
|
||||
/* x2 & x3 */
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X2, ARM64_REG_X3, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[2]), GUM_INDEX_SIGNED_OFFSET);
|
||||
/* x0 & x1 */
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X0, ARM64_REG_X1, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[0]), GUM_INDEX_SIGNED_OFFSET);
|
||||
|
||||
}
|
||||
|
||||
static void instrument_exit(GumArm64Writer *cw) {
|
||||
|
||||
gum_arm64_writer_put_mov_reg_reg(cw, ARM64_REG_X0, ARM64_REG_XZR);
|
||||
gum_arm64_writer_put_call_address_with_arguments(
|
||||
cw, GUM_ADDRESS(_exit), 1, GUM_ARG_REGISTER, ARM64_REG_X0);
|
||||
|
||||
}
|
||||
|
||||
static int instrument_afl_persistent_loop_func(void) {
|
||||
|
||||
int ret = __afl_persistent_loop(persistent_count);
|
||||
instrument_previous_pc = instrument_hash_zero;
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static void instrument_afl_persistent_loop(GumArm64Writer *cw) {
|
||||
|
||||
gum_arm64_writer_put_sub_reg_reg_imm(cw, ARM64_REG_SP, ARM64_REG_SP,
|
||||
GUM_RED_ZONE_SIZE);
|
||||
gum_arm64_writer_put_call_address_with_arguments(
|
||||
cw, GUM_ADDRESS(instrument_afl_persistent_loop_func), 0);
|
||||
gum_arm64_writer_put_add_reg_reg_imm(cw, ARM64_REG_SP, ARM64_REG_SP,
|
||||
GUM_RED_ZONE_SIZE);
|
||||
|
||||
}
|
||||
|
||||
static void persistent_prologue_hook(GumArm64Writer * cw,
|
||||
persistent_ctx_t *regs) {
|
||||
|
||||
if (persistent_hook == NULL) return;
|
||||
|
||||
gum_arm64_writer_put_sub_reg_reg_imm(cw, ARM64_REG_SP, ARM64_REG_SP,
|
||||
GUM_RED_ZONE_SIZE);
|
||||
gum_arm64_writer_put_ldr_reg_address(cw, ARM64_REG_X2,
|
||||
GUM_ADDRESS(&__afl_fuzz_len));
|
||||
gum_arm64_writer_put_ldr_reg_reg_offset(cw, ARM64_REG_X2, ARM64_REG_X2, 0);
|
||||
gum_arm64_writer_put_ldr_reg_reg_offset(cw, ARM64_REG_X2, ARM64_REG_X2, 0);
|
||||
|
||||
gum_arm64_writer_put_and_reg_reg_imm(cw, ARM64_REG_X2, ARM64_REG_X2,
|
||||
G_MAXULONG);
|
||||
|
||||
gum_arm64_writer_put_ldr_reg_address(cw, ARM64_REG_X1,
|
||||
GUM_ADDRESS(&__afl_fuzz_ptr));
|
||||
gum_arm64_writer_put_ldr_reg_reg_offset(cw, ARM64_REG_X1, ARM64_REG_X1, 0);
|
||||
|
||||
gum_arm64_writer_put_call_address_with_arguments(
|
||||
cw, GUM_ADDRESS(persistent_hook), 3, GUM_ARG_ADDRESS,
|
||||
GUM_ADDRESS(®s->ctx), GUM_ARG_REGISTER, ARM64_REG_X1, GUM_ARG_REGISTER,
|
||||
ARM64_REG_X2);
|
||||
|
||||
gum_arm64_writer_put_add_reg_reg_imm(cw, ARM64_REG_SP, ARM64_REG_SP,
|
||||
GUM_RED_ZONE_SIZE);
|
||||
|
||||
}
|
||||
|
||||
static void instrument_persitent_save_lr(GumArm64Writer *cw) {
|
||||
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X0, ARM64_REG_X1, ARM64_REG_SP, -(16 + GUM_RED_ZONE_SIZE),
|
||||
GUM_INDEX_PRE_ADJUST);
|
||||
|
||||
gum_arm64_writer_put_ldr_reg_address(cw, ARM64_REG_X0,
|
||||
GUM_ADDRESS(&saved_lr));
|
||||
|
||||
gum_arm64_writer_put_str_reg_reg_offset(cw, ARM64_REG_LR, ARM64_REG_X0, 0);
|
||||
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X0, ARM64_REG_X1, ARM64_REG_SP, 16 + GUM_RED_ZONE_SIZE,
|
||||
GUM_INDEX_POST_ADJUST);
|
||||
|
||||
}
|
||||
|
||||
void persistent_prologue_arch(GumStalkerOutput *output) {
|
||||
|
||||
/*
|
||||
* SAVE REGS
|
||||
* SAVE RET
|
||||
* POP RET
|
||||
* loop:
|
||||
* CALL instrument_afl_persistent_loop
|
||||
* TEST EAX, EAX
|
||||
* JZ end:
|
||||
* call hook (optionally)
|
||||
* RESTORE REGS
|
||||
* call original
|
||||
* jmp loop:
|
||||
*
|
||||
* end:
|
||||
* JMP SAVED RET
|
||||
*
|
||||
* original:
|
||||
* INSTRUMENTED PERSISTENT FUNC
|
||||
*/
|
||||
|
||||
GumArm64Writer *cw = output->writer.arm64;
|
||||
|
||||
gconstpointer loop = cw->code + 1;
|
||||
|
||||
OKF("Persistent loop reached");
|
||||
|
||||
instrument_persitent_save_regs(cw, &saved_regs);
|
||||
|
||||
/* loop: */
|
||||
gum_arm64_writer_put_label(cw, loop);
|
||||
|
||||
/* call instrument_prologue_func */
|
||||
instrument_afl_persistent_loop(cw);
|
||||
|
||||
/* jz done */
|
||||
gconstpointer done = cw->code + 1;
|
||||
gum_arm64_writer_put_cmp_reg_reg(cw, ARM64_REG_X0, ARM64_REG_XZR);
|
||||
gum_arm64_writer_put_b_cond_label(cw, ARM64_CC_EQ, done);
|
||||
|
||||
/* Optionally call the persistent hook */
|
||||
persistent_prologue_hook(cw, &saved_regs);
|
||||
|
||||
instrument_persitent_restore_regs(cw, &saved_regs);
|
||||
gconstpointer original = cw->code + 1;
|
||||
/* call original */
|
||||
|
||||
gum_arm64_writer_put_bl_label(cw, original);
|
||||
|
||||
/* jmp loop */
|
||||
gum_arm64_writer_put_b_label(cw, loop);
|
||||
|
||||
/* done: */
|
||||
gum_arm64_writer_put_label(cw, done);
|
||||
|
||||
instrument_exit(cw);
|
||||
|
||||
/* original: */
|
||||
gum_arm64_writer_put_label(cw, original);
|
||||
|
||||
instrument_persitent_save_lr(cw);
|
||||
|
||||
if (persistent_debug) { gum_arm64_writer_put_brk_imm(cw, 0); }
|
||||
|
||||
}
|
||||
|
||||
void persistent_epilogue_arch(GumStalkerOutput *output) {
|
||||
|
||||
GumArm64Writer *cw = output->writer.arm64;
|
||||
|
||||
if (persistent_debug) { gum_arm64_writer_put_brk_imm(cw, 0); }
|
||||
|
||||
gum_arm64_writer_put_ldr_reg_address(cw, ARM64_REG_X0,
|
||||
GUM_ADDRESS(&saved_lr));
|
||||
|
||||
gum_arm64_writer_put_ldr_reg_reg_offset(cw, ARM64_REG_X0, ARM64_REG_X0, 0);
|
||||
|
||||
gum_arm64_writer_put_br_reg(cw, ARM64_REG_X0);
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include <unistd.h>
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
@ -10,39 +10,15 @@
|
||||
|
||||
#if defined(__x86_64__)
|
||||
|
||||
struct x86_64_regs {
|
||||
typedef struct {
|
||||
|
||||
uint64_t rax, rbx, rcx, rdx, rdi, rsi, rbp, r8, r9, r10, r11, r12, r13, r14,
|
||||
r15;
|
||||
GumCpuContext ctx;
|
||||
uint64_t rflags;
|
||||
|
||||
union {
|
||||
} persistent_ctx_t;
|
||||
|
||||
uint64_t rip;
|
||||
uint64_t pc;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t rsp;
|
||||
uint64_t sp;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t rflags;
|
||||
uint64_t flags;
|
||||
|
||||
};
|
||||
|
||||
uint8_t zmm_regs[32][64];
|
||||
|
||||
};
|
||||
|
||||
typedef struct x86_64_regs arch_api_regs;
|
||||
|
||||
static arch_api_regs saved_regs = {0};
|
||||
static persistent_ctx_t saved_regs = {0};
|
||||
static gpointer saved_ret = NULL;
|
||||
|
||||
gboolean persistent_is_supported(void) {
|
||||
|
||||
@ -50,8 +26,8 @@ gboolean persistent_is_supported(void) {
|
||||
|
||||
}
|
||||
|
||||
static void instrument_persitent_save_regs(GumX86Writer * cw,
|
||||
struct x86_64_regs *regs) {
|
||||
static void instrument_persitent_save_regs(GumX86Writer * cw,
|
||||
persistent_ctx_t *regs) {
|
||||
|
||||
GumAddress regs_address = GUM_ADDRESS(regs);
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
@ -63,60 +39,60 @@ static void instrument_persitent_save_regs(GumX86Writer * cw,
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RAX, regs_address);
|
||||
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 1),
|
||||
GUM_REG_RBX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 2),
|
||||
GUM_REG_RCX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 3),
|
||||
GUM_REG_RDX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 4),
|
||||
GUM_REG_RDI);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 5),
|
||||
GUM_REG_RSI);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 6),
|
||||
GUM_REG_RBP);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 7),
|
||||
GUM_REG_R8);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 8),
|
||||
GUM_REG_R9);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 9),
|
||||
GUM_REG_R10);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 10),
|
||||
GUM_REG_R11);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 11),
|
||||
GUM_REG_R12);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 12),
|
||||
GUM_REG_R13);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 13),
|
||||
GUM_REG_R14);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 14),
|
||||
GUM_REG_R15);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rbx), GUM_REG_RBX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rcx), GUM_REG_RCX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rdx), GUM_REG_RDX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rdi), GUM_REG_RDI);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rsi), GUM_REG_RSI);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rbp), GUM_REG_RBP);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, r8), GUM_REG_R8);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, r9), GUM_REG_R9);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, r10), GUM_REG_R10);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, r11), GUM_REG_R11);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, r12), GUM_REG_R12);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, r13), GUM_REG_R13);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, r14), GUM_REG_R14);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, r15), GUM_REG_R15);
|
||||
|
||||
/* Store RIP */
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RBX,
|
||||
GUM_ADDRESS(persistent_start));
|
||||
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 15),
|
||||
GUM_REG_RBX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rip), GUM_REG_RBX);
|
||||
|
||||
/* Store adjusted RSP */
|
||||
gum_x86_writer_put_mov_reg_reg(cw, GUM_REG_RBX, GUM_REG_RSP);
|
||||
|
||||
/* RED_ZONE + Saved flags, RAX, alignment */
|
||||
gum_x86_writer_put_add_reg_imm(cw, GUM_REG_RBX,
|
||||
GUM_RED_ZONE_SIZE + (0x8 * 3));
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 16),
|
||||
GUM_REG_RBX);
|
||||
GUM_RED_ZONE_SIZE + (0x8 * 2));
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rsp), GUM_REG_RBX);
|
||||
|
||||
/* Save the flags */
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RBX, GUM_REG_RSP, 0x8);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 17),
|
||||
GUM_REG_RBX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(persistent_ctx_t, rflags), GUM_REG_RBX);
|
||||
|
||||
/* Save the RAX */
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RBX, GUM_REG_RSP, 0x0);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 0),
|
||||
GUM_REG_RBX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rax), GUM_REG_RBX);
|
||||
|
||||
/* Pop the saved values */
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, 0x10);
|
||||
@ -126,54 +102,56 @@ static void instrument_persitent_save_regs(GumX86Writer * cw,
|
||||
|
||||
}
|
||||
|
||||
static void instrument_persitent_restore_regs(GumX86Writer * cw,
|
||||
struct x86_64_regs *regs) {
|
||||
static void instrument_persitent_restore_regs(GumX86Writer * cw,
|
||||
persistent_ctx_t *regs) {
|
||||
|
||||
GumAddress regs_address = GUM_ADDRESS(regs);
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RAX, regs_address);
|
||||
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RCX, GUM_REG_RAX,
|
||||
(0x8 * 2));
|
||||
offsetof(GumCpuContext, rcx));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RDX, GUM_REG_RAX,
|
||||
(0x8 * 3));
|
||||
offsetof(GumCpuContext, rdx));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RDI, GUM_REG_RAX,
|
||||
(0x8 * 4));
|
||||
offsetof(GumCpuContext, rdi));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RSI, GUM_REG_RAX,
|
||||
(0x8 * 5));
|
||||
offsetof(GumCpuContext, rsi));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RBP, GUM_REG_RAX,
|
||||
(0x8 * 6));
|
||||
offsetof(GumCpuContext, rbp));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R8, GUM_REG_RAX,
|
||||
(0x8 * 7));
|
||||
offsetof(GumCpuContext, r8));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R9, GUM_REG_RAX,
|
||||
(0x8 * 8));
|
||||
offsetof(GumCpuContext, r9));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R10, GUM_REG_RAX,
|
||||
(0x8 * 9));
|
||||
offsetof(GumCpuContext, r10));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R11, GUM_REG_RAX,
|
||||
(0x8 * 10));
|
||||
offsetof(GumCpuContext, r11));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R12, GUM_REG_RAX,
|
||||
(0x8 * 11));
|
||||
offsetof(GumCpuContext, r12));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R13, GUM_REG_RAX,
|
||||
(0x8 * 12));
|
||||
offsetof(GumCpuContext, r13));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R14, GUM_REG_RAX,
|
||||
(0x8 * 13));
|
||||
offsetof(GumCpuContext, r14));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R15, GUM_REG_RAX,
|
||||
(0x8 * 14));
|
||||
offsetof(GumCpuContext, r15));
|
||||
|
||||
/* Don't restore RIP or RSP */
|
||||
/* Don't restore RIP */
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RSP, GUM_REG_RAX,
|
||||
offsetof(GumCpuContext, rsp));
|
||||
|
||||
/* Restore RBX, RAX & Flags */
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
-(GUM_RED_ZONE_SIZE));
|
||||
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RBX, GUM_REG_RAX,
|
||||
(0x8 * 1));
|
||||
offsetof(GumCpuContext, rbx));
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_RBX);
|
||||
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RBX, GUM_REG_RAX,
|
||||
(0x8 * 0));
|
||||
offsetof(GumCpuContext, rax));
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_RBX);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RBX, GUM_REG_RAX,
|
||||
(0x8 * 17));
|
||||
offsetof(persistent_ctx_t, rflags));
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_RBX);
|
||||
|
||||
gum_x86_writer_put_popfx(cw);
|
||||
@ -196,7 +174,7 @@ static void instrument_exit(GumX86Writer *cw) {
|
||||
static int instrument_afl_persistent_loop_func(void) {
|
||||
|
||||
int ret = __afl_persistent_loop(persistent_count);
|
||||
previous_pc = 0;
|
||||
instrument_previous_pc = instrument_hash_zero;
|
||||
return ret;
|
||||
|
||||
}
|
||||
@ -214,35 +192,59 @@ static void instrument_afl_persistent_loop(GumX86Writer *cw) {
|
||||
|
||||
}
|
||||
|
||||
static void persistent_prologue_hook(GumX86Writer * cw,
|
||||
struct x86_64_regs *regs) {
|
||||
static void persistent_prologue_hook(GumX86Writer *cw, persistent_ctx_t *regs) {
|
||||
|
||||
if (hook == NULL) return;
|
||||
if (persistent_hook == NULL) return;
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
-(GUM_RED_ZONE_SIZE));
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RCX,
|
||||
GUM_ADDRESS(&__afl_fuzz_len));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RCX, GUM_REG_RCX, 0);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RCX, GUM_REG_RCX, 0);
|
||||
gum_x86_writer_put_mov_reg_u64(cw, GUM_REG_RDI, 0xffffffff);
|
||||
gum_x86_writer_put_and_reg_reg(cw, GUM_REG_RCX, GUM_REG_RDI);
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RDX,
|
||||
GUM_ADDRESS(&__afl_fuzz_ptr));
|
||||
GUM_ADDRESS(&__afl_fuzz_len));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RDX, GUM_REG_RDX, 0);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RDX, GUM_REG_RDX, 0);
|
||||
gum_x86_writer_put_mov_reg_u64(cw, GUM_REG_RDI, 0xffffffff);
|
||||
gum_x86_writer_put_and_reg_reg(cw, GUM_REG_RDX, GUM_REG_RDI);
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RSI,
|
||||
GUM_ADDRESS(&__afl_fuzz_ptr));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RSI, GUM_REG_RSI, 0);
|
||||
|
||||
gum_x86_writer_put_call_address_with_arguments(
|
||||
cw, GUM_CALL_CAPI, GUM_ADDRESS(hook), 4, GUM_ARG_ADDRESS,
|
||||
GUM_ADDRESS(regs), GUM_ARG_ADDRESS, GUM_ADDRESS(0), GUM_ARG_REGISTER,
|
||||
GUM_REG_RDX, GUM_ARG_REGISTER, GUM_REG_RCX);
|
||||
cw, GUM_CALL_CAPI, GUM_ADDRESS(persistent_hook), 3, GUM_ARG_ADDRESS,
|
||||
GUM_ADDRESS(®s->ctx), GUM_ARG_REGISTER, GUM_REG_RSI, GUM_ARG_REGISTER,
|
||||
GUM_REG_RDX);
|
||||
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
(GUM_RED_ZONE_SIZE));
|
||||
|
||||
}
|
||||
|
||||
void persistent_prologue(GumStalkerOutput *output) {
|
||||
static void instrument_persitent_save_ret(GumX86Writer *cw) {
|
||||
|
||||
/* Stack usage by this function */
|
||||
gssize offset = GUM_RED_ZONE_SIZE + (3 * 8);
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
-(GUM_RED_ZONE_SIZE));
|
||||
|
||||
gum_x86_writer_put_pushfx(cw);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_RAX);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_RBX);
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RAX, GUM_ADDRESS(&saved_ret));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RBX, GUM_REG_RSP,
|
||||
offset);
|
||||
gum_x86_writer_put_mov_reg_ptr_reg(cw, GUM_REG_RAX, GUM_REG_RBX);
|
||||
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_REG_RBX);
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_REG_RAX);
|
||||
gum_x86_writer_put_popfx(cw);
|
||||
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
(GUM_RED_ZONE_SIZE));
|
||||
|
||||
}
|
||||
|
||||
void persistent_prologue_arch(GumStalkerOutput *output) {
|
||||
|
||||
/*
|
||||
* SAVE REGS
|
||||
@ -268,11 +270,12 @@ void persistent_prologue(GumStalkerOutput *output) {
|
||||
|
||||
gconstpointer loop = cw->code + 1;
|
||||
|
||||
/* Stack must be 16-byte aligned per ABI */
|
||||
instrument_persitent_save_regs(cw, &saved_regs);
|
||||
OKF("Persistent loop reached");
|
||||
|
||||
/* pop the return value */
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, (8));
|
||||
/* Pop the return value */
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, 8);
|
||||
|
||||
instrument_persitent_save_regs(cw, &saved_regs);
|
||||
|
||||
/* loop: */
|
||||
gum_x86_writer_put_label(cw, loop);
|
||||
@ -304,21 +307,27 @@ void persistent_prologue(GumStalkerOutput *output) {
|
||||
/* original: */
|
||||
gum_x86_writer_put_label(cw, original);
|
||||
|
||||
if (persistent_debug) { gum_x86_writer_put_breakpoint(cw); }
|
||||
instrument_persitent_save_ret(cw);
|
||||
|
||||
gum_x86_writer_flush(cw);
|
||||
if (persistent_debug) { gum_x86_writer_put_breakpoint(cw); }
|
||||
|
||||
}
|
||||
|
||||
void persistent_epilogue(GumStalkerOutput *output) {
|
||||
void persistent_epilogue_arch(GumStalkerOutput *output) {
|
||||
|
||||
GumX86Writer *cw = output->writer.x86;
|
||||
|
||||
if (persistent_debug) { gum_x86_writer_put_breakpoint(cw); }
|
||||
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
persistent_ret_offset);
|
||||
gum_x86_writer_put_ret(cw);
|
||||
/* The stack should be aligned when we re-enter our loop */
|
||||
gconstpointer zero = cw->code + 1;
|
||||
gum_x86_writer_put_test_reg_u32(cw, GUM_REG_RSP, 0xF);
|
||||
gum_x86_writer_put_jcc_near_label(cw, X86_INS_JE, zero, GUM_NO_HINT);
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, -8);
|
||||
gum_x86_writer_put_label(cw, zero);
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RAX, GUM_ADDRESS(&saved_ret));
|
||||
gum_x86_writer_put_jmp_reg_ptr(cw, GUM_REG_RAX);
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,44 +1,23 @@
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "instrument.h"
|
||||
#include "persistent.h"
|
||||
|
||||
#if defined(__i386__)
|
||||
|
||||
struct x86_regs {
|
||||
typedef struct {
|
||||
|
||||
uint32_t eax, ebx, ecx, edx, edi, esi, ebp;
|
||||
GumCpuContext ctx;
|
||||
uint32_t eflags;
|
||||
|
||||
union {
|
||||
} persistent_ctx_t;
|
||||
|
||||
uint32_t eip;
|
||||
uint32_t pc;
|
||||
static persistent_ctx_t saved_regs = {0};
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint32_t esp;
|
||||
uint32_t sp;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint32_t eflags;
|
||||
uint32_t flags;
|
||||
|
||||
};
|
||||
|
||||
uint8_t xmm_regs[8][16];
|
||||
|
||||
};
|
||||
|
||||
typedef struct x86_regs arch_api_regs;
|
||||
|
||||
static arch_api_regs saved_regs = {0};
|
||||
static gpointer saved_ret = NULL;
|
||||
|
||||
gboolean persistent_is_supported(void) {
|
||||
|
||||
@ -46,8 +25,8 @@ gboolean persistent_is_supported(void) {
|
||||
|
||||
}
|
||||
|
||||
static void instrument_persitent_save_regs(GumX86Writer * cw,
|
||||
struct x86_regs *regs) {
|
||||
static void instrument_persitent_save_regs(GumX86Writer * cw,
|
||||
persistent_ctx_t *regs) {
|
||||
|
||||
GumAddress regs_address = GUM_ADDRESS(regs);
|
||||
|
||||
@ -57,78 +36,80 @@ static void instrument_persitent_save_regs(GumX86Writer * cw,
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EAX, regs_address);
|
||||
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 1),
|
||||
GUM_REG_EBX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 2),
|
||||
GUM_REG_ECX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 3),
|
||||
GUM_REG_EDX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 4),
|
||||
GUM_REG_EDI);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 5),
|
||||
GUM_REG_ESI);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 6),
|
||||
GUM_REG_EBP);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, ebx), GUM_REG_EBX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, ecx), GUM_REG_ECX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, edx), GUM_REG_EDX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, edi), GUM_REG_EDI);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, esi), GUM_REG_ESI);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, ebp), GUM_REG_EBP);
|
||||
|
||||
/* Store RIP */
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EBX,
|
||||
GUM_ADDRESS(persistent_start));
|
||||
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 7),
|
||||
GUM_REG_EBX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, eip), GUM_REG_EBX);
|
||||
|
||||
/* Store adjusted RSP */
|
||||
gum_x86_writer_put_mov_reg_reg(cw, GUM_REG_EBX, GUM_REG_ESP);
|
||||
|
||||
/* RED_ZONE + Saved flags, RAX */
|
||||
gum_x86_writer_put_add_reg_imm(cw, GUM_REG_EBX, (0x4 * 2));
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 8),
|
||||
GUM_REG_EBX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, esp), GUM_REG_EBX);
|
||||
|
||||
/* Save the flags */
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_ESP, 0x4);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 9),
|
||||
GUM_REG_EBX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(persistent_ctx_t, eflags), GUM_REG_EBX);
|
||||
|
||||
/* Save the RAX */
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_ESP, 0x0);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 0),
|
||||
GUM_REG_EBX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, eax), GUM_REG_EBX);
|
||||
|
||||
/* Pop the saved values */
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_ESP, GUM_REG_ESP, 0x8);
|
||||
|
||||
}
|
||||
|
||||
static void instrument_persitent_restore_regs(GumX86Writer * cw,
|
||||
struct x86_regs *regs) {
|
||||
static void instrument_persitent_restore_regs(GumX86Writer * cw,
|
||||
persistent_ctx_t *regs) {
|
||||
|
||||
GumAddress regs_address = GUM_ADDRESS(regs);
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EAX, regs_address);
|
||||
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_ECX, GUM_REG_EAX,
|
||||
(0x4 * 2));
|
||||
offsetof(GumCpuContext, ecx));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EDX, GUM_REG_EAX,
|
||||
(0x4 * 3));
|
||||
offsetof(GumCpuContext, edx));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EDI, GUM_REG_EAX,
|
||||
(0x4 * 4));
|
||||
offsetof(GumCpuContext, edi));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_ESI, GUM_REG_EAX,
|
||||
(0x4 * 5));
|
||||
offsetof(GumCpuContext, esi));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBP, GUM_REG_EAX,
|
||||
(0x4 * 6));
|
||||
offsetof(GumCpuContext, ebp));
|
||||
|
||||
/* Don't restore RIP or RSP */
|
||||
/* Don't restore RIP */
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_ESP, GUM_REG_EAX,
|
||||
offsetof(GumCpuContext, esp));
|
||||
|
||||
/* Restore RBX, RAX & Flags */
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_EAX,
|
||||
(0x4 * 1));
|
||||
offsetof(GumCpuContext, ebx));
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_EBX);
|
||||
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_EAX,
|
||||
(0x4 * 0));
|
||||
offsetof(GumCpuContext, eax));
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_EBX);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_EAX,
|
||||
(0x4 * 9));
|
||||
offsetof(persistent_ctx_t, eflags));
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_EBX);
|
||||
|
||||
gum_x86_writer_put_popfx(cw);
|
||||
@ -149,7 +130,7 @@ static void instrument_exit(GumX86Writer *cw) {
|
||||
static int instrument_afl_persistent_loop_func(void) {
|
||||
|
||||
int ret = __afl_persistent_loop(persistent_count);
|
||||
previous_pc = 0;
|
||||
instrument_previous_pc = instrument_hash_zero;
|
||||
return ret;
|
||||
|
||||
}
|
||||
@ -162,9 +143,9 @@ static void instrument_afl_persistent_loop(GumX86Writer *cw) {
|
||||
|
||||
}
|
||||
|
||||
static void persistent_prologue_hook(GumX86Writer *cw, struct x86_regs *regs) {
|
||||
static void persistent_prologue_hook(GumX86Writer *cw, persistent_ctx_t *regs) {
|
||||
|
||||
if (hook == NULL) return;
|
||||
if (persistent_hook == NULL) return;
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_ECX,
|
||||
GUM_ADDRESS(&__afl_fuzz_len));
|
||||
@ -177,14 +158,33 @@ static void persistent_prologue_hook(GumX86Writer *cw, struct x86_regs *regs) {
|
||||
|
||||
/* Base address is 64-bits (hence two zero arguments) */
|
||||
gum_x86_writer_put_call_address_with_arguments(
|
||||
cw, GUM_CALL_CAPI, GUM_ADDRESS(hook), 5, GUM_ARG_ADDRESS,
|
||||
GUM_ADDRESS(regs), GUM_ARG_ADDRESS, GUM_ADDRESS(0), GUM_ARG_ADDRESS,
|
||||
GUM_ADDRESS(0), GUM_ARG_REGISTER, GUM_REG_EDX, GUM_ARG_REGISTER,
|
||||
cw, GUM_CALL_CAPI, GUM_ADDRESS(persistent_hook), 3, GUM_ARG_ADDRESS,
|
||||
GUM_ADDRESS(®s->ctx), GUM_ARG_REGISTER, GUM_REG_EDX, GUM_ARG_REGISTER,
|
||||
GUM_REG_ECX);
|
||||
|
||||
}
|
||||
|
||||
void persistent_prologue(GumStalkerOutput *output) {
|
||||
static void instrument_persitent_save_ret(GumX86Writer *cw) {
|
||||
|
||||
/* Stack usage by this function */
|
||||
gssize offset = (3 * 4);
|
||||
|
||||
gum_x86_writer_put_pushfx(cw);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_EAX);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_EBX);
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EAX, GUM_ADDRESS(&saved_ret));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_ESP,
|
||||
offset);
|
||||
gum_x86_writer_put_mov_reg_ptr_reg(cw, GUM_REG_EAX, GUM_REG_EBX);
|
||||
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_REG_EBX);
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_REG_EAX);
|
||||
gum_x86_writer_put_popfx(cw);
|
||||
|
||||
}
|
||||
|
||||
void persistent_prologue_arch(GumStalkerOutput *output) {
|
||||
|
||||
/*
|
||||
* SAVE REGS
|
||||
@ -210,11 +210,12 @@ void persistent_prologue(GumStalkerOutput *output) {
|
||||
|
||||
gconstpointer loop = cw->code + 1;
|
||||
|
||||
/* Stack must be 16-byte aligned per ABI */
|
||||
instrument_persitent_save_regs(cw, &saved_regs);
|
||||
OKF("Persistent loop reached");
|
||||
|
||||
/* Pop the return value */
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_ESP, GUM_REG_ESP, (4));
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_ESP, GUM_REG_ESP, 4);
|
||||
|
||||
instrument_persitent_save_regs(cw, &saved_regs);
|
||||
|
||||
/* loop: */
|
||||
gum_x86_writer_put_label(cw, loop);
|
||||
@ -244,22 +245,20 @@ void persistent_prologue(GumStalkerOutput *output) {
|
||||
/* original: */
|
||||
gum_x86_writer_put_label(cw, original);
|
||||
|
||||
if (persistent_debug) { gum_x86_writer_put_breakpoint(cw); }
|
||||
instrument_persitent_save_ret(cw);
|
||||
|
||||
gum_x86_writer_flush(cw);
|
||||
if (persistent_debug) { gum_x86_writer_put_breakpoint(cw); }
|
||||
|
||||
}
|
||||
|
||||
void persistent_epilogue(GumStalkerOutput *output) {
|
||||
void persistent_epilogue_arch(GumStalkerOutput *output) {
|
||||
|
||||
GumX86Writer *cw = output->writer.x86;
|
||||
|
||||
if (persistent_debug) { gum_x86_writer_put_breakpoint(cw); }
|
||||
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_ESP, GUM_REG_ESP,
|
||||
persistent_ret_offset);
|
||||
|
||||
gum_x86_writer_put_ret(cw);
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EAX, GUM_ADDRESS(&saved_ret));
|
||||
gum_x86_writer_put_jmp_reg_ptr(cw, GUM_REG_EAX);
|
||||
|
||||
}
|
||||
|
||||
|
@ -2,10 +2,11 @@
|
||||
#include <sys/shm.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "intercept.h"
|
||||
#include "prefetch.h"
|
||||
#include "stalker.h"
|
||||
|
||||
@ -20,9 +21,10 @@ typedef struct {
|
||||
|
||||
} prefetch_data_t;
|
||||
|
||||
static prefetch_data_t *prefetch_data = NULL;
|
||||
gboolean prefetch_enable = TRUE;
|
||||
|
||||
static int prefetch_shm_id = -1;
|
||||
static prefetch_data_t *prefetch_data = NULL;
|
||||
static int prefetch_shm_id = -1;
|
||||
|
||||
/*
|
||||
* We do this from the transformer since we need one anyway for coverage, this
|
||||
@ -72,14 +74,33 @@ void prefetch_read(void) {
|
||||
|
||||
}
|
||||
|
||||
void prefetch_config(void) {
|
||||
|
||||
prefetch_enable = (getenv("AFL_FRIDA_INST_NO_PREFETCH") == NULL);
|
||||
|
||||
}
|
||||
|
||||
static int prefetch_on_fork(void) {
|
||||
|
||||
prefetch_read();
|
||||
return fork();
|
||||
|
||||
}
|
||||
|
||||
static void prefetch_hook_fork(void) {
|
||||
|
||||
void *fork_addr =
|
||||
GSIZE_TO_POINTER(gum_module_find_export_by_name(NULL, "fork"));
|
||||
intercept_hook(fork_addr, prefetch_on_fork, NULL);
|
||||
|
||||
}
|
||||
|
||||
void prefetch_init(void) {
|
||||
|
||||
g_assert_cmpint(sizeof(prefetch_data_t), ==, PREFETCH_SIZE);
|
||||
gboolean prefetch = (getenv("AFL_FRIDA_INST_NO_PREFETCH") == NULL);
|
||||
OKF("Instrumentation - prefetch [%c]", prefetch_enable ? 'X' : ' ');
|
||||
|
||||
OKF("Instrumentation - prefetch [%c]", prefetch ? 'X' : ' ');
|
||||
|
||||
if (!prefetch) { return; }
|
||||
if (!prefetch_enable) { return; }
|
||||
/*
|
||||
* Make our shared memory, we can attach before we fork, just like AFL does
|
||||
* with the coverage bitmap region and fork will take care of ensuring both
|
||||
@ -108,5 +129,7 @@ void prefetch_init(void) {
|
||||
/* Clear it, not sure it's necessary, just seems like good practice */
|
||||
memset(prefetch_data, '\0', sizeof(prefetch_data_t));
|
||||
|
||||
prefetch_hook_fork();
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
@ -17,11 +17,16 @@ typedef struct {
|
||||
|
||||
} convert_name_ctx_t;
|
||||
|
||||
GArray *module_ranges = NULL;
|
||||
GArray *libs_ranges = NULL;
|
||||
GArray *include_ranges = NULL;
|
||||
GArray *exclude_ranges = NULL;
|
||||
GArray *ranges = NULL;
|
||||
gboolean ranges_debug_maps = FALSE;
|
||||
gboolean ranges_inst_libs = FALSE;
|
||||
gboolean ranges_inst_jit = FALSE;
|
||||
|
||||
static GArray *module_ranges = NULL;
|
||||
static GArray *libs_ranges = NULL;
|
||||
static GArray *jit_ranges = NULL;
|
||||
static GArray *include_ranges = NULL;
|
||||
static GArray *exclude_ranges = NULL;
|
||||
static GArray *ranges = NULL;
|
||||
|
||||
static void convert_address_token(gchar *token, GumMemoryRange *range) {
|
||||
|
||||
@ -142,11 +147,13 @@ static void convert_name_token(gchar *token, GumMemoryRange *range) {
|
||||
|
||||
static void convert_token(gchar *token, GumMemoryRange *range) {
|
||||
|
||||
if (g_strrstr(token, "-")) {
|
||||
if (g_str_has_prefix(token, "0x")) {
|
||||
|
||||
convert_address_token(token, range);
|
||||
|
||||
} else {
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
convert_name_token(token, range);
|
||||
|
||||
@ -169,19 +176,27 @@ static gboolean print_ranges_callback(const GumRangeDetails *details,
|
||||
gpointer user_data) {
|
||||
|
||||
UNUSED_PARAMETER(user_data);
|
||||
|
||||
if (details->file == NULL) {
|
||||
|
||||
OKF("MAP - 0x%016" G_GINT64_MODIFIER "x - 0x%016" G_GINT64_MODIFIER "X",
|
||||
OKF("MAP - 0x%016" G_GINT64_MODIFIER "x - 0x%016" G_GINT64_MODIFIER
|
||||
"X %c%c%c",
|
||||
details->range->base_address,
|
||||
details->range->base_address + details->range->size);
|
||||
details->range->base_address + details->range->size,
|
||||
details->protection & GUM_PAGE_READ ? 'R' : '-',
|
||||
details->protection & GUM_PAGE_WRITE ? 'W' : '-',
|
||||
details->protection & GUM_PAGE_EXECUTE ? 'X' : '-');
|
||||
|
||||
} else {
|
||||
|
||||
OKF("MAP - 0x%016" G_GINT64_MODIFIER "x - 0x%016" G_GINT64_MODIFIER
|
||||
"X %s(0x%016" G_GINT64_MODIFIER "x)",
|
||||
"X %c%c%c %s(0x%016" G_GINT64_MODIFIER "x)",
|
||||
details->range->base_address,
|
||||
details->range->base_address + details->range->size,
|
||||
details->file->path, details->file->offset);
|
||||
details->protection & GUM_PAGE_READ ? 'R' : '-',
|
||||
details->protection & GUM_PAGE_WRITE ? 'W' : '-',
|
||||
details->protection & GUM_PAGE_EXECUTE ? 'X' : '-', details->file->path,
|
||||
details->file->offset);
|
||||
|
||||
}
|
||||
|
||||
@ -225,6 +240,43 @@ static GArray *collect_module_ranges(void) {
|
||||
|
||||
}
|
||||
|
||||
static void check_for_overlaps(GArray *array) {
|
||||
|
||||
for (guint i = 1; i < array->len; i++) {
|
||||
|
||||
GumMemoryRange *prev = &g_array_index(array, GumMemoryRange, i - 1);
|
||||
GumMemoryRange *curr = &g_array_index(array, GumMemoryRange, i);
|
||||
GumAddress prev_limit = prev->base_address + prev->size;
|
||||
GumAddress curr_limit = curr->base_address + curr->size;
|
||||
if (prev_limit > curr->base_address) {
|
||||
|
||||
FATAL("OVerlapping ranges 0x%016" G_GINT64_MODIFIER
|
||||
"x-0x%016" G_GINT64_MODIFIER "x 0x%016" G_GINT64_MODIFIER
|
||||
"x-0x%016" G_GINT64_MODIFIER "x",
|
||||
prev->base_address, prev_limit, curr->base_address, curr_limit);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ranges_add_include(GumMemoryRange *range) {
|
||||
|
||||
g_array_append_val(include_ranges, *range);
|
||||
g_array_sort(include_ranges, range_sort);
|
||||
check_for_overlaps(include_ranges);
|
||||
|
||||
}
|
||||
|
||||
void ranges_add_exclude(GumMemoryRange *range) {
|
||||
|
||||
g_array_append_val(exclude_ranges, *range);
|
||||
g_array_sort(exclude_ranges, range_sort);
|
||||
check_for_overlaps(exclude_ranges);
|
||||
|
||||
}
|
||||
|
||||
static GArray *collect_ranges(char *env_key) {
|
||||
|
||||
char * env_val;
|
||||
@ -253,23 +305,7 @@ static GArray *collect_ranges(char *env_key) {
|
||||
|
||||
g_array_sort(result, range_sort);
|
||||
|
||||
/* Check for overlaps */
|
||||
for (i = 1; i < token_count; i++) {
|
||||
|
||||
GumMemoryRange *prev = &g_array_index(result, GumMemoryRange, i - 1);
|
||||
GumMemoryRange *curr = &g_array_index(result, GumMemoryRange, i);
|
||||
GumAddress prev_limit = prev->base_address + prev->size;
|
||||
GumAddress curr_limit = curr->base_address + curr->size;
|
||||
if (prev_limit > curr->base_address) {
|
||||
|
||||
FATAL("OVerlapping ranges 0x%016" G_GINT64_MODIFIER
|
||||
"x-0x%016" G_GINT64_MODIFIER "x 0x%016" G_GINT64_MODIFIER
|
||||
"x-0x%016" G_GINT64_MODIFIER "x",
|
||||
prev->base_address, prev_limit, curr->base_address, curr_limit);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
check_for_overlaps(result);
|
||||
|
||||
print_ranges(env_key, result);
|
||||
|
||||
@ -285,16 +321,16 @@ static GArray *collect_libs_ranges(void) {
|
||||
GumMemoryRange range;
|
||||
result = g_array_new(false, false, sizeof(GumMemoryRange));
|
||||
|
||||
if (getenv("AFL_INST_LIBS") == NULL) {
|
||||
|
||||
range.base_address = lib_get_text_base();
|
||||
range.size = lib_get_text_limit() - lib_get_text_base();
|
||||
|
||||
} else {
|
||||
if (ranges_inst_libs) {
|
||||
|
||||
range.base_address = 0;
|
||||
range.size = G_MAXULONG;
|
||||
|
||||
} else {
|
||||
|
||||
range.base_address = lib_get_text_base();
|
||||
range.size = lib_get_text_limit() - lib_get_text_base();
|
||||
|
||||
}
|
||||
|
||||
g_array_append_val(result, range);
|
||||
@ -305,6 +341,39 @@ static GArray *collect_libs_ranges(void) {
|
||||
|
||||
}
|
||||
|
||||
static gboolean collect_jit_ranges_callback(const GumRangeDetails *details,
|
||||
gpointer user_data) {
|
||||
|
||||
GArray *ranges = (GArray *)user_data;
|
||||
|
||||
/* If the executable code isn't backed by a file, it's probably JIT */
|
||||
if (details->file == NULL) {
|
||||
|
||||
GumMemoryRange range = *details->range;
|
||||
g_array_append_val(ranges, range);
|
||||
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
static GArray *collect_jit_ranges(void) {
|
||||
|
||||
GArray *result;
|
||||
result = g_array_new(false, false, sizeof(GumMemoryRange));
|
||||
if (!ranges_inst_jit) {
|
||||
|
||||
gum_process_enumerate_ranges(GUM_PAGE_EXECUTE, collect_jit_ranges_callback,
|
||||
result);
|
||||
|
||||
}
|
||||
|
||||
print_ranges("JIT", result);
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
static gboolean intersect_range(GumMemoryRange *rr, GumMemoryRange *ra,
|
||||
GumMemoryRange *rb) {
|
||||
|
||||
@ -480,30 +549,21 @@ static GArray *merge_ranges(GArray *a) {
|
||||
|
||||
}
|
||||
|
||||
static gboolean exclude_ranges_callback(const GumRangeDetails *details,
|
||||
gpointer user_data) {
|
||||
void ranges_config(void) {
|
||||
|
||||
UNUSED_PARAMETER(user_data);
|
||||
gchar * name;
|
||||
gboolean found;
|
||||
GumStalker *stalker;
|
||||
if (details->file == NULL) { return TRUE; }
|
||||
name = g_path_get_basename(details->file->path);
|
||||
if (getenv("AFL_FRIDA_DEBUG_MAPS") != NULL) { ranges_debug_maps = TRUE; }
|
||||
if (getenv("AFL_INST_LIBS") != NULL) { ranges_inst_libs = TRUE; }
|
||||
if (getenv("AFL_FRIDA_INST_JIT") != NULL) { ranges_inst_jit = TRUE; }
|
||||
|
||||
found = (g_strcmp0(name, "afl-frida-trace.so") == 0);
|
||||
g_free(name);
|
||||
if (!found) { return TRUE; }
|
||||
if (ranges_debug_maps) {
|
||||
|
||||
stalker = stalker_get();
|
||||
gum_stalker_exclude(stalker, details->range);
|
||||
gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, print_ranges_callback,
|
||||
NULL);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void ranges_exclude_self(void) {
|
||||
|
||||
gum_process_enumerate_ranges(GUM_PAGE_EXECUTE, exclude_ranges_callback, NULL);
|
||||
include_ranges = collect_ranges("AFL_FRIDA_INST_RANGES");
|
||||
exclude_ranges = collect_ranges("AFL_FRIDA_EXCLUDE_RANGES");
|
||||
|
||||
}
|
||||
|
||||
@ -514,17 +574,22 @@ void ranges_init(void) {
|
||||
GArray * step2;
|
||||
GArray * step3;
|
||||
GArray * step4;
|
||||
GArray * step5;
|
||||
|
||||
if (getenv("AFL_FRIDA_DEBUG_MAPS") != NULL) {
|
||||
OKF("Ranges - Instrument jit [%c]", ranges_inst_jit ? 'X' : ' ');
|
||||
OKF("Ranges - Instrument libraries [%c]", ranges_inst_libs ? 'X' : ' ');
|
||||
|
||||
gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, print_ranges_callback,
|
||||
NULL);
|
||||
print_ranges("AFL_FRIDA_INST_RANGES", include_ranges);
|
||||
print_ranges("AFL_FRIDA_EXCLUDE_RANGES", exclude_ranges);
|
||||
|
||||
}
|
||||
OKF("Ranges - Instrument libraries [%c]", ranges_inst_libs ? 'X' : ' ');
|
||||
|
||||
print_ranges("AFL_FRIDA_INST_RANGES", include_ranges);
|
||||
print_ranges("AFL_FRIDA_EXCLUDE_RANGES", exclude_ranges);
|
||||
|
||||
module_ranges = collect_module_ranges();
|
||||
libs_ranges = collect_libs_ranges();
|
||||
include_ranges = collect_ranges("AFL_FRIDA_INST_RANGES");
|
||||
jit_ranges = collect_jit_ranges();
|
||||
|
||||
/* If include ranges is empty, then assume everything is included */
|
||||
if (include_ranges->len == 0) {
|
||||
@ -535,8 +600,6 @@ void ranges_init(void) {
|
||||
|
||||
}
|
||||
|
||||
exclude_ranges = collect_ranges("AFL_FRIDA_EXCLUDE_RANGES");
|
||||
|
||||
/* Intersect with .text section of main executable unless AFL_INST_LIBS */
|
||||
step1 = intersect_ranges(module_ranges, libs_ranges);
|
||||
print_ranges("step1", step1);
|
||||
@ -549,25 +612,25 @@ void ranges_init(void) {
|
||||
step3 = subtract_ranges(step2, exclude_ranges);
|
||||
print_ranges("step3", step3);
|
||||
|
||||
/*
|
||||
* After step3, we have the total ranges to be instrumented, we now subtract
|
||||
* that from the original ranges of the modules to configure stalker.
|
||||
*/
|
||||
|
||||
step4 = subtract_ranges(module_ranges, step3);
|
||||
step4 = subtract_ranges(step3, jit_ranges);
|
||||
print_ranges("step4", step4);
|
||||
|
||||
ranges = merge_ranges(step4);
|
||||
/*
|
||||
* After step4, we have the total ranges to be instrumented, we now subtract
|
||||
* that from the original ranges of the modules to configure stalker.
|
||||
*/
|
||||
step5 = subtract_ranges(module_ranges, step4);
|
||||
print_ranges("step5", step5);
|
||||
|
||||
ranges = merge_ranges(step5);
|
||||
print_ranges("final", ranges);
|
||||
|
||||
g_array_free(step5, TRUE);
|
||||
g_array_free(step4, TRUE);
|
||||
g_array_free(step3, TRUE);
|
||||
g_array_free(step2, TRUE);
|
||||
g_array_free(step1, TRUE);
|
||||
|
||||
/* *NEVER* stalk the stalker, only bad things will ever come of this! */
|
||||
ranges_exclude_self();
|
||||
|
||||
ranges_exclude();
|
||||
|
||||
}
|
||||
|
@ -2,17 +2,46 @@
|
||||
|
||||
#include "instrument.h"
|
||||
#include "stalker.h"
|
||||
#include "util.h"
|
||||
|
||||
static GumStalker *stalker = NULL;
|
||||
|
||||
void stalker_init(void) {
|
||||
void stalker_config(void) {
|
||||
|
||||
if (!gum_stalker_is_supported()) { FATAL("Failed to initialize embedded"); }
|
||||
|
||||
}
|
||||
|
||||
static gboolean stalker_exclude_self(const GumRangeDetails *details,
|
||||
gpointer user_data) {
|
||||
|
||||
UNUSED_PARAMETER(user_data);
|
||||
gchar * name;
|
||||
gboolean found;
|
||||
GumStalker *stalker;
|
||||
if (details->file == NULL) { return TRUE; }
|
||||
name = g_path_get_basename(details->file->path);
|
||||
|
||||
found = (g_strcmp0(name, "afl-frida-trace.so") == 0);
|
||||
g_free(name);
|
||||
if (!found) { return TRUE; }
|
||||
|
||||
stalker = stalker_get();
|
||||
gum_stalker_exclude(stalker, details->range);
|
||||
|
||||
return FALSE;
|
||||
|
||||
}
|
||||
|
||||
void stalker_init(void) {
|
||||
|
||||
stalker = gum_stalker_new();
|
||||
if (stalker == NULL) { FATAL("Failed to initialize stalker"); }
|
||||
|
||||
gum_stalker_set_trust_threshold(stalker, 0);
|
||||
gum_stalker_set_trust_threshold(stalker, -1);
|
||||
|
||||
/* *NEVER* stalk the stalker, only bad things will ever come of this! */
|
||||
gum_process_enumerate_ranges(GUM_PAGE_EXECUTE, stalker_exclude_self, NULL);
|
||||
|
||||
}
|
||||
|
||||
@ -30,3 +59,9 @@ void stalker_start(void) {
|
||||
|
||||
}
|
||||
|
||||
void stalker_trust(void) {
|
||||
|
||||
gum_stalker_set_trust_threshold(stalker, 0);
|
||||
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include <sys/shm.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
@ -17,15 +17,16 @@
|
||||
|
||||
stats_data_header_t *stats_data = NULL;
|
||||
|
||||
static int stats_parent_pid = -1;
|
||||
static int stats_fd = -1;
|
||||
static gboolean stats_transitions = FALSE;
|
||||
static guint64 stats_interval = 0;
|
||||
static int stats_parent_pid = -1;
|
||||
static int stats_fd = -1;
|
||||
|
||||
void stats_init(void) {
|
||||
char * stats_filename = NULL;
|
||||
guint64 stats_interval = 0;
|
||||
gboolean stats_transitions = FALSE;
|
||||
|
||||
stats_parent_pid = getpid();
|
||||
char *filename = getenv("AFL_FRIDA_STATS_FILE");
|
||||
void stats_config(void) {
|
||||
|
||||
stats_filename = getenv("AFL_FRIDA_STATS_FILE");
|
||||
stats_interval = util_read_num("AFL_FRIDA_STATS_INTERVAL");
|
||||
if (getenv("AFL_FRIDA_STATS_TRANSITIONS") != NULL) {
|
||||
|
||||
@ -33,10 +34,16 @@ void stats_init(void) {
|
||||
|
||||
}
|
||||
|
||||
OKF("Stats - file [%s]", filename);
|
||||
}
|
||||
|
||||
void stats_init(void) {
|
||||
|
||||
stats_parent_pid = getpid();
|
||||
|
||||
OKF("Stats - file [%s]", stats_filename);
|
||||
OKF("Stats - interval [%" G_GINT64_MODIFIER "u]", stats_interval);
|
||||
|
||||
if (stats_interval != 0 && filename == NULL) {
|
||||
if (stats_interval != 0 && stats_filename == NULL) {
|
||||
|
||||
FATAL(
|
||||
"AFL_FRIDA_STATS_FILE must be specified if "
|
||||
@ -46,7 +53,7 @@ void stats_init(void) {
|
||||
|
||||
if (stats_interval == 0) { stats_interval = 10; }
|
||||
|
||||
if (filename == NULL) { return; }
|
||||
if (stats_filename == NULL) { return; }
|
||||
|
||||
if (!stats_is_supported_arch()) {
|
||||
|
||||
@ -56,11 +63,11 @@ void stats_init(void) {
|
||||
|
||||
char *path = NULL;
|
||||
|
||||
if (filename == NULL) { return; }
|
||||
if (stats_filename == NULL) { return; }
|
||||
|
||||
if (stats_transitions) { gum_stalker_set_counters_enabled(TRUE); }
|
||||
|
||||
path = g_canonicalize_filename(filename, g_get_current_dir());
|
||||
path = g_canonicalize_filename(stats_filename, g_get_current_dir());
|
||||
|
||||
OKF("Stats - path [%s]", path);
|
||||
|
||||
@ -96,7 +103,6 @@ void stats_init(void) {
|
||||
void stats_vprint(int fd, char *format, va_list ap) {
|
||||
|
||||
char buffer[4096] = {0};
|
||||
int ret;
|
||||
int len;
|
||||
|
||||
if (vsnprintf(buffer, sizeof(buffer) - 1, format, ap) < 0) { return; }
|
||||
@ -172,10 +178,12 @@ void stats_write(void) {
|
||||
|
||||
}
|
||||
|
||||
static void stats_maybe_write(void) {
|
||||
void stats_on_fork(void) {
|
||||
|
||||
guint64 current_time;
|
||||
|
||||
if (stats_filename == NULL) { return; }
|
||||
|
||||
if (stats_interval == 0) { return; }
|
||||
|
||||
current_time = g_get_monotonic_time();
|
||||
@ -202,7 +210,5 @@ void stats_collect(const cs_insn *instr, gboolean begin) {
|
||||
|
||||
stats_collect_arch(instr);
|
||||
|
||||
stats_maybe_write();
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
@ -31,6 +31,9 @@ typedef struct {
|
||||
|
||||
guint64 num_rip_relative;
|
||||
|
||||
guint64 num_rip_relative_type[X86_INS_ENDING];
|
||||
char name_rip_relative_type[X86_INS_ENDING][CS_MNEMONIC_SIZE];
|
||||
|
||||
} stats_data_arch_t;
|
||||
|
||||
gboolean stats_is_supported_arch(void) {
|
||||
@ -136,6 +139,18 @@ void stats_write_arch(void) {
|
||||
stats_data_arch->num_rip_relative,
|
||||
(stats_data_arch->num_rip_relative * 100 / num_instructions));
|
||||
|
||||
for (size_t i = 0; i < X86_INS_ENDING; i++) {
|
||||
|
||||
if (stats_data_arch->num_rip_relative_type[i] != 0) {
|
||||
|
||||
stats_print(" %10d %s\n",
|
||||
stats_data_arch->num_rip_relative_type[i],
|
||||
stats_data_arch->name_rip_relative_type[i]);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
stats_print("\n");
|
||||
stats_print("\n");
|
||||
|
||||
@ -256,6 +271,9 @@ static void stats_collect_rip_relative_arch(const cs_insn *instr) {
|
||||
if (rm != 5) { return; }
|
||||
|
||||
stats_data_arch->num_rip_relative++;
|
||||
stats_data_arch->num_rip_relative_type[instr->id]++;
|
||||
memcpy(stats_data_arch->name_rip_relative_type[instr->id], instr->mnemonic,
|
||||
CS_MNEMONIC_SIZE);
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "frida-gum.h"
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
|
@ -13,7 +13,7 @@ CMP_LOG_INPUT:=$(TEST_DATA_DIR)in
|
||||
QEMU_OUT:=$(BUILD_DIR)qemu-out
|
||||
FRIDA_OUT:=$(BUILD_DIR)frida-out
|
||||
|
||||
.PHONY: all 32 clean qemu frida format
|
||||
.PHONY: all 32 clean qemu frida frida-nocmplog format
|
||||
|
||||
all: $(TEST_CMPLOG_OBJ)
|
||||
make -C $(ROOT)frida_mode/
|
||||
@ -55,6 +55,15 @@ frida: $(TEST_CMPLOG_OBJ) $(CMP_LOG_INPUT)
|
||||
-- \
|
||||
$(TEST_CMPLOG_OBJ) @@
|
||||
|
||||
frida-nocmplog: $(TEST_CMPLOG_OBJ) $(CMP_LOG_INPUT)
|
||||
$(ROOT)afl-fuzz \
|
||||
-O \
|
||||
-i $(TEST_DATA_DIR) \
|
||||
-o $(FRIDA_OUT) \
|
||||
-Z \
|
||||
-- \
|
||||
$(TEST_CMPLOG_OBJ) @@
|
||||
|
||||
debug: $(TEST_CMPLOG_OBJ) $(CMP_LOG_INPUT)
|
||||
gdb \
|
||||
--ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \
|
||||
|
@ -15,6 +15,10 @@ qemu:
|
||||
frida:
|
||||
@gmake frida
|
||||
|
||||
|
||||
frida-nocmplog:
|
||||
@gmake frida-nocmplog
|
||||
|
||||
format:
|
||||
@gmake format
|
||||
|
||||
|
@ -53,7 +53,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
}
|
||||
|
||||
#if defined(__x86_64__)
|
||||
#if defined(__x86_64__) || defined(__aarch64__)
|
||||
uint64_t x = 0;
|
||||
fread(&x, sizeof(x), 1, file);
|
||||
if (x != 0xCAFEBABECAFEBABE) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user