mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-21 05:42:12 +00:00
Merge branch 'dev' into going_atomic
This commit is contained in:
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: AFLplusplusEU
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
||||
- name: update
|
||||
run: sudo apt-get update && sudo apt-get upgrade -y
|
||||
- name: install packages
|
||||
run: sudo apt-get install -y -m -f --install-suggests build-essential git libtool libtool-bin automake bison libglib2.0-0 clang llvm-dev libc++-dev findutils libcmocka-dev python3-dev python3-setuptools
|
||||
run: sudo apt-get install -y -m -f --install-suggests build-essential git libtool libtool-bin automake bison libglib2.0-0 clang llvm-dev libc++-dev findutils libcmocka-dev python3-dev python3-setuptools ninja-build
|
||||
- name: compiler installed
|
||||
run: gcc -v ; echo ; clang -v
|
||||
- name: install gcc plugin
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -85,3 +85,4 @@ gmon.out
|
||||
afl-frida-trace.so
|
||||
utils/afl_network_proxy/afl-network-client
|
||||
utils/afl_network_proxy/afl-network-server
|
||||
*.o.tmp
|
||||
|
@ -50,6 +50,7 @@ RUN update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 0
|
||||
|
||||
ENV LLVM_CONFIG=llvm-config-12
|
||||
ENV AFL_SKIP_CPUFREQ=1
|
||||
ENV AFL_TRY_AFFINITY=1
|
||||
ENV AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1
|
||||
|
||||
RUN git clone --depth=1 https://github.com/vanhauser-thc/afl-cov /afl-cov
|
||||
@ -61,8 +62,10 @@ WORKDIR /AFLplusplus
|
||||
RUN export CC=gcc-10 && export CXX=g++-10 && make clean && \
|
||||
make distrib && make install && make clean
|
||||
|
||||
RUN echo 'alias joe="jupp --wordwrap"' >> ~/.bashrc
|
||||
RUN echo 'export PS1="[afl++]$PS1"' >> ~/.bashrc
|
||||
RUN sh -c 'echo set encoding=utf-8 > /root/.vimrc'
|
||||
RUN echo '. /etc/bash_completion' >> ~/.bashrc
|
||||
RUN echo 'alias joe="joe --wordwrap --joe_state -nobackup"' >> ~/.bashrc
|
||||
RUN echo "export PS1='"'[afl++ \h] \w$(__git_ps1) \$ '"'" >> ~/.bashrc
|
||||
ENV IS_DOCKER="1"
|
||||
|
||||
# Disabled until we have the container ready
|
||||
|
10
GNUmakefile
10
GNUmakefile
@ -503,21 +503,21 @@ code-format:
|
||||
./.custom-format.py -i instrumentation/*.h
|
||||
./.custom-format.py -i instrumentation/*.cc
|
||||
./.custom-format.py -i instrumentation/*.c
|
||||
./.custom-format.py -i *.h
|
||||
./.custom-format.py -i *.c
|
||||
@#./.custom-format.py -i custom_mutators/*/*.c* # destroys libfuzzer :-(
|
||||
@#./.custom-format.py -i custom_mutators/*/*.h # destroys honggfuzz :-(
|
||||
./.custom-format.py -i utils/*/*.c*
|
||||
./.custom-format.py -i utils/*/*.h
|
||||
./.custom-format.py -i test/*.c
|
||||
./.custom-format.py -i frida_mode/src/*.c
|
||||
./.custom-format.py -i frida_mode/include/*.h
|
||||
-./.custom-format.py -i frida_mode/src/*/*.c
|
||||
./.custom-format.py -i qemu_mode/libcompcov/*.c
|
||||
./.custom-format.py -i qemu_mode/libcompcov/*.cc
|
||||
./.custom-format.py -i qemu_mode/libcompcov/*.h
|
||||
./.custom-format.py -i qemu_mode/libqasan/*.c
|
||||
./.custom-format.py -i qemu_mode/libqasan/*.h
|
||||
./.custom-format.py -i frida_mode/src/*.c
|
||||
./.custom-format.py -i frida_mode/include/*.h
|
||||
-./.custom-format.py -i frida_mode/src/*/*.c
|
||||
./.custom-format.py -i *.h
|
||||
./.custom-format.py -i *.c
|
||||
|
||||
|
||||
.PHONY: test_build
|
||||
|
@ -36,7 +36,7 @@ ifeq "$(SYS)" "OpenBSD"
|
||||
LLVM_CONFIG ?= $(BIN_PATH)/llvm-config
|
||||
HAS_OPT = $(shell test -x $(BIN_PATH)/opt && echo 0 || echo 1)
|
||||
ifeq "$(HAS_OPT)" "1"
|
||||
$(warning llvm_mode needs a complete llvm installation (versions 3.4 up to 12) -> e.g. "pkg_add llvm-7.0.1p9")
|
||||
$(warning llvm_mode needs a complete llvm installation (versions 6.0 up to 12) -> e.g. "pkg_add llvm-7.0.1p9")
|
||||
endif
|
||||
else
|
||||
LLVM_CONFIG ?= llvm-config
|
||||
@ -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 '^3\.[0-3]|^[0-2]\.' && echo 1 || echo 0 )
|
||||
LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^[0-5]\.' && 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 )
|
||||
@ -61,7 +61,7 @@ ifeq "$(LLVMVER)" ""
|
||||
endif
|
||||
|
||||
ifeq "$(LLVM_UNSUPPORTED)" "1"
|
||||
$(error llvm_mode only supports llvm from version 3.4 onwards)
|
||||
$(error llvm_mode only supports llvm from version 6.0 onwards)
|
||||
endif
|
||||
|
||||
ifeq "$(LLVM_TOO_NEW)" "1"
|
||||
@ -346,7 +346,7 @@ no_build:
|
||||
test_deps:
|
||||
@echo "[*] Checking for working 'llvm-config'..."
|
||||
ifneq "$(LLVM_APPLE_XCODE)" "1"
|
||||
@type $(LLVM_CONFIG) >/dev/null 2>&1 || ( echo "[-] Oops, can't find 'llvm-config'. Install clang or set \$$LLVM_CONFIG or \$$PATH beforehand."; echo " (Sometimes, the binary will be named llvm-config-3.5 or something like that.)"; exit 1 )
|
||||
@type $(LLVM_CONFIG) >/dev/null 2>&1 || ( echo "[-] Oops, can't find 'llvm-config'. Install clang or set \$$LLVM_CONFIG or \$$PATH beforehand."; echo " (Sometimes, the binary will be named llvm-config-11 or something like that.)"; exit 1 )
|
||||
endif
|
||||
@echo "[*] Checking for working '$(CC)'..."
|
||||
@type $(CC) >/dev/null 2>&1 || ( echo "[-] Oops, can't find '$(CC)'. Make sure that it's in your \$$PATH (or set \$$CC and \$$CXX)."; exit 1 )
|
||||
|
48
README.md
48
README.md
@ -84,22 +84,23 @@ behaviours and defaults:
|
||||
|
||||
## Important features of afl++
|
||||
|
||||
afl++ supports llvm up to version 12, very fast binary fuzzing with QEMU 5.1
|
||||
afl++ supports llvm from 6.0 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 |
|
||||
| -------------------------|:-------:|:---------:|:----------:|:----------:|:----------------:|:------------:|
|
||||
| NeverZero | x86[_64]| x(1) | x | | x | x |
|
||||
| Persistent Mode | | x | x | | x86[_64]/arm[64] | x |
|
||||
| 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]/arm[64] | |
|
||||
| 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 |
|
||||
|
||||
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
|
||||
@ -254,6 +255,7 @@ Here are some good writeups to show how to effectively use AFL++:
|
||||
If you are interested in fuzzing structured data (where you define what the
|
||||
structure is), these links have you covered:
|
||||
* Superion for afl++: [https://github.com/adrian-rt/superion-mutator](https://github.com/adrian-rt/superion-mutator)
|
||||
* libprotobuf for afl++: [https://github.com/P1umer/AFLplusplus-protobuf-mutator](https://github.com/P1umer/AFLplusplus-protobuf-mutator)
|
||||
* libprotobuf raw: [https://github.com/bruce30262/libprotobuf-mutator_fuzzing_learning/tree/master/4_libprotobuf_aflpp_custom_mutator](https://github.com/bruce30262/libprotobuf-mutator_fuzzing_learning/tree/master/4_libprotobuf_aflpp_custom_mutator)
|
||||
* libprotobuf for old afl++ API: [https://github.com/thebabush/afl-libprotobuf-mutator](https://github.com/thebabush/afl-libprotobuf-mutator)
|
||||
|
||||
@ -293,7 +295,7 @@ anything below 9 is not recommended.
|
||||
|
|
||||
v
|
||||
+---------------------------------+
|
||||
| clang/clang++ 3.3+ is available | --> use LLVM mode (afl-clang-fast/afl-clang-fast++)
|
||||
| clang/clang++ 6.0+ 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/++
|
||||
@ -435,7 +437,7 @@ described in [instrumentation/README.lto.md](instrumentation/README.lto.md).
|
||||
##### cmake
|
||||
|
||||
For `cmake` build systems this is usually done by:
|
||||
`mkdir build; cmake -DCMAKE_C_COMPILERC=afl-cc -DCMAKE_CXX_COMPILER=afl-c++ ..`
|
||||
`mkdir build; cd build; cmake -DCMAKE_C_COMPILER=afl-cc -DCMAKE_CXX_COMPILER=afl-c++ ..`
|
||||
|
||||
Note that if you are using the (better) afl-clang-lto compiler you also have to
|
||||
set AR to llvm-ar[-VERSION] and RANLIB to llvm-ranlib[-VERSION] - as is
|
||||
@ -677,8 +679,8 @@ If you see that an important area or a feature has not been covered so far then
|
||||
try to find an input that is able to reach that and start a new secondary in
|
||||
that fuzzing campaign with that seed as input, let it run for a few minutes,
|
||||
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` if you have no
|
||||
free core.
|
||||
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.
|
||||
@ -789,16 +791,19 @@ How this can look like can e.g. be seen at afl++'s setup in Google's [oss-fuzz](
|
||||
When source code is *NOT* available, afl++ offers various support for fast,
|
||||
on-the-fly instrumentation of black-box binaries.
|
||||
|
||||
If you do not have to use Unicorn the following setup is recommended:
|
||||
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`)
|
||||
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, zipr, fibre, etc.
|
||||
use a binary rewriter like afl-dyninst, retrowrite, zaflr, etc.
|
||||
|
||||
For Qemu mode, check out the persistent mode and snapshot features, they give
|
||||
a huge speed improvement!
|
||||
For Qemu and Frida mode, check out the persistent mode, it gives a huge speed
|
||||
improvement if it is possible to use.
|
||||
|
||||
### QEMU
|
||||
|
||||
@ -810,18 +815,31 @@ feature by doing:
|
||||
cd qemu_mode
|
||||
./build_qemu_support.sh
|
||||
```
|
||||
For additional instructions and caveats, see [qemu_mode/README.md](qemu_mode/README.md) -
|
||||
check out the snapshot feature! :-)
|
||||
For additional instructions and caveats, see [qemu_mode/README.md](qemu_mode/README.md).
|
||||
If possible you should use the persistent mode, see [qemu_mode/README.persistent.md](qemu_mode/README.persistent.md).
|
||||
The mode is approximately 2-5x slower than compile-time instrumentation, and is
|
||||
less conducive to parallelization.
|
||||
|
||||
If [afl-dyninst](https://github.com/vanhauser-thc/afl-dyninst) works for
|
||||
your binary, then you can use afl-fuzz normally and it will have twice
|
||||
the speed compared to qemu_mode (but slower than persistent mode).
|
||||
the speed compared to qemu_mode (but slower than qemu persistent mode).
|
||||
Note that several other binary rewriters exist, all with their advantages and
|
||||
caveats.
|
||||
|
||||
### Frida
|
||||
|
||||
Frida mode is sometimes faster and sometimes slower than Qemu mode.
|
||||
It is also newer, lacks COMPCOV, but supports MacOS.
|
||||
|
||||
```shell
|
||||
cd frida_mode
|
||||
make
|
||||
```
|
||||
For additional instructions and caveats, see [frida_mode/README.md](frida_mode/README.md).
|
||||
If possible you should use the persistent mode, see [qemu_frida/README.persistent.md](qemu_frida/README.persistent.md).
|
||||
The mode is approximately 2-5x slower than compile-time instrumentation, and is
|
||||
less conducive to parallelization.
|
||||
|
||||
### Unicorn
|
||||
|
||||
For non-Linux binaries you can use afl++'s unicorn mode which can emulate
|
||||
|
2
TODO.md
2
TODO.md
@ -4,7 +4,7 @@
|
||||
|
||||
- align map to 64 bytes but keep real IDs
|
||||
- Update afl->pending_not_fuzzed for MOpt
|
||||
- CPU affinity for many cores? There seems to be an issue > 96 cores
|
||||
- put fuzz target in top line of UI
|
||||
- afl-plot to support multiple plot_data
|
||||
- afl_custom_fuzz_splice_optin()
|
||||
- afl_custom_splice()
|
||||
|
10
afl-cmin
10
afl-cmin
@ -119,11 +119,13 @@ function usage() {
|
||||
"Environment variables used:\n" \
|
||||
"AFL_ALLOW_TMP: allow unsafe use of input/output directories under {/var}/tmp\n" \
|
||||
"AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\n" \
|
||||
"AFL_FORKSRV_INIT_TMOUT: time the fuzzer waits for the target to come up, initially\n" \
|
||||
"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 ID delivered to child processes on timeout, etc. (default: SIGKILL)\n"
|
||||
"AFL_PATH: path for the afl-showmap binary if not found anywhere else\n" \
|
||||
"AFL_SKIP_BIN_CHECK: skip check for target binary\n"
|
||||
"AFL_KILL_SIGNAL: Signal delivered to child processes on timeout (default: SIGKILL)\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" \
|
||||
"AFL_SKIP_BIN_CHECK: skip afl instrumentation checks for target binary\n"
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
12
afl-plot
12
afl-plot
@ -111,9 +111,9 @@ set terminal png truecolor enhanced size 1000,300 butt
|
||||
|
||||
set output '$outputdir/high_freq.png'
|
||||
|
||||
set xdata time
|
||||
set timefmt '%s'
|
||||
set format x "%b %d\n%H:%M"
|
||||
#set xdata time
|
||||
#set timefmt '%s'
|
||||
#set format x "%b %d\n%H:%M"
|
||||
set tics font 'small'
|
||||
unset mxtics
|
||||
unset mytics
|
||||
@ -127,9 +127,8 @@ set key outside
|
||||
set autoscale xfixmin
|
||||
set autoscale xfixmax
|
||||
|
||||
set xlabel "all times in UTC" font "small"
|
||||
#set xlabel "all times in UTC" font "small"
|
||||
|
||||
set ytics auto
|
||||
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, \\
|
||||
'' using 1:5 with lines title 'pending paths' linecolor rgb '#0090ff' linewidth 3, \\
|
||||
@ -139,7 +138,6 @@ plot '$inputdir/plot_data' using 1:4 with filledcurve x1 title 'total paths' lin
|
||||
set terminal png truecolor enhanced size 1000,200 butt
|
||||
set output '$outputdir/low_freq.png'
|
||||
|
||||
set ytics 1
|
||||
plot '$inputdir/plot_data' using 1:8 with filledcurve x1 title '' linecolor rgb '#c00080' fillstyle transparent solid 0.2 noborder, \\
|
||||
'' using 1:8 with lines title ' uniq crashes' linecolor rgb '#c00080' linewidth 3, \\
|
||||
'' using 1:9 with lines title 'uniq hangs' linecolor rgb '#c000f0' linewidth 3, \\
|
||||
@ -148,14 +146,12 @@ plot '$inputdir/plot_data' using 1:8 with filledcurve x1 title '' linecolor rgb
|
||||
set terminal png truecolor enhanced size 1000,200 butt
|
||||
set output '$outputdir/exec_speed.png'
|
||||
|
||||
set ytics auto
|
||||
plot '$inputdir/plot_data' using 1:11 with filledcurve x1 title '' linecolor rgb '#0090ff' fillstyle transparent solid 0.2 noborder, \\
|
||||
'$inputdir/plot_data' using 1:11 with lines title ' execs/sec' linecolor rgb '#0090ff' linewidth 3 smooth bezier;
|
||||
|
||||
set terminal png truecolor enhanced size 1000,300 butt
|
||||
set output '$outputdir/edges.png'
|
||||
|
||||
set ytics auto
|
||||
plot '$inputdir/plot_data' using 1:13 with lines title ' edges' linecolor rgb '#0090ff' linewidth 3
|
||||
|
||||
_EOF_
|
||||
|
@ -7,7 +7,7 @@ test "$1" = "-h" -o "$1" = "-hh" && {
|
||||
echo afl-system-config has no command line options
|
||||
echo
|
||||
echo afl-system reconfigures the system to a high performance fuzzing state
|
||||
echo WARNING: this reduces the security of the system
|
||||
echo "WARNING: this reduces the security of the system!"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
@ -15,14 +15,20 @@ test "$1" = "-h" -o "$1" = "-hh" && {
|
||||
DONE=
|
||||
PLATFORM=`uname -s`
|
||||
echo This reconfigures the system to have a better fuzzing performance.
|
||||
echo "WARNING: this reduces the security of the system!"
|
||||
echo
|
||||
if [ '!' "$EUID" = 0 ] && [ '!' `id -u` = 0 ] ; then
|
||||
echo "Warning: you need to be root to run this!"
|
||||
# we do not exit as other mechanisms exist that allows to do this than
|
||||
# being root. let the errors speak for themselves.
|
||||
fi
|
||||
sleep 1
|
||||
if [ "$PLATFORM" = "Linux" ] ; then
|
||||
{
|
||||
sysctl -w kernel.core_pattern=core
|
||||
sysctl -w kernel.core_uses_pid=0
|
||||
# Arch Linux requires core_pattern to be empty :(
|
||||
test -e /etc/arch-release && sysctl -w kernel.core_pattern=
|
||||
test -e /etc/arch-release || sysctl -w kernel.core_pattern=core
|
||||
sysctl -w kernel.randomize_va_space=0
|
||||
sysctl -w kernel.sched_child_runs_first=1
|
||||
sysctl -w kernel.sched_autogroup_enabled=1
|
||||
@ -35,12 +41,17 @@ if [ "$PLATFORM" = "Linux" ] ; then
|
||||
test -e /sys/devices/system/cpu/intel_pstate/no_turbo && echo 0 > /sys/devices/system/cpu/intel_pstate/no_turbo
|
||||
test -e /sys/devices/system/cpu/cpufreq/boost && echo 1 > /sys/devices/system/cpu/cpufreq/boost
|
||||
test -e /sys/devices/system/cpu/intel_pstate/max_perf_pct && echo 100 > /sys/devices/system/cpu/intel_pstate/max_perf_pct
|
||||
test -n "$(which auditctl)" && auditctl -a never,task >/dev/null 2>&1
|
||||
} > /dev/null
|
||||
echo Settings applied.
|
||||
echo
|
||||
dmesg | egrep -q 'nospectre_v2|spectre_v2=off' || {
|
||||
echo It is recommended to boot the kernel with lots of security off - if you are running a machine that is in a secured network - so set this:
|
||||
echo ' /etc/default/grub:GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=0 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 srbds=off noexec=off noexec32=off tsx=on tsx_async_abort=off arm64.nopauth audit=0 hardened_usercopy=off ssbd=force-off"'
|
||||
echo
|
||||
}
|
||||
echo If you run fuzzing instances in docker, run them with \"--security-opt seccomp=unconfined\" for more speed
|
||||
echo
|
||||
DONE=1
|
||||
fi
|
||||
if [ "$PLATFORM" = "FreeBSD" ] ; then
|
||||
@ -49,6 +60,7 @@ if [ "$PLATFORM" = "FreeBSD" ] ; then
|
||||
sysctl kern.elf64.aslr.enable=0
|
||||
} > /dev/null
|
||||
echo Settings applied.
|
||||
echo
|
||||
cat <<EOF
|
||||
In order to suppress core file generation during fuzzing it is recommended to set
|
||||
me:\\
|
||||
@ -58,11 +70,12 @@ EOF
|
||||
echo It is recommended to boot the kernel with lots of security off - if you are running a machine that is in a secured network - so set this:
|
||||
echo ' sysctl hw.ibrs_disable=1'
|
||||
echo 'Setting kern.pmap.pg_ps_enabled=0 into /boot/loader.conf might be helpful too.'
|
||||
echo
|
||||
DONE=1
|
||||
fi
|
||||
if [ "$PLATFORM" = "OpenBSD" ] ; then
|
||||
echo
|
||||
echo 'System security features cannot be disabled on OpenBSD.'
|
||||
echo
|
||||
DONE=1
|
||||
fi
|
||||
if [ "$PLATFORM" = "DragonFly" ] ; then
|
||||
@ -74,27 +87,33 @@ me:\\
|
||||
:coredumpsize=0:
|
||||
in the ~/.login_conf file for the user used for fuzzing.
|
||||
EOF
|
||||
echo
|
||||
DONE=1
|
||||
fi
|
||||
if [ "$PLATFORM" = "NetBSD" ] ; then
|
||||
{
|
||||
#echo It is recommended to enable unprivileged users to set cpu affinity
|
||||
#echo to be able to use afl-gotcpu meaningfully.
|
||||
/sbin/sysctl -w security.models.extensions.user_set_cpu_affinity=1
|
||||
} > /dev/null
|
||||
echo Settings applied.
|
||||
echo
|
||||
DONE=1
|
||||
fi
|
||||
if [ "$PLATFORM" = "Darwin" ] ; then
|
||||
if [ $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') ] ; then
|
||||
echo We unload the default crash reporter here
|
||||
SL=/System/Library; PL=com.apple.ReportCrash
|
||||
launchctl unload -w ${SL}/LaunchAgents/${PL}.plist
|
||||
sudo launchctl unload -w ${SL}/LaunchDaemons/${PL}.Root.plist
|
||||
sysctl kern.sysv.shmmax=8388608
|
||||
sysctl kern.sysv.shmseg=48
|
||||
sysctl kern.sysv.shmall=98304
|
||||
echo Settings applied.
|
||||
else
|
||||
echo Nothing to do.
|
||||
echo
|
||||
if [ $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') ] ; then
|
||||
echo
|
||||
echo Unloading the default crash reporter
|
||||
SL=/System/Library; PL=com.apple.ReportCrash
|
||||
launchctl unload -w ${SL}/LaunchAgents/${PL}.plist >/dev/null 2>&1
|
||||
sudo launchctl unload -w ${SL}/LaunchDaemons/${PL}.Root.plist >/dev/null 2>&1
|
||||
echo
|
||||
fi
|
||||
echo It is recommended to disable System Integration Protection for increased performance.
|
||||
echo
|
||||
DONE=1
|
||||
fi
|
||||
if [ "$PLATFORM" = "Haiku" ] ; then
|
||||
@ -104,7 +123,7 @@ if [ "$PLATFORM" = "Haiku" ] ; then
|
||||
[ -r ${SETTINGS} ] && grep -qE "default_action\s+kill" ${SETTINGS} && { echo "Nothing to do"; } || { \
|
||||
echo We change the debug_server default_action from user to silently kill; \
|
||||
[ ! -r ${SETTINGS} ] && echo "default_action kill" >${SETTINGS} || { mv ${SETTINGS} s.tmp; sed -e "s/default_action\s\s*user/default_action kill/" s.tmp > ${SETTINGS}; rm s.tmp; }; \
|
||||
echo Settings applied.; \
|
||||
echo Settings applied.; echo; \
|
||||
}
|
||||
DONE=1
|
||||
fi
|
||||
|
13
afl-whatsup
13
afl-whatsup
@ -21,11 +21,11 @@
|
||||
echo "$0 status check tool for afl-fuzz by Michal Zalewski"
|
||||
echo
|
||||
test "$1" = "-h" -o "$1" = "-hh" && {
|
||||
echo "$0 [-s] [-d] output_directory"
|
||||
echo "Usage: $0 [-s] [-d] afl_output_directory"
|
||||
echo
|
||||
echo Options:
|
||||
echo -s - skip details and output summary results only
|
||||
echo -d - include dead fuzzer stats
|
||||
echo " -s - skip details and output summary results only"
|
||||
echo " -d - include dead fuzzer stats"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
@ -51,10 +51,11 @@ DIR="$1"
|
||||
|
||||
if [ "$DIR" = "" ]; then
|
||||
|
||||
echo "Usage: $0 [-s] [-d] afl_sync_dir" 1>&2
|
||||
echo "Usage: $0 [-s] [-d] afl_output_directory" 1>&2
|
||||
echo 1>&2
|
||||
echo "The -s option causes the tool to skip all the per-fuzzer trivia and show" 1>&2
|
||||
echo "just the summary results. See docs/parallel_fuzzing.md for additional tips." 1>&2
|
||||
echo Options: 1>&2
|
||||
echo " -s - skip details and output summary results only" 1>&2
|
||||
echo " -d - include dead fuzzer stats" 1>&2
|
||||
echo 1>&2
|
||||
exit 1
|
||||
|
||||
|
@ -3,6 +3,14 @@
|
||||
Custom mutators enhance and alter the mutation strategies of afl++.
|
||||
For further information and documentation on how to write your own, read [the docs](../docs/custom_mutators.md).
|
||||
|
||||
## Examples
|
||||
|
||||
The `./examples` folder contains examples for custom mutators in python and C.
|
||||
|
||||
## Rust
|
||||
|
||||
In `./rust`, you will find rust bindings, including a simple example in `./rust/example` and an example for structured fuzzing, based on lain, in`./rust/example_lain`.
|
||||
|
||||
## The afl++ Grammar Mutator
|
||||
|
||||
If you use git to clone afl++, then the following will incorporate our
|
||||
@ -46,3 +54,6 @@ https://github.com/bruce30262/libprotobuf-mutator_fuzzing_learning/tree/master/4
|
||||
has a transform function you need to fill for your protobuf format, however
|
||||
needs to be ported to the updated afl++ custom mutator API (not much work):
|
||||
https://github.com/thebabush/afl-libprotobuf-mutator
|
||||
|
||||
same as above but is for current afl++:
|
||||
https://github.com/P1umer/AFLplusplus-protobuf-mutator
|
||||
|
@ -1 +1 @@
|
||||
a2d4e4a
|
||||
b79d51a
|
||||
|
Submodule custom_mutators/grammar_mutator/grammar_mutator updated: a2d4e4ab96...b79d51a8da
@ -9,7 +9,9 @@ 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.13a (development)
|
||||
- Note: plot_data switched to relative time from unix time in 3.10
|
||||
- frida_mode - new mode that uses frida to fuzz binary-only targets,
|
||||
it currently supports persistent mode and cmplog.
|
||||
thanks to @WorksButNotTested!
|
||||
- create a fuzzing dictionary with the help of CodeQL thanks to
|
||||
@microsvuln! see utils/autodict_ql
|
||||
@ -19,6 +21,7 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
|
||||
- add recording of previous fuzz attempts for persistent mode
|
||||
to allow replay of non-reproducable crashes, see
|
||||
AFL_PERSISTENT_RECORD in config.h and docs/envs.h
|
||||
- fixed a bug when trimming for stdin targets
|
||||
- default cmplog level (-l) is now 2, better efficiency.
|
||||
- cmplog level 3 (-l 3) now performs redqueen on everything.
|
||||
use with care.
|
||||
@ -26,16 +29,39 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
|
||||
- ensure one fuzzer sync per cycle
|
||||
- fix afl_custom_queue_new_entry original file name when syncing
|
||||
from fuzzers
|
||||
- on a crashing seed potentially the wrong input was disabled
|
||||
- added AFL_EXIT_ON_SEED_ISSUES env that will exit if a seed in
|
||||
-i dir crashes the target or results in a timeout. By default
|
||||
afl++ ignores these and uses them for splicing instead.
|
||||
- added AFL_EXIT_ON_TIME env that will make afl-fuzz exit fuzzing
|
||||
after no new paths have been found for n seconds
|
||||
- when AFL_FAST_CAL is set a variable path will now be calibrated
|
||||
8 times instead of originally 40. Long calibration is now 20.
|
||||
- added AFL_TRY_AFFINITY to try to bind to CPUs but don't error if
|
||||
it fails
|
||||
- afl-cc:
|
||||
- We do not support llvm versions prior 6.0 anymore
|
||||
- Fix for -pie compiled binaries with default afl-clang-fast PCGUARD
|
||||
- Leak Sanitizer (AFL_USE_LSAN) added by Joshua Rogers, thanks!
|
||||
- Removed InsTrim instrumentation as it is not as good as PCGUARD
|
||||
- Removed automatic linking with -lc++ for LTO mode
|
||||
- utils/aflpp_driver/aflpp_qemu_driver_hook fixed to work with qemu mode
|
||||
- Fixed a crash in llvm dict2file when a strncmp length was -1
|
||||
- added --afl-noopt support
|
||||
- utils/aflpp_driver:
|
||||
- aflpp_qemu_driver_hook fixed to work with qemu_mode
|
||||
- aflpp_driver now compiled with -fPIC
|
||||
- unicornafl:
|
||||
- fix MIPS delay slot caching, thanks @JackGrence
|
||||
- fixed aarch64 exit address
|
||||
- execution no longer stops at address 0x0
|
||||
- updated afl-system-config to support Arch Linux weirdness and increase
|
||||
MacOS shared memory
|
||||
- updated the grammar custom mutator to the newest version
|
||||
- add -d (add dead fuzzer stats) to afl-whatsup
|
||||
- add thread safe counters for LLVM CLASSIC (set AFL_LLVM_THREADSAFE_INST)
|
||||
- added AFL_PRINT_FILENAMES to afl-showmap/cmin to print the
|
||||
current filename
|
||||
- afl-showmap/cmin will now process queue items in alphabetical order
|
||||
|
||||
### Version ++3.12c (release)
|
||||
- afl-fuzz:
|
||||
|
@ -103,6 +103,41 @@ The llvm instrumentation requires a fully-operational installation of clang. The
|
||||
comes with Xcode is missing some of the essential headers and helper tools.
|
||||
See README.llvm.md for advice on how to build the compiler from scratch.
|
||||
|
||||
MacOS X supports SYSV shared memory used by AFL's instrumentation, but the
|
||||
default settings aren't usable with AFL++. The default settings on 10.14 seem
|
||||
to be:
|
||||
|
||||
```bash
|
||||
$ ipcs -M
|
||||
IPC status from <running system> as of XXX
|
||||
shminfo:
|
||||
shmmax: 4194304 (max shared memory segment size)
|
||||
shmmin: 1 (min shared memory segment size)
|
||||
shmmni: 32 (max number of shared memory identifiers)
|
||||
shmseg: 8 (max shared memory segments per process)
|
||||
shmall: 1024 (max amount of shared memory in pages)
|
||||
```
|
||||
|
||||
To temporarily change your settings to something minimally usable with AFL++,
|
||||
run these commands as root:
|
||||
|
||||
```bash
|
||||
sysctl kern.sysv.shmmax=8388608
|
||||
sysctl kern.sysv.shmall=4096
|
||||
```
|
||||
|
||||
If you're running more than one instance of AFL you likely want to make `shmall`
|
||||
bigger and increase `shmseg` as well:
|
||||
|
||||
```bash
|
||||
sysctl kern.sysv.shmmax=8388608
|
||||
sysctl kern.sysv.shmseg=48
|
||||
sysctl kern.sysv.shmall=98304
|
||||
```
|
||||
|
||||
See http://www.spy-hill.com/help/apple/SharedMemory.html for documentation for
|
||||
these settings and how to make them permanent.
|
||||
|
||||
## 4. Linux or *BSD on non-x86 systems
|
||||
|
||||
Standard build will fail on non-x86 systems, but you should be able to
|
||||
|
@ -1,6 +1,6 @@
|
||||
# AFL quick start guide
|
||||
|
||||
You should read [README.md](README.md) - it's pretty short. If you really can't, here's
|
||||
You should read [README.md](../README.md) - it's pretty short. If you really can't, here's
|
||||
how to hit the ground running:
|
||||
|
||||
1) Compile AFL with 'make'. If build fails, see [INSTALL.md](INSTALL.md) for tips.
|
||||
|
@ -41,6 +41,33 @@
|
||||
|
||||
As it is included in afl++ this needs no URL.
|
||||
|
||||
If you like to code a customized fuzzer without much work, we highly
|
||||
recommend to check out our sister project libafl which will support QEMU
|
||||
too:
|
||||
[https://github.com/AFLplusplus/LibAFL](https://github.com/AFLplusplus/LibAFL)
|
||||
|
||||
|
||||
## AFL FRIDA
|
||||
|
||||
In frida_mode you can fuzz binary-only targets easily like with QEMU,
|
||||
with the advantage that frida_mode also works on MacOS (both intel and M1).
|
||||
|
||||
If you want to fuzz a binary-only library then you can fuzz it with
|
||||
frida-gum via utils/afl_frida/, you will have to write a harness to
|
||||
call the target function in the library, use afl-frida.c as a template.
|
||||
|
||||
Both come with afl++ so this needs no URL.
|
||||
|
||||
You can also perform remote fuzzing with frida, e.g. if you want to fuzz
|
||||
on iPhone or Android devices, for this you can use
|
||||
[https://github.com/ttdennis/fpicker/](https://github.com/ttdennis/fpicker/)
|
||||
as an intermediate that uses afl++ for fuzzing.
|
||||
|
||||
If you like to code a customized fuzzer without much work, we highly
|
||||
recommend to check out our sister project libafl which supports Frida too:
|
||||
[https://github.com/AFLplusplus/LibAFL](https://github.com/AFLplusplus/LibAFL)
|
||||
Working examples already exist :-)
|
||||
|
||||
|
||||
## WINE+QEMU
|
||||
|
||||
@ -62,13 +89,6 @@
|
||||
As it is included in afl++ this needs no URL.
|
||||
|
||||
|
||||
## AFL FRIDA
|
||||
|
||||
If you want to fuzz a binary-only shared library then you can fuzz it with
|
||||
frida-gum via utils/afl_frida/, you will have to write a harness to
|
||||
call the target function in the library, use afl-frida.c as a template.
|
||||
|
||||
|
||||
## AFL UNTRACER
|
||||
|
||||
If you want to fuzz a binary-only shared library then you can fuzz it with
|
||||
@ -157,19 +177,6 @@
|
||||
If anyone finds any coresight implementation for afl please ping me: vh@thc.org
|
||||
|
||||
|
||||
## FRIDA
|
||||
|
||||
Frida is a dynamic instrumentation engine like Pintool, Dyninst and Dynamorio.
|
||||
What is special is that it is written Python, and scripted with Javascript.
|
||||
It is mostly used to reverse binaries on mobile phones however can be used
|
||||
everywhere.
|
||||
|
||||
There is a WIP fuzzer available at [https://github.com/andreafioraldi/frida-fuzzer](https://github.com/andreafioraldi/frida-fuzzer)
|
||||
|
||||
There is also an early implementation in an AFL++ test branch:
|
||||
[https://github.com/AFLplusplus/AFLplusplus/tree/frida](https://github.com/AFLplusplus/AFLplusplus/tree/frida)
|
||||
|
||||
|
||||
## PIN & DYNAMORIO
|
||||
|
||||
Pintool and Dynamorio are dynamic instrumentation engines, and they can be
|
||||
@ -205,7 +212,8 @@
|
||||
* QSYM: [https://github.com/sslab-gatech/qsym](https://github.com/sslab-gatech/qsym)
|
||||
* Manticore: [https://github.com/trailofbits/manticore](https://github.com/trailofbits/manticore)
|
||||
* S2E: [https://github.com/S2E](https://github.com/S2E)
|
||||
* Tinyinst [https://github.com/googleprojectzero/TinyInst](https://github.com/googleprojectzero/TinyInst) (Mac/Windows only)
|
||||
* Tinyinst: [https://github.com/googleprojectzero/TinyInst](https://github.com/googleprojectzero/TinyInst) (Mac/Windows only)
|
||||
* Jackalope: [https://github.com/googleprojectzero/Jackalope](https://github.com/googleprojectzero/Jackalope)
|
||||
* ... please send me any missing that are good
|
||||
|
||||
|
||||
|
@ -92,6 +92,9 @@ def queue_new_entry(filename_new_queue, filename_orig_queue):
|
||||
|
||||
def introspection():
|
||||
return string
|
||||
|
||||
def deinit(): # optional for Python
|
||||
pass
|
||||
```
|
||||
|
||||
### Custom Mutation
|
||||
@ -285,8 +288,8 @@ afl-fuzz /path/to/program
|
||||
|
||||
## 4) Example
|
||||
|
||||
Please see [example.c](../utils/custom_mutators/example.c) and
|
||||
[example.py](../utils/custom_mutators/example.py)
|
||||
Please see [example.c](../custom_mutators/examples/example.c) and
|
||||
[example.py](../custom_mutators/examples/example.py)
|
||||
|
||||
## 5) Other Resources
|
||||
|
||||
|
@ -289,6 +289,10 @@ checks or alter some of the more exotic semantics of the tool:
|
||||
normally indicated by the cycle counter in the UI turning green. May be
|
||||
convenient for some types of automated jobs.
|
||||
|
||||
- `AFL_EXIT_ON_TIME` Causes afl-fuzz to terminate if no new paths were
|
||||
found within a specified period of time (in seconds). May be convenient
|
||||
for some types of automated jobs.
|
||||
|
||||
- `AFL_EXIT_ON_SEED_ISSUES` will restore the vanilla afl-fuzz behaviour
|
||||
which does not allow crashes or timeout seeds in the initial -i corpus.
|
||||
|
||||
@ -313,14 +317,12 @@ checks or alter some of the more exotic semantics of the tool:
|
||||
on Linux systems. This slows things down, but lets you run more instances
|
||||
of afl-fuzz than would be prudent (if you really want to).
|
||||
|
||||
- Setting `AFL_TRY_AFFINITY` tries to attempt binding to a specific CPU core
|
||||
on Linux systems, but will not terminate if that fails.
|
||||
|
||||
- Setting `AFL_NO_AUTODICT` will not load an LTO generated auto dictionary
|
||||
that is compiled into the target.
|
||||
|
||||
- `AFL_SKIP_CRASHES` causes AFL++ to tolerate crashing files in the input
|
||||
queue. This can help with rare situations where a program crashes only
|
||||
intermittently, but it's not really recommended under normal operating
|
||||
conditions.
|
||||
|
||||
- Setting `AFL_HANG_TMOUT` allows you to specify a different timeout for
|
||||
deciding if a particular test case is a "hang". The default is 1 second
|
||||
or the value of the `-t` parameter, whichever is larger. Dialing the value
|
||||
@ -356,6 +358,7 @@ checks or alter some of the more exotic semantics of the tool:
|
||||
and shell scripts; and `AFL_DUMB_FORKSRV` in conjunction with the `-n`
|
||||
setting to instruct afl-fuzz to still follow the fork server protocol
|
||||
without expecting any instrumentation data in return.
|
||||
Note that this also turns off auto map size detection.
|
||||
|
||||
- When running in the `-M` or `-S` mode, setting `AFL_IMPORT_FIRST` causes the
|
||||
fuzzer to import test cases from other instances before doing anything
|
||||
@ -568,6 +571,9 @@ The corpus minimization script offers very little customization:
|
||||
a modest security risk on multi-user systems with rogue users, but should
|
||||
be safe on dedicated fuzzing boxes.
|
||||
|
||||
- `AFL_PRINT_FILENAMES` prints each filename to stdout, as it gets processed.
|
||||
This can help when embedding `afl-cmin` or `afl-showmap` in other scripts scripting.
|
||||
|
||||
## 7) Settings for afl-tmin
|
||||
|
||||
Virtually nothing to play with. Well, in QEMU mode (`-Q`), `AFL_PATH` will be
|
||||
|
@ -83,5 +83,5 @@ You can find a simple solution in utils/argv_fuzzing.
|
||||
## Attacking a format that uses checksums?
|
||||
|
||||
Remove the checksum-checking code or use a postprocessor!
|
||||
See utils/custom_mutators/ for more.
|
||||
See `afl_custom_post_process` in custom_mutators/examples/example.c for more.
|
||||
|
||||
|
188
frida_mode/GNUmakefile
Normal file
188
frida_mode/GNUmakefile
Normal file
@ -0,0 +1,188 @@
|
||||
PWD:=$(shell pwd)/
|
||||
ROOT:=$(shell realpath $(PWD)..)/
|
||||
INC_DIR:=$(PWD)include/
|
||||
SRC_DIR:=$(PWD)src/
|
||||
INCLUDES:=$(wildcard $(INC_DIR)*.h)
|
||||
BUILD_DIR:=$(PWD)build/
|
||||
OBJ_DIR:=$(BUILD_DIR)obj/
|
||||
|
||||
SOURCES:=$(wildcard $(SRC_DIR)**/*.c) $(wildcard $(SRC_DIR)*.c)
|
||||
OBJS:=$(foreach src,$(SOURCES),$(OBJ_DIR)$(notdir $(patsubst %.c, %.o, $(src))))
|
||||
CFLAGS+=-fPIC \
|
||||
-D_GNU_SOURCE \
|
||||
-D_FORTIFY_SOURCE=2 \
|
||||
-g \
|
||||
-O3 \
|
||||
-funroll-loops \
|
||||
|
||||
RT_CFLAGS:=-Wno-unused-parameter \
|
||||
-Wno-sign-compare \
|
||||
-Wno-unused-function \
|
||||
-Wno-unused-result \
|
||||
-Wno-int-to-pointer-cast \
|
||||
|
||||
LDFLAGS+=-shared \
|
||||
-lpthread \
|
||||
-lresolv \
|
||||
-ldl \
|
||||
-z noexecstack \
|
||||
|
||||
ifdef DEBUG
|
||||
CFLAGS+=-Werror \
|
||||
-Wall \
|
||||
-Wextra \
|
||||
-Wpointer-arith
|
||||
else
|
||||
CFLAGS+=-Wno-pointer-arith
|
||||
endif
|
||||
|
||||
FRIDA_BUILD_DIR:=$(BUILD_DIR)frida/
|
||||
FRIDA_TRACE:=$(BUILD_DIR)afl-frida-trace.so
|
||||
FRIDA_TRACE_EMBEDDED:=$(BUILD_DIR)afl-frida-trace-embedded
|
||||
|
||||
ifndef ARCH
|
||||
|
||||
ARCH=$(shell uname -m)
|
||||
ifeq "$(ARCH)" "aarch64"
|
||||
ARCH:=arm64
|
||||
endif
|
||||
|
||||
ifeq "$(ARCH)" "i686"
|
||||
ARCH:=x86
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq "$(shell uname)" "Darwin"
|
||||
OS:=macos
|
||||
RT_CFLAGS:=$(RT_CFLAGS) -Wno-deprecated-declarations
|
||||
else
|
||||
ifdef DEBUG
|
||||
RT_CFLAGS:=$(RT_CFLAGS) -Wno-prio-ctor-dtor
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq "$(shell uname)" "Linux"
|
||||
OS:=linux
|
||||
endif
|
||||
|
||||
ifndef OS
|
||||
$(error "Operating system unsupported")
|
||||
endif
|
||||
|
||||
GUM_DEVKIT_VERSION=14.2.18
|
||||
GUM_DEVKIT_FILENAME=frida-gum-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
|
||||
|
||||
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_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_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)
|
||||
|
||||
############################## ALL #############################################
|
||||
|
||||
all: $(FRIDA_TRACE)
|
||||
|
||||
32:
|
||||
CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
|
||||
|
||||
$(BUILD_DIR):
|
||||
mkdir -p $(BUILD_DIR)
|
||||
|
||||
$(OBJ_DIR): | $(BUILD_DIR)
|
||||
mkdir -p $@
|
||||
|
||||
############################# FRIDA ############################################
|
||||
|
||||
$(FRIDA_MAKEFILE): | $(BUILD_DIR)
|
||||
git clone --recursive https://github.com/frida/frida.git $(FRIDA_DIR)
|
||||
|
||||
$(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_GUM_DEVKIT_TARBALL): $(FRIDA_GUM_DEVKIT_HEADER)
|
||||
cd $(FRIDA_GUM_DEVKIT_DIR) && tar cvf $(FRIDA_GUM_DEVKIT_TARBALL) .
|
||||
|
||||
$(FRIDA_GUM_DEVKIT_COMPRESSED_TARBALL): $(FRIDA_GUM_DEVKIT_TARBALL)
|
||||
xz -k -f -0 $(FRIDA_GUM_DEVKIT_TARBALL)
|
||||
|
||||
############################# DEVKIT ###########################################
|
||||
|
||||
$(FRIDA_BUILD_DIR): | $(BUILD_DIR)
|
||||
mkdir -p $@
|
||||
|
||||
ifdef FRIDA_SOURCE
|
||||
$(GUM_DEVKIT_TARBALL): $(FRIDA_GUM_DEVKIT_COMPRESSED_TARBALL)| $(FRIDA_BUILD_DIR)
|
||||
cp -v $< $@
|
||||
else
|
||||
$(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_HEADER): | $(GUM_DEVKIT_TARBALL)
|
||||
tar Jxvf $(GUM_DEVKIT_TARBALL) -C $(FRIDA_BUILD_DIR)
|
||||
|
||||
############################## AFL #############################################
|
||||
$(AFL_COMPILER_RT_OBJ): $(AFL_COMPILER_RT_SRC)
|
||||
$(CC) \
|
||||
$(CFLAGS) \
|
||||
$(RT_CFLAGS) \
|
||||
-I $(ROOT) \
|
||||
-I $(ROOT)include \
|
||||
-o $@ \
|
||||
-c $<
|
||||
|
||||
############################# SOURCE ###########################################
|
||||
|
||||
define BUILD_SOURCE
|
||||
$(2): $(1) $(INCLUDES) GNUmakefile | $(OBJ_DIR)
|
||||
$(CC) \
|
||||
$(CFLAGS) \
|
||||
-I $(ROOT)include \
|
||||
-I $(FRIDA_BUILD_DIR) \
|
||||
-I $(INC_DIR) \
|
||||
-c $1 \
|
||||
-o $2
|
||||
endef
|
||||
|
||||
$(foreach src,$(SOURCES),$(eval $(call BUILD_SOURCE,$(src),$(OBJ_DIR)$(notdir $(patsubst %.c, %.o, $(src))))))
|
||||
|
||||
######################## AFL-FRIDA-TRACE #######################################
|
||||
|
||||
$(FRIDA_TRACE): $(GUM_DEVIT_LIBRARY) $(GUM_DEVIT_HEADER) $(OBJS) $(AFL_COMPILER_RT_OBJ) GNUmakefile | $(BUILD_DIR)
|
||||
$(CC) \
|
||||
$(OBJS) \
|
||||
$(GUM_DEVIT_LIBRARY) \
|
||||
$(AFL_COMPILER_RT_OBJ) \
|
||||
$(LDFLAGS) \
|
||||
-o $@ \
|
||||
|
||||
cp -v $(FRIDA_TRACE) $(ROOT)
|
||||
|
||||
############################# CLEAN ############################################
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR)
|
||||
|
||||
############################# FORMAT ###########################################
|
||||
format:
|
||||
cd $(ROOT) && echo $(SOURCES) | xargs -L1 ./.custom-format.py -i
|
||||
cd $(ROOT) && echo $(INCLUDES) | xargs -L1 ./.custom-format.py -i
|
||||
|
||||
############################# RUN #############################################
|
@ -1,348 +1,13 @@
|
||||
PWD:=$(shell pwd)/
|
||||
INC_DIR:=$(PWD)include/
|
||||
SRC_DIR:=$(PWD)src/
|
||||
INCLUDES:=$(wildcard $(INC_DIR)*.h)
|
||||
SOURCES:=$(wildcard $(SRC_DIR)*.c)
|
||||
BUILD_DIR:=$(PWD)build/
|
||||
CFLAGS+=-fPIC -D_GNU_SOURCE
|
||||
all:
|
||||
@echo trying to use GNU make...
|
||||
@gmake all || echo please install GNUmake
|
||||
|
||||
FRIDA_BUILD_DIR:=$(BUILD_DIR)frida/
|
||||
FRIDA_TRACE:=$(FRIDA_BUILD_DIR)afl-frida-trace.so
|
||||
32:
|
||||
@echo trying to use GNU make...
|
||||
@gmake 32 || echo please install GNUmake
|
||||
|
||||
ARCH=$(shell uname -m)
|
||||
ifeq "$(ARCH)" "aarch64"
|
||||
ARCH:=arm64
|
||||
TESTINSTR_BASE:=0x0000aaaaaaaaa000
|
||||
endif
|
||||
|
||||
ifeq "$(ARCH)" "x86_64"
|
||||
TESTINSTR_BASE:=0x0000555555554000
|
||||
endif
|
||||
|
||||
ifeq "$(shell uname)" "Darwin"
|
||||
OS:=macos
|
||||
AFL_FRIDA_INST_RANGES=0x0000000000001000-0xFFFFFFFFFFFFFFFF
|
||||
CFLAGS:=$(CFLAGS) -Wno-deprecated-declarations
|
||||
TEST_LDFLAGS:=-undefined dynamic_lookup
|
||||
endif
|
||||
ifeq "$(shell uname)" "Linux"
|
||||
OS:=linux
|
||||
AFL_FRIDA_INST_RANGES=$(shell $(PWD)test/testinstr.py -f $(BUILD_DIR)testinstr -s .testinstr -b $(TESTINSTR_BASE))
|
||||
CFLAGS:=$(CFLAGS) -Wno-prio-ctor-dtor
|
||||
TEST_LDFLAGS:=
|
||||
endif
|
||||
|
||||
ifndef OS
|
||||
$(error "Operating system unsupported")
|
||||
endif
|
||||
|
||||
VERSION=14.2.13
|
||||
GUM_DEVKIT_FILENAME=frida-gum-devkit-$(VERSION)-$(OS)-$(ARCH).tar.xz
|
||||
GUM_DEVKIT_URL="https://github.com/frida/frida/releases/download/$(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
|
||||
|
||||
TEST_BUILD_DIR:=$(BUILD_DIR)test/
|
||||
|
||||
LIBPNG_FILE:=$(TEST_BUILD_DIR)libpng-1.2.56.tar.gz
|
||||
LIBPNG_URL:=https://downloads.sourceforge.net/project/libpng/libpng12/older-releases/1.2.56/libpng-1.2.56.tar.gz
|
||||
LIBPNG_DIR:=$(TEST_BUILD_DIR)libpng-1.2.56/
|
||||
LIBPNG_MAKEFILE:=$(LIBPNG_DIR)Makefile
|
||||
LIBPNG_LIB:=$(LIBPNG_DIR).libs/libpng12.a
|
||||
|
||||
HARNESS_FILE:=$(TEST_BUILD_DIR)StandaloneFuzzTargetMain.c
|
||||
HARNESS_OBJ:=$(TEST_BUILD_DIR)StandaloneFuzzTargetMain.o
|
||||
HARNESS_URL:="https://raw.githubusercontent.com/llvm/llvm-project/main/compiler-rt/lib/fuzzer/standalone/StandaloneFuzzTargetMain.c"
|
||||
|
||||
PNGTEST_FILE:=$(TEST_BUILD_DIR)target.cc
|
||||
PNGTEST_OBJ:=$(TEST_BUILD_DIR)target.o
|
||||
PNGTEST_URL:="https://raw.githubusercontent.com/google/fuzzbench/master/benchmarks/libpng-1.2.56/target.cc"
|
||||
|
||||
TEST_BIN:=$(TEST_BUILD_DIR)pngtest
|
||||
|
||||
TESTINSTBIN:=$(BUILD_DIR)testinstr
|
||||
TESTINSTSRC:=$(PWD)test/testinstr.c
|
||||
|
||||
TEST_DATA_DIR:=$(PWD)build/test/libpng-1.2.56/contrib/pngsuite/
|
||||
|
||||
TESTINSTR_DATA_DIR:=$(BUILD_DIR)testinstr_in/
|
||||
TESTINSTR_DATA_FILE:=$(TESTINSTR_DATA_DIR)test.dat
|
||||
FRIDA_OUT:=$(PWD)frida_out
|
||||
QEMU_OUT:=$(PWD)qemu_out
|
||||
|
||||
.PHONY: all frida test clean format test_frida test_qemu compare testinstr test_testinstr standalone
|
||||
|
||||
all: $(FRIDA_TRACE)
|
||||
|
||||
frida: $(FRIDA_TRACE)
|
||||
|
||||
$(BUILD_DIR):
|
||||
mkdir -p $(BUILD_DIR)
|
||||
|
||||
############################# FRIDA ############################################
|
||||
$(FRIDA_BUILD_DIR): | $(BUILD_DIR)
|
||||
mkdir -p $@
|
||||
|
||||
$(GUM_DEVKIT_TARBALL): | $(FRIDA_BUILD_DIR)
|
||||
wget -O $@ $(GUM_DEVKIT_URL)
|
||||
|
||||
$(GUM_DEVIT_LIBRARY): | $(GUM_DEVKIT_TARBALL)
|
||||
tar Jxvf $(GUM_DEVKIT_TARBALL) -C $(FRIDA_BUILD_DIR)
|
||||
|
||||
$(GUM_DEVIT_HEADER): | $(GUM_DEVKIT_TARBALL)
|
||||
tar Jxvf $(GUM_DEVKIT_TARBALL) -C $(FRIDA_BUILD_DIR)
|
||||
|
||||
$(FRIDA_TRACE): $(GUM_DEVIT_LIBRARY) $(GUM_DEVIT_HEADER) $(SOURCES) Makefile | $(FRIDA_BUILD_DIR)
|
||||
$(CC) -shared \
|
||||
$(CFLAGS) \
|
||||
-o $@ $(SOURCES) \
|
||||
$(GUM_DEVIT_LIBRARY) \
|
||||
-I $(FRIDA_BUILD_DIR) \
|
||||
-I .. \
|
||||
-I ../include \
|
||||
-I $(INC_DIR) \
|
||||
../instrumentation/afl-compiler-rt.o.c \
|
||||
-lpthread -ldl -lresolv
|
||||
|
||||
cp -v $(FRIDA_TRACE) ../
|
||||
|
||||
############################# TEST #############################################
|
||||
|
||||
test: $(TEST_BIN)
|
||||
|
||||
$(TEST_BUILD_DIR): $(BUILD_DIR)
|
||||
mkdir -p $@
|
||||
|
||||
$(HARNESS_FILE): | $(TEST_BUILD_DIR)
|
||||
wget -O $@ $(HARNESS_URL)
|
||||
|
||||
$(HARNESS_OBJ): $(HARNESS_FILE)
|
||||
$(CC) -o $@ -c $<
|
||||
|
||||
$(PNGTEST_FILE): | $(TEST_BUILD_DIR)
|
||||
wget -O $@ $(PNGTEST_URL)
|
||||
|
||||
$(PNGTEST_OBJ): $(PNGTEST_FILE) | $(LIBPNG_DIR)
|
||||
$(CXX) -std=c++11 -I $(LIBPNG_DIR) -o $@ -c $<
|
||||
|
||||
$(LIBPNG_FILE): | $(TEST_BUILD_DIR)
|
||||
wget -O $@ $(LIBPNG_URL)
|
||||
|
||||
$(LIBPNG_DIR): $(LIBPNG_FILE)
|
||||
tar zxvf $(LIBPNG_FILE) -C $(TEST_BUILD_DIR)
|
||||
|
||||
$(LIBPNG_MAKEFILE): | $(LIBPNG_DIR)
|
||||
cd $(LIBPNG_DIR) && ./configure
|
||||
|
||||
$(LIBPNG_LIB): $(LIBPNG_MAKEFILE)
|
||||
make -C $(LIBPNG_DIR)
|
||||
|
||||
$(TEST_BIN): $(HARNESS_OBJ) $(PNGTEST_OBJ) $(LIBPNG_LIB)
|
||||
$(CXX) \
|
||||
-o $@ \
|
||||
$(HARNESS_OBJ) $(PNGTEST_OBJ) $(LIBPNG_LIB) \
|
||||
-lz \
|
||||
$(TEST_LDFLAGS)
|
||||
|
||||
############################# TESTINSR #########################################
|
||||
$(TESTINSTR_DATA_DIR): | $(BUILD_DIR)
|
||||
mkdir -p $@
|
||||
|
||||
$(TESTINSTR_DATA_FILE): | $(TESTINSTR_DATA_DIR)
|
||||
echo -n "000" > $@
|
||||
|
||||
$(TESTINSTBIN): $(TESTINSTSRC) | $(BUILD_DIR)
|
||||
$(CC) -o $@ $<
|
||||
|
||||
testinstr: $(TESTINSTBIN)
|
||||
|
||||
############################# CLEAN ############################################
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR)
|
||||
@gmake clean
|
||||
|
||||
############################# FORMAT ###########################################
|
||||
format:
|
||||
cd .. && echo $(SOURCES) | xargs -L1 ./.custom-format.py -i
|
||||
cd .. && echo $(INCLUDES) | xargs -L1 ./.custom-format.py -i
|
||||
cd .. && ./.custom-format.py -i $(TESTINSTSRC)
|
||||
|
||||
############################# RUN #############################################
|
||||
|
||||
# Add the environment variable AFL_DEBUG_CHILD=1 to show printf's from the target
|
||||
|
||||
png_frida: $(FRIDA_TRACE) $(TEST_BIN)
|
||||
make -C ..
|
||||
cd .. && \
|
||||
./afl-fuzz \
|
||||
-O \
|
||||
-i $(TEST_DATA_DIR) \
|
||||
-o $(FRIDA_OUT) \
|
||||
-- \
|
||||
$(TEST_BIN) @@
|
||||
|
||||
png_qemu: $(TEST_BIN)
|
||||
make -C ..
|
||||
cd .. && \
|
||||
./afl-fuzz \
|
||||
-Q \
|
||||
-i $(TEST_DATA_DIR) \
|
||||
-o $(QEMU_OUT) \
|
||||
-- \
|
||||
$(TEST_BIN) @@
|
||||
|
||||
compare: $(FRIDA_TRACE) $(TEST_BIN)
|
||||
cd .. && \
|
||||
./afl-fuzz \
|
||||
-V30 \
|
||||
-O \
|
||||
-i $(TEST_DATA_DIR) \
|
||||
-o $(FRIDA_OUT) \
|
||||
-- \
|
||||
$(TEST_BIN) @@
|
||||
cd .. && \
|
||||
./afl-fuzz \
|
||||
-V30 \
|
||||
-Q \
|
||||
-i $(TEST_DATA_DIR) \
|
||||
-o $(QEMU_OUT) \
|
||||
-- \
|
||||
$(TEST_BIN) @@
|
||||
cat frida_out/default/fuzzer_stats
|
||||
cat qemu_out/default/fuzzer_stats
|
||||
|
||||
testinstr_qemu: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
|
||||
make -C ..
|
||||
cd .. && \
|
||||
AFL_QEMU_INST_RANGES=$(AFL_FRIDA_INST_RANGES) \
|
||||
./afl-fuzz \
|
||||
-Q \
|
||||
-i $(TESTINSTR_DATA_DIR) \
|
||||
-o $(QEMU_OUT) \
|
||||
-- \
|
||||
$(TESTINSTBIN) @@
|
||||
|
||||
testinstr_frida: $(FRIDA_TRACE) $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
|
||||
make -C ..
|
||||
cd .. && \
|
||||
AFL_FRIDA_INST_RANGES=$(AFL_FRIDA_INST_RANGES) \
|
||||
AFL_FRIDA_INST_NO_OPTIMIZE=1 \
|
||||
AFL_FRIDA_INST_NO_PREFETCH=1 \
|
||||
AFL_FRIDA_INST_STRICT=1 \
|
||||
./afl-fuzz \
|
||||
-O \
|
||||
-i $(TESTINSTR_DATA_DIR) \
|
||||
-o $(FRIDA_OUT) \
|
||||
-- \
|
||||
$(TESTINSTBIN) @@
|
||||
|
||||
standalone: $(FRIDA_TRACE) $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
|
||||
cd .. && \
|
||||
AFL_FRIDA_INST_RANGES=$(AFL_FRIDA_INST_RANGES) \
|
||||
AFL_DEBUG_CHILD=1 \
|
||||
AFL_FRIDA_DEBUG_MAPS=1 \
|
||||
AFL_FRIDA_INST_NO_OPTIMIZE=1 \
|
||||
AFL_FRIDA_INST_NO_PREFETCH=1 \
|
||||
AFL_FRIDA_INST_TRACE=1 \
|
||||
AFL_FRIDA_INST_STRICT=1 \
|
||||
LD_PRELOAD=$(FRIDA_TRACE) \
|
||||
DYLD_INSERT_LIBRARIES=$(FRIDA_TRACE) \
|
||||
$(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
|
||||
|
||||
tmin_qemu: $(TEST_BIN)
|
||||
make -C ..
|
||||
cd .. && \
|
||||
./afl-tmin \
|
||||
-Q \
|
||||
-i $(TEST_DATA_DIR)basn0g01.png \
|
||||
-o $(QEMU_OUT)/qemu-min-basn0g01.png \
|
||||
-- \
|
||||
$(TEST_BIN) @@
|
||||
|
||||
tmin_frida: $(TEST_BIN)
|
||||
make -C ..
|
||||
cd .. && \
|
||||
./afl-tmin \
|
||||
-O \
|
||||
-i $(TEST_DATA_DIR)basn0g01.png \
|
||||
-o $(FRIDA_OUT)/qemu-min-basn0g01.png \
|
||||
-- \
|
||||
$(TEST_BIN)
|
||||
|
||||
showmap_qemu: $(TEST_BIN)
|
||||
make -C ..
|
||||
cd .. && \
|
||||
./afl-showmap \
|
||||
-Q \
|
||||
-i $(TEST_DATA_DIR) \
|
||||
-o $(QEMU_OUT) \
|
||||
-- \
|
||||
$(TEST_BIN) @@
|
||||
|
||||
showmap_frida: $(TEST_BIN)
|
||||
make -C ..
|
||||
cd .. && \
|
||||
./afl-showmap \
|
||||
-O \
|
||||
-i $(TEST_DATA_DIR) \
|
||||
-o $(FRIDA_OUT) \
|
||||
-- \
|
||||
$(TEST_BIN) @@
|
||||
|
||||
analyze_qemu: $(TEST_BIN)
|
||||
make -C ..
|
||||
cd .. && \
|
||||
./afl-analyze \
|
||||
-Q \
|
||||
-i $(TEST_DATA_DIR)basn0g01.png \
|
||||
-- \
|
||||
$(TEST_BIN) @@
|
||||
|
||||
analyze_frida: $(TEST_BIN)
|
||||
make -C ..
|
||||
cd .. && \
|
||||
./afl-analyze \
|
||||
-O \
|
||||
-i $(TEST_DATA_DIR)basn0g01.png \
|
||||
-- \
|
||||
$(TEST_BIN) @@
|
||||
|
||||
cmin_qemu: $(TEST_BIN)
|
||||
make -C ..
|
||||
cd .. && \
|
||||
./afl-cmin \
|
||||
-Q \
|
||||
-i $(TEST_DATA_DIR) \
|
||||
-o $(QEMU_OUT) \
|
||||
-- \
|
||||
$(TEST_BIN) @@
|
||||
|
||||
cmin_frida: $(TEST_BIN)
|
||||
make -C ..
|
||||
cd .. && \
|
||||
./afl-cmin \
|
||||
-O \
|
||||
-i $(TEST_DATA_DIR) \
|
||||
-o $(FRIDA_OUT) \
|
||||
-- \
|
||||
$(TEST_BIN) @@
|
||||
|
||||
cmin_bash_qemu: $(TEST_BIN)
|
||||
make -C ..
|
||||
cd .. && \
|
||||
./afl-cmin.bash \
|
||||
-Q \
|
||||
-i $(TEST_DATA_DIR) \
|
||||
-o $(QEMU_OUT) \
|
||||
-- \
|
||||
$(TEST_BIN) @@
|
||||
|
||||
cmin_bash_frida: $(TEST_BIN)
|
||||
make -C ..
|
||||
cd .. && \
|
||||
./afl-cmin.bash \
|
||||
-O \
|
||||
-i $(TEST_DATA_DIR) \
|
||||
-o $(FRIDA_OUT) \
|
||||
-- \
|
||||
$(TEST_BIN) @@
|
||||
@gmake format
|
||||
|
@ -1,34 +1,38 @@
|
||||
# FRIDA MODE
|
||||
The purpose of FRIDA mode is to provide an alternative binary only fuzzer for AFL
|
||||
just like that provided by QEMU mode. The intention is to provide a very similar
|
||||
user experience, right down to the options provided through environment variables.
|
||||
|
||||
The purpose of FRIDA mode is to provide an alternative binary only fuzzer for
|
||||
AFL just like that provided by QEMU mode. The intention is to provide a very
|
||||
similar user experience, right down to the options provided through environment
|
||||
variables.
|
||||
|
||||
Whilst AFLplusplus already has some support for running on FRIDA [here](https://github.com/AFLplusplus/AFLplusplus/tree/stable/utils/afl_frida)
|
||||
this requires the code to be fuzzed to be provided as a shared library, it
|
||||
cannot be used to fuzz executables. Additionally, it requires the user to write
|
||||
a small harness around their target code of interest, FRIDA mode instead takes a
|
||||
different approach to avoid these limitations.
|
||||
a small harness around their target code of interest.
|
||||
FRIDA mode instead takes a different approach to avoid these limitations.
|
||||
In Frida mode binary programs are instrumented, similarly to QEMU mode.
|
||||
|
||||
# Current Progress
|
||||
As FRIDA mode is new, it is missing a lot of features. Most importantly,
|
||||
persistent mode. The design is such that it should be possible to add these
|
||||
features in a similar manner to QEMU mode and perhaps leverage some of its
|
||||
design and implementation.
|
||||
## Current Progress
|
||||
|
||||
| Feature/Instrumentation | frida-mode |
|
||||
| -------------------------|:----------:|
|
||||
| NeverZero | |
|
||||
| Persistent Mode | |
|
||||
| LAF-Intel / CompCov | |
|
||||
| CmpLog | |
|
||||
| Selective Instrumentation| x |
|
||||
| Non-Colliding Coverage | |
|
||||
| Ngram prev_loc Coverage | |
|
||||
| Context Coverage | |
|
||||
| Auto Dictionary | |
|
||||
| Snapshot LKM Support | |
|
||||
As FRIDA mode is new, it is missing a lot of features. The design is such that it
|
||||
should be possible to add these features in a similar manner to QEMU mode and
|
||||
perhaps leverage some of its design and implementation.
|
||||
|
||||
# Compatibility
|
||||
| Feature/Instrumentation | frida-mode | Notes |
|
||||
| -------------------------|:----------:|:--------------------------------------------:|
|
||||
| NeverZero | x | |
|
||||
| Persistent Mode | x | (x86/x64 only)(Only on function boundaries) |
|
||||
| LAF-Intel / CompCov | - | (CMPLOG is better 90% of the time) |
|
||||
| CMPLOG | x | (x86/x64 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) |
|
||||
|
||||
## Compatibility
|
||||
Currently FRIDA mode supports Linux and macOS targets on both x86/x64
|
||||
architecture and aarch64. Later releases may add support for aarch32 and Windows
|
||||
targets as well as embedded linux environments.
|
||||
@ -38,54 +42,61 @@ runtime libraries, so porting should be possible. However, the current build
|
||||
system does not support cross compilation.
|
||||
|
||||
## Getting Started
|
||||
To build everything run `make`.
|
||||
|
||||
To run the benchmark sample with qemu run `make png_qemu`.
|
||||
To run the benchmark sample with frida run `make png_frida`.
|
||||
To build everything run `make`. To build for x86 run `make 32`. Note that in
|
||||
x86 bit mode, it is not necessary for afl-fuzz to be built for 32-bit. However,
|
||||
the shared library for frida_mode must be since it is injected into the target
|
||||
process.
|
||||
|
||||
Various tests can be found in subfolders within the `test/` directory. To use
|
||||
these, first run `make` to build any dependencies. Then run `make qemu` or
|
||||
`make frida` to run on either QEMU of FRIDA mode respectively. To run frida
|
||||
tests in 32-bit mode, run `make ARCH=x86 frida`. When switching between
|
||||
architectures it may be necessary to run `make clean` first for a given build
|
||||
target to remove previously generated binaries for a different architecture.
|
||||
|
||||
## Usage
|
||||
FRIDA mode requires some small modifications to `afl-fuzz` and similar tools
|
||||
in AFLplusplus. The intention is that it behaves identically to QEMU, but uses
|
||||
|
||||
FRIDA mode added some small modifications to `afl-fuzz` and similar tools
|
||||
in AFLplusplus. The intention was that it behaves identically to QEMU, but it uses
|
||||
the 'O' switch rather than 'Q'. Whilst the options 'f', 'F', 's' or 'S' may have
|
||||
made more sense for a mode powered by FRIDA Stalker, they were all taken, so
|
||||
instead we use 'O' in hommage to the [author](https://github.com/oleavr) of
|
||||
FRIDA.
|
||||
|
||||
Similarly, the intention is to mimic the use of environment variables used by
|
||||
QEMU where possible (although replacing `s/QEMU/FRIDA/g`). Accodingly, the
|
||||
following options are currently supported.
|
||||
QEMU where possible (by replacing `s/QEMU/FRIDA/g`). Accordingly, the
|
||||
following options are currently supported:
|
||||
|
||||
* `AFL_FRIDA_DEBUG_MAPS` - See `AFL_QEMU_DEBUG_MAPS`
|
||||
* `AFL_FRIDA_EXCLUDE_RANGES` - See `AFL_QEMU_EXCLUDE_RANGES`
|
||||
* `AFL_FRIDA_INST_RANGES` - See `AFL_QEMU_INST_RANGES`
|
||||
* `AFL_FRIDA_PERSISTENT_ADDR` - See `AFL_QEMU_PERSISTENT_ADDR`
|
||||
* `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`
|
||||
|
||||
# Performance
|
||||
To enable the powerful CMPLOG mechanism, set `-c 0` for `afl-fuzz`.
|
||||
|
||||
## Performance
|
||||
|
||||
Additionally, the intention is to be able to make a direct performance
|
||||
comparison between the two approaches. Accordingly, FRIDA mode includes a test
|
||||
target based on the [libpng](https://libpng.sourceforge.io/) benchmark used by
|
||||
[fuzzbench](https://google.github.io/fuzzbench/) and integrated with the
|
||||
comparison between the two approaches. Accordingly, FRIDA mode includes various
|
||||
test targets based on the [libpng](https://libpng.sourceforge.io/) benchmark
|
||||
used by [fuzzbench](https://google.github.io/fuzzbench/) and integrated with the
|
||||
[StandaloneFuzzTargetMain](https://raw.githubusercontent.com/llvm/llvm-project/main/compiler-rt/lib/fuzzer/standalone/StandaloneFuzzTargetMain.c)
|
||||
from the llvm project. This is built and linked without any special
|
||||
modifications to suit FRIDA or QEMU. We use the test data provided with libpng
|
||||
as our corpus.
|
||||
from the llvm project. These tests include basic fork-server support, persistent
|
||||
mode and persistent mode with in-memory test-cases. These are built and linked
|
||||
without any special modifications to suit FRIDA or QEMU. The test data provided
|
||||
with libpng is used as the corpus.
|
||||
|
||||
Whilst not much performance tuning has been completed to date, performance is
|
||||
around 30-50% of that of QEMU mode, however, this gap may reduce with the
|
||||
introduction of persistent mode. Performance can be tested by running
|
||||
`make compare`, albeit a longer time measurement may be required for more
|
||||
accurate results.
|
||||
The intention is to add support for FRIDA mode to the FuzzBench project and
|
||||
perform a like-for-like comparison with QEMU mode to get an accurate
|
||||
appreciation of its performance.
|
||||
|
||||
Whilst [afl_frida](https://github.com/AFLplusplus/AFLplusplus/tree/stable/utils/afl_frida)
|
||||
claims a 5-10x performance increase over QEMU, it has not been possible to
|
||||
reproduce these claims. However, the number of executions per second can vary
|
||||
dramatically as a result of the randomization of the fuzzer input. Some inputs
|
||||
may traverse relatively few paths before being rejected as invalid whilst others
|
||||
may be valid inputs or be subject to much more processing before rejection.
|
||||
Accordingly, it is recommended that testing be carried out over prolongued
|
||||
periods to gather timings which are more than indicative.
|
||||
## Design
|
||||
|
||||
# Design
|
||||
FRIDA mode is supported by using `LD_PRELOAD` (`DYLD_INSERT_LIBRARIES` on macOS)
|
||||
to inject a shared library (`afl-frida-trace.so`) into the target. This shared
|
||||
library is built using the [frida-gum](https://github.com/frida/frida-gum)
|
||||
@ -105,31 +116,179 @@ shared memory region to avoid the need to regenerate instrumented blocks on each
|
||||
fork.
|
||||
|
||||
Whilst FRIDA allows for a normal C function to be used to augment instrumented
|
||||
code, to minimize the costs of storing and restoring all of the registers, FRIDA
|
||||
mode instead makes use of optimized assembly instead on AARCH64 and x86/64
|
||||
targets.
|
||||
code, FRIDA mode instead makes use of optimized assembly instead on AARCH64 and
|
||||
x86/64 targets. By injecting these small snippets of assembly, we avoid having
|
||||
to push and pop the full register context. Note that since this instrumentation
|
||||
is used on every basic block to generate coverage, it has a large impact on
|
||||
performance.
|
||||
|
||||
# Advanced configuration options
|
||||
CMPLOG support also adds code to the assembly, however, at present this code
|
||||
makes use of a basic C function and is yet to be optimized. Since not all
|
||||
instances run CMPLOG mode and instrumentation of the binary is less frequent
|
||||
(only on CMP, SUB and CALL instructions) performance is not quite so critical.
|
||||
|
||||
## Advanced configuration options
|
||||
|
||||
* `AFL_FRIDA_INST_DEBUG_FILE` - File to write raw assembly of original blocks
|
||||
and their instrumented counterparts during block compilation.
|
||||
```
|
||||
***
|
||||
|
||||
Creating block for 0x7ffff7953313:
|
||||
0x7ffff7953313 mov qword ptr [rax], 0
|
||||
0x7ffff795331a add rsp, 8
|
||||
0x7ffff795331e ret
|
||||
|
||||
Generated block 0x7ffff75e98e2
|
||||
0x7ffff75e98e2 mov qword ptr [rax], 0
|
||||
0x7ffff75e98e9 add rsp, 8
|
||||
0x7ffff75e98ed lea rsp, [rsp - 0x80]
|
||||
0x7ffff75e98f5 push rcx
|
||||
0x7ffff75e98f6 movabs rcx, 0x7ffff795331e
|
||||
0x7ffff75e9900 jmp 0x7ffff75e9384
|
||||
|
||||
|
||||
***
|
||||
```
|
||||
* `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_STRICT` - Under certain conditions, Stalker may encroach into
|
||||
excluded regions and generate both instrumented blocks and coverage data (e.g.
|
||||
indirect calls on x86). The excluded block is generally honoured as soon as
|
||||
another function is called within the excluded region and so such encroachment
|
||||
is usually of little consequence. This detail may however, hinder you when
|
||||
checking that the correct number of paths are found for testing purposes or
|
||||
similar. There is a performance penatly for this option during block compilation
|
||||
where we check the block isn't in a list of excluded ranges.
|
||||
* `AFL_FRIDA_INST_TRACE` - Generate some logging when running instrumented code.
|
||||
Requires `AFL_FRIDA_INST_NO_OPTIMIZE`.
|
||||
* `AFL_FRIDA_INST_TRACE` - Log to stdout the address of executed blocks
|
||||
`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.
|
||||
|
||||
# TODO
|
||||
As can be seen from the progress section above, there are a number of features
|
||||
which are missing in its currently form. Chief amongst which is persistent mode.
|
||||
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.
|
||||
```
|
||||
|
||||
gdb \
|
||||
--ex 'set environment AFL_FRIDA_PERSISTENT_ADDR=XXXXXXXXXX' \
|
||||
--ex 'set environment AFL_FRIDA_PERSISTENT_RET=XXXXXXXXXX' \
|
||||
--ex 'set environment AFL_FRIDA_PERSISTENT_DEBUG=1' \
|
||||
--ex 'set environment AFL_DEBUG_CHILD=1' \
|
||||
--ex 'set environment LD_PRELOAD=afl-frida-trace.so' \
|
||||
--args <my-executable> [my arguments]
|
||||
|
||||
```
|
||||
* `AFL_FRIDA_STATS_FILE` - Write statistics information about the code being
|
||||
instrumented to the given file name. The statistics are written only for the
|
||||
child process when new block is instrumented (when the
|
||||
`AFL_FRIDA_STATS_INTERVAL` has expired). Note that simply because a new path is
|
||||
found does not mean a new block needs to be compiled. It could simply be that
|
||||
the existing blocks instrumented have been executed in a different order.
|
||||
```
|
||||
stats
|
||||
-----
|
||||
Index: 2
|
||||
Pid: 1815944
|
||||
Time: 2021-05-28 15:26:41
|
||||
Blocks: 1985
|
||||
Instructions: 9192
|
||||
Avg Instructions / Block: 4
|
||||
|
||||
Call Immediates: 391 (4.25%)
|
||||
Call Immediates Excluded: 65 (0.71%)
|
||||
Call Register: 0 (0.00%)
|
||||
Call Memory: 0 (0.00%)
|
||||
|
||||
Jump Immediates: 202 (2.20%)
|
||||
Jump Register: 10 (0.11%)
|
||||
Jump Memory: 12 (0.13%)
|
||||
|
||||
Conditional Jump Immediates: 1210 (13.16%)
|
||||
Conditional Jump CX Immediate: 0 (0.00%)
|
||||
Conditional Jump Register: 0 (0.00%)
|
||||
Conditional Jump Memory: 0 (0.00%)
|
||||
|
||||
Returns: 159 (0.00%)
|
||||
|
||||
Rip Relative: 247 (0.00%)
|
||||
|
||||
```
|
||||
* `AFL_FRIDA_STATS_INTERVAL` - The maximum frequency to output statistics
|
||||
information. Stats will be written whenever they are updated if the given
|
||||
interval has elapsed since last time they were written.
|
||||
* `AFL_FRIDA_STATS_TRANSITIONS` - Also dump the internal stalker counters to
|
||||
stderr when the regular stats are written. Note that these stats are reset in
|
||||
the child each time a new fork occurs since they are not stored in shared
|
||||
memory. Unfortunately, these stats are internal to stalker, so this is the best
|
||||
we can do for now.
|
||||
```
|
||||
stats
|
||||
-----
|
||||
Index: 2
|
||||
Pid: 1816794
|
||||
Time: 2021-05-28 15:26:41
|
||||
|
||||
|
||||
total_transitions: 786
|
||||
call_imms: 97
|
||||
call_regs: 0
|
||||
call_mems: 0
|
||||
post_call_invokes: 86
|
||||
excluded_call_imms: 29
|
||||
ret_slow_paths: 23
|
||||
|
||||
jmp_imms: 58
|
||||
jmp_mems: 7
|
||||
jmp_regs: 26
|
||||
|
||||
jmp_cond_imms: 460
|
||||
jmp_cond_mems: 0
|
||||
jmp_cond_regs: 0
|
||||
jmp_cond_jcxzs: 0
|
||||
|
||||
jmp_continuations: 0
|
||||
```
|
||||
## FASAN - Frida Address Sanitizer Mode
|
||||
Frida mode also supports FASAN. The design of this is actually quite simple and
|
||||
very similar to that used when instrumenting applications compiled from source.
|
||||
|
||||
### Address Sanitizer Basics
|
||||
|
||||
When Address Sanitizer is used to instrument programs built from source, the
|
||||
compiler first adds a dependency (`DT_NEEDED` entry) for the Address Sanitizer
|
||||
dynamic shared object (DSO). This shared object contains the main logic for Address
|
||||
Sanitizer, including setting and managing up the shadow memory. It also provides
|
||||
replacement implementations for a number of functions in standard libraries.
|
||||
|
||||
These replacements include things like `malloc` and `free` which allows for those
|
||||
allocations to be marked in the shadow memory, but also a number of other fuctions.
|
||||
Consider `memcpy` for example, this is instrumented to validate the paramters
|
||||
(test the source and destination buffers against the shadow memory. This is much
|
||||
easier than instrumenting those standard libraries since, first it would require
|
||||
you to re-compile them and secondly it would mean that the instrumentation would
|
||||
be applied at a more expensive granular level. Lastly, load-widening (typically
|
||||
found in highy optimized code) can also make this instrumentation more difficult.
|
||||
|
||||
Since the DSO is loaded before all of the standard libraries (in fact it insists
|
||||
on being first), the dynamic loader will use it to resolve imports from other
|
||||
modules which depend on it.
|
||||
|
||||
### FASAN Implementation
|
||||
|
||||
FASAN takes a similar approach. It requires the user to add the Address Sanitizer
|
||||
DSO to the `AFL_PRELOAD` environment variable such that it is loaded into the target.
|
||||
Again, it must be first in the list. This means that it is not necessary to
|
||||
instrument the standard libraries to detect when an application has provided an
|
||||
incorrect argument to `memcpy` for example. This avoids issues with load-widening
|
||||
and should also mean a huge improvement in performance.
|
||||
|
||||
FASAN then adds instrumentation for any instrucutions which use memory operands and
|
||||
then calls into the `__asan_loadN` and `__asan_storeN` functions provided by the DSO
|
||||
to validate memory accesses against the shadow memory.
|
||||
|
||||
## TODO
|
||||
|
||||
The next features to be added are Aarch64 and 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.
|
||||
|
13
frida_mode/include/asan.h
Normal file
13
frida_mode/include/asan.h
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef _ASAN_H
|
||||
#define _ASAN_H
|
||||
|
||||
#include "frida-gum.h"
|
||||
|
||||
extern gboolean asan_initialized;
|
||||
|
||||
void asan_init(void);
|
||||
void asan_arch_init(void);
|
||||
void asan_instrument(const cs_insn *instr, GumStalkerIterator *iterator);
|
||||
|
||||
#endif
|
||||
|
11
frida_mode/include/ctx.h
Normal file
11
frida_mode/include/ctx.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef _CTX_H
|
||||
#define _CTX_H
|
||||
|
||||
#include "frida-gum.h"
|
||||
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
gsize ctx_read_reg(GumCpuContext *ctx, x86_reg reg);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
15
frida_mode/include/entry.h
Normal file
15
frida_mode/include/entry.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef _ENTRY_H
|
||||
#define _ENTRY_H
|
||||
|
||||
#include "frida-gum.h"
|
||||
|
||||
extern guint64 entry_start;
|
||||
|
||||
void entry_init(void);
|
||||
|
||||
void entry_run(void);
|
||||
|
||||
void entry_prologue(GumStalkerIterator *iterator, GumStalkerOutput *output);
|
||||
|
||||
#endif
|
||||
|
14
frida_mode/include/frida_cmplog.h
Normal file
14
frida_mode/include/frida_cmplog.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef _CMPLOG_H
|
||||
#define _CMPLOG_H
|
||||
|
||||
extern struct cmp_map *__afl_cmp_map;
|
||||
|
||||
void cmplog_init(void);
|
||||
|
||||
/* Functions to be implemented by the different architectures */
|
||||
void cmplog_instrument(const cs_insn *instr, GumStalkerIterator *iterator);
|
||||
|
||||
gboolean cmplog_is_readable(guint64 addr, size_t size);
|
||||
|
||||
#endif
|
||||
|
@ -1,7 +1,27 @@
|
||||
#ifndef _INSTRUMENT_H
|
||||
#define _INSTRUMENT_H
|
||||
|
||||
#include "frida-gum.h"
|
||||
|
||||
void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output,
|
||||
gpointer user_data);
|
||||
#include "config.h"
|
||||
|
||||
void instrument_init();
|
||||
extern __thread uint64_t previous_pc;
|
||||
extern uint8_t * __afl_area_ptr;
|
||||
extern uint32_t __afl_map_size;
|
||||
|
||||
void instrument_init(void);
|
||||
|
||||
GumStalkerTransformer *instrument_get_transformer(void);
|
||||
|
||||
/* Functions to be implemented by the different architectures */
|
||||
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);
|
||||
#endif
|
||||
|
||||
|
@ -1,4 +1,11 @@
|
||||
#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
|
||||
|
||||
|
13
frida_mode/include/lib.h
Normal file
13
frida_mode/include/lib.h
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef _LIB_H
|
||||
#define _LIB_H
|
||||
|
||||
#include "frida-gum.h"
|
||||
|
||||
void lib_init(void);
|
||||
|
||||
guint64 lib_get_text_base(void);
|
||||
|
||||
guint64 lib_get_text_limit(void);
|
||||
|
||||
#endif
|
||||
|
9
frida_mode/include/output.h
Normal file
9
frida_mode/include/output.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef _OUTPUT_H
|
||||
#define _OUTPUT_H
|
||||
|
||||
#include "frida-gum.h"
|
||||
|
||||
void output_init(void);
|
||||
|
||||
#endif
|
||||
|
35
frida_mode/include/persistent.h
Normal file
35
frida_mode/include/persistent.h
Normal file
@ -0,0 +1,35 @@
|
||||
|
||||
#ifndef _PERSISTENT_H
|
||||
#define _PERSISTENT_H
|
||||
|
||||
#include "frida-gum.h"
|
||||
#include "config.h"
|
||||
|
||||
typedef struct arch_api_regs api_regs;
|
||||
|
||||
typedef void (*afl_persistent_hook_fn)(api_regs *regs, uint64_t guest_base,
|
||||
uint8_t *input_buf,
|
||||
uint32_t input_buf_len);
|
||||
|
||||
extern int __afl_persistent_loop(unsigned int max_cnt);
|
||||
|
||||
extern unsigned int * __afl_fuzz_len;
|
||||
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;
|
||||
|
||||
void persistent_init(void);
|
||||
|
||||
/* Functions to be implemented by the different architectures */
|
||||
gboolean persistent_is_supported(void);
|
||||
|
||||
void persistent_prologue(GumStalkerOutput *output);
|
||||
void persistent_epilogue(GumStalkerOutput *output);
|
||||
|
||||
#endif
|
||||
|
@ -1,5 +1,11 @@
|
||||
void prefetch_init();
|
||||
void prefetch_start(GumStalker *stalker);
|
||||
void prefetch_write(void *addr);
|
||||
void prefetch_read(GumStalker *stalker);
|
||||
#ifndef _PREFETCH_H
|
||||
#define _PREFETCH_H
|
||||
|
||||
#include "frida-gum.h"
|
||||
|
||||
void prefetch_init(void);
|
||||
void prefetch_write(void *addr);
|
||||
void prefetch_read(void);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1,6 +1,13 @@
|
||||
#ifndef _RANGES_H
|
||||
#define _RANGES_H
|
||||
|
||||
#include "frida-gum.h"
|
||||
|
||||
void ranges_init(GumStalker *stalker);
|
||||
void ranges_init(void);
|
||||
|
||||
gboolean range_is_excluded(gpointer address);
|
||||
|
||||
void ranges_exclude();
|
||||
|
||||
#endif
|
||||
|
||||
|
11
frida_mode/include/stalker.h
Normal file
11
frida_mode/include/stalker.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef _STALKER_H
|
||||
#define _STALKER_H
|
||||
|
||||
#include "frida-gum.h"
|
||||
|
||||
void stalker_init(void);
|
||||
GumStalker *stalker_get(void);
|
||||
void stalker_start(void);
|
||||
|
||||
#endif
|
||||
|
28
frida_mode/include/stats.h
Normal file
28
frida_mode/include/stats.h
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef _STATS_H
|
||||
#define _STATS_H
|
||||
|
||||
#include "frida-gum.h"
|
||||
|
||||
typedef struct {
|
||||
|
||||
guint64 num_blocks;
|
||||
guint64 num_instructions;
|
||||
guint64 stats_last_time;
|
||||
guint64 stats_idx;
|
||||
guint64 transitions_idx;
|
||||
|
||||
} stats_data_header_t;
|
||||
|
||||
extern stats_data_header_t *stats_data;
|
||||
|
||||
void stats_init(void);
|
||||
void stats_collect(const cs_insn *instr, gboolean begin);
|
||||
void stats_print(char *format, ...);
|
||||
|
||||
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);
|
||||
|
||||
#endif
|
||||
|
14
frida_mode/include/util.h
Normal file
14
frida_mode/include/util.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef _UTIL_H
|
||||
#define _UTIL_H
|
||||
|
||||
#include "frida-gum.h"
|
||||
|
||||
#define UNUSED_PARAMETER(x) (void)(x)
|
||||
#define IGNORED_RETURN(x) (void)!(x)
|
||||
|
||||
guint64 util_read_address(char *key);
|
||||
|
||||
guint64 util_read_num(char *key);
|
||||
|
||||
#endif
|
||||
|
24
frida_mode/src/asan/asan.c
Normal file
24
frida_mode/src/asan/asan.c
Normal file
@ -0,0 +1,24 @@
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "asan.h"
|
||||
|
||||
gboolean asan_initialized = FALSE;
|
||||
|
||||
void asan_init(void) {
|
||||
|
||||
if (getenv("AFL_USE_FASAN") != NULL) {
|
||||
|
||||
OKF("Frida ASAN mode enabled");
|
||||
asan_arch_init();
|
||||
asan_initialized = TRUE;
|
||||
|
||||
} else {
|
||||
|
||||
OKF("Frida ASAN mode disabled");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
28
frida_mode/src/asan/asan_arm.c
Normal file
28
frida_mode/src/asan/asan_arm.c
Normal file
@ -0,0 +1,28 @@
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "asan.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined(__arm__)
|
||||
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");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void asan_arch_init(void) {
|
||||
|
||||
FATAL("ASAN mode not supported on this architecture");
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
28
frida_mode/src/asan/asan_arm64.c
Normal file
28
frida_mode/src/asan/asan_arm64.c
Normal file
@ -0,0 +1,28 @@
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "asan.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined(__aarch64__)
|
||||
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");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void asan_arch_init(void) {
|
||||
|
||||
FATAL("ASAN mode not supported on this architecture");
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
93
frida_mode/src/asan/asan_x64.c
Normal file
93
frida_mode/src/asan/asan_x64.c
Normal file
@ -0,0 +1,93 @@
|
||||
#include <dlfcn.h>
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "asan.h"
|
||||
#include "ctx.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined(__x86_64__)
|
||||
|
||||
typedef void (*asan_loadN_t)(uint64_t address, uint8_t size);
|
||||
typedef void (*asan_storeN_t)(uint64_t 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) {
|
||||
|
||||
UNUSED_PARAMETER(user_data);
|
||||
|
||||
cs_x86_op * operand = (cs_x86_op *)user_data;
|
||||
x86_op_mem *mem = &operand->mem;
|
||||
gsize base = 0;
|
||||
gsize index = 0;
|
||||
gsize address;
|
||||
uint8_t size;
|
||||
|
||||
if (mem->base != X86_REG_INVALID) { base = ctx_read_reg(ctx, mem->base); }
|
||||
|
||||
if (mem->index != X86_REG_INVALID) { index = ctx_read_reg(ctx, mem->index); }
|
||||
|
||||
address = base + (mem->scale * index) + mem->disp;
|
||||
size = operand->size;
|
||||
|
||||
if (operand->access == CS_AC_READ) {
|
||||
|
||||
asan_loadN(address, size);
|
||||
|
||||
} else if (operand->access == CS_AC_WRITE) {
|
||||
|
||||
asan_storeN(address, size);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void asan_instrument(const cs_insn *instr, GumStalkerIterator *iterator) {
|
||||
|
||||
UNUSED_PARAMETER(iterator);
|
||||
|
||||
cs_x86 x86 = instr->detail->x86;
|
||||
cs_x86_op * operand;
|
||||
x86_op_mem *mem;
|
||||
cs_x86_op * ctx;
|
||||
|
||||
if (!asan_initialized) return;
|
||||
|
||||
if (instr->id == X86_INS_LEA) return;
|
||||
|
||||
if (instr->id == X86_INS_NOP) return;
|
||||
|
||||
for (uint8_t i = 0; i < x86.op_count; i++) {
|
||||
|
||||
operand = &x86.operands[i];
|
||||
|
||||
if (operand->type != X86_OP_MEM) { continue; }
|
||||
|
||||
mem = &operand->mem;
|
||||
if (mem->segment != X86_REG_INVALID) { continue; }
|
||||
|
||||
ctx = g_malloc0(sizeof(cs_x86_op));
|
||||
memcpy(ctx, operand, sizeof(cs_x86_op));
|
||||
gum_stalker_iterator_put_callout(iterator, asan_callout, ctx, g_free);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void asan_arch_init(void) {
|
||||
|
||||
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'");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
93
frida_mode/src/asan/asan_x86.c
Normal file
93
frida_mode/src/asan/asan_x86.c
Normal file
@ -0,0 +1,93 @@
|
||||
#include <dlfcn.h>
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "asan.h"
|
||||
#include "ctx.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined(__i386__)
|
||||
|
||||
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) {
|
||||
|
||||
UNUSED_PARAMETER(user_data);
|
||||
|
||||
cs_x86_op * operand = (cs_x86_op *)user_data;
|
||||
x86_op_mem *mem = &operand->mem;
|
||||
gsize base = 0;
|
||||
gsize index = 0;
|
||||
gsize address;
|
||||
uint8_t size;
|
||||
|
||||
if (mem->base != X86_REG_INVALID) { base = ctx_read_reg(ctx, mem->base); }
|
||||
|
||||
if (mem->index != X86_REG_INVALID) { index = ctx_read_reg(ctx, mem->index); }
|
||||
|
||||
address = base + (mem->scale * index) + mem->disp;
|
||||
size = operand->size;
|
||||
|
||||
if (operand->access == CS_AC_READ) {
|
||||
|
||||
asan_loadN(address, size);
|
||||
|
||||
} else if (operand->access == CS_AC_WRITE) {
|
||||
|
||||
asan_storeN(address, size);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void asan_instrument(const cs_insn *instr, GumStalkerIterator *iterator) {
|
||||
|
||||
UNUSED_PARAMETER(iterator);
|
||||
|
||||
cs_x86 x86 = instr->detail->x86;
|
||||
cs_x86_op * operand;
|
||||
x86_op_mem *mem;
|
||||
cs_x86_op * ctx;
|
||||
|
||||
if (!asan_initialized) return;
|
||||
|
||||
if (instr->id == X86_INS_LEA) return;
|
||||
|
||||
if (instr->id == X86_INS_NOP) return;
|
||||
|
||||
for (uint8_t i = 0; i < x86.op_count; i++) {
|
||||
|
||||
operand = &x86.operands[i];
|
||||
|
||||
if (operand->type != X86_OP_MEM) { continue; }
|
||||
|
||||
mem = &operand->mem;
|
||||
if (mem->segment != X86_REG_INVALID) { continue; }
|
||||
|
||||
ctx = g_malloc0(sizeof(cs_x86_op));
|
||||
memcpy(ctx, operand, sizeof(cs_x86_op));
|
||||
gum_stalker_iterator_put_callout(iterator, asan_callout, ctx, g_free);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void asan_arch_init(void) {
|
||||
|
||||
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'");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
87
frida_mode/src/cmplog/cmplog.c
Normal file
87
frida_mode/src/cmplog/cmplog.c
Normal file
@ -0,0 +1,87 @@
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#define DEFAULT_MMAP_MIN_ADDR (32UL << 10)
|
||||
|
||||
extern struct cmp_map *__afl_cmp_map;
|
||||
|
||||
static GArray *cmplog_ranges = NULL;
|
||||
|
||||
static gboolean cmplog_range(const GumRangeDetails *details,
|
||||
gpointer user_data) {
|
||||
|
||||
UNUSED_PARAMETER(user_data);
|
||||
GumMemoryRange range = *details->range;
|
||||
g_array_append_val(cmplog_ranges, range);
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
static gint cmplog_sort(gconstpointer a, gconstpointer b) {
|
||||
|
||||
return ((GumMemoryRange *)b)->base_address -
|
||||
((GumMemoryRange *)a)->base_address;
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static gboolean cmplog_contains(GumAddress inner_base, GumAddress inner_limit,
|
||||
GumAddress outer_base, GumAddress outer_limit) {
|
||||
|
||||
return (inner_base >= outer_base && inner_limit <= outer_limit);
|
||||
|
||||
}
|
||||
|
||||
gboolean cmplog_is_readable(guint64 addr, size_t size) {
|
||||
|
||||
if (cmplog_ranges == NULL) FATAL("CMPLOG not initialized");
|
||||
|
||||
/*
|
||||
* The Linux kernel prevents mmap from allocating from the very bottom of the
|
||||
* address space to mitigate NULL pointer dereference attacks. The exact size
|
||||
* is set by sysctl by setting mmap_min_addr and 64k is suggested on most
|
||||
* platforms with 32k on ARM systems. We therefore fail fast if the address
|
||||
* is lower than this. This should avoid some overhead when functions are
|
||||
* called where one of the parameters is a size, or a some other small value.
|
||||
*/
|
||||
if (addr < DEFAULT_MMAP_MIN_ADDR) { 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;
|
||||
|
||||
if (cmplog_contains(inner_base, inner_limit, outer_base, outer_limit))
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
19
frida_mode/src/cmplog/cmplog_arm.c
Normal file
19
frida_mode/src/cmplog/cmplog_arm.c
Normal file
@ -0,0 +1,19 @@
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "frida_cmplog.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined(__arm__)
|
||||
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");
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
19
frida_mode/src/cmplog/cmplog_arm64.c
Normal file
19
frida_mode/src/cmplog/cmplog_arm64.c
Normal file
@ -0,0 +1,19 @@
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "frida_cmplog.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined(__aarch64__)
|
||||
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");
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
272
frida_mode/src/cmplog/cmplog_x64.c
Normal file
272
frida_mode/src/cmplog/cmplog_x64.c
Normal file
@ -0,0 +1,272 @@
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "cmplog.h"
|
||||
|
||||
#include "ctx.h"
|
||||
#include "frida_cmplog.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined(__x86_64__)
|
||||
|
||||
typedef struct {
|
||||
|
||||
x86_op_type type;
|
||||
uint8_t size;
|
||||
|
||||
union {
|
||||
|
||||
x86_op_mem mem;
|
||||
x86_reg reg;
|
||||
int64_t imm;
|
||||
|
||||
};
|
||||
|
||||
} cmplog_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
cmplog_ctx_t operand1;
|
||||
cmplog_ctx_t operand2;
|
||||
|
||||
} cmplog_pair_ctx_t;
|
||||
|
||||
static gboolean cmplog_read_mem(GumCpuContext *ctx, uint8_t size,
|
||||
x86_op_mem *mem, gsize *val) {
|
||||
|
||||
gsize base = 0;
|
||||
gsize index = 0;
|
||||
gsize address;
|
||||
|
||||
if (mem->base != X86_REG_INVALID) base = ctx_read_reg(ctx, mem->base);
|
||||
|
||||
if (mem->index != X86_REG_INVALID) index = ctx_read_reg(ctx, mem->index);
|
||||
|
||||
address = base + (index * mem->scale) + 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 X86_OP_REG:
|
||||
*val = ctx_read_reg(context, ctx->reg);
|
||||
return TRUE;
|
||||
case X86_OP_IMM:
|
||||
*val = ctx->imm;
|
||||
return TRUE;
|
||||
case X86_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 = ctx_read_reg(context, X86_REG_RIP);
|
||||
gsize rdi = ctx_read_reg(context, X86_REG_RDI);
|
||||
gsize rsi = ctx_read_reg(context, X86_REG_RSI);
|
||||
|
||||
if (((G_MAXULONG - rdi) < 32) || ((G_MAXULONG - rsi) < 32)) return;
|
||||
|
||||
if (!cmplog_is_readable(rdi, 32) || !cmplog_is_readable(rsi, 32)) return;
|
||||
|
||||
void *ptr1 = GSIZE_TO_POINTER(rdi);
|
||||
void *ptr2 = GSIZE_TO_POINTER(rsi);
|
||||
|
||||
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_x86_op * operand) {
|
||||
|
||||
ctx->type = operand->type;
|
||||
ctx->size = operand->size;
|
||||
switch (operand->type) {
|
||||
|
||||
case X86_OP_REG:
|
||||
gum_memcpy(&ctx->reg, &operand->reg, sizeof(x86_reg));
|
||||
break;
|
||||
case X86_OP_IMM:
|
||||
gum_memcpy(&ctx->imm, &operand->imm, sizeof(int64_t));
|
||||
break;
|
||||
case X86_OP_MEM:
|
||||
gum_memcpy(&ctx->mem, &operand->mem, sizeof(x86_op_mem));
|
||||
break;
|
||||
default:
|
||||
FATAL("Invalid operand type: %d\n", operand->type);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void cmplog_instrument_call(const cs_insn * instr,
|
||||
GumStalkerIterator *iterator) {
|
||||
|
||||
cs_x86 x86 = instr->detail->x86;
|
||||
cs_x86_op *operand;
|
||||
|
||||
if (instr->id != X86_INS_CALL) return;
|
||||
|
||||
if (x86.op_count != 1) return;
|
||||
|
||||
operand = &x86.operands[0];
|
||||
|
||||
if (operand->type == X86_OP_INVALID) return;
|
||||
if (operand->type == X86_OP_MEM && operand->mem.segment != X86_REG_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 = ctx_read_reg(context, X86_REG_RIP);
|
||||
|
||||
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 (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; }
|
||||
|
||||
cmplog_handle_cmp_sub(context, operand1, operand2, ctx->operand1.size);
|
||||
|
||||
}
|
||||
|
||||
static void cmplog_instrument_cmp_sub_put_callout(GumStalkerIterator *iterator,
|
||||
cs_x86_op * operand1,
|
||||
cs_x86_op *operand2) {
|
||||
|
||||
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);
|
||||
|
||||
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_x86 x86 = instr->detail->x86;
|
||||
cs_x86_op *operand1;
|
||||
cs_x86_op *operand2;
|
||||
|
||||
switch (instr->id) {
|
||||
|
||||
case X86_INS_CMP:
|
||||
case X86_INS_SUB:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (x86.op_count != 2) return;
|
||||
|
||||
operand1 = &x86.operands[0];
|
||||
operand2 = &x86.operands[1];
|
||||
|
||||
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;
|
||||
|
||||
cmplog_instrument_cmp_sub_put_callout(iterator, operand1, operand2);
|
||||
|
||||
}
|
||||
|
||||
void cmplog_instrument(const cs_insn *instr, GumStalkerIterator *iterator) {
|
||||
|
||||
if (__afl_cmp_map == NULL) return;
|
||||
|
||||
cmplog_instrument_call(instr, iterator);
|
||||
cmplog_instrument_cmp_sub(instr, iterator);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
277
frida_mode/src/cmplog/cmplog_x86.c
Normal file
277
frida_mode/src/cmplog/cmplog_x86.c
Normal file
@ -0,0 +1,277 @@
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "cmplog.h"
|
||||
|
||||
#include "ctx.h"
|
||||
#include "frida_cmplog.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined(__i386__)
|
||||
|
||||
typedef struct {
|
||||
|
||||
x86_op_type type;
|
||||
uint8_t size;
|
||||
|
||||
union {
|
||||
|
||||
x86_op_mem mem;
|
||||
x86_reg reg;
|
||||
int64_t imm;
|
||||
|
||||
};
|
||||
|
||||
} cmplog_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
cmplog_ctx_t operand1;
|
||||
cmplog_ctx_t operand2;
|
||||
|
||||
} cmplog_pair_ctx_t;
|
||||
|
||||
static gboolean cmplog_read_mem(GumCpuContext *ctx, uint8_t size,
|
||||
x86_op_mem *mem, gsize *val) {
|
||||
|
||||
gsize base = 0;
|
||||
gsize index = 0;
|
||||
gsize address;
|
||||
|
||||
if (mem->base != X86_REG_INVALID) base = ctx_read_reg(ctx, mem->base);
|
||||
|
||||
if (mem->index != X86_REG_INVALID) index = ctx_read_reg(ctx, mem->index);
|
||||
|
||||
address = base + (index * mem->scale) + 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;
|
||||
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 X86_OP_REG:
|
||||
*val = ctx_read_reg(context, ctx->reg);
|
||||
return TRUE;
|
||||
case X86_OP_IMM:
|
||||
*val = ctx->imm;
|
||||
return TRUE;
|
||||
case X86_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 = ctx_read_reg(context, X86_REG_EIP);
|
||||
gsize *esp = (gsize *)ctx_read_reg(context, X86_REG_ESP);
|
||||
|
||||
if (!cmplog_is_readable(GPOINTER_TO_SIZE(esp), 12)) return;
|
||||
|
||||
/*
|
||||
* This callout is place immediately before the call instruction, and hence
|
||||
* the return address is not yet pushed on the top of the stack.
|
||||
*/
|
||||
gsize arg1 = esp[0];
|
||||
gsize arg2 = esp[1];
|
||||
|
||||
if (((G_MAXULONG - arg1) < 32) || ((G_MAXULONG - arg2) < 32)) return;
|
||||
|
||||
if (!cmplog_is_readable(arg1, 32) || !cmplog_is_readable(arg2, 32)) return;
|
||||
|
||||
void *ptr1 = GSIZE_TO_POINTER(arg1);
|
||||
void *ptr2 = GSIZE_TO_POINTER(arg2);
|
||||
|
||||
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_x86_op * operand) {
|
||||
|
||||
ctx->type = operand->type;
|
||||
ctx->size = operand->size;
|
||||
switch (operand->type) {
|
||||
|
||||
case X86_OP_REG:
|
||||
gum_memcpy(&ctx->reg, &operand->reg, sizeof(x86_reg));
|
||||
break;
|
||||
case X86_OP_IMM:
|
||||
gum_memcpy(&ctx->imm, &operand->imm, sizeof(int64_t));
|
||||
break;
|
||||
case X86_OP_MEM:
|
||||
gum_memcpy(&ctx->mem, &operand->mem, sizeof(x86_op_mem));
|
||||
break;
|
||||
default:
|
||||
FATAL("Invalid operand type: %d\n", operand->type);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void cmplog_instrument_call(const cs_insn * instr,
|
||||
GumStalkerIterator *iterator) {
|
||||
|
||||
cs_x86 x86 = instr->detail->x86;
|
||||
cs_x86_op *operand;
|
||||
|
||||
if (instr->id != X86_INS_CALL) return;
|
||||
|
||||
if (x86.op_count != 1) return;
|
||||
|
||||
operand = &x86.operands[0];
|
||||
|
||||
if (operand->type == X86_OP_INVALID) return;
|
||||
if (operand->type == X86_OP_MEM && operand->mem.segment != X86_REG_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 = ctx_read_reg(context, X86_REG_EIP);
|
||||
|
||||
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 (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; }
|
||||
|
||||
cmplog_handle_cmp_sub(context, operand1, operand2, ctx->operand1.size);
|
||||
|
||||
}
|
||||
|
||||
static void cmplog_instrument_cmp_sub_put_callout(GumStalkerIterator *iterator,
|
||||
cs_x86_op * operand1,
|
||||
cs_x86_op *operand2) {
|
||||
|
||||
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);
|
||||
|
||||
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_x86 x86 = instr->detail->x86;
|
||||
cs_x86_op *operand1;
|
||||
cs_x86_op *operand2;
|
||||
|
||||
switch (instr->id) {
|
||||
|
||||
case X86_INS_CMP:
|
||||
case X86_INS_SUB:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (x86.op_count != 2) return;
|
||||
|
||||
operand1 = &x86.operands[0];
|
||||
operand2 = &x86.operands[1];
|
||||
|
||||
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;
|
||||
|
||||
cmplog_instrument_cmp_sub_put_callout(iterator, operand1, operand2);
|
||||
|
||||
}
|
||||
|
||||
void cmplog_instrument(const cs_insn *instr, GumStalkerIterator *iterator) {
|
||||
|
||||
if (__afl_cmp_map == NULL) return;
|
||||
|
||||
cmplog_instrument_call(instr, iterator);
|
||||
cmplog_instrument_cmp_sub(instr, iterator);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
114
frida_mode/src/ctx/ctx_x64.c
Normal file
114
frida_mode/src/ctx/ctx_x64.c
Normal file
@ -0,0 +1,114 @@
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "ctx.h"
|
||||
|
||||
#if defined(__x86_64__)
|
||||
|
||||
#define X86_REG_8L(LABEL, REG) \
|
||||
case LABEL: { \
|
||||
\
|
||||
return REG & GUM_INT8_MASK; \
|
||||
\
|
||||
}
|
||||
|
||||
#define X86_REG_8H(LABEL, REG) \
|
||||
case LABEL: { \
|
||||
\
|
||||
return (REG & GUM_INT16_MASK) >> 8; \
|
||||
\
|
||||
}
|
||||
|
||||
#define X86_REG_16(LABEL, REG) \
|
||||
case LABEL: { \
|
||||
\
|
||||
return (REG & GUM_INT16_MASK); \
|
||||
\
|
||||
}
|
||||
|
||||
#define X86_REG_32(LABEL, REG) \
|
||||
case LABEL: { \
|
||||
\
|
||||
return (REG & GUM_INT32_MASK); \
|
||||
\
|
||||
}
|
||||
|
||||
#define X86_REG_64(LABEL, REG) \
|
||||
case LABEL: { \
|
||||
\
|
||||
return (REG); \
|
||||
\
|
||||
}
|
||||
|
||||
gsize ctx_read_reg(GumX64CpuContext *ctx, x86_reg reg) {
|
||||
|
||||
switch (reg) {
|
||||
|
||||
X86_REG_8L(X86_REG_AL, ctx->rax)
|
||||
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_BPL, ctx->rbp)
|
||||
X86_REG_8L(X86_REG_SIL, ctx->rsi)
|
||||
X86_REG_8L(X86_REG_DIL, ctx->rdi)
|
||||
|
||||
X86_REG_8H(X86_REG_AH, ctx->rax)
|
||||
X86_REG_8H(X86_REG_BH, ctx->rbx)
|
||||
X86_REG_8H(X86_REG_CH, ctx->rcx)
|
||||
X86_REG_8H(X86_REG_DH, ctx->rdx)
|
||||
|
||||
X86_REG_16(X86_REG_AX, ctx->rax)
|
||||
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_DI, ctx->rdi)
|
||||
X86_REG_16(X86_REG_SI, ctx->rsi)
|
||||
X86_REG_16(X86_REG_BP, ctx->rbp)
|
||||
|
||||
X86_REG_32(X86_REG_EAX, ctx->rax)
|
||||
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)
|
||||
X86_REG_32(X86_REG_EDI, ctx->rdi)
|
||||
X86_REG_32(X86_REG_R8D, ctx->r8)
|
||||
X86_REG_32(X86_REG_R9D, ctx->r9)
|
||||
X86_REG_32(X86_REG_R10D, ctx->r10)
|
||||
X86_REG_32(X86_REG_R11D, ctx->r11)
|
||||
X86_REG_32(X86_REG_R12D, ctx->r12)
|
||||
X86_REG_32(X86_REG_R13D, ctx->r13)
|
||||
X86_REG_32(X86_REG_R14D, ctx->r14)
|
||||
X86_REG_32(X86_REG_R15D, ctx->r15)
|
||||
X86_REG_32(X86_REG_EIP, ctx->rip)
|
||||
|
||||
X86_REG_64(X86_REG_RAX, ctx->rax)
|
||||
X86_REG_64(X86_REG_RCX, ctx->rcx)
|
||||
X86_REG_64(X86_REG_RDX, ctx->rdx)
|
||||
X86_REG_64(X86_REG_RBX, ctx->rbx)
|
||||
X86_REG_64(X86_REG_RSP, ctx->rsp)
|
||||
X86_REG_64(X86_REG_RBP, ctx->rbp)
|
||||
X86_REG_64(X86_REG_RSI, ctx->rsi)
|
||||
X86_REG_64(X86_REG_RDI, ctx->rdi)
|
||||
X86_REG_64(X86_REG_R8, ctx->r8)
|
||||
X86_REG_64(X86_REG_R9, ctx->r9)
|
||||
X86_REG_64(X86_REG_R10, ctx->r10)
|
||||
X86_REG_64(X86_REG_R11, ctx->r11)
|
||||
X86_REG_64(X86_REG_R12, ctx->r12)
|
||||
X86_REG_64(X86_REG_R13, ctx->r13)
|
||||
X86_REG_64(X86_REG_R14, ctx->r14)
|
||||
X86_REG_64(X86_REG_R15, ctx->r15)
|
||||
X86_REG_64(X86_REG_RIP, ctx->rip)
|
||||
|
||||
default:
|
||||
FATAL("Failed to read register: %d", reg);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
81
frida_mode/src/ctx/ctx_x86.c
Normal file
81
frida_mode/src/ctx/ctx_x86.c
Normal file
@ -0,0 +1,81 @@
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "ctx.h"
|
||||
|
||||
#if defined(__i386__)
|
||||
|
||||
#define X86_REG_8L(LABEL, REG) \
|
||||
case LABEL: { \
|
||||
\
|
||||
return REG & GUM_INT8_MASK; \
|
||||
\
|
||||
}
|
||||
|
||||
#define X86_REG_8H(LABEL, REG) \
|
||||
case LABEL: { \
|
||||
\
|
||||
return (REG & GUM_INT16_MASK) >> 8; \
|
||||
\
|
||||
}
|
||||
|
||||
#define X86_REG_16(LABEL, REG) \
|
||||
case LABEL: { \
|
||||
\
|
||||
return (REG & GUM_INT16_MASK); \
|
||||
\
|
||||
}
|
||||
|
||||
#define X86_REG_32(LABEL, REG) \
|
||||
case LABEL: { \
|
||||
\
|
||||
return (REG & GUM_INT32_MASK); \
|
||||
\
|
||||
}
|
||||
|
||||
gsize ctx_read_reg(GumIA32CpuContext *ctx, x86_reg reg) {
|
||||
|
||||
switch (reg) {
|
||||
|
||||
X86_REG_8L(X86_REG_AL, ctx->eax)
|
||||
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_BPL, ctx->ebp)
|
||||
X86_REG_8L(X86_REG_SIL, ctx->esi)
|
||||
X86_REG_8L(X86_REG_DIL, ctx->edi)
|
||||
|
||||
X86_REG_8H(X86_REG_AH, ctx->eax)
|
||||
X86_REG_8H(X86_REG_BH, ctx->ebx)
|
||||
X86_REG_8H(X86_REG_CH, ctx->ecx)
|
||||
X86_REG_8H(X86_REG_DH, ctx->edx)
|
||||
|
||||
X86_REG_16(X86_REG_AX, ctx->eax)
|
||||
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_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_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)
|
||||
X86_REG_32(X86_REG_EDI, ctx->edi)
|
||||
X86_REG_32(X86_REG_EIP, ctx->eip)
|
||||
|
||||
default:
|
||||
FATAL("Failed to read register: %d", reg);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
50
frida_mode/src/entry.c
Normal file
50
frida_mode/src/entry.c
Normal file
@ -0,0 +1,50 @@
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "entry.h"
|
||||
#include "instrument.h"
|
||||
#include "stalker.h"
|
||||
#include "util.h"
|
||||
|
||||
extern void __afl_manual_init();
|
||||
|
||||
guint64 entry_start = 0;
|
||||
|
||||
static void entry_launch(void) {
|
||||
|
||||
__afl_manual_init();
|
||||
|
||||
/* Child here */
|
||||
previous_pc = 0;
|
||||
|
||||
}
|
||||
|
||||
void entry_init(void) {
|
||||
|
||||
entry_start = util_read_address("AFL_ENTRYPOINT");
|
||||
OKF("entry_point: 0x%016" G_GINT64_MODIFIER "X", entry_start);
|
||||
|
||||
}
|
||||
|
||||
void entry_run(void) {
|
||||
|
||||
if (entry_start == 0) { entry_launch(); }
|
||||
|
||||
}
|
||||
|
||||
static void entry_callout(GumCpuContext *cpu_context, gpointer user_data) {
|
||||
|
||||
UNUSED_PARAMETER(cpu_context);
|
||||
UNUSED_PARAMETER(user_data);
|
||||
entry_launch();
|
||||
|
||||
}
|
||||
|
||||
void entry_prologue(GumStalkerIterator *iterator, GumStalkerOutput *output) {
|
||||
|
||||
UNUSED_PARAMETER(output);
|
||||
gum_stalker_iterator_put_callout(iterator, entry_callout, NULL, NULL);
|
||||
|
||||
}
|
||||
|
@ -1,271 +0,0 @@
|
||||
#include "frida-gum.h"
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
#include "prefetch.h"
|
||||
#include "ranges.h"
|
||||
#include "unistd.h"
|
||||
|
||||
extern uint8_t *__afl_area_ptr;
|
||||
extern u32 __afl_map_size;
|
||||
|
||||
uint64_t __thread previous_pc = 0;
|
||||
GumAddress current_log_impl = GUM_ADDRESS(0);
|
||||
|
||||
static gboolean tracing = false;
|
||||
static gboolean optimize = false;
|
||||
static gboolean strict = false;
|
||||
|
||||
#if defined(__x86_64__)
|
||||
static const guint8 afl_log_code[] = {
|
||||
|
||||
0x9c, /* pushfq */
|
||||
0x50, /* push rax */
|
||||
0x51, /* push rcx */
|
||||
0x52, /* push rdx */
|
||||
|
||||
0x48, 0x8d, 0x05, 0x27,
|
||||
0x00, 0x00, 0x00, /* lea rax, sym._afl_area_ptr_ptr */
|
||||
0x48, 0x8b, 0x00, /* mov rax, qword [rax] */
|
||||
0x48, 0x8b, 0x00, /* mov rax, qword [rax] */
|
||||
0x48, 0x8d, 0x0d, 0x22,
|
||||
0x00, 0x00, 0x00, /* lea rcx, sym.previous_pc */
|
||||
0x48, 0x8b, 0x11, /* mov rdx, qword [rcx] */
|
||||
0x48, 0x8b, 0x12, /* mov rdx, qword [rdx] */
|
||||
0x48, 0x31, 0xfa, /* xor rdx, rdi */
|
||||
0xfe, 0x04, 0x10, /* inc byte [rax + rdx] */
|
||||
0x48, 0xd1, 0xef, /* shr rdi, 1 */
|
||||
0x48, 0x8b, 0x01, /* mov rax, qword [rcx] */
|
||||
0x48, 0x89, 0x38, /* mov qword [rax], rdi */
|
||||
|
||||
0x5a, /* pop rdx */
|
||||
0x59, /* pop rcx */
|
||||
0x58, /* pop rax */
|
||||
0x9d, /* popfq */
|
||||
|
||||
0xc3, /* ret */
|
||||
|
||||
/* Read-only data goes here: */
|
||||
/* uint8_t** afl_area_ptr_ptr */
|
||||
/* uint64_t* afl_prev_loc_ptr */
|
||||
|
||||
};
|
||||
|
||||
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;
|
||||
GumX86Writer *cw = output->writer.x86;
|
||||
|
||||
if (current_log_impl == 0 ||
|
||||
!gum_x86_writer_can_branch_directly_between(cw->pc, current_log_impl) ||
|
||||
!gum_x86_writer_can_branch_directly_between(cw->pc + 128,
|
||||
current_log_impl)) {
|
||||
|
||||
gconstpointer after_log_impl = cw->code + 1;
|
||||
|
||||
gum_x86_writer_put_jmp_near_label(cw, after_log_impl);
|
||||
|
||||
current_log_impl = cw->pc;
|
||||
gum_x86_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;
|
||||
gum_x86_writer_put_bytes(cw, (const guint8 *)&afl_area_ptr_ptr,
|
||||
sizeof(afl_area_ptr_ptr));
|
||||
gum_x86_writer_put_bytes(cw, (const guint8 *)&afl_prev_loc_ptr,
|
||||
sizeof(afl_prev_loc_ptr));
|
||||
|
||||
gum_x86_writer_put_label(cw, after_log_impl);
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RDI, area_offset);
|
||||
gum_x86_writer_put_call_address(cw, current_log_impl);
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_REG_RDI);
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
GUM_RED_ZONE_SIZE);
|
||||
|
||||
}
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
static const guint8 afl_log_code[] = {
|
||||
|
||||
// __afl_area_ptr[current_pc ^ previous_pc]++;
|
||||
// previous_pc = current_pc >> 1;
|
||||
0xE1, 0x0B, 0xBF, 0xA9, // stp x1, x2, [sp, -0x10]!
|
||||
0xE3, 0x13, 0xBF, 0xA9, // stp x3, x4, [sp, -0x10]!
|
||||
|
||||
// x0 = current_pc
|
||||
0xc1, 0x01, 0x00, 0x58, // ldr x1, #0x38, =&__afl_area_ptr
|
||||
0x21, 0x00, 0x40, 0xf9, // ldr x1, [x1] (=__afl_area_ptr)
|
||||
|
||||
0xc2, 0x01, 0x00, 0x58, // ldr x2, #0x38, =&previous_pc
|
||||
0x42, 0x00, 0x40, 0xf9, // ldr x2, [x2] (=previous_pc)
|
||||
|
||||
// __afl_area_ptr[current_pc ^ previous_pc]++;
|
||||
0x42, 0x00, 0x00, 0xca, // eor x2, x2, x0
|
||||
0x23, 0x68, 0x62, 0xf8, // ldr x3, [x1, x2]
|
||||
0x63, 0x04, 0x00, 0x91, // add x3, x3, #1
|
||||
0x23, 0x68, 0x22, 0xf8, // str x3, [x1, x2]
|
||||
|
||||
// previous_pc = current_pc >> 1;
|
||||
0xe0, 0x07, 0x40, 0x8b, // add x0, xzr, x0, LSR #1
|
||||
0xe2, 0x00, 0x00, 0x58, // ldr x2, #0x1c, =&previous_pc
|
||||
0x40, 0x00, 0x00, 0xf9, // str x0, [x2]
|
||||
|
||||
0xE3, 0x13, 0xc1, 0xA8, // ldp x3, x4, [sp], #0x10
|
||||
0xE1, 0x0B, 0xc1, 0xA8, // ldp x1, x2, [sp], #0x10
|
||||
0xC0, 0x03, 0x5F, 0xD6, // ret
|
||||
|
||||
// &afl_area_ptr_ptr
|
||||
// &afl_prev_loc_ptr
|
||||
|
||||
};
|
||||
|
||||
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;
|
||||
GumArm64Writer *cw = output->writer.arm64;
|
||||
|
||||
if (current_log_impl == 0 ||
|
||||
!gum_arm64_writer_can_branch_directly_between(cw, cw->pc,
|
||||
current_log_impl) ||
|
||||
!gum_arm64_writer_can_branch_directly_between(cw, cw->pc + 128,
|
||||
current_log_impl)) {
|
||||
|
||||
gconstpointer after_log_impl = cw->code + 1;
|
||||
|
||||
gum_arm64_writer_put_b_label(cw, after_log_impl);
|
||||
|
||||
current_log_impl = cw->pc;
|
||||
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;
|
||||
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,
|
||||
sizeof(afl_prev_loc_ptr));
|
||||
|
||||
gum_arm64_writer_put_label(cw, after_log_impl);
|
||||
|
||||
}
|
||||
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_LR, ARM64_REG_X0, ARM64_REG_SP, -(16 + GUM_RED_ZONE_SIZE),
|
||||
GUM_INDEX_PRE_ADJUST);
|
||||
gum_arm64_writer_put_ldr_reg_u64(cw, ARM64_REG_X0, area_offset);
|
||||
gum_arm64_writer_put_bl_imm(cw, current_log_impl);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_LR, ARM64_REG_X0, ARM64_REG_SP, 16 + GUM_RED_ZONE_SIZE,
|
||||
GUM_INDEX_POST_ADJUST);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void on_basic_block(GumCpuContext *context, gpointer user_data) {
|
||||
|
||||
/*
|
||||
* 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;
|
||||
guint64 current_pc = (guint64)user_data;
|
||||
if (tracing) {
|
||||
|
||||
/* 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);
|
||||
|
||||
write(STDOUT_FILENO, buffer, len + 1);
|
||||
|
||||
}
|
||||
|
||||
current_pc = (current_pc >> 4) ^ (current_pc << 8);
|
||||
current_pc &= MAP_SIZE - 1;
|
||||
|
||||
__afl_area_ptr[current_pc ^ previous_pc]++;
|
||||
previous_pc = current_pc >> 1;
|
||||
|
||||
}
|
||||
|
||||
void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output,
|
||||
gpointer user_data) {
|
||||
|
||||
const cs_insn *instr;
|
||||
gboolean begin = TRUE;
|
||||
while (gum_stalker_iterator_next(iterator, &instr)) {
|
||||
|
||||
if (begin) {
|
||||
|
||||
prefetch_write((void *)instr->address);
|
||||
if (!strict || !range_is_excluded((void *)instr->address)) {
|
||||
|
||||
if (optimize) {
|
||||
|
||||
instrument_coverage_optimize(instr, output);
|
||||
|
||||
} else {
|
||||
|
||||
gum_stalker_iterator_put_callout(iterator, on_basic_block,
|
||||
(gpointer)instr->address, NULL);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
begin = FALSE;
|
||||
|
||||
}
|
||||
|
||||
gum_stalker_iterator_keep(iterator);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void instrument_init() {
|
||||
|
||||
optimize = (getenv("AFL_FRIDA_INST_NO_OPTIMIZE") == NULL);
|
||||
tracing = (getenv("AFL_FRIDA_INST_TRACE") != NULL);
|
||||
strict = (getenv("AFL_FRIDA_INST_STRICT") != NULL);
|
||||
|
||||
#if !defined(__x86_64__) && !defined(__aarch64__)
|
||||
optimize = false;
|
||||
#endif
|
||||
|
||||
OKF("Instrumentation - optimize [%c]", optimize ? 'X' : ' ');
|
||||
OKF("Instrumentation - tracing [%c]", tracing ? 'X' : ' ');
|
||||
OKF("Instrumentation - strict [%c]", strict ? 'X' : ' ');
|
||||
|
||||
if (tracing && optimize) {
|
||||
|
||||
FATAL("AFL_FRIDA_INST_OPTIMIZE and AFL_FRIDA_INST_TRACE are incompatible");
|
||||
|
||||
}
|
||||
|
||||
if (__afl_map_size != 0x10000) {
|
||||
|
||||
FATAL("Bad map size: 0x%08x", __afl_map_size);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
199
frida_mode/src/instrument/instrument.c
Normal file
199
frida_mode/src/instrument/instrument.c
Normal file
@ -0,0 +1,199 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "asan.h"
|
||||
#include "entry.h"
|
||||
#include "frida_cmplog.h"
|
||||
#include "instrument.h"
|
||||
#include "persistent.h"
|
||||
#include "prefetch.h"
|
||||
#include "ranges.h"
|
||||
#include "stalker.h"
|
||||
#include "stats.h"
|
||||
#include "util.h"
|
||||
|
||||
static gboolean tracing = false;
|
||||
static gboolean optimize = false;
|
||||
static GumStalkerTransformer *transformer = NULL;
|
||||
|
||||
__thread uint64_t previous_pc = 0;
|
||||
|
||||
__attribute__((hot)) static void on_basic_block(GumCpuContext *context,
|
||||
gpointer user_data) {
|
||||
|
||||
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)) {
|
||||
|
||||
/* 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);
|
||||
|
||||
IGNORED_RETURN(write(STDOUT_FILENO, buffer, len + 1));
|
||||
|
||||
}
|
||||
|
||||
current_pc = (current_pc >> 4) ^ (current_pc << 8);
|
||||
current_pc &= MAP_SIZE - 1;
|
||||
|
||||
cursor = &__afl_area_ptr[current_pc ^ previous_pc];
|
||||
value = *cursor;
|
||||
|
||||
if (value == 0xff) {
|
||||
|
||||
value = 1;
|
||||
|
||||
} else {
|
||||
|
||||
value++;
|
||||
|
||||
}
|
||||
|
||||
*cursor = value;
|
||||
previous_pc = current_pc >> 1;
|
||||
|
||||
}
|
||||
|
||||
static void instr_basic_block(GumStalkerIterator *iterator,
|
||||
GumStalkerOutput *output, gpointer user_data) {
|
||||
|
||||
UNUSED_PARAMETER(user_data);
|
||||
|
||||
const cs_insn *instr;
|
||||
gboolean begin = TRUE;
|
||||
gboolean excluded;
|
||||
|
||||
while (gum_stalker_iterator_next(iterator, &instr)) {
|
||||
|
||||
if (instr->address == entry_start) { entry_prologue(iterator, output); }
|
||||
if (instr->address == persistent_start) { persistent_prologue(output); }
|
||||
if (instr->address == persistent_ret) { persistent_epilogue(output); }
|
||||
|
||||
/*
|
||||
* Until we reach AFL_ENTRYPOINT (assumed to be main if not specified) or
|
||||
* AFL_FRIDA_PERSISTENT_ADDR (if specified), we don't mark our ranges
|
||||
* excluded as we wish to remain inside stalker at all times so that we can
|
||||
* instrument our entry point and persistent loop (if present). This allows
|
||||
* the user to exclude ranges which would be traversed between main and the
|
||||
* AFL_ENTRYPOINT, but which they don't want included in their coverage
|
||||
* information when fuzzing.
|
||||
*
|
||||
* Since we have no means to discard the instrumented copies of blocks
|
||||
* (setting the trust threshold simply causes a new copy to be made on each
|
||||
* execution), we instead ensure that we honour the additional
|
||||
* instrumentation requested (e.g. coverage, asan and complog) when a block
|
||||
* is compiled no matter where we are during initialization. We will end up
|
||||
* re-using these blocks if the code under test calls a block which is also
|
||||
* used during initialization.
|
||||
*
|
||||
* Coverage data generated during initialization isn't a problem since the
|
||||
* map is zeroed each time the target is forked or each time the persistent
|
||||
* loop is run.
|
||||
*
|
||||
* Lastly, we don't enable pre-fetching back to the parent until we reach
|
||||
* our AFL_ENTRYPOINT, since it is not until then that we start the
|
||||
* fork-server and thus start executing in the child.
|
||||
*/
|
||||
excluded = range_is_excluded(GSIZE_TO_POINTER(instr->address));
|
||||
|
||||
stats_collect(instr, begin);
|
||||
|
||||
if (unlikely(begin)) {
|
||||
|
||||
instrument_debug_start(instr->address, output);
|
||||
|
||||
prefetch_write(GSIZE_TO_POINTER(instr->address));
|
||||
|
||||
if (likely(!excluded)) {
|
||||
|
||||
if (likely(optimize)) {
|
||||
|
||||
instrument_coverage_optimize(instr, output);
|
||||
|
||||
} else {
|
||||
|
||||
gum_stalker_iterator_put_callout(
|
||||
iterator, on_basic_block, GSIZE_TO_POINTER(instr->address), NULL);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
begin = FALSE;
|
||||
|
||||
}
|
||||
|
||||
instrument_debug_instruction(instr->address, instr->size);
|
||||
|
||||
if (likely(!excluded)) {
|
||||
|
||||
asan_instrument(instr, iterator);
|
||||
cmplog_instrument(instr, iterator);
|
||||
|
||||
}
|
||||
|
||||
gum_stalker_iterator_keep(iterator);
|
||||
|
||||
}
|
||||
|
||||
instrument_debug_end(output);
|
||||
|
||||
}
|
||||
|
||||
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()) optimize = false;
|
||||
|
||||
OKF("Instrumentation - optimize [%c]", optimize ? 'X' : ' ');
|
||||
OKF("Instrumentation - tracing [%c]", tracing ? 'X' : ' ');
|
||||
|
||||
if (tracing && optimize) {
|
||||
|
||||
FATAL("AFL_FRIDA_INST_OPTIMIZE and AFL_FRIDA_INST_TRACE are incompatible");
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
instrument_debug_init();
|
||||
asan_init();
|
||||
cmplog_init();
|
||||
|
||||
}
|
||||
|
||||
GumStalkerTransformer *instrument_get_transformer(void) {
|
||||
|
||||
if (transformer == NULL) { FATAL("Instrumentation not initialized"); }
|
||||
return transformer;
|
||||
|
||||
}
|
||||
|
26
frida_mode/src/instrument/instrument_arm32.c
Normal file
26
frida_mode/src/instrument/instrument_arm32.c
Normal file
@ -0,0 +1,26 @@
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "instrument.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined(__arm__)
|
||||
|
||||
gboolean instrument_is_coverage_optimize_supported(void) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
void instrument_coverage_optimize(const cs_insn * instr,
|
||||
GumStalkerOutput *output) {
|
||||
|
||||
UNUSED_PARAMETER(instr);
|
||||
UNUSED_PARAMETER(output);
|
||||
FATAL("Optimized coverage not supported on this architecture");
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
97
frida_mode/src/instrument/instrument_arm64.c
Normal file
97
frida_mode/src/instrument/instrument_arm64.c
Normal file
@ -0,0 +1,97 @@
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "instrument.h"
|
||||
|
||||
#if defined(__aarch64__)
|
||||
|
||||
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;
|
||||
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, 0x00, 0x40, 0xf9, // ldr x1, [x1] (=__afl_area_ptr)
|
||||
|
||||
0xe2, 0x01, 0x00, 0x58, // ldr x2, #0x3c, =&previous_pc
|
||||
0x42, 0x00, 0x40, 0xf9, // ldr x2, [x2] (=previous_pc)
|
||||
|
||||
// __afl_area_ptr[current_pc ^ previous_pc]++;
|
||||
0x42, 0x00, 0x00, 0xca, // eor x2, x2, x0
|
||||
0x23, 0x68, 0x62, 0xf8, // ldr x3, [x1, x2]
|
||||
0x63, 0x04, 0x00, 0x91, // add x3, x3, #1
|
||||
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
|
||||
0xe2, 0x00, 0x00, 0x58, // ldr x2, #0x1c, =&previous_pc
|
||||
0x40, 0x00, 0x00, 0xf9, // str x0, [x2]
|
||||
|
||||
0xE3, 0x13, 0xc1, 0xA8, // ldp x3, x4, [sp], #0x10
|
||||
0xE1, 0x0B, 0xc1, 0xA8, // ldp x1, x2, [sp], #0x10
|
||||
0xC0, 0x03, 0x5F, 0xD6, // ret
|
||||
|
||||
// &afl_area_ptr_ptr
|
||||
// &afl_prev_loc_ptr
|
||||
|
||||
};
|
||||
|
||||
gboolean instrument_is_coverage_optimize_supported(void) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
GumArm64Writer *cw = output->writer.arm64;
|
||||
|
||||
if (current_log_impl == 0 ||
|
||||
!gum_arm64_writer_can_branch_directly_between(cw, cw->pc,
|
||||
current_log_impl) ||
|
||||
!gum_arm64_writer_can_branch_directly_between(cw, cw->pc + 128,
|
||||
current_log_impl)) {
|
||||
|
||||
gconstpointer after_log_impl = cw->code + 1;
|
||||
|
||||
gum_arm64_writer_put_b_label(cw, after_log_impl);
|
||||
|
||||
current_log_impl = cw->pc;
|
||||
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;
|
||||
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,
|
||||
sizeof(afl_prev_loc_ptr));
|
||||
|
||||
gum_arm64_writer_put_label(cw, after_log_impl);
|
||||
|
||||
}
|
||||
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_LR, ARM64_REG_X0, ARM64_REG_SP, -(16 + GUM_RED_ZONE_SIZE),
|
||||
GUM_INDEX_PRE_ADJUST);
|
||||
gum_arm64_writer_put_ldr_reg_u64(cw, ARM64_REG_X0, area_offset);
|
||||
gum_arm64_writer_put_bl_imm(cw, current_log_impl);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_LR, ARM64_REG_X0, ARM64_REG_SP, 16 + GUM_RED_ZONE_SIZE,
|
||||
GUM_INDEX_POST_ADJUST);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
129
frida_mode/src/instrument/instrument_debug.c
Normal file
129
frida_mode/src/instrument/instrument_debug.c
Normal file
@ -0,0 +1,129 @@
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "util.h"
|
||||
|
||||
static int debugging_fd = -1;
|
||||
static gpointer instrument_gen_start = NULL;
|
||||
|
||||
static void instrument_debug(char *format, ...) {
|
||||
|
||||
va_list ap;
|
||||
char buffer[4096] = {0};
|
||||
int ret;
|
||||
int len;
|
||||
|
||||
va_start(ap, format);
|
||||
ret = vsnprintf(buffer, sizeof(buffer) - 1, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (ret < 0) { return; }
|
||||
|
||||
len = strnlen(buffer, sizeof(buffer));
|
||||
|
||||
IGNORED_RETURN(write(debugging_fd, buffer, len));
|
||||
|
||||
}
|
||||
|
||||
static void instrument_disasm(guint8 *code, guint size) {
|
||||
|
||||
csh capstone;
|
||||
cs_err err;
|
||||
cs_insn *insn;
|
||||
size_t count, i;
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
cs_free(insn, count);
|
||||
|
||||
cs_close(&capstone);
|
||||
|
||||
}
|
||||
|
||||
static gpointer instrument_cur(GumStalkerOutput *output) {
|
||||
|
||||
#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
|
||||
|
||||
}
|
||||
|
||||
void instrument_debug_init(void) {
|
||||
|
||||
char *filename = getenv("AFL_FRIDA_INST_DEBUG_FILE");
|
||||
OKF("Instrumentation debugging - enabled [%c]", filename == NULL ? ' ' : 'X');
|
||||
|
||||
if (filename == NULL) { return; }
|
||||
|
||||
OKF("Instrumentation debugging - file [%s]", filename);
|
||||
|
||||
if (filename == NULL) { return; }
|
||||
|
||||
char *path = g_canonicalize_filename(filename, g_get_current_dir());
|
||||
|
||||
OKF("Instrumentation debugging - path [%s]", path);
|
||||
|
||||
debugging_fd = open(path, O_RDWR | O_CREAT | O_TRUNC,
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
|
||||
|
||||
if (debugging_fd < 0) { FATAL("Failed to open stats file '%s'", path); }
|
||||
|
||||
g_free(path);
|
||||
|
||||
}
|
||||
|
||||
void instrument_debug_start(uint64_t address, GumStalkerOutput *output) {
|
||||
|
||||
if (likely(debugging_fd < 0)) { return; }
|
||||
|
||||
instrument_gen_start = instrument_cur(output);
|
||||
|
||||
instrument_debug("\n\n***\n\nCreating block for 0x%" G_GINT64_MODIFIER "x:\n",
|
||||
address);
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
93
frida_mode/src/instrument/instrument_x64.c
Normal file
93
frida_mode/src/instrument/instrument_x64.c
Normal file
@ -0,0 +1,93 @@
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "instrument.h"
|
||||
|
||||
#if defined(__x86_64__)
|
||||
|
||||
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,
|
||||
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,
|
||||
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 */
|
||||
0x48, 0x89, 0x39, /* mov qword [rcx], rdi */
|
||||
|
||||
0x5a, /* pop rdx */
|
||||
0x59, /* pop rcx */
|
||||
0x9d, /* popfq */
|
||||
|
||||
0xc3, /* ret */
|
||||
0x90, 0x90, 0x90 /* nop pad */
|
||||
|
||||
/* Read-only data goes here: */
|
||||
/* uint8_t* __afl_area_ptr */
|
||||
/* uint64_t* &previous_pc */
|
||||
|
||||
};
|
||||
|
||||
gboolean instrument_is_coverage_optimize_supported(void) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
GumX86Writer *cw = output->writer.x86;
|
||||
|
||||
if (current_log_impl == 0 ||
|
||||
!gum_x86_writer_can_branch_directly_between(cw->pc, current_log_impl) ||
|
||||
!gum_x86_writer_can_branch_directly_between(cw->pc + 128,
|
||||
current_log_impl)) {
|
||||
|
||||
gconstpointer after_log_impl = cw->code + 1;
|
||||
|
||||
gum_x86_writer_put_jmp_near_label(cw, after_log_impl);
|
||||
|
||||
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;
|
||||
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,
|
||||
sizeof(afl_prev_loc_ptr));
|
||||
|
||||
gum_x86_writer_put_label(cw, after_log_impl);
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RDI, area_offset);
|
||||
gum_x86_writer_put_call_address(cw, current_log_impl);
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_REG_RDI);
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
GUM_RED_ZONE_SIZE);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
85
frida_mode/src/instrument/instrument_x86.c
Normal file
85
frida_mode/src/instrument/instrument_x86.c
Normal file
@ -0,0 +1,85 @@
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "instrument.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined(__i386__)
|
||||
|
||||
static GumAddress current_log_impl = GUM_ADDRESS(0);
|
||||
|
||||
static void instrument_coverage_function(GumX86Writer *cw) {
|
||||
|
||||
gum_x86_writer_put_pushfx(cw);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_ECX);
|
||||
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_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);
|
||||
|
||||
gum_x86_writer_put_add_reg_imm(cw, GUM_REG_EDX, GUM_ADDRESS(__afl_area_ptr));
|
||||
|
||||
/* add byte ptr [edx], 1 */
|
||||
uint8_t add_byte_ptr_edx_1[] = {0x80, 0x02, 0x01};
|
||||
gum_x86_writer_put_bytes(cw, add_byte_ptr_edx_1, sizeof(add_byte_ptr_edx_1));
|
||||
|
||||
/* adc byte ptr [edx], 0 */
|
||||
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);
|
||||
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);
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_REG_ECX);
|
||||
gum_x86_writer_put_popfx(cw);
|
||||
gum_x86_writer_put_ret(cw);
|
||||
|
||||
}
|
||||
|
||||
gboolean instrument_is_coverage_optimize_supported(void) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
void instrument_coverage_optimize(const cs_insn * instr,
|
||||
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 ||
|
||||
!gum_x86_writer_can_branch_directly_between(cw->pc, current_log_impl) ||
|
||||
!gum_x86_writer_can_branch_directly_between(cw->pc + 128,
|
||||
current_log_impl)) {
|
||||
|
||||
gconstpointer after_log_impl = cw->code + 1;
|
||||
|
||||
gum_x86_writer_put_jmp_near_label(cw, after_log_impl);
|
||||
|
||||
current_log_impl = cw->pc;
|
||||
instrument_coverage_function(cw);
|
||||
|
||||
gum_x86_writer_put_label(cw, after_log_impl);
|
||||
|
||||
}
|
||||
|
||||
// gum_x86_writer_put_breakpoint(cw);
|
||||
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);
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_REG_EDI);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "interceptor.h"
|
||||
@ -9,8 +10,26 @@ void intercept(void *address, gpointer replacement, gpointer user_data) {
|
||||
gum_interceptor_begin_transaction(interceptor);
|
||||
GumReplaceReturn ret =
|
||||
gum_interceptor_replace(interceptor, address, replacement, user_data);
|
||||
if (ret != GUM_ATTACH_OK) { FATAL("gum_interceptor_attach: %d", ret); }
|
||||
if (ret != GUM_REPLACE_OK) { FATAL("gum_interceptor_attach: %d", ret); }
|
||||
gum_interceptor_end_transaction(interceptor);
|
||||
|
||||
}
|
||||
|
||||
void unintercept(void *address) {
|
||||
|
||||
GumInterceptor *interceptor = gum_interceptor_obtain();
|
||||
|
||||
gum_interceptor_begin_transaction(interceptor);
|
||||
gum_interceptor_revert(interceptor, address);
|
||||
gum_interceptor_end_transaction(interceptor);
|
||||
gum_interceptor_flush(interceptor);
|
||||
|
||||
}
|
||||
|
||||
void unintercept_self(void) {
|
||||
|
||||
GumInvocationContext *ctx = gum_interceptor_get_current_invocation();
|
||||
unintercept(ctx->function);
|
||||
|
||||
}
|
||||
|
||||
|
179
frida_mode/src/lib/lib.c
Normal file
179
frida_mode/src/lib/lib.c
Normal file
@ -0,0 +1,179 @@
|
||||
#ifndef __APPLE__
|
||||
#include <elf.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "lib.h"
|
||||
|
||||
#if defined(__arm__) || defined(__i386__)
|
||||
#define ELFCLASS ELFCLASS32
|
||||
typedef Elf32_Ehdr Elf_Ehdr;
|
||||
typedef Elf32_Phdr Elf_Phdr;
|
||||
typedef Elf32_Shdr Elf_Shdr;
|
||||
typedef Elf32_Addr Elf_Addr;
|
||||
#elif defined(__aarch64__) || defined(__x86_64__)
|
||||
#define ELFCLASS ELFCLASS64
|
||||
typedef Elf64_Ehdr Elf_Ehdr;
|
||||
typedef Elf64_Phdr Elf_Phdr;
|
||||
typedef Elf64_Shdr Elf_Shdr;
|
||||
typedef Elf64_Addr Elf_Addr;
|
||||
#else
|
||||
#error "Unsupported platform"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
|
||||
gchar name[PATH_MAX + 1];
|
||||
gchar path[PATH_MAX + 1];
|
||||
GumAddress base_address;
|
||||
gsize size;
|
||||
|
||||
} lib_details_t;
|
||||
|
||||
static guint64 text_base = 0;
|
||||
static guint64 text_limit = 0;
|
||||
|
||||
static gboolean lib_find_exe(const GumModuleDetails *details,
|
||||
gpointer user_data) {
|
||||
|
||||
lib_details_t *lib_details = (lib_details_t *)user_data;
|
||||
|
||||
memcpy(lib_details->name, details->name, PATH_MAX);
|
||||
memcpy(lib_details->path, details->path, PATH_MAX);
|
||||
lib_details->base_address = details->range->base_address;
|
||||
lib_details->size = details->range->size;
|
||||
return FALSE;
|
||||
|
||||
}
|
||||
|
||||
static void lib_validate_hdr(Elf_Ehdr *hdr) {
|
||||
|
||||
if (hdr->e_ident[0] != ELFMAG0) FATAL("Invalid e_ident[0]");
|
||||
if (hdr->e_ident[1] != ELFMAG1) FATAL("Invalid e_ident[1]");
|
||||
if (hdr->e_ident[2] != ELFMAG2) FATAL("Invalid e_ident[2]");
|
||||
if (hdr->e_ident[3] != ELFMAG3) FATAL("Invalid e_ident[3]");
|
||||
if (hdr->e_ident[4] != ELFCLASS) FATAL("Invalid class");
|
||||
|
||||
}
|
||||
|
||||
static void lib_read_text_section(lib_details_t *lib_details, Elf_Ehdr *hdr) {
|
||||
|
||||
Elf_Phdr *phdr;
|
||||
gboolean found_preferred_base = FALSE;
|
||||
Elf_Addr preferred_base;
|
||||
Elf_Shdr *shdr;
|
||||
Elf_Shdr *shstrtab;
|
||||
char * shstr;
|
||||
char * section_name;
|
||||
Elf_Shdr *curr;
|
||||
char text_name[] = ".text";
|
||||
|
||||
phdr = (Elf_Phdr *)((char *)hdr + hdr->e_phoff);
|
||||
for (size_t i = 0; i < hdr->e_phnum; i++) {
|
||||
|
||||
if (phdr[i].p_type == PT_LOAD) {
|
||||
|
||||
preferred_base = phdr[i].p_vaddr;
|
||||
found_preferred_base = TRUE;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!found_preferred_base) { FATAL("Failed to find preferred load address"); }
|
||||
|
||||
OKF("Image preferred load address 0x%016" G_GSIZE_MODIFIER "x",
|
||||
preferred_base);
|
||||
|
||||
shdr = (Elf_Shdr *)((char *)hdr + hdr->e_shoff);
|
||||
shstrtab = &shdr[hdr->e_shstrndx];
|
||||
shstr = (char *)hdr + shstrtab->sh_offset;
|
||||
|
||||
OKF("shdr: %p", shdr);
|
||||
OKF("shstrtab: %p", shstrtab);
|
||||
OKF("shstr: %p", shstr);
|
||||
|
||||
for (size_t i = 0; i < hdr->e_shnum; i++) {
|
||||
|
||||
curr = &shdr[i];
|
||||
|
||||
if (curr->sh_name == 0) continue;
|
||||
|
||||
section_name = &shstr[curr->sh_name];
|
||||
OKF("Section: %2" G_GSIZE_MODIFIER "u - base: 0x%016" G_GSIZE_MODIFIER
|
||||
"X size: 0x%016" G_GSIZE_MODIFIER "X %s",
|
||||
i, curr->sh_addr, curr->sh_size, section_name);
|
||||
if (memcmp(section_name, text_name, sizeof(text_name)) == 0 &&
|
||||
text_base == 0) {
|
||||
|
||||
text_base = lib_details->base_address + curr->sh_addr - preferred_base;
|
||||
text_limit = text_base + curr->sh_size;
|
||||
OKF("> text_addr: 0x%016" G_GINT64_MODIFIER "X", text_base);
|
||||
OKF("> text_limit: 0x%016" G_GINT64_MODIFIER "X", text_limit);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void lib_get_text_section(lib_details_t *details) {
|
||||
|
||||
int fd = -1;
|
||||
off_t len;
|
||||
Elf_Ehdr *hdr;
|
||||
|
||||
fd = open(details->path, O_RDONLY);
|
||||
if (fd < 0) { FATAL("Failed to open %s", details->path); }
|
||||
|
||||
len = lseek(fd, 0, SEEK_END);
|
||||
|
||||
if (len == (off_t)-1) { FATAL("Failed to lseek %s", details->path); }
|
||||
|
||||
OKF("len: %ld", len);
|
||||
|
||||
hdr = (Elf_Ehdr *)mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (hdr == MAP_FAILED) { FATAL("Failed to map %s", details->path); }
|
||||
|
||||
lib_validate_hdr(hdr);
|
||||
lib_read_text_section(details, hdr);
|
||||
|
||||
munmap(hdr, len);
|
||||
close(fd);
|
||||
|
||||
}
|
||||
|
||||
void lib_init(void) {
|
||||
|
||||
lib_details_t lib_details;
|
||||
gum_process_enumerate_modules(lib_find_exe, &lib_details);
|
||||
OKF("Executable: 0x%016" G_GINT64_MODIFIER "x - %s", lib_details.base_address,
|
||||
lib_details.path);
|
||||
lib_get_text_section(&lib_details);
|
||||
|
||||
}
|
||||
|
||||
guint64 lib_get_text_base(void) {
|
||||
|
||||
if (text_base == 0) FATAL("Lib not initialized");
|
||||
return text_base;
|
||||
|
||||
}
|
||||
|
||||
guint64 lib_get_text_limit(void) {
|
||||
|
||||
if (text_limit == 0) FATAL("Lib not initialized");
|
||||
return text_limit;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
82
frida_mode/src/lib/lib_apple.c
Normal file
82
frida_mode/src/lib/lib_apple.c
Normal file
@ -0,0 +1,82 @@
|
||||
#ifdef __APPLE__
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "lib.h"
|
||||
#include "util.h"
|
||||
|
||||
extern mach_port_t mach_task_self();
|
||||
extern void gum_darwin_enumerate_modules(mach_port_t task,
|
||||
GumFoundModuleFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
static guint64 text_base = 0;
|
||||
static guint64 text_limit = 0;
|
||||
|
||||
static gboolean lib_get_main_module(const GumModuleDetails *details,
|
||||
gpointer user_data) {
|
||||
|
||||
GumDarwinModule **ret = (GumDarwinModule **)user_data;
|
||||
GumDarwinModule * module = gum_darwin_module_new_from_memory(
|
||||
details->path, mach_task_self(), details->range->base_address,
|
||||
GUM_DARWIN_MODULE_FLAGS_NONE, NULL);
|
||||
|
||||
OKF("Found main module: %s", module->name);
|
||||
|
||||
*ret = module;
|
||||
|
||||
return FALSE;
|
||||
|
||||
}
|
||||
|
||||
gboolean lib_get_text_section(const GumDarwinSectionDetails *details,
|
||||
gpointer user_data) {
|
||||
|
||||
UNUSED_PARAMETER(user_data);
|
||||
static size_t idx = 0;
|
||||
char text_name[] = "__text";
|
||||
|
||||
OKF("Section: %2lu - base: 0x%016" G_GINT64_MODIFIER
|
||||
"X size: 0x%016" G_GINT64_MODIFIER "X %s",
|
||||
idx++, details->vm_address, details->vm_address + details->size,
|
||||
details->section_name);
|
||||
|
||||
if (memcmp(details->section_name, text_name, sizeof(text_name)) == 0 &&
|
||||
text_base == 0) {
|
||||
|
||||
text_base = details->vm_address;
|
||||
text_limit = details->vm_address + details->size;
|
||||
OKF("> text_addr: 0x%016" G_GINT64_MODIFIER "X", text_base);
|
||||
OKF("> text_limit: 0x%016" G_GINT64_MODIFIER "X", text_limit);
|
||||
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
void lib_init(void) {
|
||||
|
||||
GumDarwinModule *module = NULL;
|
||||
gum_darwin_enumerate_modules(mach_task_self(), lib_get_main_module, &module);
|
||||
gum_darwin_module_enumerate_sections(module, lib_get_text_section, NULL);
|
||||
|
||||
}
|
||||
|
||||
guint64 lib_get_text_base(void) {
|
||||
|
||||
if (text_base == 0) FATAL("Lib not initialized");
|
||||
return text_base;
|
||||
|
||||
}
|
||||
|
||||
guint64 lib_get_text_limit(void) {
|
||||
|
||||
if (text_limit == 0) FATAL("Lib not initialized");
|
||||
return text_limit;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
@ -10,13 +11,21 @@
|
||||
#endif
|
||||
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "interceptor.h"
|
||||
#include "entry.h"
|
||||
#include "instrument.h"
|
||||
#include "interceptor.h"
|
||||
#include "lib.h"
|
||||
#include "output.h"
|
||||
#include "persistent.h"
|
||||
#include "prefetch.h"
|
||||
#include "ranges.h"
|
||||
#include "stalker.h"
|
||||
#include "stats.h"
|
||||
#include "util.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
extern mach_port_t mach_task_self();
|
||||
@ -31,15 +40,10 @@ extern int __libc_start_main(int *(main)(int, char **, char **), int argc,
|
||||
typedef int *(*main_fn_t)(int argc, char **argv, char **envp);
|
||||
|
||||
static main_fn_t main_fn = NULL;
|
||||
static GumStalker * stalker = NULL;
|
||||
static GumMemoryRange code_range = {0};
|
||||
|
||||
extern void __afl_manual_init();
|
||||
extern __thread uint64_t previous_pc;
|
||||
static int on_fork(void) {
|
||||
|
||||
static int on_fork() {
|
||||
|
||||
prefetch_read(stalker);
|
||||
prefetch_read();
|
||||
return fork();
|
||||
|
||||
}
|
||||
@ -47,14 +51,20 @@ static int on_fork() {
|
||||
#ifdef __APPLE__
|
||||
static void on_main_os(int argc, char **argv, char **envp) {
|
||||
|
||||
UNUSED_PARAMETER(argc);
|
||||
UNUSED_PARAMETER(argv);
|
||||
UNUSED_PARAMETER(envp);
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
static void on_main_os(int argc, char **argv, char **envp) {
|
||||
|
||||
UNUSED_PARAMETER(argc);
|
||||
/* Personality doesn't affect the current process, it only takes effect on
|
||||
* evec */
|
||||
int persona = personality(ADDR_NO_RANDOMIZE);
|
||||
if (persona == -1) { WARNF("Failed to set ADDR_NO_RANDOMIZE: %d", errno); }
|
||||
if ((persona & ADDR_NO_RANDOMIZE) == 0) { execvpe(argv[0], argv, envp); }
|
||||
|
||||
GumInterceptor *interceptor = gum_interceptor_obtain();
|
||||
@ -68,39 +78,64 @@ static void on_main_os(int argc, char **argv, char **envp) {
|
||||
|
||||
#endif
|
||||
|
||||
static void embedded_init() {
|
||||
|
||||
static gboolean initialized = false;
|
||||
if (!initialized) {
|
||||
|
||||
gum_init_embedded();
|
||||
initialized = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void afl_frida_start() {
|
||||
|
||||
embedded_init();
|
||||
stalker_init();
|
||||
lib_init();
|
||||
entry_init();
|
||||
instrument_init();
|
||||
output_init();
|
||||
persistent_init();
|
||||
prefetch_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);
|
||||
|
||||
stalker_start();
|
||||
entry_run();
|
||||
|
||||
}
|
||||
|
||||
static int *on_main(int argc, char **argv, char **envp) {
|
||||
|
||||
on_main_os(argc, argv, envp);
|
||||
|
||||
stalker = gum_stalker_new();
|
||||
if (stalker == NULL) { FATAL("Failed to initialize stalker"); }
|
||||
unintercept_self();
|
||||
|
||||
gum_stalker_set_trust_threshold(stalker, 0);
|
||||
afl_frida_start();
|
||||
|
||||
GumStalkerTransformer *transformer =
|
||||
gum_stalker_transformer_make_from_callback(instr_basic_block, NULL, NULL);
|
||||
|
||||
instrument_init();
|
||||
prefetch_init();
|
||||
ranges_init(stalker);
|
||||
|
||||
intercept(fork, on_fork, stalker);
|
||||
|
||||
gum_stalker_follow_me(stalker, transformer, NULL);
|
||||
gum_stalker_deactivate(stalker);
|
||||
|
||||
__afl_manual_init();
|
||||
|
||||
/* Child here */
|
||||
previous_pc = 0;
|
||||
prefetch_start(stalker);
|
||||
main_fn(argc, argv, envp);
|
||||
_exit(0);
|
||||
return main_fn(argc, argv, envp);
|
||||
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
static void intercept_main() {
|
||||
#if defined(EMBEDDED)
|
||||
extern int *main(int argc, char **argv, char **envp);
|
||||
|
||||
static void intercept_main(void) {
|
||||
|
||||
main_fn = main;
|
||||
intercept(main, on_main, NULL);
|
||||
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
static void intercept_main(void) {
|
||||
|
||||
mach_port_t task = mach_task_self();
|
||||
OKF("Task Id: %u", task);
|
||||
@ -119,13 +154,14 @@ 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);
|
||||
return __libc_start_main(main, argc, ubp_av, init, fini, rtld_fini,
|
||||
stack_end);
|
||||
|
||||
}
|
||||
|
||||
static void intercept_main() {
|
||||
static void intercept_main(void) {
|
||||
|
||||
intercept(__libc_start_main, on_libc_start_main, NULL);
|
||||
|
||||
@ -133,15 +169,9 @@ static void intercept_main() {
|
||||
|
||||
#endif
|
||||
|
||||
__attribute__((constructor)) static void init() {
|
||||
__attribute__((constructor)) static void init(void) {
|
||||
|
||||
gum_init_embedded();
|
||||
if (!gum_stalker_is_supported()) {
|
||||
|
||||
gum_deinit_embedded();
|
||||
FATAL("Failed to initialize embedded");
|
||||
|
||||
}
|
||||
embedded_init();
|
||||
|
||||
intercept_main();
|
||||
|
||||
|
45
frida_mode/src/output.c
Normal file
45
frida_mode/src/output.c
Normal file
@ -0,0 +1,45 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "output.h"
|
||||
|
||||
static int output_fd = -1;
|
||||
|
||||
static void output_redirect(int fd, char *variable) {
|
||||
|
||||
char *filename = getenv(variable);
|
||||
char *path = NULL;
|
||||
|
||||
if (filename == NULL) { return; }
|
||||
|
||||
path = g_canonicalize_filename(filename, g_get_current_dir());
|
||||
|
||||
OKF("Redirect %d -> '%s'", fd, path);
|
||||
|
||||
output_fd = open(path, O_RDWR | O_CREAT | O_TRUNC,
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
|
||||
|
||||
g_free(path);
|
||||
|
||||
if (output_fd < 0) { FATAL("Failed to open fd(%d) error %d", fd, errno); }
|
||||
|
||||
if (dup2(output_fd, fd) < 0) {
|
||||
|
||||
FATAL("Failed to set fd(%d) error %d", fd, errno);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void output_init(void) {
|
||||
|
||||
output_redirect(STDOUT_FILENO, "AFL_FRIDA_OUTPUT_STDOUT");
|
||||
output_redirect(STDERR_FILENO, "AFL_FRIDA_OUTPUT_STDERR");
|
||||
|
||||
}
|
||||
|
97
frida_mode/src/persistent/persistent.c
Normal file
97
frida_mode/src/persistent/persistent.c
Normal file
@ -0,0 +1,97 @@
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "persistent.h"
|
||||
#include "util.h"
|
||||
|
||||
int __afl_sharedmem_fuzzing = 0;
|
||||
afl_persistent_hook_fn 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");
|
||||
|
||||
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; }
|
||||
|
||||
if (persistent_count != 0 && persistent_start == 0) {
|
||||
|
||||
FATAL(
|
||||
"AFL_FRIDA_PERSISTENT_ADDR must be specified if "
|
||||
"AFL_FRIDA_PERSISTENT_CNT is");
|
||||
|
||||
}
|
||||
|
||||
if (persistent_ret != 0 && persistent_start == 0) {
|
||||
|
||||
FATAL(
|
||||
"AFL_FRIDA_PERSISTENT_ADDR must be specified if "
|
||||
"AFL_FRIDA_PERSISTENT_RET is");
|
||||
|
||||
}
|
||||
|
||||
if (persistent_ret_offset != 0 && persistent_ret == 0) {
|
||||
|
||||
FATAL(
|
||||
"AFL_FRIDA_PERSISTENT_RET must be specified if "
|
||||
"AFL_FRIDA_PERSISTENT_RETADDR_OFFSET is");
|
||||
|
||||
}
|
||||
|
||||
if (persistent_start != 0 && persistent_count == 0) persistent_count = 1000;
|
||||
|
||||
if (persistent_count != 0 && persistent_count < 100)
|
||||
WARNF("Persistent count out of recommended range (<100)");
|
||||
|
||||
if (persistent_start != 0 && !persistent_is_supported())
|
||||
FATAL("Persistent mode not supported on this architecture");
|
||||
|
||||
OKF("Instrumentation - persistent mode [%c] (0x%016" G_GINT64_MODIFIER "X)",
|
||||
persistent_start == 0 ? ' ' : 'X', persistent_start);
|
||||
OKF("Instrumentation - persistent count [%c] (%" G_GINT64_MODIFIER "d)",
|
||||
persistent_start == 0 ? ' ' : 'X', persistent_count);
|
||||
OKF("Instrumentation - hook [%s]", hook_name);
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
79
frida_mode/src/persistent/persistent_arm32.c
Normal file
79
frida_mode/src/persistent/persistent_arm32.c
Normal file
@ -0,0 +1,79 @@
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "persistent.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined(__arm__)
|
||||
|
||||
struct arm_regs {
|
||||
|
||||
uint32_t r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10;
|
||||
|
||||
union {
|
||||
|
||||
uint32_t r11;
|
||||
uint32_t fp;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint32_t r12;
|
||||
uint32_t ip;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint32_t r13;
|
||||
uint32_t sp;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint32_t r14;
|
||||
uint32_t lr;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint32_t r15;
|
||||
uint32_t pc;
|
||||
|
||||
};
|
||||
|
||||
uint32_t cpsr;
|
||||
|
||||
uint8_t vfp_zregs[32][16];
|
||||
uint32_t vfp_xregs[16];
|
||||
|
||||
};
|
||||
|
||||
typedef struct arm_regs arch_api_regs;
|
||||
|
||||
gboolean persistent_is_supported(void) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
void persistent_prologue(GumStalkerOutput *output) {
|
||||
|
||||
UNUSED_PARAMETER(output);
|
||||
FATAL("Persistent mode not supported on this architecture");
|
||||
|
||||
}
|
||||
|
||||
void persistent_epilogue(GumStalkerOutput *output) {
|
||||
|
||||
UNUSED_PARAMETER(output);
|
||||
FATAL("Persistent mode not supported on this architecture");
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
122
frida_mode/src/persistent/persistent_arm64.c
Normal file
122
frida_mode/src/persistent/persistent_arm64.c
Normal file
@ -0,0 +1,122 @@
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "instrument.h"
|
||||
#include "util.h"
|
||||
|
||||
#if 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];
|
||||
|
||||
};
|
||||
|
||||
typedef struct arm64_regs arch_api_regs;
|
||||
|
||||
gboolean persistent_is_supported(void) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
void persistent_prologue(GumStalkerOutput *output) {
|
||||
|
||||
UNUSED_PARAMETER(output);
|
||||
FATAL("Persistent mode not supported on this architecture");
|
||||
|
||||
}
|
||||
|
||||
void persistent_epilogue(GumStalkerOutput *output) {
|
||||
|
||||
UNUSED_PARAMETER(output);
|
||||
FATAL("Persistent mode not supported on this architecture");
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
326
frida_mode/src/persistent/persistent_x64.c
Normal file
326
frida_mode/src/persistent/persistent_x64.c
Normal file
@ -0,0 +1,326 @@
|
||||
#include <unistd.h>
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "instrument.h"
|
||||
#include "persistent.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined(__x86_64__)
|
||||
|
||||
struct x86_64_regs {
|
||||
|
||||
uint64_t rax, rbx, rcx, rdx, rdi, rsi, rbp, r8, r9, r10, r11, r12, r13, r14,
|
||||
r15;
|
||||
|
||||
union {
|
||||
|
||||
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};
|
||||
|
||||
gboolean persistent_is_supported(void) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
static void instrument_persitent_save_regs(GumX86Writer * cw,
|
||||
struct x86_64_regs *regs) {
|
||||
|
||||
GumAddress regs_address = GUM_ADDRESS(regs);
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
-(GUM_RED_ZONE_SIZE));
|
||||
|
||||
/* Should be pushing FPU here, but meh */
|
||||
gum_x86_writer_put_pushfx(cw);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_RAX);
|
||||
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Pop the saved values */
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, 0x10);
|
||||
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
(GUM_RED_ZONE_SIZE));
|
||||
|
||||
}
|
||||
|
||||
static void instrument_persitent_restore_regs(GumX86Writer * cw,
|
||||
struct x86_64_regs *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));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RDX, GUM_REG_RAX,
|
||||
(0x8 * 3));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RDI, GUM_REG_RAX,
|
||||
(0x8 * 4));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RSI, GUM_REG_RAX,
|
||||
(0x8 * 5));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RBP, GUM_REG_RAX,
|
||||
(0x8 * 6));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R8, GUM_REG_RAX,
|
||||
(0x8 * 7));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R9, GUM_REG_RAX,
|
||||
(0x8 * 8));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R10, GUM_REG_RAX,
|
||||
(0x8 * 9));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R11, GUM_REG_RAX,
|
||||
(0x8 * 10));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R12, GUM_REG_RAX,
|
||||
(0x8 * 11));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R13, GUM_REG_RAX,
|
||||
(0x8 * 12));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R14, GUM_REG_RAX,
|
||||
(0x8 * 13));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R15, GUM_REG_RAX,
|
||||
(0x8 * 14));
|
||||
|
||||
/* Don't restore RIP or 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));
|
||||
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));
|
||||
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));
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_RBX);
|
||||
|
||||
gum_x86_writer_put_popfx(cw);
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_REG_RAX);
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_REG_RBX);
|
||||
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
(GUM_RED_ZONE_SIZE));
|
||||
|
||||
}
|
||||
|
||||
static void instrument_exit(GumX86Writer *cw) {
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RAX, GUM_ADDRESS(_exit));
|
||||
gum_x86_writer_put_mov_reg_u32(cw, GUM_REG_RDI, 0);
|
||||
gum_x86_writer_put_call_reg(cw, GUM_REG_RAX);
|
||||
|
||||
}
|
||||
|
||||
static int instrument_afl_persistent_loop_func(void) {
|
||||
|
||||
int ret = __afl_persistent_loop(persistent_count);
|
||||
previous_pc = 0;
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static void instrument_afl_persistent_loop(GumX86Writer *cw) {
|
||||
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
-(GUM_RED_ZONE_SIZE));
|
||||
gum_x86_writer_put_call_address_with_arguments(
|
||||
cw, GUM_CALL_CAPI, GUM_ADDRESS(instrument_afl_persistent_loop_func), 0);
|
||||
gum_x86_writer_put_test_reg_reg(cw, GUM_REG_RAX, GUM_REG_RAX);
|
||||
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
(GUM_RED_ZONE_SIZE));
|
||||
|
||||
}
|
||||
|
||||
static void persistent_prologue_hook(GumX86Writer * cw,
|
||||
struct x86_64_regs *regs) {
|
||||
|
||||
if (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_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RDX, GUM_REG_RDX, 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);
|
||||
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
(GUM_RED_ZONE_SIZE));
|
||||
|
||||
}
|
||||
|
||||
void persistent_prologue(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
|
||||
*/
|
||||
|
||||
GumX86Writer *cw = output->writer.x86;
|
||||
|
||||
gconstpointer loop = cw->code + 1;
|
||||
|
||||
/* Stack must be 16-byte aligned per ABI */
|
||||
instrument_persitent_save_regs(cw, &saved_regs);
|
||||
|
||||
/* pop the return value */
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, (8));
|
||||
|
||||
/* loop: */
|
||||
gum_x86_writer_put_label(cw, loop);
|
||||
|
||||
/* call instrument_prologue_func */
|
||||
instrument_afl_persistent_loop(cw);
|
||||
|
||||
/* jz done */
|
||||
gconstpointer done = cw->code + 1;
|
||||
gum_x86_writer_put_jcc_near_label(cw, X86_INS_JE, done, GUM_UNLIKELY);
|
||||
|
||||
/* 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_x86_writer_put_call_near_label(cw, original);
|
||||
|
||||
/* jmp loop */
|
||||
gum_x86_writer_put_jmp_near_label(cw, loop);
|
||||
|
||||
/* done: */
|
||||
gum_x86_writer_put_label(cw, done);
|
||||
|
||||
instrument_exit(cw);
|
||||
|
||||
/* original: */
|
||||
gum_x86_writer_put_label(cw, original);
|
||||
|
||||
if (persistent_debug) { gum_x86_writer_put_breakpoint(cw); }
|
||||
|
||||
gum_x86_writer_flush(cw);
|
||||
|
||||
}
|
||||
|
||||
void persistent_epilogue(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);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
267
frida_mode/src/persistent/persistent_x86.c
Normal file
267
frida_mode/src/persistent/persistent_x86.c
Normal file
@ -0,0 +1,267 @@
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "instrument.h"
|
||||
#include "persistent.h"
|
||||
|
||||
#if defined(__i386__)
|
||||
|
||||
struct x86_regs {
|
||||
|
||||
uint32_t eax, ebx, ecx, edx, edi, esi, ebp;
|
||||
|
||||
union {
|
||||
|
||||
uint32_t eip;
|
||||
uint32_t pc;
|
||||
|
||||
};
|
||||
|
||||
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};
|
||||
|
||||
gboolean persistent_is_supported(void) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
static void instrument_persitent_save_regs(GumX86Writer * cw,
|
||||
struct x86_regs *regs) {
|
||||
|
||||
GumAddress regs_address = GUM_ADDRESS(regs);
|
||||
|
||||
/* Should be pushing FPU here, but meh */
|
||||
gum_x86_writer_put_pushfx(cw);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_EAX);
|
||||
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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) {
|
||||
|
||||
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));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EDX, GUM_REG_EAX,
|
||||
(0x4 * 3));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EDI, GUM_REG_EAX,
|
||||
(0x4 * 4));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_ESI, GUM_REG_EAX,
|
||||
(0x4 * 5));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBP, GUM_REG_EAX,
|
||||
(0x4 * 6));
|
||||
|
||||
/* Don't restore RIP or RSP */
|
||||
|
||||
/* Restore RBX, RAX & Flags */
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_EAX,
|
||||
(0x4 * 1));
|
||||
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));
|
||||
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));
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_EBX);
|
||||
|
||||
gum_x86_writer_put_popfx(cw);
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_REG_EAX);
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_REG_EBX);
|
||||
|
||||
}
|
||||
|
||||
static void instrument_exit(GumX86Writer *cw) {
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EAX, GUM_ADDRESS(_exit));
|
||||
gum_x86_writer_put_mov_reg_u32(cw, GUM_REG_EDI, 0);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_EDI);
|
||||
gum_x86_writer_put_call_reg(cw, GUM_REG_EAX);
|
||||
|
||||
}
|
||||
|
||||
static int instrument_afl_persistent_loop_func(void) {
|
||||
|
||||
int ret = __afl_persistent_loop(persistent_count);
|
||||
previous_pc = 0;
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static void instrument_afl_persistent_loop(GumX86Writer *cw) {
|
||||
|
||||
gum_x86_writer_put_call_address_with_arguments(
|
||||
cw, GUM_CALL_CAPI, GUM_ADDRESS(instrument_afl_persistent_loop_func), 0);
|
||||
gum_x86_writer_put_test_reg_reg(cw, GUM_REG_EAX, GUM_REG_EAX);
|
||||
|
||||
}
|
||||
|
||||
static void persistent_prologue_hook(GumX86Writer *cw, struct x86_regs *regs) {
|
||||
|
||||
if (hook == NULL) return;
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_ECX,
|
||||
GUM_ADDRESS(&__afl_fuzz_len));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_ECX, GUM_REG_ECX, 0);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_ECX, GUM_REG_ECX, 0);
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EDX,
|
||||
GUM_ADDRESS(&__afl_fuzz_ptr));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EDX, GUM_REG_EDX, 0);
|
||||
|
||||
/* 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,
|
||||
GUM_REG_ECX);
|
||||
|
||||
}
|
||||
|
||||
void persistent_prologue(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
|
||||
*/
|
||||
|
||||
GumX86Writer *cw = output->writer.x86;
|
||||
|
||||
gconstpointer loop = cw->code + 1;
|
||||
|
||||
/* Stack must be 16-byte aligned per ABI */
|
||||
instrument_persitent_save_regs(cw, &saved_regs);
|
||||
|
||||
/* Pop the return value */
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_ESP, GUM_REG_ESP, (4));
|
||||
|
||||
/* loop: */
|
||||
gum_x86_writer_put_label(cw, loop);
|
||||
|
||||
/* call instrument_prologue_func */
|
||||
instrument_afl_persistent_loop(cw);
|
||||
|
||||
/* jz done */
|
||||
gconstpointer done = cw->code + 1;
|
||||
gum_x86_writer_put_jcc_near_label(cw, X86_INS_JE, done, GUM_UNLIKELY);
|
||||
|
||||
/* 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_x86_writer_put_call_near_label(cw, original);
|
||||
/* jmp loop */
|
||||
gum_x86_writer_put_jmp_near_label(cw, loop);
|
||||
|
||||
/* done: */
|
||||
gum_x86_writer_put_label(cw, done);
|
||||
|
||||
instrument_exit(cw);
|
||||
|
||||
/* original: */
|
||||
gum_x86_writer_put_label(cw, original);
|
||||
|
||||
if (persistent_debug) { gum_x86_writer_put_breakpoint(cw); }
|
||||
|
||||
gum_x86_writer_flush(cw);
|
||||
|
||||
}
|
||||
|
||||
void persistent_epilogue(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);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -3,9 +3,12 @@
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "frida-gum.h"
|
||||
#include "prefetch.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "prefetch.h"
|
||||
#include "stalker.h"
|
||||
|
||||
#define TRUST 0
|
||||
#define PREFETCH_SIZE 65536
|
||||
#define PREFETCH_ENTRIES ((PREFETCH_SIZE - sizeof(size_t)) / sizeof(void *))
|
||||
@ -49,8 +52,9 @@ void prefetch_write(void *addr) {
|
||||
/*
|
||||
* Read the IPC region one block at the time and prefetch it
|
||||
*/
|
||||
void prefetch_read(GumStalker *stalker) {
|
||||
void prefetch_read(void) {
|
||||
|
||||
GumStalker *stalker = stalker_get();
|
||||
if (prefetch_data == NULL) return;
|
||||
|
||||
for (size_t i = 0; i < prefetch_data->count; i++) {
|
||||
@ -68,7 +72,7 @@ void prefetch_read(GumStalker *stalker) {
|
||||
|
||||
}
|
||||
|
||||
void prefetch_init() {
|
||||
void prefetch_init(void) {
|
||||
|
||||
g_assert_cmpint(sizeof(prefetch_data_t), ==, PREFETCH_SIZE);
|
||||
gboolean prefetch = (getenv("AFL_FRIDA_INST_NO_PREFETCH") == NULL);
|
||||
@ -106,16 +110,3 @@ void prefetch_init() {
|
||||
|
||||
}
|
||||
|
||||
__attribute__((noinline)) static void prefetch_activation() {
|
||||
|
||||
asm volatile("");
|
||||
|
||||
}
|
||||
|
||||
void prefetch_start(GumStalker *stalker) {
|
||||
|
||||
gum_stalker_activate(stalker, prefetch_activation);
|
||||
prefetch_activation();
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,12 @@
|
||||
// 0x123-0x321
|
||||
// module.so
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "ranges.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "lib.h"
|
||||
#include "ranges.h"
|
||||
#include "stalker.h"
|
||||
#include "util.h"
|
||||
|
||||
#define MAX_RANGES 20
|
||||
|
||||
typedef struct {
|
||||
@ -14,15 +17,11 @@ typedef struct {
|
||||
|
||||
} convert_name_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
GumStalker *stalker;
|
||||
GArray * array;
|
||||
|
||||
} include_range_ctx_t;
|
||||
|
||||
GArray * ranges = NULL;
|
||||
gboolean exclude_ranges = false;
|
||||
GArray *module_ranges = NULL;
|
||||
GArray *libs_ranges = NULL;
|
||||
GArray *include_ranges = NULL;
|
||||
GArray *exclude_ranges = NULL;
|
||||
GArray *ranges = NULL;
|
||||
|
||||
static void convert_address_token(gchar *token, GumMemoryRange *range) {
|
||||
|
||||
@ -159,90 +158,6 @@ static void convert_token(gchar *token, GumMemoryRange *range) {
|
||||
|
||||
}
|
||||
|
||||
static gboolean include_ranges(const GumRangeDetails *details,
|
||||
gpointer user_data) {
|
||||
|
||||
include_range_ctx_t *ctx = (include_range_ctx_t *)user_data;
|
||||
GArray * array = (GArray *)ctx->array;
|
||||
GumAddress base = details->range->base_address;
|
||||
GumAddress limit = details->range->base_address + details->range->size;
|
||||
|
||||
OKF("Range for inclusion 0x%016" G_GINT64_MODIFIER
|
||||
"x-0x%016" G_GINT64_MODIFIER "x",
|
||||
base, limit);
|
||||
|
||||
for (int i = 0; i < array->len; i++) {
|
||||
|
||||
GumMemoryRange *range = &g_array_index(array, GumMemoryRange, i);
|
||||
GumAddress range_base = range->base_address;
|
||||
GumAddress range_limit = range->base_address + range->size;
|
||||
|
||||
/* Before the region */
|
||||
if (range_limit < base) { continue; }
|
||||
|
||||
/* After the region */
|
||||
if (range_base > limit) {
|
||||
|
||||
GumMemoryRange exclude = {.base_address = base, .size = limit - base};
|
||||
OKF("\t Excluding 0x%016" G_GINT64_MODIFIER "x-0x%016" G_GINT64_MODIFIER
|
||||
"x",
|
||||
base, limit);
|
||||
gum_stalker_exclude(ctx->stalker, &exclude);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/* Overlap the start of the region */
|
||||
if (range_base < base) {
|
||||
|
||||
/* Range contains the region */
|
||||
if (range_limit > limit) {
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
base = range_limit;
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
/* Overlap the end of the region */
|
||||
|
||||
} else {
|
||||
|
||||
GumMemoryRange exclude = {.base_address = base,
|
||||
.size = range_base - base};
|
||||
OKF("\t Excluding 0x%016" G_GINT64_MODIFIER "x-0x%016" G_GINT64_MODIFIER
|
||||
"x",
|
||||
base, range_base);
|
||||
gum_stalker_exclude(ctx->stalker, &exclude);
|
||||
/* Extend past the end of the region */
|
||||
if (range_limit >= limit) {
|
||||
|
||||
return true;
|
||||
|
||||
/* Contained within the region */
|
||||
|
||||
} else {
|
||||
|
||||
base = range_limit;
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GumMemoryRange exclude = {.base_address = base, .size = limit - base};
|
||||
OKF("\t Excluding 0x%016" G_GINT64_MODIFIER "x-0x%016" G_GINT64_MODIFIER "x",
|
||||
base, limit);
|
||||
gum_stalker_exclude(ctx->stalker, &exclude);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
gint range_sort(gconstpointer a, gconstpointer b) {
|
||||
|
||||
return ((GumMemoryRange *)a)->base_address -
|
||||
@ -250,9 +165,10 @@ gint range_sort(gconstpointer a, gconstpointer b) {
|
||||
|
||||
}
|
||||
|
||||
static gboolean print_ranges(const GumRangeDetails *details,
|
||||
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",
|
||||
@ -273,62 +189,75 @@ static gboolean print_ranges(const GumRangeDetails *details,
|
||||
|
||||
}
|
||||
|
||||
void ranges_init(GumStalker *stalker) {
|
||||
static void print_ranges(char *key, GArray *ranges) {
|
||||
|
||||
char * showmaps;
|
||||
char * include;
|
||||
char * exclude;
|
||||
char * list;
|
||||
OKF("Range: %s Length: %d", key, ranges->len);
|
||||
for (guint i = 0; i < ranges->len; i++) {
|
||||
|
||||
GumMemoryRange *curr = &g_array_index(ranges, GumMemoryRange, i);
|
||||
GumAddress curr_limit = curr->base_address + curr->size;
|
||||
OKF("Range: %s Idx: %3d - 0x%016" G_GINT64_MODIFIER
|
||||
"x-0x%016" G_GINT64_MODIFIER "x",
|
||||
key, i, curr->base_address, curr_limit);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static gboolean collect_module_ranges_callback(const GumRangeDetails *details,
|
||||
gpointer user_data) {
|
||||
|
||||
GArray * ranges = (GArray *)user_data;
|
||||
GumMemoryRange range = *details->range;
|
||||
g_array_append_val(ranges, range);
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
static GArray *collect_module_ranges(void) {
|
||||
|
||||
GArray *result;
|
||||
result = g_array_new(false, false, sizeof(GumMemoryRange));
|
||||
gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS,
|
||||
collect_module_ranges_callback, result);
|
||||
print_ranges("Modules", result);
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
static GArray *collect_ranges(char *env_key) {
|
||||
|
||||
char * env_val;
|
||||
gchar ** tokens;
|
||||
int token_count;
|
||||
GumMemoryRange range;
|
||||
|
||||
int i;
|
||||
GArray * result;
|
||||
|
||||
showmaps = getenv("AFL_FRIDA_DEBUG_MAPS");
|
||||
include = getenv("AFL_FRIDA_INST_RANGES");
|
||||
exclude = getenv("AFL_FRIDA_EXCLUDE_RANGES");
|
||||
result = g_array_new(false, false, sizeof(GumMemoryRange));
|
||||
|
||||
if (showmaps) {
|
||||
env_val = getenv(env_key);
|
||||
if (env_val == NULL) return result;
|
||||
|
||||
gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, print_ranges, NULL);
|
||||
|
||||
}
|
||||
|
||||
if (include != NULL && exclude != NULL) {
|
||||
|
||||
FATAL(
|
||||
"Cannot specifify both AFL_FRIDA_INST_RANGES and "
|
||||
"AFL_FRIDA_EXCLUDE_RANGES");
|
||||
|
||||
}
|
||||
|
||||
if (include == NULL && exclude == NULL) { return; }
|
||||
|
||||
list = include == NULL ? exclude : include;
|
||||
exclude_ranges = include == NULL ? true : false;
|
||||
|
||||
tokens = g_strsplit(list, ",", MAX_RANGES);
|
||||
tokens = g_strsplit(env_val, ",", MAX_RANGES);
|
||||
|
||||
for (token_count = 0; tokens[token_count] != NULL; token_count++)
|
||||
;
|
||||
|
||||
ranges = g_array_sized_new(false, false, sizeof(GumMemoryRange), token_count);
|
||||
|
||||
for (i = 0; i < token_count; i++) {
|
||||
|
||||
convert_token(tokens[i], &range);
|
||||
g_array_append_val(ranges, range);
|
||||
g_array_append_val(result, range);
|
||||
|
||||
}
|
||||
|
||||
g_array_sort(ranges, range_sort);
|
||||
g_array_sort(result, range_sort);
|
||||
|
||||
/* Check for overlaps */
|
||||
for (i = 1; i < token_count; i++) {
|
||||
|
||||
GumMemoryRange *prev = &g_array_index(ranges, GumMemoryRange, i - 1);
|
||||
GumMemoryRange *curr = &g_array_index(ranges, GumMemoryRange, 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) {
|
||||
@ -342,53 +271,341 @@ void ranges_init(GumStalker *stalker) {
|
||||
|
||||
}
|
||||
|
||||
for (i = 0; i < token_count; i++) {
|
||||
print_ranges(env_key, result);
|
||||
|
||||
GumMemoryRange *curr = &g_array_index(ranges, GumMemoryRange, i);
|
||||
GumAddress curr_limit = curr->base_address + curr->size;
|
||||
OKF("Range %3d - 0x%016" G_GINT64_MODIFIER "x-0x%016" G_GINT64_MODIFIER "x",
|
||||
i, curr->base_address, curr_limit);
|
||||
g_strfreev(tokens);
|
||||
|
||||
}
|
||||
return result;
|
||||
|
||||
if (include == NULL) {
|
||||
}
|
||||
|
||||
for (i = 0; i < token_count; i++) {
|
||||
static GArray *collect_libs_ranges(void) {
|
||||
|
||||
gum_stalker_exclude(stalker, &g_array_index(ranges, GumMemoryRange, i));
|
||||
GArray * result;
|
||||
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 {
|
||||
|
||||
include_range_ctx_t ctx = {.stalker = stalker, .array = ranges};
|
||||
gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, include_ranges, &ctx);
|
||||
range.base_address = 0;
|
||||
range.size = G_MAXULONG;
|
||||
|
||||
}
|
||||
|
||||
g_strfreev(tokens);
|
||||
g_array_append_val(result, range);
|
||||
|
||||
print_ranges("AFL_INST_LIBS", result);
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
static gboolean intersect_range(GumMemoryRange *rr, GumMemoryRange *ra,
|
||||
GumMemoryRange *rb) {
|
||||
|
||||
GumAddress rab = ra->base_address;
|
||||
GumAddress ral = rab + ra->size;
|
||||
|
||||
GumAddress rbb = rb->base_address;
|
||||
GumAddress rbl = rbb + rb->size;
|
||||
|
||||
GumAddress rrb = 0;
|
||||
GumAddress rrl = 0;
|
||||
|
||||
rr->base_address = 0;
|
||||
rr->size = 0;
|
||||
|
||||
/* ra is before rb */
|
||||
if (ral < rbb) { return false; }
|
||||
|
||||
/* ra is after rb */
|
||||
if (rab > rbl) { return true; }
|
||||
|
||||
/* The largest of the two base addresses */
|
||||
rrb = rab > rbb ? rab : rbb;
|
||||
|
||||
/* The smallest of the two limits */
|
||||
rrl = ral < rbl ? ral : rbl;
|
||||
|
||||
rr->base_address = rrb;
|
||||
rr->size = rrl - rrb;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
static GArray *intersect_ranges(GArray *a, GArray *b) {
|
||||
|
||||
GArray * result;
|
||||
GumMemoryRange *ra;
|
||||
GumMemoryRange *rb;
|
||||
GumMemoryRange ri;
|
||||
|
||||
result = g_array_new(false, false, sizeof(GumMemoryRange));
|
||||
|
||||
for (guint i = 0; i < a->len; i++) {
|
||||
|
||||
ra = &g_array_index(a, GumMemoryRange, i);
|
||||
for (guint j = 0; j < b->len; j++) {
|
||||
|
||||
rb = &g_array_index(b, GumMemoryRange, j);
|
||||
|
||||
if (!intersect_range(&ri, ra, rb)) { break; }
|
||||
|
||||
if (ri.size == 0) { continue; }
|
||||
|
||||
g_array_append_val(result, ri);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
static GArray *subtract_ranges(GArray *a, GArray *b) {
|
||||
|
||||
GArray * result;
|
||||
GumMemoryRange *ra;
|
||||
GumAddress ral;
|
||||
GumMemoryRange *rb;
|
||||
GumMemoryRange ri;
|
||||
GumMemoryRange rs;
|
||||
|
||||
result = g_array_new(false, false, sizeof(GumMemoryRange));
|
||||
|
||||
for (guint i = 0; i < a->len; i++) {
|
||||
|
||||
ra = &g_array_index(a, GumMemoryRange, i);
|
||||
ral = ra->base_address + ra->size;
|
||||
for (guint j = 0; j < b->len; j++) {
|
||||
|
||||
rb = &g_array_index(b, GumMemoryRange, j);
|
||||
|
||||
/*
|
||||
* If rb is after ra, we have no more possible intersections and we can
|
||||
* simply keep the remaining range
|
||||
*/
|
||||
if (!intersect_range(&ri, ra, rb)) { break; }
|
||||
|
||||
/*
|
||||
* If there is no intersection, then rb must be before ra, so we must
|
||||
* continue
|
||||
*/
|
||||
if (ri.size == 0) { continue; }
|
||||
|
||||
/*
|
||||
* If the intersection is part way through the range, then we keep the
|
||||
* start of the range
|
||||
*/
|
||||
if (ra->base_address < ri.base_address) {
|
||||
|
||||
rs.base_address = ra->base_address;
|
||||
rs.size = ri.base_address - ra->base_address;
|
||||
g_array_append_val(result, rs);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* If the intersection extends past the limit of the range, then we should
|
||||
* continue with the next range
|
||||
*/
|
||||
if ((ri.base_address + ri.size) > ral) {
|
||||
|
||||
ra->base_address = ral;
|
||||
ra->size = 0;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Otherwise we advance the base of the range to the end of the
|
||||
* intersection and continue with the remainder of the range
|
||||
*/
|
||||
ra->base_address = ri.base_address + ri.size;
|
||||
ra->size = ral - ra->base_address;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* When we have processed all the possible intersections, we add what is
|
||||
* left
|
||||
*/
|
||||
if (ra->size != 0) g_array_append_val(result, *ra);
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
static GArray *merge_ranges(GArray *a) {
|
||||
|
||||
GArray * result;
|
||||
GumMemoryRange rp;
|
||||
GumMemoryRange *r;
|
||||
|
||||
result = g_array_new(false, false, sizeof(GumMemoryRange));
|
||||
if (a->len == 0) return result;
|
||||
|
||||
rp = g_array_index(a, GumMemoryRange, 0);
|
||||
|
||||
for (guint i = 1; i < a->len; i++) {
|
||||
|
||||
r = &g_array_index(a, GumMemoryRange, i);
|
||||
|
||||
if (rp.base_address + rp.size == r->base_address) {
|
||||
|
||||
rp.size += r->size;
|
||||
|
||||
} else {
|
||||
|
||||
g_array_append_val(result, rp);
|
||||
rp.base_address = r->base_address;
|
||||
rp.size = r->size;
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
g_array_append_val(result, rp);
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
static gboolean exclude_ranges_callback(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;
|
||||
|
||||
}
|
||||
|
||||
static void ranges_exclude_self(void) {
|
||||
|
||||
gum_process_enumerate_ranges(GUM_PAGE_EXECUTE, exclude_ranges_callback, NULL);
|
||||
|
||||
}
|
||||
|
||||
void ranges_init(void) {
|
||||
|
||||
GumMemoryRange ri;
|
||||
GArray * step1;
|
||||
GArray * step2;
|
||||
GArray * step3;
|
||||
GArray * step4;
|
||||
|
||||
if (getenv("AFL_FRIDA_DEBUG_MAPS") != NULL) {
|
||||
|
||||
gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, print_ranges_callback,
|
||||
NULL);
|
||||
|
||||
}
|
||||
|
||||
module_ranges = collect_module_ranges();
|
||||
libs_ranges = collect_libs_ranges();
|
||||
include_ranges = collect_ranges("AFL_FRIDA_INST_RANGES");
|
||||
|
||||
/* If include ranges is empty, then assume everything is included */
|
||||
if (include_ranges->len == 0) {
|
||||
|
||||
ri.base_address = 0;
|
||||
ri.size = G_MAXULONG;
|
||||
g_array_append_val(include_ranges, ri);
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/* Intersect with AFL_FRIDA_INST_RANGES */
|
||||
step2 = intersect_ranges(step1, include_ranges);
|
||||
print_ranges("step2", step2);
|
||||
|
||||
/* Subtract AFL_FRIDA_EXCLUDE_RANGES */
|
||||
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);
|
||||
print_ranges("step4", step4);
|
||||
|
||||
ranges = merge_ranges(step4);
|
||||
print_ranges("final", ranges);
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
gboolean range_is_excluded(gpointer address) {
|
||||
|
||||
int i;
|
||||
GumAddress test = GUM_ADDRESS(address);
|
||||
|
||||
if (ranges == NULL) { return false; }
|
||||
|
||||
for (i = 0; i < ranges->len; i++) {
|
||||
for (guint i = 0; i < ranges->len; i++) {
|
||||
|
||||
GumMemoryRange *curr = &g_array_index(ranges, GumMemoryRange, i);
|
||||
GumAddress curr_limit = curr->base_address + curr->size;
|
||||
|
||||
if (test < curr->base_address) { return !exclude_ranges; }
|
||||
if (test < curr->base_address) { return false; }
|
||||
|
||||
if (test < curr_limit) { return exclude_ranges; }
|
||||
if (test < curr_limit) { return true; }
|
||||
|
||||
}
|
||||
|
||||
return !exclude_ranges;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
void ranges_exclude() {
|
||||
|
||||
GumMemoryRange *r;
|
||||
GumStalker * stalker = stalker_get();
|
||||
|
||||
OKF("Excluding ranges");
|
||||
|
||||
for (guint i = 0; i < ranges->len; i++) {
|
||||
|
||||
r = &g_array_index(ranges, GumMemoryRange, i);
|
||||
gum_stalker_exclude(stalker, r);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
32
frida_mode/src/stalker.c
Normal file
32
frida_mode/src/stalker.c
Normal file
@ -0,0 +1,32 @@
|
||||
#include "debug.h"
|
||||
|
||||
#include "instrument.h"
|
||||
#include "stalker.h"
|
||||
|
||||
static GumStalker *stalker = NULL;
|
||||
|
||||
void stalker_init(void) {
|
||||
|
||||
if (!gum_stalker_is_supported()) { FATAL("Failed to initialize embedded"); }
|
||||
|
||||
stalker = gum_stalker_new();
|
||||
if (stalker == NULL) { FATAL("Failed to initialize stalker"); }
|
||||
|
||||
gum_stalker_set_trust_threshold(stalker, 0);
|
||||
|
||||
}
|
||||
|
||||
GumStalker *stalker_get(void) {
|
||||
|
||||
if (stalker == NULL) { FATAL("Stalker uninitialized"); }
|
||||
return stalker;
|
||||
|
||||
}
|
||||
|
||||
void stalker_start(void) {
|
||||
|
||||
GumStalkerTransformer *transformer = instrument_get_transformer();
|
||||
gum_stalker_follow_me(stalker, transformer, NULL);
|
||||
|
||||
}
|
||||
|
208
frida_mode/src/stats/stats.c
Normal file
208
frida_mode/src/stats/stats.c
Normal file
@ -0,0 +1,208 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "stats.h"
|
||||
|
||||
#define MICRO_TO_SEC 1000000
|
||||
|
||||
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;
|
||||
|
||||
void stats_init(void) {
|
||||
|
||||
stats_parent_pid = getpid();
|
||||
char *filename = getenv("AFL_FRIDA_STATS_FILE");
|
||||
stats_interval = util_read_num("AFL_FRIDA_STATS_INTERVAL");
|
||||
if (getenv("AFL_FRIDA_STATS_TRANSITIONS") != NULL) {
|
||||
|
||||
stats_transitions = TRUE;
|
||||
|
||||
}
|
||||
|
||||
OKF("Stats - file [%s]", filename);
|
||||
OKF("Stats - interval [%" G_GINT64_MODIFIER "u]", stats_interval);
|
||||
|
||||
if (stats_interval != 0 && filename == NULL) {
|
||||
|
||||
FATAL(
|
||||
"AFL_FRIDA_STATS_FILE must be specified if "
|
||||
"AFL_FRIDA_STATS_INTERVAL is");
|
||||
|
||||
}
|
||||
|
||||
if (stats_interval == 0) { stats_interval = 10; }
|
||||
|
||||
if (filename == NULL) { return; }
|
||||
|
||||
if (!stats_is_supported_arch()) {
|
||||
|
||||
FATAL("Stats is not supported on this architecture");
|
||||
|
||||
}
|
||||
|
||||
char *path = NULL;
|
||||
|
||||
if (filename == NULL) { return; }
|
||||
|
||||
if (stats_transitions) { gum_stalker_set_counters_enabled(TRUE); }
|
||||
|
||||
path = g_canonicalize_filename(filename, g_get_current_dir());
|
||||
|
||||
OKF("Stats - path [%s]", path);
|
||||
|
||||
stats_fd = open(path, O_RDWR | O_CREAT | O_TRUNC,
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
|
||||
|
||||
if (stats_fd < 0) { FATAL("Failed to open stats file '%s'", path); }
|
||||
|
||||
g_free(path);
|
||||
|
||||
size_t data_size = stats_data_size_arch();
|
||||
|
||||
int shm_id = shmget(IPC_PRIVATE, data_size, IPC_CREAT | IPC_EXCL | 0600);
|
||||
if (shm_id < 0) { FATAL("shm_id < 0 - errno: %d\n", errno); }
|
||||
|
||||
stats_data = shmat(shm_id, NULL, 0);
|
||||
g_assert(stats_data != 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(stats_data, '\0', data_size);
|
||||
|
||||
}
|
||||
|
||||
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; }
|
||||
|
||||
len = strnlen(buffer, sizeof(buffer));
|
||||
IGNORED_RETURN(write(fd, buffer, len));
|
||||
|
||||
}
|
||||
|
||||
void stats_print_fd(int fd, char *format, ...) {
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
stats_vprint(fd, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
}
|
||||
|
||||
void stats_print(char *format, ...) {
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
stats_vprint(stats_fd, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
}
|
||||
|
||||
void stats_write(void) {
|
||||
|
||||
if (stats_parent_pid == getpid()) { return; }
|
||||
|
||||
GDateTime *date_time = g_date_time_new_now_local();
|
||||
char *date_time_string = g_date_time_format(date_time, "%Y-%m-%e %H:%M:%S");
|
||||
|
||||
stats_print("stats\n");
|
||||
stats_print("-----\n");
|
||||
|
||||
stats_print("Index: %" G_GINT64_MODIFIER "u\n",
|
||||
stats_data->stats_idx++);
|
||||
stats_print("Pid: %d\n", getpid());
|
||||
stats_print("Time: %s\n", date_time_string);
|
||||
stats_print("Blocks: %" G_GINT64_MODIFIER "u\n",
|
||||
stats_data->num_blocks);
|
||||
stats_print("Instructions: %" G_GINT64_MODIFIER "u\n",
|
||||
stats_data->num_instructions);
|
||||
stats_print("Avg Instructions / Block: %" G_GINT64_MODIFIER "u\n",
|
||||
stats_data->num_instructions / stats_data->num_blocks);
|
||||
|
||||
stats_print("\n");
|
||||
|
||||
g_free(date_time_string);
|
||||
g_date_time_unref(date_time);
|
||||
|
||||
stats_write_arch();
|
||||
|
||||
if (stats_transitions) {
|
||||
|
||||
GDateTime *date_time = g_date_time_new_now_local();
|
||||
char *date_time_string = g_date_time_format(date_time, "%Y-%m-%e %H:%M:%S");
|
||||
|
||||
stats_print_fd(STDERR_FILENO, "stats\n");
|
||||
stats_print_fd(STDERR_FILENO, "-----\n");
|
||||
stats_print_fd(STDERR_FILENO, "Index: %" G_GINT64_MODIFIER "u\n",
|
||||
stats_data->transitions_idx++);
|
||||
stats_print_fd(STDERR_FILENO, "Pid: %d\n", getpid());
|
||||
stats_print_fd(STDERR_FILENO, "Time: %s\n", date_time_string);
|
||||
|
||||
g_free(date_time_string);
|
||||
g_date_time_unref(date_time);
|
||||
gum_stalker_dump_counters();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void stats_maybe_write(void) {
|
||||
|
||||
guint64 current_time;
|
||||
|
||||
if (stats_interval == 0) { return; }
|
||||
|
||||
current_time = g_get_monotonic_time();
|
||||
|
||||
if ((current_time - stats_data->stats_last_time) >
|
||||
(stats_interval * MICRO_TO_SEC)) {
|
||||
|
||||
stats_write();
|
||||
stats_data->stats_last_time = current_time;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void stats_collect(const cs_insn *instr, gboolean begin) {
|
||||
|
||||
UNUSED_PARAMETER(instr);
|
||||
UNUSED_PARAMETER(begin);
|
||||
|
||||
if (stats_fd < 0) { return; }
|
||||
|
||||
if (begin) { stats_data->num_blocks++; }
|
||||
stats_data->num_instructions++;
|
||||
|
||||
stats_collect_arch(instr);
|
||||
|
||||
stats_maybe_write();
|
||||
|
||||
}
|
||||
|
36
frida_mode/src/stats/stats_arm.c
Normal file
36
frida_mode/src/stats/stats_arm.c
Normal file
@ -0,0 +1,36 @@
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "stats.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined(__arm__)
|
||||
|
||||
gboolean stats_is_supported_arch(void) {
|
||||
|
||||
return FALSE;
|
||||
|
||||
}
|
||||
|
||||
size_t stats_data_size_arch(void) {
|
||||
|
||||
FATAL("Stats not supported on this architecture");
|
||||
|
||||
}
|
||||
|
||||
void stats_write_arch(void) {
|
||||
|
||||
FATAL("Stats not supported on this architecture");
|
||||
|
||||
}
|
||||
|
||||
void stats_collect_arch(const cs_insn *instr) {
|
||||
|
||||
UNUSED_PARAMETER(instr);
|
||||
FATAL("Stats not supported on this architecture");
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
36
frida_mode/src/stats/stats_arm64.c
Normal file
36
frida_mode/src/stats/stats_arm64.c
Normal file
@ -0,0 +1,36 @@
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "stats.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined(__aarch64__)
|
||||
|
||||
gboolean stats_is_supported_arch(void) {
|
||||
|
||||
return FALSE;
|
||||
|
||||
}
|
||||
|
||||
size_t stats_data_size_arch(void) {
|
||||
|
||||
FATAL("Stats not supported on this architecture");
|
||||
|
||||
}
|
||||
|
||||
void stats_write_arch(void) {
|
||||
|
||||
FATAL("Stats not supported on this architecture");
|
||||
|
||||
}
|
||||
|
||||
void stats_collect_arch(const cs_insn *instr) {
|
||||
|
||||
UNUSED_PARAMETER(instr);
|
||||
FATAL("Stats not supported on this architecture");
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
307
frida_mode/src/stats/stats_x64.c
Normal file
307
frida_mode/src/stats/stats_x64.c
Normal file
@ -0,0 +1,307 @@
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "ranges.h"
|
||||
#include "stats.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined(__x86_64__)
|
||||
|
||||
typedef struct {
|
||||
|
||||
stats_data_header_t header;
|
||||
|
||||
guint64 num_call_imm;
|
||||
guint64 num_call_imm_excluded;
|
||||
guint64 num_call_reg;
|
||||
guint64 num_call_mem;
|
||||
|
||||
guint64 num_jmp_imm;
|
||||
guint64 num_jmp_reg;
|
||||
guint64 num_jmp_mem;
|
||||
|
||||
guint64 num_jmp_cond_imm;
|
||||
guint64 num_jmp_cond_reg;
|
||||
guint64 num_jmp_cond_mem;
|
||||
|
||||
guint64 num_jmp_cond_jcxz;
|
||||
|
||||
guint64 num_ret;
|
||||
|
||||
guint64 num_rip_relative;
|
||||
|
||||
} stats_data_arch_t;
|
||||
|
||||
gboolean stats_is_supported_arch(void) {
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
size_t stats_data_size_arch(void) {
|
||||
|
||||
return sizeof(stats_data_arch_t);
|
||||
|
||||
}
|
||||
|
||||
void stats_write_arch(void) {
|
||||
|
||||
stats_data_arch_t *stats_data_arch = (stats_data_arch_t *)stats_data;
|
||||
guint64 num_instructions = stats_data_arch->header.num_instructions;
|
||||
|
||||
stats_print(
|
||||
"Call Immediates: %" G_GINT64_MODIFIER
|
||||
"u "
|
||||
"(%3.2f%%)\n",
|
||||
stats_data_arch->num_call_imm,
|
||||
((float)(stats_data_arch->num_call_imm * 100) / num_instructions));
|
||||
stats_print("Call Immediates Excluded: %" G_GINT64_MODIFIER
|
||||
"u "
|
||||
"(%3.2f%%)\n",
|
||||
stats_data_arch->num_call_imm_excluded,
|
||||
((float)(stats_data_arch->num_call_imm_excluded * 100) /
|
||||
num_instructions));
|
||||
stats_print(
|
||||
"Call Register: %" G_GINT64_MODIFIER
|
||||
"u "
|
||||
"(%3.2f%%)\n",
|
||||
stats_data_arch->num_call_reg,
|
||||
((float)(stats_data_arch->num_call_reg * 100) / num_instructions));
|
||||
stats_print(
|
||||
"Call Memory: %" G_GINT64_MODIFIER
|
||||
"u "
|
||||
"(%3.2f%%)\n",
|
||||
stats_data_arch->num_call_mem,
|
||||
((float)(stats_data_arch->num_call_mem * 100) / num_instructions));
|
||||
|
||||
stats_print("\n");
|
||||
|
||||
stats_print("Jump Immediates: %" G_GINT64_MODIFIER
|
||||
"u "
|
||||
"(%3.2f%%)\n",
|
||||
stats_data_arch->num_jmp_imm,
|
||||
((float)(stats_data_arch->num_jmp_imm * 100) / num_instructions));
|
||||
stats_print("Jump Register: %" G_GINT64_MODIFIER
|
||||
"u "
|
||||
"(%3.2f%%)\n",
|
||||
stats_data_arch->num_jmp_reg,
|
||||
((float)(stats_data_arch->num_jmp_reg * 100) / num_instructions));
|
||||
stats_print("Jump Memory: %" G_GINT64_MODIFIER
|
||||
"u "
|
||||
"(%3.2f%%)\n",
|
||||
stats_data_arch->num_jmp_mem,
|
||||
((float)(stats_data_arch->num_jmp_mem * 100) / num_instructions));
|
||||
|
||||
stats_print("\n");
|
||||
|
||||
stats_print(
|
||||
"Conditional Jump Immediates: %" G_GINT64_MODIFIER
|
||||
"u "
|
||||
"(%3.2f%%)\n",
|
||||
stats_data_arch->num_jmp_cond_imm,
|
||||
((float)(stats_data_arch->num_jmp_cond_imm * 100) / num_instructions));
|
||||
stats_print(
|
||||
"Conditional Jump CX Immediate: %" G_GINT64_MODIFIER
|
||||
"u "
|
||||
"(%3.2f%%)\n",
|
||||
stats_data_arch->num_jmp_cond_jcxz,
|
||||
((float)(stats_data_arch->num_jmp_cond_jcxz * 100) / num_instructions));
|
||||
stats_print(
|
||||
"Conditional Jump Register: %" G_GINT64_MODIFIER
|
||||
"u "
|
||||
"(%3.2f%%)\n",
|
||||
stats_data_arch->num_jmp_cond_reg,
|
||||
((float)(stats_data_arch->num_jmp_cond_reg * 100) / num_instructions));
|
||||
stats_print(
|
||||
"Conditional Jump Memory: %" G_GINT64_MODIFIER
|
||||
"u "
|
||||
"(%3.2f%%)\n",
|
||||
stats_data_arch->num_jmp_cond_mem,
|
||||
((float)(stats_data_arch->num_jmp_cond_mem * 100) / num_instructions));
|
||||
|
||||
stats_print("\n");
|
||||
|
||||
stats_print("Returns: %" G_GINT64_MODIFIER
|
||||
"u "
|
||||
"(%3.2f%%)\n",
|
||||
stats_data_arch->num_ret,
|
||||
(stats_data_arch->num_ret * 100 / num_instructions));
|
||||
|
||||
stats_print("\n");
|
||||
|
||||
stats_print("Rip Relative: %" G_GINT64_MODIFIER
|
||||
"u "
|
||||
"(%3.2f%%)\n",
|
||||
stats_data_arch->num_rip_relative,
|
||||
(stats_data_arch->num_rip_relative * 100 / num_instructions));
|
||||
|
||||
stats_print("\n");
|
||||
stats_print("\n");
|
||||
|
||||
}
|
||||
|
||||
static x86_op_type stats_get_operand_type(const cs_insn *instr) {
|
||||
|
||||
cs_x86 * x86 = &instr->detail->x86;
|
||||
cs_x86_op *operand;
|
||||
|
||||
if (x86->op_count != 1) {
|
||||
|
||||
FATAL("Unexpected operand count (%d): %s %s\n", x86->op_count,
|
||||
instr->mnemonic, instr->op_str);
|
||||
|
||||
}
|
||||
|
||||
operand = &x86->operands[0];
|
||||
|
||||
return operand->type;
|
||||
|
||||
}
|
||||
|
||||
static void stats_collect_call_imm_excluded_arch(const cs_insn *instr) {
|
||||
|
||||
stats_data_arch_t *stats_data_arch = (stats_data_arch_t *)stats_data;
|
||||
cs_x86 * x86 = &instr->detail->x86;
|
||||
cs_x86_op * operand = &x86->operands[0];
|
||||
|
||||
if (range_is_excluded((gpointer)operand->imm)) {
|
||||
|
||||
stats_data_arch->num_call_imm_excluded++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void stats_collect_call_arch(const cs_insn *instr) {
|
||||
|
||||
stats_data_arch_t *stats_data_arch = (stats_data_arch_t *)stats_data;
|
||||
x86_op_type type = stats_get_operand_type(instr);
|
||||
switch (type) {
|
||||
|
||||
case X86_OP_IMM:
|
||||
stats_data_arch->num_call_imm++;
|
||||
stats_collect_call_imm_excluded_arch(instr);
|
||||
break;
|
||||
case X86_OP_REG:
|
||||
stats_data_arch->num_call_reg++;
|
||||
break;
|
||||
case X86_OP_MEM:
|
||||
stats_data_arch->num_call_mem++;
|
||||
break;
|
||||
default:
|
||||
FATAL("Invalid operand type: %s %s\n", instr->mnemonic, instr->op_str);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void stats_collect_jump_arch(const cs_insn *instr) {
|
||||
|
||||
stats_data_arch_t *stats_data_arch = (stats_data_arch_t *)stats_data;
|
||||
x86_op_type type = stats_get_operand_type(instr);
|
||||
switch (type) {
|
||||
|
||||
case X86_OP_IMM:
|
||||
stats_data_arch->num_jmp_imm++;
|
||||
break;
|
||||
case X86_OP_REG:
|
||||
stats_data_arch->num_jmp_reg++;
|
||||
break;
|
||||
case X86_OP_MEM:
|
||||
stats_data_arch->num_jmp_mem++;
|
||||
break;
|
||||
default:
|
||||
FATAL("Invalid operand type: %s %s\n", instr->mnemonic, instr->op_str);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void stats_collect_jump_cond_arch(const cs_insn *instr) {
|
||||
|
||||
stats_data_arch_t *stats_data_arch = (stats_data_arch_t *)stats_data;
|
||||
x86_op_type type = stats_get_operand_type(instr);
|
||||
switch (type) {
|
||||
|
||||
case X86_OP_IMM:
|
||||
stats_data_arch->num_jmp_cond_imm++;
|
||||
break;
|
||||
case X86_OP_REG:
|
||||
stats_data_arch->num_jmp_cond_reg++;
|
||||
break;
|
||||
case X86_OP_MEM:
|
||||
stats_data_arch->num_jmp_cond_mem++;
|
||||
break;
|
||||
default:
|
||||
FATAL("Invalid operand type: %s %s\n", instr->mnemonic, instr->op_str);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void stats_collect_rip_relative_arch(const cs_insn *instr) {
|
||||
|
||||
stats_data_arch_t *stats_data_arch = (stats_data_arch_t *)stats_data;
|
||||
cs_x86 * x86 = &instr->detail->x86;
|
||||
guint mod;
|
||||
guint rm;
|
||||
|
||||
if (x86->encoding.modrm_offset == 0) { return; }
|
||||
|
||||
mod = (x86->modrm & 0xc0) >> 6;
|
||||
if (mod != 0) { return; }
|
||||
|
||||
rm = (x86->modrm & 0x07) >> 0;
|
||||
if (rm != 5) { return; }
|
||||
|
||||
stats_data_arch->num_rip_relative++;
|
||||
|
||||
}
|
||||
|
||||
void stats_collect_arch(const cs_insn *instr) {
|
||||
|
||||
stats_data_arch_t *stats_data_arch = (stats_data_arch_t *)stats_data;
|
||||
switch (instr->id) {
|
||||
|
||||
case X86_INS_CALL:
|
||||
stats_collect_call_arch(instr);
|
||||
break;
|
||||
case X86_INS_JMP:
|
||||
stats_collect_jump_arch(instr);
|
||||
break;
|
||||
case X86_INS_JA:
|
||||
case X86_INS_JAE:
|
||||
case X86_INS_JB:
|
||||
case X86_INS_JBE:
|
||||
case X86_INS_JE:
|
||||
case X86_INS_JG:
|
||||
case X86_INS_JGE:
|
||||
case X86_INS_JL:
|
||||
case X86_INS_JLE:
|
||||
case X86_INS_JNE:
|
||||
case X86_INS_JNO:
|
||||
case X86_INS_JNP:
|
||||
case X86_INS_JNS:
|
||||
case X86_INS_JO:
|
||||
case X86_INS_JP:
|
||||
case X86_INS_JS:
|
||||
stats_collect_jump_cond_arch(instr);
|
||||
break;
|
||||
case X86_INS_JECXZ:
|
||||
case X86_INS_JRCXZ:
|
||||
stats_data_arch->num_jmp_cond_jcxz++;
|
||||
break;
|
||||
case X86_INS_RET:
|
||||
stats_data_arch->num_ret++;
|
||||
break;
|
||||
default:
|
||||
stats_collect_rip_relative_arch(instr);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
36
frida_mode/src/stats/stats_x86.c
Normal file
36
frida_mode/src/stats/stats_x86.c
Normal file
@ -0,0 +1,36 @@
|
||||
#include "frida-gum.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "stats.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined(__i386__)
|
||||
|
||||
gboolean stats_is_supported_arch(void) {
|
||||
|
||||
return FALSE;
|
||||
|
||||
}
|
||||
|
||||
size_t stats_data_size_arch(void) {
|
||||
|
||||
FATAL("Stats not supported on this architecture");
|
||||
|
||||
}
|
||||
|
||||
void stats_write_arch(void) {
|
||||
|
||||
FATAL("Stats not supported on this architecture");
|
||||
|
||||
}
|
||||
|
||||
void stats_collect_arch(const cs_insn *instr) {
|
||||
|
||||
UNUSED_PARAMETER(instr);
|
||||
FATAL("Stats not supported on this architecture");
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
68
frida_mode/src/util.c
Normal file
68
frida_mode/src/util.c
Normal file
@ -0,0 +1,68 @@
|
||||
#include "util.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
guint64 util_read_address(char *key) {
|
||||
|
||||
char *value_str = getenv(key);
|
||||
|
||||
if (value_str == NULL) { return 0; }
|
||||
|
||||
if (!g_str_has_prefix(value_str, "0x")) {
|
||||
|
||||
FATAL("Invalid address should have 0x prefix: %s=%s\n", key, value_str);
|
||||
|
||||
}
|
||||
|
||||
char *value_str2 = &value_str[2];
|
||||
|
||||
for (char *c = value_str2; *c != '\0'; c++) {
|
||||
|
||||
if (!g_ascii_isxdigit(*c)) {
|
||||
|
||||
FATAL("Invalid address not formed of hex digits: %s=%s ('%c')\n", key,
|
||||
value_str, *c);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
guint64 value = g_ascii_strtoull(value_str2, NULL, 16);
|
||||
if (value == 0) {
|
||||
|
||||
FATAL("Invalid address failed hex conversion: %s=%s\n", key, value_str2);
|
||||
|
||||
}
|
||||
|
||||
return value;
|
||||
|
||||
}
|
||||
|
||||
guint64 util_read_num(char *key) {
|
||||
|
||||
char *value_str = getenv(key);
|
||||
|
||||
if (value_str == NULL) { return 0; }
|
||||
|
||||
for (char *c = value_str; *c != '\0'; c++) {
|
||||
|
||||
if (!g_ascii_isdigit(*c)) {
|
||||
|
||||
FATAL("Invalid address not formed of decimal digits: %s=%s\n", key,
|
||||
value_str);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
guint64 value = g_ascii_strtoull(value_str, NULL, 10);
|
||||
if (value == 0) {
|
||||
|
||||
FATAL("Invalid address failed numeric conversion: %s=%s\n", key, value_str);
|
||||
|
||||
}
|
||||
|
||||
return value;
|
||||
|
||||
}
|
||||
|
69
frida_mode/test/cmplog/GNUmakefile
Normal file
69
frida_mode/test/cmplog/GNUmakefile
Normal file
@ -0,0 +1,69 @@
|
||||
PWD:=$(shell pwd)/
|
||||
ROOT:=$(shell realpath $(PWD)../../../)/
|
||||
BUILD_DIR:=$(PWD)build/
|
||||
|
||||
TEST_CMPLOG_SRC=$(PWD)cmplog.c
|
||||
TEST_CMPLOG_OBJ=$(BUILD_DIR)compcovtest
|
||||
|
||||
TEST_BIN:=$(PWD)../../build/test
|
||||
|
||||
|
||||
TEST_DATA_DIR:=$(BUILD_DIR)in/
|
||||
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
|
||||
|
||||
all: $(TEST_CMPLOG_OBJ)
|
||||
make -C $(ROOT)frida_mode/
|
||||
|
||||
32:
|
||||
CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
|
||||
|
||||
$(BUILD_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
$(TEST_DATA_DIR): | $(BUILD_DIR)
|
||||
mkdir -p $@
|
||||
|
||||
$(CMP_LOG_INPUT): | $(TEST_DATA_DIR)
|
||||
echo -n "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" > $@
|
||||
|
||||
$(TEST_CMPLOG_OBJ): $(TEST_CMPLOG_SRC) | $(BUILD_DIR)
|
||||
$(CXX) -std=c++11 -g $(CFLAGS) $(LDFLAGS) $< -o $@
|
||||
|
||||
qemu: $(TEST_CMPLOG_OBJ) $(CMP_LOG_INPUT)
|
||||
$(ROOT)afl-fuzz \
|
||||
-Q \
|
||||
-i $(TEST_DATA_DIR) \
|
||||
-o $(QEMU_OUT) \
|
||||
-c 0 \
|
||||
-l 3AT \
|
||||
-Z \
|
||||
-- \
|
||||
$(TEST_CMPLOG_OBJ) @@
|
||||
|
||||
frida: $(TEST_CMPLOG_OBJ) $(CMP_LOG_INPUT)
|
||||
$(ROOT)afl-fuzz \
|
||||
-O \
|
||||
-i $(TEST_DATA_DIR) \
|
||||
-o $(FRIDA_OUT) \
|
||||
-c 0 \
|
||||
-l 3AT \
|
||||
-Z \
|
||||
-- \
|
||||
$(TEST_CMPLOG_OBJ) @@
|
||||
|
||||
debug: $(TEST_CMPLOG_OBJ) $(CMP_LOG_INPUT)
|
||||
gdb \
|
||||
--ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \
|
||||
--ex 'set disassembly-flavor intel' \
|
||||
--ex 'r $(CMP_LOG_INPUT)' \
|
||||
--args $(TEST_CMPLOG_OBJ) $(CMP_LOG_INPUT)
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR)
|
||||
|
||||
format:
|
||||
cd $(ROOT) && echo $(TEST_CMPLOG_SRC) | xargs -L1 ./.custom-format.py -i
|
22
frida_mode/test/cmplog/Makefile
Normal file
22
frida_mode/test/cmplog/Makefile
Normal file
@ -0,0 +1,22 @@
|
||||
all:
|
||||
@echo trying to use GNU make...
|
||||
@gmake all || echo please install GNUmake
|
||||
|
||||
32:
|
||||
@echo trying to use GNU make...
|
||||
@gmake 32 || echo please install GNUmake
|
||||
|
||||
clean:
|
||||
@gmake clean
|
||||
|
||||
qemu:
|
||||
@gmake qemu
|
||||
|
||||
frida:
|
||||
@gmake frida
|
||||
|
||||
format:
|
||||
@gmake format
|
||||
|
||||
debug:
|
||||
@gmake debug
|
100
frida_mode/test/cmplog/cmplog.c
Normal file
100
frida_mode/test/cmplog/cmplog.c
Normal file
@ -0,0 +1,100 @@
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Author: Mateusz Jurczyk (mjurczyk@google.com)
|
||||
//
|
||||
// Copyright 2019-2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
// solution: echo -ne 'The quick brown fox jumps over the lazy
|
||||
// dog\xbe\xba\xfe\xca\xbe\xba\xfe\xca\xde\xc0\xad\xde\xef\xbe' | ./compcovtest
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
char buffer[44] = {/* zero padding */};
|
||||
|
||||
FILE *file = stdin;
|
||||
|
||||
if (argc > 1) {
|
||||
|
||||
if ((file = fopen(argv[1], "r")) == NULL) {
|
||||
|
||||
perror(argv[1]);
|
||||
exit(-1);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fread(buffer, 1, sizeof(buffer) - 1, file);
|
||||
|
||||
if (memcmp(&buffer[0], "The quick brown fox ", 20) != 0 ||
|
||||
strncmp(&buffer[20], "jumps over ", 11) != 0 ||
|
||||
strcmp(&buffer[31], "the lazy dog") != 0) {
|
||||
|
||||
if (argc > 1) { fclose(file); }
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
#if defined(__x86_64__)
|
||||
uint64_t x = 0;
|
||||
fread(&x, sizeof(x), 1, file);
|
||||
if (x != 0xCAFEBABECAFEBABE) {
|
||||
|
||||
if (argc > 1) { fclose(file); }
|
||||
return 2;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
uint32_t y = 0;
|
||||
fread(&y, sizeof(y), 1, file);
|
||||
|
||||
if (y != 0xDEADC0DE) {
|
||||
|
||||
if (argc > 1) { fclose(file); }
|
||||
return 3;
|
||||
|
||||
}
|
||||
|
||||
uint16_t z = 0;
|
||||
fread(&z, sizeof(z), 1, file);
|
||||
|
||||
switch (z) {
|
||||
|
||||
case 0xBEEF:
|
||||
break;
|
||||
|
||||
default:
|
||||
if (argc > 1) { fclose(file); }
|
||||
return 4;
|
||||
|
||||
}
|
||||
|
||||
printf("Puzzle solved, congrats!\n");
|
||||
abort();
|
||||
|
||||
if (argc > 1) { fclose(file); }
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
71
frida_mode/test/deferred/GNUmakefile
Normal file
71
frida_mode/test/deferred/GNUmakefile
Normal file
@ -0,0 +1,71 @@
|
||||
PWD:=$(shell pwd)/
|
||||
ROOT:=$(shell realpath $(PWD)../../..)/
|
||||
BUILD_DIR:=$(PWD)build/
|
||||
TESTINSTR_DATA_DIR:=$(BUILD_DIR)in/
|
||||
TESTINSTR_DATA_FILE:=$(TESTINSTR_DATA_DIR)in
|
||||
|
||||
TESTINSTBIN:=$(BUILD_DIR)testinstr
|
||||
TESTINSTSRC:=$(PWD)testinstr.c
|
||||
|
||||
QEMU_OUT:=$(BUILD_DIR)qemu-out
|
||||
FRIDA_OUT:=$(BUILD_DIR)frida-out
|
||||
|
||||
GET_SYMBOL_ADDR:=$(ROOT)frida_mode/test/png/persistent/get_symbol_addr.py
|
||||
|
||||
ifndef ARCH
|
||||
|
||||
ARCH=$(shell uname -m)
|
||||
ifeq "$(ARCH)" "aarch64"
|
||||
ARCH:=arm64
|
||||
endif
|
||||
|
||||
ifeq "$(ARCH)" "i686"
|
||||
ARCH:=x86
|
||||
endif
|
||||
endif
|
||||
|
||||
ARCH=$(shell uname -m)
|
||||
ifeq "$(ARCH)" "aarch64"
|
||||
AFL_ENTRYPOINT=$(shell $(GET_SYMBOL_ADDR) -f $(TESTINSTBIN) -s run -b 0x0000aaaaaaaaa000)
|
||||
endif
|
||||
|
||||
ifeq "$(ARCH)" "x86_64"
|
||||
AFL_ENTRYPOINT=$(shell $(GET_SYMBOL_ADDR) -f $(TESTINSTBIN) -s run -b 0x0000555555554000)
|
||||
endif
|
||||
|
||||
ifeq "$(ARCH)" "x86"
|
||||
AFL_ENTRYPOINT=$(shell $(GET_SYMBOL_ADDR) -f $(TESTINSTBIN) -s run -b 0x56555000)
|
||||
endif
|
||||
|
||||
.PHONY: all clean qemu frida
|
||||
|
||||
all: $(TESTINSTBIN)
|
||||
make -C $(ROOT)frida_mode/
|
||||
|
||||
32:
|
||||
CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
|
||||
|
||||
$(BUILD_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
$(TESTINSTR_DATA_DIR): | $(BUILD_DIR)
|
||||
mkdir -p $@
|
||||
|
||||
$(TESTINSTR_DATA_FILE): | $(TESTINSTR_DATA_DIR)
|
||||
echo -n "000" > $@
|
||||
|
||||
$(TESTINSTBIN): $(TESTINSTSRC) | $(BUILD_DIR)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR)
|
||||
|
||||
frida: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
|
||||
AFL_ENTRYPOINT=$(AFL_ENTRYPOINT) \
|
||||
$(ROOT)afl-fuzz \
|
||||
-D \
|
||||
-O \
|
||||
-i $(TESTINSTR_DATA_DIR) \
|
||||
-o $(FRIDA_OUT) \
|
||||
-- \
|
||||
$(TESTINSTBIN) @@
|
13
frida_mode/test/deferred/Makefile
Normal file
13
frida_mode/test/deferred/Makefile
Normal file
@ -0,0 +1,13 @@
|
||||
all:
|
||||
@echo trying to use GNU make...
|
||||
@gmake all || echo please install GNUmake
|
||||
|
||||
32:
|
||||
@echo trying to use GNU make...
|
||||
@gmake 32 || echo please install GNUmake
|
||||
|
||||
clean:
|
||||
@gmake clean
|
||||
|
||||
frida:
|
||||
@gmake frida
|
125
frida_mode/test/deferred/testinstr.c
Normal file
125
frida_mode/test/deferred/testinstr.c
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
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 <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define TESTINSTR_SECTION
|
||||
#else
|
||||
#define TESTINSTR_SECTION __attribute__((section(".testinstr")))
|
||||
#endif
|
||||
|
||||
void testinstr(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);
|
||||
|
||||
testinstr(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);
|
||||
|
||||
}
|
||||
|
||||
TESTINSTR_SECTION int do_run(char * file) {
|
||||
return run(file);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
if (argc != 2) { return 1; }
|
||||
slow();
|
||||
return do_run(argv[1]);
|
||||
|
||||
}
|
||||
|
80
frida_mode/test/entry_point/GNUmakefile
Normal file
80
frida_mode/test/entry_point/GNUmakefile
Normal file
@ -0,0 +1,80 @@
|
||||
PWD:=$(shell pwd)/
|
||||
ROOT:=$(shell realpath $(PWD)../../..)/
|
||||
BUILD_DIR:=$(PWD)build/
|
||||
TESTINSTR_DATA_DIR:=$(BUILD_DIR)in/
|
||||
TESTINSTR_DATA_FILE:=$(TESTINSTR_DATA_DIR)in
|
||||
|
||||
TESTINSTBIN:=$(BUILD_DIR)testinstr
|
||||
TESTINSTSRC:=$(PWD)testinstr.c
|
||||
|
||||
QEMU_OUT:=$(BUILD_DIR)qemu-out
|
||||
FRIDA_OUT:=$(BUILD_DIR)frida-out
|
||||
|
||||
GET_SYMBOL_ADDR:=$(ROOT)frida_mode/test/png/persistent/get_symbol_addr.py
|
||||
|
||||
ifndef ARCH
|
||||
|
||||
ARCH=$(shell uname -m)
|
||||
ifeq "$(ARCH)" "aarch64"
|
||||
ARCH:=arm64
|
||||
endif
|
||||
|
||||
ifeq "$(ARCH)" "i686"
|
||||
ARCH:=x86
|
||||
endif
|
||||
endif
|
||||
|
||||
ARCH=$(shell uname -m)
|
||||
ifeq "$(ARCH)" "aarch64"
|
||||
AFL_ENTRYPOINT=$(shell $(GET_SYMBOL_ADDR) -f $(TESTINSTBIN) -s run -b 0x0000aaaaaaaaa000)
|
||||
endif
|
||||
|
||||
ifeq "$(ARCH)" "x86_64"
|
||||
AFL_ENTRYPOINT=$(shell $(GET_SYMBOL_ADDR) -f $(TESTINSTBIN) -s run -b 0x0000555555554000)
|
||||
endif
|
||||
|
||||
ifeq "$(ARCH)" "x86"
|
||||
AFL_ENTRYPOINT=$(shell $(GET_SYMBOL_ADDR) -f $(TESTINSTBIN) -s run -b 0x56555000)
|
||||
endif
|
||||
|
||||
.PHONY: all clean qemu frida
|
||||
|
||||
all: $(TESTINSTBIN)
|
||||
make -C $(ROOT)frida_mode/
|
||||
|
||||
32:
|
||||
CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
|
||||
|
||||
$(BUILD_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
$(TESTINSTR_DATA_DIR): | $(BUILD_DIR)
|
||||
mkdir -p $@
|
||||
|
||||
$(TESTINSTR_DATA_FILE): | $(TESTINSTR_DATA_DIR)
|
||||
echo -n "000" > $@
|
||||
|
||||
$(TESTINSTBIN): $(TESTINSTSRC) | $(BUILD_DIR)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR)
|
||||
|
||||
frida: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
|
||||
$(ROOT)afl-fuzz \
|
||||
-D \
|
||||
-O \
|
||||
-i $(TESTINSTR_DATA_DIR) \
|
||||
-o $(FRIDA_OUT) \
|
||||
-- \
|
||||
$(TESTINSTBIN) @@
|
||||
|
||||
frida_entry: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
|
||||
AFL_ENTRYPOINT=$(AFL_ENTRYPOINT) \
|
||||
$(ROOT)afl-fuzz \
|
||||
-D \
|
||||
-O \
|
||||
-i $(TESTINSTR_DATA_DIR) \
|
||||
-o $(FRIDA_OUT) \
|
||||
-- \
|
||||
$(TESTINSTBIN) @@
|
16
frida_mode/test/entry_point/Makefile
Normal file
16
frida_mode/test/entry_point/Makefile
Normal file
@ -0,0 +1,16 @@
|
||||
all:
|
||||
@echo trying to use GNU make...
|
||||
@gmake all || echo please install GNUmake
|
||||
|
||||
32:
|
||||
@echo trying to use GNU make...
|
||||
@gmake 32 || echo please install GNUmake
|
||||
|
||||
clean:
|
||||
@gmake clean
|
||||
|
||||
frida:
|
||||
@gmake frida
|
||||
|
||||
frida_entry:
|
||||
@gmake frida
|
121
frida_mode/test/entry_point/testinstr.c
Normal file
121
frida_mode/test/entry_point/testinstr.c
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
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 <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define TESTINSTR_SECTION
|
||||
#else
|
||||
#define TESTINSTR_SECTION __attribute__((section(".testinstr")))
|
||||
#endif
|
||||
|
||||
void testinstr(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);
|
||||
|
||||
testinstr(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]);
|
||||
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user