├── AUTHORS ├── CHANGES ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── afl-dyninst.cpp ├── afl-dyninst.sh ├── afl-fuzz-dyninst.sh └── libAflDyninst.cpp /AUTHORS: -------------------------------------------------------------------------------- 1 | This code was developed as part of a project with the Cisco Talos VULNDEV Team 2 | 3 | Authors: 4 | Aleksandar Nikolic 5 | Marc "van Hauser" Heuse || 6 | 7 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | https://github.com/vanhauser-thc/afl-dyninst 5 | - performance level 1 is now standard and only -x needed for extra performance 6 | flags. There was no disadvantage in previous level 1 so its default now. 7 | - removed -l option because of bugs in the implementation, bugs in dyninst 8 | and behavior of dyninst that was not good for our purpose. Use -r instead 9 | - Only compile dyninst9 bug workaround when necessary 10 | - added -I option (only instrument specific functions) 11 | - updated the README for guidance to build with dyninst version 10 12 | - added support for dyninst version 10 13 | - added -x performance optimization option and a speed enhancement patch that 14 | is always active. before this afl-dyninst was meh, now it is OK. 15 | top speed is now ~110% on stock afl-dyninst and ~65% on stock afl-gcc 16 | - -e option now also understands function names, not only 0x1234 addresses 17 | - searches for multiple entrypoints now: main, init, start and _NAME variants 18 | - afl-dyninst now works fine with AARCH64 and PPC :) 19 | - more verbose output, -vv -vvv support 20 | => at this stage did talos-vulndev/afl-dyninst the last pull from this repo 21 | - fixed some typos 22 | - renamed afl-fuzz.sh to afl-fuzz-dyninst.sh and make install 23 | installs now the scripts 24 | - added -E switch to force a program exit at specific addresses 25 | - added -D switch which does not do basic block instrumentation 26 | - the -S switch can now be specified several times 27 | - checks the dyninst version and comments on the -f option 28 | - added -S switch to skip instrumenting a specific function 29 | - added make install target 30 | - updated README 31 | - Fix for programs that were unable to print to stdout after instrumentation 32 | - added -f switch to fix a bug in dyninst where sometimes the edi/rdi 33 | register is not saved which is used in the instrumentation function 34 | when a function is using edi/rdi for parameter passing 35 | - added -m switch to only instrument basic blocks of a minimum size 36 | - code beautifications, more detailed output, spelling fixes 37 | - added afl-dyninst.sh and afl-fuzz.sh helper scripts which presets 38 | the necessary environment variables 39 | => intial fork from https://github.com/talos-vulndev/afl-dyninst 40 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM kalilinux/kali-rolling AS afl-dyninst 2 | MAINTAINER vh@thc.org 3 | 4 | ARG DEBIAN_FRONTEND=noninteractive 5 | RUN apt-get update && apt-get -y upgrade && apt-get -y install \ 6 | build-essential \ 7 | gcc \ 8 | g++ \ 9 | make \ 10 | cmake \ 11 | git \ 12 | gdb \ 13 | ca-certificates \ 14 | tar \ 15 | gzip \ 16 | vim \ 17 | joe \ 18 | wget \ 19 | curl \ 20 | apt-utils \ 21 | libelf-dev \ 22 | libelf1 \ 23 | libiberty-dev \ 24 | libboost-all-dev \ 25 | libdw-dev \ 26 | libtbb2 \ 27 | libtbb-dev \ 28 | && apt-get -y autoremove && rm -rf /var/lib/apt/lists/* 29 | 30 | RUN git clone --depth=1 https://github.com/dyninst/dyninst \ 31 | && cd dyninst && mkdir build && cd build \ 32 | && cmake .. \ 33 | && make -j3 \ 34 | && make install 35 | 36 | RUN git clone --depth=1 https://github.com/AFLplusplus/AFLplusplus \ 37 | && cd AFLplusplus \ 38 | && make source-only \ 39 | && make install 40 | 41 | RUN git clone --depth=1 https://github.com/vanhauser-thc/afl-dyninst \ 42 | && cd afl-dyninst \ 43 | && ln -s ../AFLplusplus afl \ 44 | && make \ 45 | && make install 46 | 47 | RUN echo "/usr/local/lib" > /etc/ld.so.conf.d/dyninst.conf && ldconfig \ 48 | && echo "export DYNINSTAPI_RT_LIB=/usr/local/lib/libdyninstAPI_RT.so" >> .bashrc 49 | 50 | RUN rm -rf afl-dyninst AFLplusplus dyninst 51 | 52 | ENV DYNINSTAPI_RT_LIB /usr/local/lib/libdyninstAPI_RT.so 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # EDIT: path to dyninst binaries 2 | DYNINST_ROOT = /usr/local 3 | 4 | # EDIT: you must set this to your dyninst build directory 5 | DYNINST_BUILD = /path/to/dyninst/build-directory 6 | #DYNINST_BUILD = /prg/tmp/dyninst/build-10.0.1 7 | 8 | # EDIT: path to afl src if you do not set a symlink from ./afl to the afl directory 9 | AFL_ROOT = ./afl 10 | 11 | # better dont touch these 12 | DYNINST9=-lcommon -liberty -lboost_system 13 | DYNINST10=-I$(DYNINST_BUILD)/tbb/src/TBB/src/include -lboost_system -ltbb 14 | 15 | # EDIT: set this to either DYNINST9 or DYNINST10 depending on what you installed 16 | DYNINST_OPT = $(DYNINST10) 17 | 18 | # path to libelf and libdwarf 19 | DEPS_ROOT = /usr/local 20 | 21 | # where to install 22 | INSTALL_ROOT = /usr/local 23 | 24 | #CXX = g++ 25 | CXXFLAGS = -Wall -O3 -std=c++11 -g -O3 -std=c++11 26 | LIBFLAGS = -fpic -shared 27 | 28 | CC = gcc 29 | CFLAGS = -Wall -O3 -g -std=gnu99 30 | 31 | all: afl-dyninst libAflDyninst.so 32 | 33 | afl-dyninst: afl-dyninst.o 34 | $(CXX) $(CXXFLAGS) -L$(DYNINST_ROOT)/lib -L$(DEPS_ROOT)/lib -o afl-dyninst afl-dyninst.o $(DYNINST_OPT) -ldyninstAPI 35 | 36 | libAflDyninst.so: libAflDyninst.cpp 37 | $(CXX) -O3 -std=c++11 $(LIBFLAGS) -I$(AFL_ROOT) -I$(DYNINST_ROOT)/include -I$(DEPS_ROOT)/include libAflDyninst.cpp -o libAflDyninst.so 38 | 39 | afl-dyninst.o: afl-dyninst.cpp 40 | $(CXX) $(CXXFLAGS) $(DYNINST_OPT) -I$(DEPS_ROOT)/include -I$(DYNINST_ROOT)/include -c afl-dyninst.cpp 41 | 42 | clean: 43 | rm -f afl-dyninst *.so *.o 44 | 45 | install: all 46 | install -d $(INSTALL_ROOT)/bin 47 | install -d $(INSTALL_ROOT)/lib 48 | install afl-dyninst afl-dyninst.sh afl-fuzz-dyninst.sh $(INSTALL_ROOT)/bin 49 | install libAflDyninst.so $(INSTALL_ROOT)/lib 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # American Fuzzy Lop + Dyninst == AFL Fuzzing blackbox binaries 2 | 3 | The tool has two parts. The instrumentation tool and the instrumentation 4 | library. Instrumentation library has an initialization callback and basic 5 | block callback functions which are designed to emulate what AFL is doing 6 | with afl-gcc/afl-g++/afl-as. 7 | 8 | Instrumentation tool (afl-dyninst) instruments the supplied binary by 9 | inserting callbacks for each basic block and an initialization 10 | callback either at _init or at specified entry point. 11 | 12 | You can pull a docker image from hub (last push February 2021): 13 | ``` 14 | docker pull vanhauser/afl-dyninst 15 | ``` 16 | 17 | ## Building / Compiling 18 | 19 | ### docker 20 | 21 | simply run 22 | ``` 23 | docker build -t afl-dyninst . 24 | ``` 25 | which will take ~25 minutes. 26 | Afterwards you have a containers with afl-dyninst and afl++: 27 | ``` 28 | docker run -ti afl-dyninst 29 | ``` 30 | 31 | ### on your own 32 | 33 | 1. Clone, compile and install dyninst: [https://github.com/dyninst/dyninst](https://github.com/dyninst/dyninst) 34 | 35 | Note that you could also use dyninst 9.3.2, but has less platform support and 36 | quite a few bugs. For using dyninst 9.x you have to edit the Makefile 37 | Using at least 10.1.0 is highly recommended! 38 | 39 | 2. Download and install afl++ from [https://github.com/vanhauser-thc/AFLplusplus](https://github.com/vanhauser-thc/AFLplusplus) 40 | It's an up to date and enhanced version to the original afl with better 41 | performance, new features and bugfixes. 42 | 43 | 3. Edit the Makefile and set DYNINST_ROOT and AFL_ROOT to appropriate paths. 44 | 45 | 4. make 46 | 47 | 5. sudo make install 48 | 49 | 50 | ### Building dyninst 10 51 | 52 | Building dyninst10 can be a pain. 53 | If you are not on debian-testing or kali-rolling, I recommend the following steps: 54 | 1. remove elfutils if installed as a distribution package 55 | 2. install libboost-all-dev for your distribution 56 | 3. execute (depending where your libboost is installed, for me its /usr/lib/x86_64-linux-gnu): 57 | ```shell 58 | cd /usr/lib/x86_64-linux-gnu && for i in libboost*.so libboost*.a; do 59 | n=`echo $i|sed 's/\./-mt./'` 60 | ln -s $i $n 2> /dev/null 61 | done 62 | ``` 63 | 4. git clone https://github.com/dyninst/dyninst ; mkdir build ; cd build ; cmake .. ; make ; make install 64 | If dyninst complains about any missing packages - install them. 65 | Depending on the age of your Linux OS you can try to use packages from your distro, and install from source otherwise. 66 | 67 | 68 | ## Commandline options 69 | ``` 70 | Usage: afl-dyninst -dfvxD -i binary -o binary -l library -e address -E address -s number -S funcname -I funcname -m size 71 | -i: input binary program 72 | -o: output binary program 73 | -r: runtime library to instrument (path to, repeat for more than one) 74 | -e: entry point address to patch (required for stripped binaries) 75 | -E: exit point - force exit(0) at this address (repeat for more than one) 76 | -s: number of initial basic blocks to skip in binary 77 | -m: minimum size of a basic bock to instrument (default: 10) 78 | -f: try to fix a dyninst bug that leads to crashes (loss of 20%% performance, only required for dyninst9) 79 | -I: only instrument this function and nothing else (repeat for more than one) 80 | -S: do not instrument this function (repeat for more than one) 81 | -D: instrument only a simple fork server and also forced exit functions 82 | -x: experimental performance mode (~25-50% speed improvement) 83 | -v: verbose output 84 | Note: options -l and -d have been deprecated, use -r and -D instead. 85 | ``` 86 | 87 | Switch -e is used to manualy specify the entry point where initialization 88 | callback is to be inserted. For unstipped binaries, afl-dyninst defaults 89 | to using _init of the binary as an entry point. In case of stripped binaries 90 | this option is required and is best set to the address of main which 91 | can easily be determined by disassembling the binary and looking for an 92 | argument to __libc_start_main. 93 | 94 | Switch -E is used to specify addresses that should force a clean exit 95 | when reached. This can speed up the fuzzing tremendously. 96 | 97 | Switch -s instructs afl-dyninst to skip the first NUMBER of basic blocks. 98 | Currently, it is used to work around a bug in Dyninst but doubles as an 99 | optimization option, as skipping the basic blocks of the initialization 100 | routines makes things run faster. If the instrumented binary is crashing by 101 | itself, try skiping a number of blocks. 102 | 103 | Switch -r allows you to specify a path to a library that is loaded 104 | via dlopen() at runtime. Instrumented runtime libraries will be 105 | written to the same location with a ".ins" suffix as not to overwrite 106 | the original ones. Make sure to backup the originals and then rename the 107 | instrumented ones to original name. 108 | 109 | Switch -m allows you to only instrument basic blocks of a minimum size - the 110 | default minimum size is 1 111 | 112 | Switch -f fixes a dyninst bug that lead to bugs in the instrumented program: 113 | our basic block instrumentation function loaded into the instrumentd binaries 114 | uses the edi/rdi. However dyninst does not always saves and restores it when 115 | instrumenting that function leading to crashes and changed program behaviour 116 | when the register is used for function parameters. 117 | 118 | Switch -S allows you to not instrument specific functions. 119 | This options is mainly to hunt down bugs in dyninst. 120 | Can be specified multiple times. 121 | 122 | Switch -I specified to only instrument specific functions. 123 | This option is amazing with large and threaded targets. 124 | Can be specified multiple times. 125 | 126 | Switch -D installs the afl fork server and forced exit functions but no 127 | basic block instrumentation. That would serve no purpose - unless there are 128 | other tools that need that: 129 | * [https://github.com/vanhauser-thc/afl-dynamorio](https://github.com/vanhauser-thc/afl-dynamorio) 130 | * [https://github.com/vanhauser-thc/afl-pin](https://github.com/vanhauser-thc/afl-pin) 131 | 132 | Switch -x enables an experimental performance mode (+25-50% speed). Just try it 133 | and if the target crashes too often, instrument again without this. Should not 134 | crash though. 135 | 136 | Note that the -l option has been deprecated, use -r instead. 137 | 138 | Note that the -d option has been deprecated, use -D instead (or ignore the -o output file). 139 | 140 | 141 | ## Example of instrumenting a target binary 142 | 143 | Dyninst requires DYNINSTAPI_RT_LIB environment variable to point to the location 144 | of libdyninstAPI_RT.so. 145 | 146 | ``` 147 | $ export DYNINSTAPI_RT_LIB=/usr/local/lib/libdyninstAPI_RT.so 148 | $ ./afl-dyninst -i ./unrar -o ./rar_ins -e 0x4034c0 -x 149 | Skipping library: libAflDyninst.so 150 | Instrumenting module: DEFAULT_MODULE 151 | Inserting init callback. 152 | Saving the instrumented binary to ./unrar_ins... 153 | All done! Happy fuzzing! 154 | ``` 155 | 156 | Here we are instrumenting the rar binary with entrypoint at 0x4034c0 157 | (manually found address of main), skipping the first 10 basic blocks 158 | and outputing to unrar_ins 159 | 160 | You can also use the afl-dyninst.sh helper script which sets the required 161 | environment variables for you: 162 | ``` 163 | $ ./afl-dyninst.sh -i ./unrar -o ./rar_ins -e 0x4034c0 -x 164 | ``` 165 | 166 | 167 | ## Running AFL on the instrumented binary 168 | 169 | NOTE: The instrumentation library "libDyninst.so" must be available in the current working 170 | directory or LD_LIBRARY_PATH as that is where the instrumented binary will be looking for it. 171 | 172 | Since AFL checks if the binary has been instrumented by afl-gcc, the 173 | AFL_SKIP_BIN_CHECK environment variable needs to be set. 174 | No modifications to AFL itself is needed. 175 | ``` 176 | $ export AFL_SKIP_BIN_CHECK=1 177 | ``` 178 | Then, AFL can be run as usual: 179 | ``` 180 | $ afl-fuzz -i testcases/archives/common/gzip/ -o test_gzip -- ./gzip_ins -d -c 181 | ``` 182 | 183 | You can also use the afl-fuzz-dyninst.sh helper script which sets the required 184 | environment variables for you. 185 | ``` 186 | $ afl-fuzz-dyninst.sh -i testcases/archives/common/gzip/ -o test_gzip -- ./gzip_ins -d -c 187 | ``` 188 | 189 | ## Problems 190 | 191 | After instrumenting the target binary always check if it works. 192 | Dyninst is making big changes to the code, and hence more often than not 193 | things are not working anymore. 194 | 195 | Problem 1: The binary does not work (crashes or hangs) 196 | 197 | Solution: increase the -m parameter. -m 8 is the minimum recommended, on some 198 | targets -m 16 is required etc. 199 | You can also try to remove the -x performance enhancer 200 | 201 | 202 | Problem 2: Basically every fuzzing test case is reported as crash although it 203 | does not when running it from the command line 204 | 205 | Solution: This happens if the target is using throw/catch, and dyninst's 206 | modification result in that the caught exception is not resetted and 207 | hence abort() is triggered. 208 | No solution to this issue is known yet. 209 | Binary editing the target binary to perform _exit(0) would help though. 210 | 211 | More problems? Create an issue at [https://github.com/vanhauser-thc/afl-dyninst](https://github.com/vanhauser-thc/afl-dyninst) 212 | -------------------------------------------------------------------------------- /afl-dyninst.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | // DyninstAPI includes 19 | #include "BPatch.h" 20 | #include "BPatch_addressSpace.h" 21 | #include "BPatch_binaryEdit.h" 22 | #include "BPatch_flowGraph.h" 23 | #include "BPatch_function.h" 24 | #include "BPatch_point.h" 25 | #include "BPatch_process.h" 26 | #include "dyninstversion.h" // if this include errors, compile and install https://github.com/dyninst/dyninst 27 | 28 | using namespace std; 29 | using namespace Dyninst; 30 | 31 | // cmd line options 32 | char *originalBinary; 33 | char *instrumentedBinary; 34 | char *entryPointName = NULL; 35 | int verbose = 0; 36 | 37 | Dyninst::Address entryPoint; 38 | set todo; 39 | set instrumentLibraries; 40 | set runtimeLibraries; 41 | set skipAddresses; 42 | set onlyAddresses; 43 | set exitAddresses; 44 | unsigned int bbMinSize = 10; 45 | int bbSkip = 0, performance = 1; 46 | bool skipMainModule = false, do_bb = true, dynfix = false; 47 | unsigned long int insertions = 0; 48 | uintptr_t mapaddr = 0; 49 | 50 | BPatch_function *save_rdi; 51 | BPatch_function *restore_rdi; 52 | 53 | const char *functions[] = {"main", "_main", "_initproc", "_init", "start", "_start", NULL}; 54 | 55 | const char *instLibrary = "libAflDyninst.so"; 56 | 57 | static const char *OPT_STR = "fi:o:l:e:E:vs:dr:m:S:I:Dx"; 58 | static const char *USAGE = " -fvxD -i -o -e
-E
-s -S -I -m \n \ 59 | -i: input binary \n \ 60 | -o: output binary\n \ 61 | -r: runtime library to instrument (path to, repeat for more than one)\n \ 62 | -e: entry point address to patch (required for stripped binaries)\n \ 63 | -E: exit point - force exit(0) at this address (repeat for more than one)\n \ 64 | -s: number of initial basic blocks to skip in binary\n \ 65 | -m: minimum size of a basic bock to instrument (default: 10)\n \ 66 | -f: fix a dyninst bug that leads to crashes (performance loss, only dyninst9)\n \ 67 | -I: only instrument this function and nothing else (repeat for more than one)\n \ 68 | -S: do not instrument this function (repeat for more than one)\n \ 69 | -D: instrument only a simple fork server and also forced exit functions\n \ 70 | -x: experimental performance mode (~25-50% speed improvement)\n \ 71 | -v: verbose output\n \ 72 | Note: options -l and -d have been deprecated, use -r and -D instead.\n"; 73 | 74 | bool parseOptions(int argc, char **argv) { 75 | int c; 76 | 77 | while ((c = getopt(argc, argv, OPT_STR)) != -1) { 78 | switch ((char)c) { 79 | case 'x': 80 | performance++; 81 | /* 82 | if (performance == 3) { 83 | #if ( __amd64__ || __x86_64__ ) 84 | fprintf(stderr, "Warning: performance level 3 is currently totally experimental\n"); 85 | #else 86 | fprintf(stderr, "Warning: maximum performance level for non-intelx64 x86 is 2\n"); 87 | performance = 2; 88 | #endif 89 | } else*/ 90 | if (performance > 2) performance = 2; 91 | break; 92 | case 'I': 93 | onlyAddresses.insert(optarg); 94 | break; 95 | case 'S': 96 | skipAddresses.insert(optarg); 97 | break; 98 | case 'e': 99 | if ((entryPoint = strtoul(optarg, NULL, 16)) < 0x1000) 100 | entryPointName = optarg; 101 | break; 102 | case 'i': 103 | originalBinary = optarg; 104 | instrumentLibraries.insert(optarg); 105 | break; 106 | case 'o': 107 | instrumentedBinary = optarg; 108 | break; 109 | case 'l': 110 | fprintf(stderr, "Error: option -l has been removed due implementation issues, dyninst behaviour and dyninst bugs. Please use -r.\n"); 111 | exit(-1); 112 | break; 113 | case 'E': 114 | exitAddresses.insert(strtoul(optarg, NULL, 16)); 115 | break; 116 | case 'r': 117 | runtimeLibraries.insert(optarg); 118 | break; 119 | case 's': 120 | bbSkip = atoi(optarg); 121 | break; 122 | case 'm': 123 | bbMinSize = atoi(optarg); 124 | break; 125 | case 'd': 126 | //skipMainModule = true; 127 | fprintf(stderr, "Warning: option -d has been deprecated, use -D instead or ignore the generated -o file.\n"); 128 | break; 129 | case 'f': 130 | #if (__amd64__ || __x86_64__) 131 | #if (DYNINST_MAJOR_VERSION < 10) 132 | dynfix = true; 133 | #endif 134 | #endif 135 | break; 136 | case 'D': 137 | do_bb = false; 138 | break; 139 | case 'v': 140 | verbose++; 141 | break; 142 | default: 143 | cerr << "Usage: " << argv[0] << USAGE; 144 | return false; 145 | } 146 | } 147 | 148 | if (originalBinary == NULL) { 149 | cerr << "Input binary is required!" << endl; 150 | cerr << "Usage: " << argv[0] << USAGE; 151 | return false; 152 | } 153 | 154 | if (instrumentedBinary == NULL) { 155 | cerr << "Output binary is required!" << endl; 156 | cerr << "Usage: " << argv[0] << USAGE; 157 | return false; 158 | } 159 | 160 | return true; 161 | } 162 | 163 | BPatch_function *findFuncByName(BPatch_image *appImage, char *funcName) { 164 | BPatch_Vector funcs; 165 | 166 | if (NULL == appImage->findFunction(funcName, funcs) || !funcs.size() || NULL == funcs[0]) { 167 | cerr << "Failed to find " << funcName << " function." << endl; 168 | return NULL; 169 | } 170 | 171 | return funcs[0]; 172 | } 173 | 174 | // insert callback to initialization function in the instrumentation library 175 | // either at _init or at manualy specified entry point. 176 | bool insertCallToInit(BPatch_addressSpace *appBin, BPatch_function *instIncFunc, BPatch_module *module, BPatch_function *funcInit, bool install_hack) { 177 | /* Find the instrumentation points */ 178 | vector points; 179 | vector *funcEntry = funcInit->findPoint(BPatch_entry); 180 | BPatch_image *appImage = appBin->getImage(); 181 | BPatchSnippetHandle *handle; 182 | 183 | if (NULL == funcEntry) { 184 | cerr << "Failed to find entry for function. " << endl; 185 | return false; 186 | } 187 | 188 | // THIS BLOCK IS DISABLED - dyninst is too volatile for this to work reliably 189 | // disabled because performance can not be greater than 2 190 | if (performance >= 3 && install_hack == true) { 191 | cout << "Inserting global variables" << endl; 192 | // we set up a fake map so we do not have crashes if the the forkserver 193 | // is not installed in _init but later for speed reasons. 194 | // we could also check in the bb() code if map == 0 but that would 195 | // cost precious instructions. 196 | BPatch_variableExpr *fakemap = appBin->malloc(65536); 197 | BPatch_constExpr fakemap_ptr(fakemap->getBaseAddr()); 198 | BPatch_variableExpr *map = appBin->malloc(*(appImage->findType("size_t")), "map"); 199 | BPatch_arithExpr initmap(BPatch_assign, *map, fakemap_ptr); 200 | 201 | appBin->insertSnippet(initmap, *funcEntry, BPatch_firstSnippet); 202 | BPatch_constExpr map_ptr(map->getBaseAddr()); 203 | BPatch_variableExpr *prev_id = appBin->malloc(*(appImage->findType("size_t")), "prev_id"); 204 | BPatch_arithExpr initprevid(BPatch_assign, *prev_id, BPatch_constExpr(0)); 205 | 206 | appBin->insertSnippet(initprevid, *funcEntry); 207 | BPatch_Vector instArgs; 208 | cout << "Inserting init callback." << endl; 209 | instArgs.push_back(&map_ptr); 210 | BPatch_funcCallExpr instIncExpr(*instIncFunc, instArgs); 211 | 212 | handle = appBin->insertSnippet(instIncExpr, *funcEntry, BPatch_callBefore, BPatch_lastSnippet); 213 | } else { 214 | BPatch_Vector instArgs; 215 | cout << "Inserting init callback." << endl; 216 | BPatch_funcCallExpr instIncExpr(*instIncFunc, instArgs); 217 | 218 | handle = appBin->insertSnippet(instIncExpr, *funcEntry, BPatch_callBefore, BPatch_lastSnippet); 219 | } 220 | 221 | if (!handle) { 222 | cerr << "Failed to insert init callback." << endl; 223 | return false; 224 | } 225 | return true; 226 | } 227 | 228 | // inserts a callback for each basic block assigning it an instrumentation 229 | // time 16bit random ID just as afl 230 | bool insertBBCallback(BPatch_addressSpace *appBin, BPatch_function *curFunc, char *funcName, BPatch_function *instBBIncFunc, int *bbIndex) { 231 | BPatch_image *appImage = appBin->getImage(); 232 | BPatch_flowGraph *appCFG = curFunc->getCFG(); 233 | unsigned short randID; 234 | 235 | if (!appCFG) { 236 | cerr << "Failed to find CFG for function " << funcName << endl; 237 | return false; 238 | } 239 | 240 | BPatch_Set allBlocks; 241 | if (!appCFG->getAllBasicBlocks(allBlocks)) { 242 | cerr << "Failed to find basic blocks for function " << funcName << endl; 243 | return false; 244 | } else if (allBlocks.size() == 0) { 245 | cerr << "No basic blocks for function " << funcName << endl; 246 | return false; 247 | } 248 | 249 | BPatch_Set::iterator iter; 250 | for (iter = allBlocks.begin(); iter != allBlocks.end(); iter++) { 251 | if (*bbIndex < bbSkip || (*iter)->size() < bbMinSize) { // skip over first bbSkip bbs or below minimum size 252 | (*bbIndex)++; 253 | continue; 254 | } 255 | 256 | BPatch_point *bbEntry = (*iter)->findEntryPoint(); 257 | 258 | if (performance >= 1) { 259 | if ((*iter)->isEntryBlock() == false) { 260 | bool good = false; 261 | 262 | BPatch_Vector sources; 263 | (*iter)->getSources(sources); 264 | for (unsigned int i = 0; i < sources.size() && good == false; i++) { 265 | BPatch_Vector targets; 266 | sources[i]->getTargets(targets); 267 | if (targets.size() > 1) 268 | good = true; 269 | } 270 | if (good == false) 271 | continue; 272 | } 273 | } 274 | 275 | unsigned long address = (*iter)->getStartAddress(); 276 | 277 | randID = rand() % USHRT_MAX; 278 | if (verbose >= 1) { 279 | cout << "Instrumenting Basic Block 0x" << hex << address << " of " << funcName << " with size " << dec << (*iter)->size() << " with random id " << randID << "/0x" << hex << randID << endl; 280 | } 281 | 282 | if (NULL == bbEntry) { 283 | // warn the user, but continue 284 | cerr << "Failed to find entry for basic block at 0x" << hex << address << endl; 285 | (*bbIndex)++; 286 | continue; 287 | } 288 | 289 | BPatchSnippetHandle *handle; 290 | 291 | // level 3 is disabled 292 | if (performance >= 3) { 293 | // these are dummy instructions we overwrite later 294 | BPatch_variableExpr *pid = appImage->findVariable("prev_id"); 295 | BPatch_arithExpr new_prev_id(BPatch_assign, *pid, BPatch_arithExpr(BPatch_divide, BPatch_constExpr(8), BPatch_constExpr(2))); 296 | 297 | handle = appBin->insertSnippet(new_prev_id, *bbEntry, BPatch_lastSnippet); 298 | BPatch_variableExpr *map = appImage->findVariable("map"); 299 | BPatch_variableExpr *pid2 = appImage->findVariable("prev_id"); 300 | BPatch_arithExpr map_idx(BPatch_arithExpr(BPatch_plus, *map, BPatch_arithExpr(BPatch_divide, *pid2, BPatch_constExpr(2)))); 301 | 302 | if (mapaddr == 0) { 303 | printf("Map for AFL is installed at: %p\n", (void *)map->getBaseAddr()); 304 | mapaddr = (uintptr_t)map->getBaseAddr(); 305 | } 306 | handle = appBin->insertSnippet(map_idx, *bbEntry, BPatch_firstSnippet); 307 | } else { 308 | BPatch_Vector instArgs1; 309 | BPatch_Vector instArgs; 310 | BPatch_constExpr bbId(randID); 311 | 312 | instArgs.push_back(&bbId); 313 | #if (DYNINST_MAJOR_VERSION < 10) 314 | BPatch_funcCallExpr instIncExpr1(*save_rdi, instArgs1); 315 | BPatch_funcCallExpr instIncExpr3(*restore_rdi, instArgs1); 316 | #endif 317 | BPatch_funcCallExpr instIncExpr(*instBBIncFunc, instArgs); 318 | 319 | 320 | #if (DYNINST_MAJOR_VERSION < 10) 321 | if (dynfix == true) 322 | handle = appBin->insertSnippet(instIncExpr1, *bbEntry, BPatch_callBefore, BPatch_firstSnippet); 323 | #endif 324 | handle = appBin->insertSnippet(instIncExpr, *bbEntry, BPatch_callBefore); 325 | #if (DYNINST_MAJOR_VERSION < 10) 326 | if (dynfix == true) 327 | handle = appBin->insertSnippet(instIncExpr3, *bbEntry, BPatch_callBefore, BPatch_lastSnippet); 328 | #endif 329 | } 330 | 331 | if (!handle) { 332 | // warn the user, but continue to next bb 333 | cerr << "Failed to insert instrumention in basic block at 0x" << hex << address << endl; 334 | (*bbIndex)++; 335 | continue; 336 | } else 337 | insertions++; 338 | 339 | (*bbIndex)++; 340 | } 341 | 342 | return true; 343 | } 344 | 345 | int main(int argc, char **argv) { 346 | char *func2patch = NULL; 347 | int loop; 348 | 349 | cout << "afl-dyninst (c) 2017-2021 by Aleksandar Nikolic and Marc Heuse [https://github.com/vanhauser-thc/afl-dyninst] Apache 2.0 License" << endl; 350 | 351 | if (argc < 3 || strncmp(argv[1], "-h", 2) == 0 || strncmp(argv[1], "--h", 3) == 0) { 352 | cout << "Usage: " << argv[0] << USAGE; 353 | return false; 354 | } 355 | 356 | if (!parseOptions(argc, argv)) { 357 | return EXIT_FAILURE; 358 | } 359 | #if (__amd64__ || __x86_64__) 360 | if (do_bb == true) { 361 | if (DYNINST_MAJOR_VERSION < 9 || (DYNINST_MAJOR_VERSION == 9 && DYNINST_MINOR_VERSION < 3) || (DYNINST_MAJOR_VERSION == 9 && DYNINST_MINOR_VERSION == 3 && DYNINST_PATCH_VERSION <= 2)) { 362 | if (dynfix == false) 363 | fprintf(stderr, "Warning: your dyninst version does not include a critical fix, you should use the -f option!\n"); 364 | } else { 365 | if (dynfix == true) 366 | fprintf(stderr, "Notice: your dyninst version is fixed, the -f option should not be necessary.\n"); 367 | } 368 | } 369 | #endif 370 | 371 | BPatch bpatch; 372 | 373 | if (performance >= 2) { 374 | bpatch.setSaveFPR(false); 375 | bpatch.setTrampRecursive(true); 376 | } 377 | 378 | BPatch_addressSpace *appBin = bpatch.openBinary(originalBinary, instrumentLibraries.size() != 1); 379 | 380 | if (appBin == NULL) { 381 | cerr << "Failed to open binary" << endl; 382 | return EXIT_FAILURE; 383 | } 384 | 385 | BPatch_image *appImage = appBin->getImage(); 386 | 387 | // get and iterate over all modules, instrumenting only the default and manually specified ones 388 | vector *modules = appImage->getModules(); 389 | vector::iterator moduleIter; 390 | vector *funcsInModule; 391 | BPatch_module *defaultModule = NULL, *firstModule = NULL; 392 | string defaultModuleName; 393 | 394 | // look for _init 395 | if (defaultModuleName.empty()) { 396 | for (loop = 0; functions[loop] != NULL && func2patch == NULL; loop++) { 397 | for (moduleIter = modules->begin(); moduleIter != modules->end(); ++moduleIter) { 398 | vector::iterator funcsIterator; 399 | char moduleName[1024]; 400 | 401 | if (firstModule == NULL) 402 | firstModule = (*moduleIter); 403 | (*moduleIter)->getName(moduleName, 1024); 404 | funcsInModule = (*moduleIter)->getProcedures(); 405 | if (verbose >= 2) 406 | cout << "Looking for init function " << functions[loop] << " in " << moduleName << endl; 407 | for (funcsIterator = funcsInModule->begin(); funcsIterator != funcsInModule->end(); ++funcsIterator) { 408 | char funcName[1024]; 409 | 410 | (*funcsIterator)->getName(funcName, 1024); 411 | if (verbose >= 3 && loop == 0) 412 | printf("module: %s function: %s\n", moduleName, funcName); 413 | if (string(funcName) == string(functions[loop])) { 414 | func2patch = (char *)functions[loop]; 415 | defaultModuleName = string(moduleName); 416 | defaultModule = (*moduleIter); 417 | if (verbose >= 1) { 418 | cout << "Found " << func2patch << " in " << moduleName << endl; 419 | } 420 | break; 421 | } 422 | } 423 | if (!defaultModuleName.empty()) 424 | break; 425 | } 426 | if (func2patch != NULL) 427 | break; 428 | } 429 | } 430 | // last resort, by name of the binary 431 | if (defaultModuleName.empty()) 432 | defaultModuleName = string(originalBinary).substr(string(originalBinary).find_last_of("\\/") + 1); 433 | if (defaultModule == NULL) 434 | defaultModule = firstModule; 435 | 436 | if (!appBin->loadLibrary(instLibrary)) { 437 | cerr << "Failed to open instrumentation library " << instLibrary << endl; 438 | cerr << "It needs to be located in the current working directory." << endl; 439 | return EXIT_FAILURE; 440 | } 441 | 442 | /* Find code coverage functions in the instrumentation library */ 443 | BPatch_function *initAflForkServer; 444 | 445 | #if (DYNINST_MAJOR_VERSION < 10) 446 | save_rdi = findFuncByName(appImage, (char *)"save_rdi"); 447 | restore_rdi = findFuncByName(appImage, (char *)"restore_rdi"); 448 | #endif 449 | BPatch_function *bbCallback = findFuncByName(appImage, (char *)"bbCallback"); 450 | BPatch_function *forceCleanExit = findFuncByName(appImage, (char *)"forceCleanExit"); 451 | 452 | if (do_bb == true) { 453 | if (performance >= 3) 454 | initAflForkServer = findFuncByName(appImage, (char *)"initAflForkServerVar"); 455 | else 456 | initAflForkServer = findFuncByName(appImage, (char *)"initAflForkServer"); 457 | } else 458 | initAflForkServer = findFuncByName(appImage, (char *)"initOnlyAflForkServer"); 459 | 460 | if (!initAflForkServer || !bbCallback || !forceCleanExit 461 | #if (DYNINST_MAJOR_VERSION < 10) 462 | || !save_rdi || !restore_rdi 463 | #endif 464 | ) { 465 | cerr << "Instrumentation library lacks callbacks!" << endl; 466 | return EXIT_FAILURE; 467 | } 468 | 469 | int bbIndex = 0; 470 | 471 | // if an entrypoint was set then find function, else find _init 472 | BPatch_function *funcToPatch = NULL; 473 | 474 | if (entryPoint == 0 && entryPointName == NULL) { 475 | if (func2patch == NULL) { 476 | cerr << "Couldn't locate _init, specify entry point manually with -e 0xaddr" << endl; 477 | return EXIT_FAILURE; 478 | } 479 | BPatch_Vector funcs; 480 | defaultModule->findFunction(func2patch, funcs); 481 | if (!funcs.size()) { 482 | cerr << "Couldn't locate _init, specify entry point manually with -e 0xaddr" << endl; 483 | return EXIT_FAILURE; 484 | } 485 | // there should really be only one 486 | funcToPatch = funcs[0]; 487 | } else { 488 | if (entryPointName != NULL) { 489 | for (moduleIter = modules->begin(); moduleIter != modules->end() && funcToPatch == 0; ++moduleIter) { 490 | BPatch_Vector funcs; 491 | (*moduleIter)->findFunction(entryPointName, funcs); 492 | if (funcs.size() > 0) { 493 | char moduleName[1024]; 494 | 495 | funcToPatch = funcs[0]; 496 | defaultModule = (*moduleIter); 497 | defaultModule->getName(moduleName, 1024); 498 | defaultModuleName = string(moduleName); 499 | printf("Found entypoint %s in module %s\n", entryPointName, moduleName); 500 | break; 501 | } 502 | } 503 | } 504 | if (!funcToPatch) { 505 | if (verbose > 1) 506 | printf("Looking for entrypoint %p\n", (char *)entryPoint); 507 | funcToPatch = defaultModule->findFunctionByEntry(entryPoint); 508 | if (!funcToPatch && defaultModule != firstModule) { 509 | funcToPatch = firstModule->findFunctionByEntry(entryPoint); 510 | if (funcToPatch) 511 | defaultModule = firstModule; 512 | } 513 | if (!funcToPatch) { // ok lets go hardcore ... 514 | if (verbose > 1) 515 | printf("OK we did not find the entrypoint so far, lets dig deeper ...\n"); 516 | for (moduleIter = modules->begin(); moduleIter != modules->end() && funcToPatch != NULL; ++moduleIter) { 517 | vector::iterator funcsIterator; 518 | funcToPatch = (*moduleIter)->findFunctionByEntry(entryPoint); 519 | if (funcToPatch) 520 | defaultModule = (*moduleIter); 521 | } 522 | } 523 | if (funcToPatch && verbose >= 1) { 524 | char moduleName[1024]; 525 | 526 | defaultModule->getName(moduleName, 1024); 527 | defaultModuleName = string(moduleName); 528 | printf("Found entypoint %p in module %s\n", (void *)entryPoint, moduleName); 529 | } 530 | } 531 | } 532 | if (!funcToPatch) { 533 | cerr << "Couldn't locate function at given entry point. " << endl; 534 | cerr << "Try: readelf -ls " << originalBinary << " | egrep 'Entry|FUNC.*GLOBAL.*DEFAULT' | egrep -v '@|UND'" << endl; 535 | return EXIT_FAILURE; 536 | } 537 | if (!insertCallToInit(appBin, initAflForkServer, defaultModule, funcToPatch, true)) { 538 | cerr << "Could not insert init callback at given entry point." << endl; 539 | return EXIT_FAILURE; 540 | } 541 | 542 | for (moduleIter = modules->begin(); moduleIter != modules->end(); ++moduleIter) { 543 | char moduleName[1024]; 544 | 545 | (*moduleIter)->getName(moduleName, 1024); 546 | if ((*moduleIter)->isSharedLib()) { 547 | if (instrumentLibraries.find(moduleName) == instrumentLibraries.end() && string(moduleName).find(".so") != string::npos) { 548 | cout << "Skipping library: " << moduleName << endl; 549 | continue; 550 | } 551 | } 552 | 553 | if (string(moduleName).find(defaultModuleName) != string::npos) { 554 | if (skipMainModule) 555 | continue; 556 | } 557 | 558 | if (do_bb == true) { 559 | cout << "Instrumenting module: " << moduleName << endl; 560 | vector *allFunctions = (*moduleIter)->getProcedures(); 561 | vector::iterator funcIter; 562 | // iterate over all functions in the module 563 | for (funcIter = allFunctions->begin(); funcIter != allFunctions->end(); ++funcIter) { 564 | BPatch_function *curFunc = *funcIter; 565 | char funcName[1024]; 566 | int do_patch = 1; 567 | 568 | curFunc->getName(funcName, 1024); 569 | if (string(funcName) == string("_init") || string(funcName) == string("__libc_csu_init") || string(funcName) == string("_start")) { 570 | if (verbose) 571 | cout << "Skipping instrumenting function " << funcName << endl; 572 | continue; // here's a bug on hlt // XXX: check what happens if removed 573 | } 574 | if (!skipAddresses.empty()) { 575 | set::iterator saiter; 576 | for (saiter = skipAddresses.begin(); saiter != skipAddresses.end() && do_patch == 1; saiter++) 577 | if (*saiter == string(funcName)) 578 | do_patch = 0; 579 | if (do_patch == 0) { 580 | cout << "Skipping instrumenting function " << funcName << endl; 581 | continue; 582 | } 583 | } 584 | if (!onlyAddresses.empty()) { 585 | do_patch = 0; 586 | set::iterator saiter; 587 | for (saiter = onlyAddresses.begin(); saiter != onlyAddresses.end() && do_patch == 1; saiter++) 588 | if (*saiter == string(funcName)) 589 | do_patch = 1; 590 | if (do_patch == 0) { 591 | cout << "Skipping instrumenting function " << funcName << endl; 592 | continue; 593 | } 594 | } 595 | insertBBCallback(appBin, curFunc, funcName, bbCallback, &bbIndex); 596 | } 597 | } 598 | } 599 | 600 | if (!exitAddresses.empty()) { 601 | cout << "Instrumenting forced exit addresses." << endl; 602 | set::iterator uliter; 603 | 604 | for (uliter = exitAddresses.begin(); uliter != exitAddresses.end(); uliter++) { 605 | if (*uliter > 0 && (signed long)*uliter != -1) { 606 | funcToPatch = defaultModule->findFunctionByEntry(*uliter); 607 | if (!funcToPatch) { 608 | cerr << "Could not find enty point 0x" << hex << *uliter << " (continuing)" << endl; 609 | } else { 610 | if (!insertCallToInit(appBin, forceCleanExit, defaultModule, funcToPatch, false)) 611 | cerr << "Could not insert force clean exit callback at 0x" << hex << *uliter << " (continuing)" << endl; 612 | } 613 | } 614 | } 615 | } 616 | 617 | cout << "Saving the instrumented binary to " << instrumentedBinary << " ..." << endl; 618 | // Output the instrumented binary 619 | BPatch_binaryEdit *appBinr = dynamic_cast(appBin); 620 | 621 | if (!appBinr->writeFile(instrumentedBinary)) { 622 | cerr << "Failed to write output file: " << instrumentedBinary << endl; 623 | return EXIT_FAILURE; 624 | } 625 | todo.insert(instrumentedBinary); 626 | 627 | if (!runtimeLibraries.empty()) { 628 | cout << "Instrumenting runtime libraries." << endl; 629 | set::iterator rtLibIter; 630 | for (rtLibIter = runtimeLibraries.begin(); rtLibIter != runtimeLibraries.end(); rtLibIter++) { 631 | BPatch_addressSpace *libBin = bpatch.openBinary((*rtLibIter).c_str(), false); 632 | 633 | if (libBin == NULL) { 634 | cerr << "Failed to open binary " << *rtLibIter << endl; 635 | return EXIT_FAILURE; 636 | } 637 | BPatch_image *libImg = libBin->getImage(); 638 | 639 | vector *modules = libImg->getModules(); 640 | moduleIter = modules->begin(); 641 | for (; moduleIter != modules->end(); ++moduleIter) { 642 | char moduleName[1024]; 643 | 644 | (*moduleIter)->getName(moduleName, 1024); 645 | cout << "Instrumenting module: " << moduleName << endl; 646 | vector *allFunctions = (*moduleIter)->getProcedures(); 647 | vector::iterator funcIter; 648 | // iterate over all functions in the module 649 | for (funcIter = allFunctions->begin(); funcIter != allFunctions->end(); ++funcIter) { 650 | BPatch_function *curFunc = *funcIter; 651 | char funcName[1024]; 652 | int do_patch = 1; 653 | 654 | curFunc->getName(funcName, 1024); 655 | if (string(funcName) == string("_init") || string(funcName) == string("__libc_csu_init") || string(funcName) == string("_start")) 656 | continue; 657 | if (!skipAddresses.empty()) { 658 | set::iterator saiter; 659 | for (saiter = skipAddresses.begin(); saiter != skipAddresses.end() && do_patch == 1; saiter++) 660 | if (*saiter == string(funcName)) 661 | do_patch = 0; 662 | if (do_patch == 0) { 663 | cout << "Skipping instrumenting function " << funcName << endl; 664 | continue; 665 | } 666 | } 667 | 668 | insertBBCallback(libBin, curFunc, funcName, bbCallback, &bbIndex); 669 | } 670 | } 671 | appBinr = dynamic_cast(libBin); 672 | if (!appBinr->writeFile((*rtLibIter + ".ins").c_str())) { 673 | cerr << "Failed to write output file: " << (*rtLibIter + ".ins").c_str() << endl; 674 | return EXIT_FAILURE; 675 | } else { 676 | cout << "Saved the instrumented library to " << (*rtLibIter + ".ins").c_str() << "." << endl; 677 | todo.insert(*rtLibIter + ".ins"); 678 | } 679 | } 680 | } 681 | 682 | printf("Did a total of %lu basic block insertions\n", insertions); 683 | 684 | if (performance >= 3) { 685 | int fd; 686 | struct stat st; 687 | uint64_t i, found = 0; 688 | unsigned char *ptr; 689 | 690 | unsigned char snip1[] = {0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00}; 691 | unsigned char snip2[] = {0x08, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00}; 692 | unsigned char fullsnip[] = {0x53, 0x50, 0x41, 0x52, 0x48, 0xBB, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x03, 0x48, 0x85, 0xc0, 0x74, 0x28, 0x49, 0xBA, 0x08, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x41, 0x8b, 0x1a, 0x66, 0x81, 0xf3, 0x99, 0x99, 0x48, 0x0f, 0xb7, 0xdb, 0x80, 0x04, 0x18, 0x01, 0x66, 0x41, 0x8b, 0x1a, 0x66, 0xd1, 0xfb, 0x66, 0x41, 0x89, 0x1a, 0x41, 0x5a, 0x58, 0x5b, 0x90, 0x90, 0x90, 0x90}; 693 | memcpy(snip1, (char *)&mapaddr, sizeof(mapaddr)); 694 | memcpy(fullsnip + 6, (char *)&mapaddr, sizeof(mapaddr)); 695 | mapaddr += sizeof(mapaddr); 696 | memcpy(snip2, (char *)&mapaddr, sizeof(mapaddr)); 697 | memcpy(fullsnip + 24, (char *)&mapaddr, sizeof(mapaddr)); 698 | set::iterator fn; 699 | for (fn = todo.begin(); fn != todo.end(); fn++) { 700 | cout << "Reinstrumenting " << *fn << " ..." << endl; 701 | if ((fd = open((const char *)(fn->c_str()), O_RDWR)) == -1 || fstat(fd, &st) != 0) { 702 | cerr << "Error: file is gone: " << *fn << endl; 703 | exit(-1); 704 | } 705 | if ((size_t)st.st_size < (size_t)sizeof(fullsnip)) { 706 | cerr << "Error: somethings horrible wrong here with " << *fn << " ..." << endl; 707 | continue; 708 | } 709 | ptr = (unsigned char *)mmap(NULL, st.st_size, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0); 710 | for (i = 2; i < (size_t)st.st_size - (size_t)sizeof(fullsnip); i++) { 711 | if (memcmp(ptr + i, snip1, sizeof(snip1)) == 0 && memcmp(ptr + i + sizeof(snip1) + 4, snip2, sizeof(snip2)) == 0) { 712 | found++; 713 | fullsnip[0x27] = rand() % 256; 714 | fullsnip[0x28] = rand() % 256; 715 | memcpy(ptr + i - 2, fullsnip, sizeof(fullsnip)); 716 | } 717 | } 718 | // printf("found %lu entries, snipsize %u\n", found, (unsigned int)sizeof(fullsnip)); 719 | munmap((void *)ptr, st.st_size); 720 | close(fd); 721 | } 722 | if (found == insertions) { 723 | printf("SUCCESS! Performance level 3 succeeded :)\n"); 724 | } else { 725 | fprintf(stderr, "Error: can not complete performance level 3, could not find all insertions (%lu of %lu).\n", found, insertions); 726 | exit(-1); 727 | } 728 | } 729 | 730 | cout << "All done! Happy fuzzing!" << endl; 731 | return EXIT_SUCCESS; 732 | } 733 | -------------------------------------------------------------------------------- /afl-dyninst.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export DYNINSTAPI_RT_LIB=/usr/local/lib/libdyninstAPI_RT.so 3 | afl-dyninst $* 4 | -------------------------------------------------------------------------------- /afl-fuzz-dyninst.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | test -z "$1" -o "$1" = "-h" && { echo Syntax: $0 afl-fuzz-options ; echo sets the afl-dyninst environment variables ; exit 1 ; } 3 | # 4 | # remember to run afl-system-config ! 5 | # 6 | export AFL_SKIP_BIN_CHECK=1 7 | export AFL_MAP_SIZE=65536 8 | export DYNINSTAPI_RT_LIB=/usr/local/lib/libdyninstAPI_RT.so 9 | export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib:. 10 | echo " $*" | grep -q " -m" || { echo "Warning: no -m memory option specified!" ; sleep 1 ; } 11 | #export AFL_EXIT_WHEN_DONE=1 12 | #export AFL_TMPDIR=/run/$$ 13 | #export AFL_PRELOAD=./desock.so:./libdislocator/libdislocator.so 14 | afl-fuzz $* 15 | -------------------------------------------------------------------------------- /libAflDyninst.cpp: -------------------------------------------------------------------------------- 1 | #include "config.h" // do symlink: ln -s ../AFLplusplus/include afl 2 | #include "types.h" // Also needed via symlink for newer AFL versions 3 | #include "dyninstversion.h" // if this include errors, compile and install https://github.com/dyninst/dyninst 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | using namespace std; 18 | 19 | static u8 dummy[65536]; 20 | static u8 *__afl_area_ptr = dummy; // this saves a test + jz instruction 21 | static s32 shm_id; 22 | static int __afl_temp_data; 23 | static pid_t __afl_fork_pid; 24 | static unsigned short int prev_id = 0; 25 | static bool forkserver_installed = false; 26 | #if (__amd64__ || __x86_64__) 27 | #if (DYNINST_MAJOR_VERSION < 10) 28 | static long saved_di; 29 | register long rdi asm("di"); // the warning is fine - we need the warning because of a bug in dyninst9 30 | #endif 31 | #endif 32 | 33 | #define PRINT_ERROR(string) (void)(write(2, string, strlen(string)) + 1) // the (...+1) weirdness is so we do not get an ignoring return value warning 34 | 35 | void initAflForkServer() { 36 | if (forkserver_installed == true) 37 | return; 38 | forkserver_installed = true; 39 | 40 | // we can not use fprint* stdout/stderr functions here, it fucks up some programs 41 | char *shm_env_var = getenv(SHM_ENV_VAR); 42 | 43 | if (!shm_env_var) { 44 | PRINT_ERROR("Error getting shm\n"); 45 | return; 46 | } 47 | shm_id = atoi(shm_env_var); 48 | __afl_area_ptr = (u8 *)shmat(shm_id, NULL, 0); 49 | if (__afl_area_ptr == (u8 *)-1) { 50 | PRINT_ERROR("Error: shmat\n"); 51 | return; 52 | } 53 | // enter fork() server thyme! 54 | int n = write(FORKSRV_FD + 1, &__afl_temp_data, 4); 55 | 56 | if (n != 4) { 57 | PRINT_ERROR("Error writing fork server\n"); 58 | return; 59 | } 60 | while (1) { 61 | n = read(FORKSRV_FD, &__afl_temp_data, 4); 62 | if (n != 4) { 63 | PRINT_ERROR("Error reading fork server\n"); 64 | return; 65 | } 66 | 67 | __afl_fork_pid = fork(); 68 | if (__afl_fork_pid < 0) { 69 | PRINT_ERROR("Error on fork()\n"); 70 | return; 71 | } 72 | if (__afl_fork_pid == 0) { 73 | close(FORKSRV_FD); 74 | close(FORKSRV_FD + 1); 75 | prev_id = 0; 76 | break; 77 | } else { 78 | // parrent stuff 79 | n = write(FORKSRV_FD + 1, &__afl_fork_pid, 4); 80 | pid_t temp_pid = waitpid(__afl_fork_pid, &__afl_temp_data, 2); 81 | 82 | if (temp_pid == 0) { 83 | return; 84 | } 85 | n = write(FORKSRV_FD + 1, &__afl_temp_data, 4); 86 | } 87 | } 88 | } 89 | 90 | // Should be called on basic block entry 91 | void bbCallback(unsigned short id) { 92 | __afl_area_ptr[prev_id ^ id]++; 93 | prev_id = id >> 1; 94 | } 95 | 96 | void forceCleanExit() { exit(0); } 97 | 98 | #if (DYNINST_MAJOR_VERSION < 10) 99 | void save_rdi() { 100 | #if __amd64__ || __x86_64__ 101 | saved_di = rdi; 102 | #endif 103 | } 104 | 105 | void restore_rdi() { 106 | #if __amd64__ || __x86_64__ 107 | rdi = saved_di; 108 | #endif 109 | } 110 | #endif 111 | 112 | void initOnlyAflForkServer() { 113 | if (forkserver_installed == true) 114 | return; 115 | forkserver_installed = true; 116 | 117 | // enter fork() server thyme! 118 | int n = write(FORKSRV_FD + 1, &__afl_temp_data, 4); 119 | 120 | if (n != 4) { 121 | PRINT_ERROR("Error writting fork server\n"); 122 | return; 123 | } 124 | while (1) { 125 | n = read(FORKSRV_FD, &__afl_temp_data, 4); 126 | if (n != 4) { 127 | PRINT_ERROR("Error reading fork server\n"); 128 | return; 129 | } 130 | 131 | __afl_fork_pid = fork(); 132 | if (__afl_fork_pid < 0) { 133 | PRINT_ERROR("Error on fork()\n"); 134 | return; 135 | } 136 | if (__afl_fork_pid == 0) { 137 | close(FORKSRV_FD); 138 | close(FORKSRV_FD + 1); 139 | prev_id = 0; 140 | break; 141 | } else { 142 | // parrent stuff 143 | n = write(FORKSRV_FD + 1, &__afl_fork_pid, 4); 144 | pid_t temp_pid = waitpid(__afl_fork_pid, &__afl_temp_data, 2); 145 | 146 | if (temp_pid == 0) { 147 | return; 148 | } 149 | n = write(FORKSRV_FD + 1, &__afl_temp_data, 4); 150 | } 151 | } 152 | } 153 | 154 | void initAflForkServerVar(u8 *map) { 155 | // we can not use fprint* stdout/stderr functions here, it fucks up some programs 156 | if (forkserver_installed == true) 157 | return; 158 | forkserver_installed = true; 159 | 160 | u8 **ptr = (u8 **)map; 161 | char *shm_env_var = getenv(SHM_ENV_VAR); 162 | if (!shm_env_var) { 163 | char buf[256]; 164 | PRINT_ERROR("Error getting shm\n"); 165 | snprintf(buf, sizeof(buf), "__afl_area_ptr: %p\n", ptr); 166 | PRINT_ERROR(buf); 167 | return; 168 | } 169 | 170 | shm_id = atoi(shm_env_var); 171 | *ptr = (u8 *)shmat(shm_id, NULL, 0); 172 | if ((u8 *)*ptr == (u8 *)-1) { 173 | PRINT_ERROR("Error: shmat\n"); 174 | return; 175 | } 176 | // enter fork() server thyme! 177 | int n = write(FORKSRV_FD + 1, &__afl_temp_data, 4); 178 | 179 | if (n != 4) { 180 | PRINT_ERROR("Error writing fork server\n"); 181 | return; 182 | } 183 | while (1) { 184 | n = read(FORKSRV_FD, &__afl_temp_data, 4); 185 | if (n != 4) { 186 | PRINT_ERROR("Error reading fork server\n"); 187 | return; 188 | } 189 | 190 | __afl_fork_pid = fork(); 191 | if (__afl_fork_pid < 0) { 192 | PRINT_ERROR("Error on fork()\n"); 193 | return; 194 | } 195 | if (__afl_fork_pid == 0) { 196 | close(FORKSRV_FD); 197 | close(FORKSRV_FD + 1); 198 | prev_id = 0; 199 | break; 200 | } else { 201 | // parrent stuff 202 | n = write(FORKSRV_FD + 1, &__afl_fork_pid, 4); 203 | pid_t temp_pid = waitpid(__afl_fork_pid, &__afl_temp_data, 2); 204 | 205 | if (temp_pid == 0) { 206 | return; 207 | } 208 | n = write(FORKSRV_FD + 1, &__afl_temp_data, 4); 209 | } 210 | } 211 | } 212 | --------------------------------------------------------------------------------