├── tmp └── .gitkeep ├── rust-toolchain ├── fitm-qemu ├── QEMUAFL_VERSION ├── unsigaction │ ├── unsigaction.c │ ├── README.md │ └── Makefile ├── build_incremental.sh ├── libqasan │ ├── README.md │ ├── Makefile │ ├── map_macro.h │ ├── libqasan.c │ ├── uninstrument.c │ ├── libqasan.h │ ├── patch.c │ ├── string.c │ ├── malloc.c │ └── hooks.c ├── update_ref.sh ├── libcompcov │ ├── Makefile │ ├── README.md │ ├── compcovtest.cc │ ├── pmparser.h │ └── libcompcov.so.c ├── README.persistent.md ├── README.md └── build_qemu_support.sh ├── debug └── namespacing │ ├── .gitignore │ ├── test.sh │ ├── startcriu.sh │ ├── afl-init.sh │ ├── Cargo.toml │ └── src │ └── main.rs ├── fitm.pdf ├── tests ├── targets │ └── custom │ │ ├── exit │ │ ├── echo_server │ │ ├── pseudoclient_complex │ │ ├── pseudoserver_complex │ │ ├── exit.c │ │ ├── syscalls.c │ │ ├── echo_server.c │ │ ├── snapshot_creation.c │ │ ├── echo_loop.c │ │ ├── pseudoclient_simple.c │ │ ├── pseudoclient_complex.c │ │ ├── pseudoserver_simple.c │ │ └── pseudoserver_complex.c ├── get_traces_test.rs ├── getenv_from_file_test.c ├── download_teamspeak.sh ├── fitm-args.teamspeak.json ├── README.md ├── common │ └── mod.rs ├── afl-fuzz_test.sh ├── snapshot_creation_test.sh ├── afl-showmap_test.sh ├── snapshot_test.rs ├── output_test.rs ├── pipe_test.c ├── self-dump_test.c ├── Makefile ├── syscall_test.c └── gen_afl_maps_test.rs ├── .cargo └── config.toml ├── config ├── fitm-args.ftp.json ├── fitm-args.rtsp.json └── fitm-args.ts3.json ├── Cargo.toml ├── .vscode ├── launch.json └── tasks.json ├── Vagrantfile ├── misc ├── provision.sh ├── print_connection.py └── fitm_pseudocode.py ├── .gitignore ├── .github └── workflows │ └── rust.yml ├── .gitmodules ├── LICENSE ├── Makefile ├── restore.sh.tmp ├── create_restore.py ├── src ├── main.rs └── namespacing.rs └── README.md /tmp/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | stable 2 | -------------------------------------------------------------------------------- /fitm-qemu/QEMUAFL_VERSION: -------------------------------------------------------------------------------- 1 | 0fb212daab 2 | -------------------------------------------------------------------------------- /debug/namespacing/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | dump 3 | -------------------------------------------------------------------------------- /fitm.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fgsect/FitM/HEAD/fitm.pdf -------------------------------------------------------------------------------- /debug/namespacing/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "hi" 3 | sleep 5s 4 | echo "foo" -------------------------------------------------------------------------------- /tests/targets/custom/exit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fgsect/FitM/HEAD/tests/targets/custom/exit -------------------------------------------------------------------------------- /tests/targets/custom/echo_server: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fgsect/FitM/HEAD/tests/targets/custom/echo_server -------------------------------------------------------------------------------- /fitm-qemu/unsigaction/unsigaction.c: -------------------------------------------------------------------------------- 1 | int sigaction(int signum, void *act, void *oldact) { 2 | return 0; 3 | } 4 | -------------------------------------------------------------------------------- /debug/namespacing/startcriu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | sudo ./criu/criu/criu service -v4 --address /tmp/criu_service.socket 4 | -------------------------------------------------------------------------------- /tests/targets/custom/pseudoclient_complex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fgsect/FitM/HEAD/tests/targets/custom/pseudoclient_complex -------------------------------------------------------------------------------- /tests/targets/custom/pseudoserver_complex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fgsect/FitM/HEAD/tests/targets/custom/pseudoserver_complex -------------------------------------------------------------------------------- /tests/get_traces_test.rs: -------------------------------------------------------------------------------- 1 | use fitm::get_traces; 2 | 3 | #[test] 4 | fn test_get_traces() { 5 | let traces = get_traces(); 6 | println!("{:?}", traces); 7 | } 8 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | # Wraps run/test/bench in the specified binary 2 | # https://doc.rust-lang.org/cargo/reference/config.html#targettriplerunner 3 | [target.x86_64-unknown-linux-gnu] 4 | runner = 'sudo -E' -------------------------------------------------------------------------------- /debug/namespacing/afl-init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Had the feeling that this had to be reinitialized in the namespace 4 | echo core >/proc/sys/kernel/core_pattern 5 | cd /sys/devices/system/cpu 6 | echo performance | tee cpu*/cpufreq/scaling_governor -------------------------------------------------------------------------------- /fitm-qemu/unsigaction/README.md: -------------------------------------------------------------------------------- 1 | # unsigaction 2 | 3 | This library disables sigaction handlers when preloaded. 4 | 5 | Mainly needed by Wine mode but can be used as a separate tool. 6 | 7 | A similar solution can be found in [preeny](https://github.com/zardus/preeny). 8 | -------------------------------------------------------------------------------- /debug/namespacing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "Namespace-testing" 3 | version = "0.1.0" 4 | authors = ["M:Munier "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | libc="*" -------------------------------------------------------------------------------- /tests/getenv_from_file_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../qemu/qemu/linux-user/fitm.h" 4 | 5 | int main() { 6 | char* foo = getenv_from_file("TESTENV"); 7 | if(foo){ 8 | printf("%s\n", foo); 9 | } else { 10 | puts("var not found"); 11 | } 12 | return 0; 13 | } -------------------------------------------------------------------------------- /tests/download_teamspeak.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | TEAMSPEAK_TAR='teamspeak3-server_linux_amd64-3.13.3.tar.bz2' 4 | TEAMSPEAK_URL='https://files.teamspeak-services.com/releases/server/3.13.3/teamspeak3-server_linux_amd64-3.13.3.tar.bz2' 5 | 6 | cd targets 7 | wget "$TEAMSPEAK_URL" 8 | tar -xvf "$TEAMSPEAK_TAR" 9 | rm "$TEAMSPEAK_TAR" 10 | cd .. -------------------------------------------------------------------------------- /config/fitm-args.ftp.json: -------------------------------------------------------------------------------- 1 | { 2 | "client": "../tests/targets/LightFTP/Source/Release/fftp", 3 | "client_args": ["../tests/targets/LightFTP/fftp.conf"], 4 | "client_envs": {"QEMU_STRACE": "1"}, 5 | "client_files": [], 6 | "server": "/usr/bin/ftp", 7 | "server_args": ["127.0.0.1", "2200"], 8 | "server_envs": {"QEMU_STRACE": "1"}, 9 | "server_files": [], 10 | "run_time": 60, 11 | "server_only": false 12 | } -------------------------------------------------------------------------------- /config/fitm-args.rtsp.json: -------------------------------------------------------------------------------- 1 | { 2 | "client": "../tests/targets/live555/testProgs/testRTSPClient", 3 | "client_args": ["rtsp://127.0.0.1:8554/wavAudioTest"], 4 | "client_envs": [["QEMU_STRACE", "1"]], 5 | "server": "../tests/targets/live555/testProgs/testOnDemandRTSPServer", 6 | "server_args": [""], 7 | "server_envs": [["QEMU_STRACE", "1"], ["INIT_RECV_SKIP", "1"]], 8 | "run_time": 1, 9 | "server_only": false 10 | } -------------------------------------------------------------------------------- /tests/targets/custom/exit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | int sock = socket(AF_INET, SOCK_STREAM, 0); 9 | char buf[500]; 10 | recv(sock, buf, 500, 0); 11 | 12 | // printf("randomsg: %s\n", buf); 13 | if(!strcmp(buf, "abcde")){ 14 | exit(0); 15 | } 16 | 17 | char *new_msg = "ACK! Got correct init signal\n"; 18 | send(sock , new_msg , strlen(new_msg) , 0); 19 | 20 | recv(sock, buf, 100, 0); 21 | return sock; 22 | } 23 | -------------------------------------------------------------------------------- /tests/fitm-args.teamspeak.json: -------------------------------------------------------------------------------- 1 | { 2 | "client": "../tests/targets/tsclientlib/target/debug/examples/simple", 3 | "client_args": [], 4 | "client_envs": [["QEMU_STRACE", "1"], ["INIT_SOCKET_SKIP", "0"], ["COMMENT", "INIT_SOCKET_SKIP is needed for getifaddr to create a valid udp sock"]], 5 | "server": "../tests/targets/teamspeak3-server_linux_amd64/ts3server", 6 | "server_args": [""], 7 | "server_envs": [["QEMU_STRACE", "1"], ["INIT_SOCKET_SKIP", "1"], ["SINGLE_SOCKET", "1"], ["TS3SERVER_LICENSE", "accept"]], 8 | "run_time": 1, 9 | "server_only": false 10 | } 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fitm" 3 | version = "0.1.0" 4 | authors = ["Julian ", "Otto Bittner 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | // getsid() - returns the Session ID 9 | // sauce: http://dcjtech.info/topic/using-linux-syscalls-in-c-programming/ 10 | int main(int argc, char *argv[]) { 11 | int pid, sid; 12 | if (argc > 2) { 13 | printf("Expected one or no parameters, but was given %d\n", argc - 1); 14 | return 1; 15 | } else if (argc == 1) { 16 | pid = 0; 17 | } else { 18 | pid = atoi(argv[1]); 19 | } 20 | pid_t getsid(pid_t pid); 21 | sid = getsid(pid); 22 | printf("%d\n", sid); 23 | return 0; 24 | } -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | Vagrant.configure("2") do |config| 5 | # For a complete reference, please see the online documentation at 6 | # https://docs.vagrantup.com. 7 | config.vm.box = "ubuntu/focal64" 8 | 9 | config.vm.network "forwarded_port", guest: 22, host: 2223, host_ip: "127.0.0.1" 10 | 11 | config.vm.provider "virtualbox" do |vb| 12 | vb.memory = "4096" 13 | vb.name = "FitM" 14 | end 15 | config.vm.define :vm do |vm_settings| 16 | 17 | # Small hack for development, mounting the apt cache to a local tmp folder 18 | # to accell the process of fetching mysql packges. 19 | vm_settings.vm.synced_folder "./tmp", "/var/cache/apt/archives/" 20 | end 21 | 22 | config.vm.provision "shell" do |s| 23 | s.path = "misc/provision.sh" 24 | end 25 | 26 | end -------------------------------------------------------------------------------- /misc/provision.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | chown -Rv _apt:root /var/cache/apt/archives/partial/ 3 | chmod -Rv 700 /var/cache/apt/archives/partial/ 4 | apt-get -y update && apt-get -y upgrade 5 | apt-get -y install ntp # get rid of clock-skew in the vm 6 | apt-get -y install build-essential binutils pkg-config python-ipaddress make protobuf-compiler protobuf-c-compiler libprotobuf-c-dev libprotobuf-dev libnet-dev python3-protobuf python3-yaml protobuf-c-compiler libbsd-dev libprotobuf-dev libprotobuf-c-dev protobuf-c-compiler protobuf-compiler python-protobuf libnl-3-dev libcap-dev ninja-build libglib2.0-dev cmake libcapstone-dev libaio-dev libnftables-dev iproute2 7 | sudo -u vagrant -- sh -c "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y" 8 | 9 | # target deps 10 | apt-get -y install libgnutls28-dev bison flex libssl-dev autoconf libtool libsdl2-dev libopus-dev || true -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # Testing 2 | 3 | If in doubt, check the Makefile. If there is nothing in the makefile just compile the `whatever_test.c` and run it. 4 | Rust tests are run with: `cargo test` 5 | 6 | # self-dump test 7 | 8 | PoC to play with criu's RPC dump functionality. Practically the same as criu's `test-c.c`. 9 | Prints counter 10x. Dumps after 2 counts. Then stays dead until restored. 10 | ```sh 11 | # Start criu server with: 12 | sudo ./criu/criu service -v4 --address /tmp/criu_service.socket --images-dir /tmp/criu_snapshot 13 | 14 | # Build test with: 15 | make clean && make CRIUPATH=/home/user/repos 16 | 17 | # In extra terminal: 18 | watch cat log.txt 19 | 20 | # Run self-dump: 21 | setsid stdbuf -oL ./self-dump < /dev/null &> log.txt 22 | 23 | # Restore: 24 | sudo ./criu/criu restore -d -vvv -o restore.log --images-dir /tmp/criu_snapshot && echo OK 25 | ``` -------------------------------------------------------------------------------- /fitm-qemu/build_incremental.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Just build incremental. No extra checks. 4 | 5 | if [ -n "$HOST" ]; then 6 | echo "[+] Configuring host architecture to $HOST..." 7 | CROSS_PREFIX=$HOST- 8 | else 9 | CROSS_PREFIX= 10 | fi 11 | 12 | echo "[*] Configuring QEMU for $CPU_TARGET..." 13 | 14 | ORIG_CPU_TARGET="$CPU_TARGET" 15 | 16 | if [ "$ORIG_CPU_TARGET" = "" ]; then 17 | CPU_TARGET="`uname -m`" 18 | test "$CPU_TARGET" = "i686" && CPU_TARGET="i386" 19 | test "$CPU_TARGET" = "arm64v8" && CPU_TARGET="aarch64" 20 | case "$CPU_TARGET" in 21 | *arm*) 22 | CPU_TARGET="arm" 23 | ;; 24 | esac 25 | fi 26 | 27 | echo "Building for CPU target $CPU_TARGET incrementally" 28 | 29 | cd ./FitM-qemu 30 | make || exit 1 31 | echo "[+] copying to ../../fitm-qemu-trace" 32 | cp -f "build/${CPU_TARGET}-linux-user/qemu-${CPU_TARGET}" "../../fitm-qemu-trace" || exit 1 33 | echo "[+] done" 34 | 35 | -------------------------------------------------------------------------------- /config/fitm-args.ts3.json: -------------------------------------------------------------------------------- 1 | { 2 | "client": "../tests/targets/custom/echo_loop", 3 | "client_args": [], 4 | "client_envs": { 5 | "INIT_SOCKET_SKIP": "0", 6 | "COMMENT": "INIT_SOCKET_SKIP is needed for getifaddr to create a valid udp sock" 7 | }, 8 | "client_files": [], 9 | "server": "./ts3server", 10 | "server_args": ["license_accepted=1"], 11 | "server_envs": { 12 | "QEMU_STRACE": "1", 13 | "AFL_DEBUG": "1" 14 | }, 15 | "server_files": [ 16 | "tests/targets/teamspeak3-server_linux_amd64/ts3server", 17 | "tests/targets/teamspeak3-server_linux_amd64/sql", 18 | "tests/targets/teamspeak3-server_linux_amd64/redist", 19 | "tests/targets/teamspeak3-server_linux_amd64/tsdns", 20 | "tests/targets/teamspeak3-server_linux_amd64/libts3_ssh.so", 21 | "tests/targets/teamspeak3-server_linux_amd64/libts3db_sqlite3.so" 22 | ], 23 | "run_time": 300, 24 | "server_only": true 25 | } 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | ./qemu/qemu/roms/seabios/config.mak 3 | ./qemu/qemu/roms/vgabios/config.mak 4 | /target 5 | Cargo.lock 6 | .idea 7 | *.out 8 | *.o 9 | *.so 10 | *.cmake 11 | qemu-x86_64 12 | *.d 13 | self-dump 14 | */tmp/* 15 | rpc.* 16 | states/ 17 | in/ 18 | out/ 19 | active-state/ 20 | saved-states/ 21 | pseudoclient 22 | pseudoserver 23 | venv 24 | config.mak 25 | restore.sh 26 | 27 | fdinfo 28 | file 29 | tests/targets/snapshot_creation 30 | fitm-qemu-trace 31 | 32 | cmin-tmp/ 33 | tests/targets/pseudoclient_simple 34 | tests/targets/pseudoserver_simple 35 | tests/targets/custom/pseudoclient_simple 36 | tests/targets/custom/pseudoserver_simple 37 | tests/targets/custom/echo_loop 38 | dump 39 | criu_stderr 40 | criu_stdout 41 | debug 42 | fitm-state.json 43 | 44 | teamspeak3-server_linux_amd64 45 | *.swp 46 | logs 47 | ts3server.sqlitedb 48 | ts3server.sqlitedb-shm 49 | ts3server.sqlitedb-wal 50 | 51 | .vagrant 52 | tmp 53 | -------------------------------------------------------------------------------- /tests/targets/custom/echo_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main() 9 | { 10 | // necessary preamble 11 | int sock = 0; 12 | char *buffer = (char *)calloc(100, 1); 13 | if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) 14 | { 15 | system("echo 'Socket creation error'"); 16 | return -1; 17 | } 18 | 19 | // printf("00: pre recv\n"); 20 | recv(sock, buffer, 100, 0); 21 | // printf("01: received: %s\n", buffer); 22 | // printf("02: post recv/pre send\n"); 23 | // printf("%i\n", sock); 24 | system("whoami > /tmp/fitm-who"); 25 | int bytes = send(sock, buffer, strlen(buffer), 0); 26 | printf("sent bytes: %d\n", bytes); 27 | perror("error: "); 28 | // write(3, (char *) msg, len); 29 | // printf("03: post send\n"); 30 | 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ disabled ] 6 | pull_request: 7 | branches: [ disabled ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | with: 20 | fetch-depth: 1 21 | submodules: 'true' 22 | token: ${{ secrets.ACCESS_TOKEN }} 23 | 24 | - name: Build AFL/QEMU 25 | run: sudo apt-get install libprotobuf-dev libprotobuf-c0-dev protobuf-c-compiler protobuf-compiler python-protobuf libuuid1 uuid-dev && make 26 | - name: Build Rust 27 | run: cargo build --verbose 28 | 29 | - name: Install criu 30 | run: sudo apt-get install --no-install-recommends criu 31 | - name: Run criu service 32 | run: sudo criu service -v4 --address /tmp/criu_service.socket &> /tmp/criu.out & 33 | - name: Run tests 34 | run: cargo test --verbose 35 | -------------------------------------------------------------------------------- /fitm-qemu/unsigaction/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # american fuzzy lop++ - unsigaction 3 | # -------------------------------- 4 | # 5 | # Written by Andrea Fioraldi 6 | # 7 | # Copyright 2019-2020 Andrea Fioraldi. All rights reserved. 8 | # 9 | # Licensed under the Apache License, Version 2.0 (the "License"); 10 | # you may not use this file except in compliance with the License. 11 | # You may obtain a copy of the License at: 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | .POSIX: 16 | 17 | _UNIQ=_QINU_ 18 | 19 | TARGETCANDIDATES=unsigaction.so 20 | _TARGETS=$(_UNIQ)$(AFL_NO_X86)$(_UNIQ) 21 | __TARGETS=$(_TARGETS:$(_UNIQ)1$(_UNIQ)=) 22 | TARGETS=$(__TARGETS:$(_UNIQ)$(_UNIQ)=$(TARGETCANDIDATES)) 23 | 24 | all: $(TARGETS) 25 | 26 | unsigaction.so: unsigaction.c 27 | @if $(CC) -fPIC -shared unsigaction.c -o unsigaction.so 2>/dev/null ; then echo "unsigaction build success"; else echo "unsigaction build failure (that's fine)"; fi 28 | 29 | clean: 30 | rm -f unsigaction.so 31 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "criu"] 2 | path = criu 3 | url = https://github.com/checkpoint-restore/criu.git 4 | [submodule "AFLplusplus"] 5 | path = AFLplusplus 6 | url = https://github.com/AFLplusplus/AFLplusplus.git 7 | [submodule "tests/targets/live555"] 8 | path = tests/targets/live555 9 | url = git@github.com:derpsteb/live555.git 10 | [submodule "tests/targets/LightFTP"] 11 | path = tests/targets/LightFTP 12 | url = git@github.com:derpsteb/LightFTP.git 13 | [submodule "tests/targets/dcmtk"] 14 | path = tests/targets/dcmtk 15 | url = git@github.com:derpsteb/dcmtk.git 16 | [submodule "tests/targets/kamailio"] 17 | path = tests/targets/kamailio 18 | url = git@github.com:derpsteb/kamailio.git 19 | [submodule "tests/targets/pjproject"] 20 | path = tests/targets/pjproject 21 | url = git@github.com:derpsteb/pjproject.git 22 | [submodule "fitm-qemu/FitM-qemu"] 23 | path = fitm-qemu/FitM-qemu 24 | url = git@github.com:fgsect/FitM-qemu.git 25 | [submodule "tests/targets/tsclientlib"] 26 | path = tests/targets/tsclientlib 27 | url = git@github.com:ReSpeak/tsclientlib.git 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Otto Bittner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all afl qemu criu fitm debug tests subinit 2 | 3 | CRIUPATH?=./criu 4 | 5 | all: criu symlink qemu afl tests fitm 6 | 7 | subinit: 8 | git submodule init || true 9 | git submodule update || true 10 | 11 | afl: subinit 12 | make -C ./AFLplusplus 13 | 14 | fitm-qemu-trace: 15 | $(MAKE) criu 16 | cd ./fitm-qemu && ./build_qemu_support.sh 17 | 18 | qemu: fitm-qemu-trace criu subinit 19 | # rebuild each time, lightly 20 | cd ./fitm-qemu && ./build_incremental.sh 21 | 22 | criu: subinit 23 | make -C ./criu 24 | 25 | fitm: 26 | cargo build --release 27 | 28 | debug: 29 | cargo build 30 | 31 | reset: 32 | sudo rm fitm-state.json || true 33 | sudo rm -rf ./active-state 34 | sudo rm -rf ./saved-states 35 | sudo rm -rf ./cmin-tmp 36 | 37 | run: fitm #tests debug 38 | sudo rm -rf ./active-state 39 | sudo rm -rf ./cmin-tmp 40 | sudo ./target/release/fitm $(FITM_ARGS) 41 | sudo chown -R $(USER) ./active_state 42 | sudo chown -R $(USER) ./saved_states 43 | 44 | 45 | tests: 46 | $(MAKE) -C ./tests 47 | 48 | # Invoke with: make symlink CRIUPATH=/home/hirnheiner/repos/criu 49 | symlink: criu 50 | ln -s $(CRIUPATH)/images/rpc.proto || true 51 | -------------------------------------------------------------------------------- /tests/targets/custom/snapshot_creation.c: -------------------------------------------------------------------------------- 1 | // Client side C/C++ program to demonstrate Socket programming 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main() 10 | { 11 | // necessary preamble 12 | int sock = 0; 13 | char *msg = ""; 14 | char *buffer = (char *)calloc(1, 1); 15 | if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) 16 | { 17 | system("echo 'Socket creation error'"); 18 | return -1; 19 | } 20 | 21 | 22 | // send(sock , msg, strlen(msg) , 0 ); 23 | // printf("client sent: %s\n", msg); 24 | system("echo '00'"); 25 | recv(sock, buffer, 1, 0); 26 | system("echo '01'"); 27 | send(sock, msg, 1, 0); 28 | system("echo '02'"); 29 | 30 | // printf("client recv #1: %s\n", buffer); 31 | 32 | // char *new_msg = "Need more state!\n"; 33 | // send(sock, new_msg, strlen(new_msg), 0); 34 | // printf("client send #2: %s\n", new_msg); 35 | // 36 | // free(buffer); 37 | // buffer = (char *) calloc(1, 1); 38 | recv(sock, buffer, 1, 0); 39 | system("echo '03'"); 40 | // printf("client recv #2: %s\n", buffer); 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /tests/targets/custom/echo_loop.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main() 9 | { 10 | // necessary preamble 11 | int sock = 0; 12 | char *buffer = (char *)calloc(1024, 1); 13 | if (!buffer) { 14 | perror("OOM"); 15 | return -1; 16 | } 17 | if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) 18 | { 19 | perror("Socket creation error"); 20 | return -1; 21 | } 22 | 23 | struct sockaddr_in sock_addr = { 24 | .sin_addr.s_addr = htonl(INADDR_ANY), 25 | .sin_port = 1337, 26 | .sin_family = AF_INET, 27 | }; 28 | 29 | if (connect(sock, (struct sockaddr *) &sock_addr, sizeof(struct sockaddr_in)) < 0) { 30 | perror("Connect failed"); 31 | return 1; 32 | } 33 | if (send(sock, "FITM", 4, 0) <= 0) { 34 | perror("Could not send any data"); 35 | return 1; 36 | } 37 | 38 | while (1) { 39 | int bytes_recieved = recv(sock, buffer, 1024, 0); 40 | if (bytes_recieved <= 0) { 41 | break; 42 | } 43 | 44 | int bytes_sent = send(sock, buffer, bytes_recieved, 0); 45 | if (bytes_sent <= 0) { 46 | break; 47 | } 48 | } 49 | 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /fitm-qemu/libqasan/README.md: -------------------------------------------------------------------------------- 1 | # QEMU AddressSanitizer Runtime 2 | 3 | This library is the injected runtime used by QEMU AddressSanitizer (QASan). 4 | 5 | The original repository is [here](https://github.com/andreafioraldi/qasan). 6 | 7 | The version embedded in qemuafl is an updated version of just the usermode part 8 | and this runtime is injected via LD_PRELOAD (so works just for dynamically 9 | linked binaries). 10 | 11 | The usage is super simple, just set the env var `AFL_USE_QASAN=1` when fuzzing 12 | in qemu mode (-Q). afl-fuzz will automatically set AFL_PRELOAD to load this 13 | library and enable the QASan instrumentation in afl-qemu-trace. 14 | 15 | For debugging purposes, we still suggest to run the original QASan as the 16 | stacktrace support for ARM (just a debug feature, it does not affect the bug 17 | finding capabilities during fuzzing) is WIP. 18 | 19 | ### When should I use QASan? 20 | 21 | If your target binary is PIC x86_64, you should also give a try to 22 | [retrowrite](https://github.com/HexHive/retrowrite) for static rewriting. 23 | 24 | If it fails, or if your binary is for another architecture, or you want to use 25 | persistent and snapshot mode, AFL++ QASan mode is what you want/have to use. 26 | 27 | Note that the overhead of libdislocator when combined with QEMU mode is much 28 | lower but it can catch less bugs. This is a short blanket, take your choice. 29 | -------------------------------------------------------------------------------- /fitm-qemu/update_ref.sh: -------------------------------------------------------------------------------- 1 | #/bin/sh 2 | 3 | ################################################## 4 | # AFL++ internal tool to update qemuafl ref. 5 | # Usage: ./update_ref.sh 6 | # If no commit hash was provided, it'll take HEAD. 7 | ################################################## 8 | 9 | UC_VERSION_FILE='./QEMUAFL_VERSION' 10 | 11 | NEW_VERSION="$1" 12 | 13 | if [ "$NEW_VERSION" = "-h" ]; then 14 | echo "Internal script to update bound qemuafl version." 15 | echo 16 | echo "Usage: ./update_ref.sh " 17 | echo "If no commit hash is provided, will use HEAD." 18 | echo "-h to show this help screen." 19 | exit 1 20 | fi 21 | 22 | git submodule init && git submodule update || exit 1 23 | cd ./qemuafl || exit 1 24 | git fetch origin master 1>/dev/null || exit 1 25 | git stash 1>/dev/null 2>/dev/null 26 | git stash drop 1>/dev/null 2>/dev/null 27 | git checkout master 28 | git pull origin master 1>/dev/null || exit 1 29 | 30 | if [ -z "$NEW_VERSION" ]; then 31 | # No version provided, take HEAD. 32 | NEW_VERSION=$(git rev-parse --short HEAD) 33 | fi 34 | 35 | if [ -z "$NEW_VERSION" ]; then 36 | echo "Error getting version." 37 | exit 1 38 | fi 39 | 40 | git checkout "$NEW_VERSION" || exit 1 41 | 42 | cd .. 43 | 44 | rm "$UC_VERSION_FILE" 45 | echo "$NEW_VERSION" > "$UC_VERSION_FILE" 46 | 47 | echo "Done. New qemuafl version is $NEW_VERSION." 48 | -------------------------------------------------------------------------------- /fitm-qemu/libqasan/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # american fuzzy lop++ - libqasan 3 | # ------------------------------- 4 | # 5 | # Written by Andrea Fioraldi 6 | # 7 | # Copyright 2019-2020 Andrea Fioraldi. All rights reserved. 8 | # 9 | # Licensed under the Apache License, Version 2.0 (the "License"); 10 | # you may not use this file except in compliance with the License. 11 | # You may obtain a copy of the License at: 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | 16 | PREFIX ?= /usr/local 17 | HELPER_PATH = $(PREFIX)/lib/afl 18 | DOC_PATH ?= $(PREFIX)/share/doc/afl 19 | MAN_PATH ?= $(PREFIX)/share/man/man8 20 | 21 | VERSION = $(shell grep '^\#define VERSION ' ../config.h | cut -d '"' -f2) 22 | 23 | CFLAGS += -I ../FitM-qemu/qemuafl/ 24 | CFLAGS += -Wno-int-to-void-pointer-cast -ggdb 25 | LDFLAGS += -ldl -pthread 26 | 27 | SRC := libqasan.c hooks.c malloc.c string.c uninstrument.c patch.c dlmalloc.c 28 | HDR := libqasan.h 29 | 30 | all: libqasan.so 31 | 32 | libqasan.so: $(HDR) $(SRC) 33 | $(CC) $(CFLAGS) -fPIC -shared $(SRC) -o ../../$@ $(LDFLAGS) 34 | 35 | .NOTPARALLEL: clean 36 | 37 | clean: 38 | rm -f *.o *.so *~ a.out core core.[1-9][0-9]* 39 | rm -f ../../libqasan.so 40 | 41 | install: all 42 | install -m 755 ../../libqasan.so $${DESTDIR}$(HELPER_PATH) 43 | install -m 644 -T README.md $${DESTDIR}$(DOC_PATH)/README.qasan.md 44 | 45 | -------------------------------------------------------------------------------- /fitm-qemu/libcompcov/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # american fuzzy lop++ - libcompcov 3 | # -------------------------------- 4 | # 5 | # Written by Andrea Fioraldi 6 | # 7 | # Copyright 2019-2020 Andrea Fioraldi. All rights reserved. 8 | # 9 | # Licensed under the Apache License, Version 2.0 (the "License"); 10 | # you may not use this file except in compliance with the License. 11 | # You may obtain a copy of the License at: 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | 16 | PREFIX ?= /usr/local 17 | HELPER_PATH = $(PREFIX)/lib/afl 18 | DOC_PATH ?= $(PREFIX)/share/doc/afl 19 | MAN_PATH ?= $(PREFIX)/share/man/man8 20 | 21 | VERSION = $(shell grep '^\#define VERSION ' ../config.h | cut -d '"' -f2) 22 | 23 | CFLAGS ?= -O3 -funroll-loops 24 | CFLAGS += -I ../../include/ 25 | CFLAGS += -Wall -Wno-unused-result -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign 26 | LDFLAGS += -ldl 27 | 28 | all: libcompcov.so 29 | 30 | libcompcov.so: libcompcov.so.c ../../config.h 31 | $(CC) $(CFLAGS) -shared -fPIC $< -o ../../$@ $(LDFLAGS) 32 | 33 | .NOTPARALLEL: clean 34 | 35 | clean: 36 | rm -f *.o *.so *~ a.out core core.[1-9][0-9]* 37 | rm -f ../../libcompcov.so compcovtest 38 | 39 | compcovtest: compcovtest.cc 40 | $(CXX) -std=c++11 $< -o $@ 41 | 42 | install: all 43 | install -m 755 ../../libcompcov.so $${DESTDIR}$(HELPER_PATH) 44 | install -m 644 -T README.md $${DESTDIR}$(DOC_PATH)/README.compcov.md 45 | 46 | -------------------------------------------------------------------------------- /tests/common/mod.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | pub fn setup() { 4 | let active_state = "./active-state"; 5 | let saved_states = "./saved-states"; 6 | if Path::new(active_state).exists() { 7 | std::fs::remove_dir_all(active_state).expect("[!] Can't delete ./active-state"); 8 | } 9 | 10 | if Path::new(saved_states).exists() { 11 | std::fs::remove_dir_all(saved_states).expect("[!] Can't delete ./saved-states"); 12 | } 13 | 14 | std::fs::create_dir(active_state).expect("[!] Can't create ./active-state"); 15 | 16 | std::fs::create_dir(saved_states).expect("[!] Can't create ./saved-states"); 17 | } 18 | 19 | pub fn teardown() { 20 | let active_state = "./active-state"; 21 | let saved_states = "./saved-states"; 22 | let cmin_tmp = "./cmin-tmp"; 23 | let generation_inputs = "./generation-inputs"; 24 | 25 | match std::fs::remove_dir_all(active_state) { 26 | Ok(()) => (), 27 | Err(_) => println!("[!] No ./active-state to delete"), 28 | }; 29 | 30 | match std::fs::remove_dir_all(saved_states) { 31 | Ok(()) => (), 32 | Err(_) => println!("[!] No ./saved-states to delete"), 33 | }; 34 | 35 | match std::fs::remove_dir_all(cmin_tmp) { 36 | Ok(()) => (), 37 | Err(_) => println!("[!] No ./saved-states to delete"), 38 | }; 39 | 40 | match std::fs::remove_dir_all(generation_inputs) { 41 | Ok(()) => (), 42 | Err(_) => println!("[!] No ./saved-states to delete"), 43 | }; 44 | } 45 | -------------------------------------------------------------------------------- /restore.sh.tmp: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | # This file has to write the entire env into the envfile 4 | # Expects relative path to state folder it's supposed to restore as arg 5 | # TODO: Clean this up, this looks/is ugly 6 | 7 | export INPUT_FILENAME=$(realpath $1) 8 | 9 | ENVFILE="envfile" 10 | env | sort > $ENVFILE 11 | pwd 12 | # When running with afl-fuzz this script receives 198/199 as pipes for RPC with the forkserver. 13 | # To forward these into the restored process we use --inherit-fd. 14 | # The target for --inherit-fd within the restored process are the named pipes that are stored in the pipes file. 15 | PIPE1=$(cat ./pipes | grep -o "pipe:\[.*\]" | head -n 1 ) 16 | PIPE2=$(cat ./pipes | grep -o "pipe:\[.*\]" | head -n 2 | tail -n 1 ) 17 | 18 | # echo "PIPE1:\"$PIPE1\"" 19 | # echo "PIPE2:\"$PIPE2\"" 20 | 21 | #echo "=======" 22 | #cp $INPUT_FILENAME /tmp 23 | #ls -la 24 | #echo $INPUT_FILENAME 25 | #ls -la /proc/self/fd 26 | #pwd 27 | #echo $@ 28 | #echo "=======" 29 | 30 | echo -n "" > ./out/.cur_input 31 | 32 | if [[ -z "$CRIU_SNAPSHOT_DIR" ]]; then 33 | CRIU_SNAPSHOT_DIR="./snapshot" 34 | fi 35 | 36 | ## TEMPLATE ## 37 | 38 | if [[ -z "${__AFL_SHM_ID}" ]]; then 39 | exec 198< /dev/null 40 | exec 199> /dev/null 41 | # We are in a snapshot run force PIDs out of the usual range. 42 | # 1<<15 is the largest that's legal on WSL2. 43 | echo "$((1<<15))" > /proc/sys/kernel/ns_last_pid 44 | else 45 | echo "Running in AFL, no dummy FDs necessary" 46 | fi 47 | 48 | ../criu/criu/criu restore -d \ 49 | -vvv \ 50 | -o ../restore.log \ 51 | --images-dir $CRIU_SNAPSHOT_DIR \ 52 | --inherit-fd "fd[198]:$PIPE1" \ 53 | --inherit-fd "fd[199]:$PIPE2" \ -------------------------------------------------------------------------------- /tests/afl-fuzz_test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | # Make was annoying with errors/env and I was too tired for learning it atm 3 | clean(){ 4 | rm -rf envfile 5 | unset LETS_DO_THE_TIMEWARP_AGAIN 6 | unset TESTENV 7 | unset AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES 8 | unset AFL_SKIP_CPUFREQ 9 | unset AFL_DEBUG_CHILD_OUTPUT 10 | 11 | rm -rf /tmp/criu_snapshot/ 12 | rm -rf /tmp/log 13 | rm -rf active-state/test 14 | } 15 | 16 | create_snap(){ 17 | export LETS_DO_THE_TIMEWARP_AGAIN=1 18 | export AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 19 | export AFL_SKIP_CPUFREQ=1 20 | export AFL_DEBUG_CHILD_OUTPUT=1 21 | export AFL_SKIP_BIN_CHECK=1 22 | 23 | old_pwd=$PWD 24 | state_dir=$(pwd)/active-state/test 25 | export CRIU_SNAPSHOT_DIR=$state_dir/snapshot 26 | mkdir -p $CRIU_SNAPSHOT_DIR 27 | cd $state_dir 28 | mkdir out 29 | mkdir fd 30 | touch out/.cur_input 31 | chmod 600 out/.cur_input 32 | touch stderr 33 | touch stdout 34 | # This throws a weird error(?) but seems to work: 35 | # test.sh: line 15: 608344 Killed setsid stdbuf -oL AFLplusplus/afl-qemu-trace test/pseudoserver < /dev/null &> /tmp/log 36 | setsid stdbuf -oL ../../AFLplusplus/afl-qemu-trace ../../test/pseudoserver < /dev/null 1> ./stdout 2> ./stderr && echo "Initial snap successful" 37 | } 38 | 39 | fuzz_snap(){ 40 | unset LETS_DO_THE_TIMEWARP_AGAIN 41 | sudo rm -f out/* &> /dev/null || echo "rm failed" 42 | mkdir -p "in" "out" "fd" &> /dev/null || echo "mkdir failed" 43 | echo "RI" > "in/foobar" 44 | backup_snap 45 | sudo -E ../../AFLplusplus/afl-fuzz -i ./in -o ./out -m none -- sh ../../restore.sh ./test @@ 46 | } 47 | 48 | backup_snap(){ 49 | sudo rm -rf /tmp/test 50 | sudo cp -r $state_dir /tmp/test 51 | } 52 | 53 | clean 54 | create_snap 55 | fuzz_snap -------------------------------------------------------------------------------- /fitm-qemu/libcompcov/README.md: -------------------------------------------------------------------------------- 1 | # strcmp() / memcmp() CompareCoverage library for afl++ QEMU 2 | 3 | Written by Andrea Fioraldi 4 | 5 | This Linux-only companion library allows you to instrument `strcmp()`, `memcmp()`, 6 | and related functions to log the CompareCoverage of these libcalls. 7 | 8 | Use this with caution. While this can speedup a lot the bypass of hard 9 | branch conditions it can also waste a lot of time and take up unnecessary space 10 | in the shared memory when logging the coverage related to functions that 11 | doesn't process input-related data. 12 | 13 | To use the library, you *need* to make sure that your fuzzing target is linked 14 | dynamically and make use of strcmp(), memcmp(), and related functions. 15 | For optimized binaries this is an issue, those functions are often inlined 16 | and this module is not capable to log the coverage in this case. 17 | 18 | If you have the source code of the fuzzing target you should nto use this 19 | library and QEMU but build it with afl-clang-fast and the laf-intel options. 20 | 21 | To use this library make sure to preload it with AFL_PRELOAD. 22 | 23 | ``` 24 | export AFL_PRELOAD=/path/to/libcompcov.so 25 | export AFL_COMPCOV_LEVEL=1 26 | 27 | afl-fuzz -Q -i input -o output -- 28 | ``` 29 | 30 | The AFL_COMPCOV_LEVEL tells to QEMU and libcompcov how to log comaprisons. 31 | Level 1 logs just comparison with immediates / read-only memory and level 2 32 | logs all the comparisons. 33 | 34 | The library make use of https://github.com/ouadev/proc_maps_parser and so it is 35 | Linux specific. However this is not a strict dependency, other UNIX operating 36 | systems can be supported simply replacing the code related to the 37 | /proc/self/maps parsing. 38 | -------------------------------------------------------------------------------- /tests/snapshot_creation_test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | # Make was annoying with errors/env and I was too tired for learning it atm 3 | clean(){ 4 | rm -rf envfile 5 | unset LETS_DO_THE_TIMEWARP_AGAIN 6 | unset TESTENV 7 | unset AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES 8 | unset AFL_SKIP_CPUFREQ 9 | unset AFL_DEBUG_CHILD_OUTPUT 10 | 11 | rm -rf /tmp/criu_snapshot/ 12 | rm -rf /tmp/log 13 | rm -rf test-state 14 | } 15 | 16 | create_snap(){ 17 | export LETS_DO_THE_TIMEWARP_AGAIN=1 18 | export AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 19 | export AFL_SKIP_CPUFREQ=1 20 | export AFL_DEBUG_CHILD_OUTPUT=1 21 | export AFL_SKIP_BIN_CHECK=1 22 | export INPUT_FILENAME="./input_file" 23 | 24 | old_pwd=$PWD 25 | state_dir=$(pwd)/test-state 26 | export CRIU_SNAPSHOT_DIR=$state_dir/snapshot 27 | mkdir -p $CRIU_SNAPSHOT_DIR 28 | cd $state_dir 29 | mkdir fd 30 | touch stderr 31 | touch stdout 32 | touch input_file 33 | setsid stdbuf -oL ../../AFLplusplus/afl-qemu-trace ../snapshot_creation < /dev/null &> stdout 34 | cd .. 35 | } 36 | 37 | 38 | backup_snap(){ 39 | sudo rm -rf /tmp/test 40 | sudo cp -r $state_dir /tmp/test 41 | } 42 | 43 | restore(){ 44 | # unset LETS_DO_THE_TIMEWARP_AGAIN 45 | export CRIU_SNAPSHOT_DIR=$state_dir/snapshot2 46 | mkdir -p $CRIU_SNAPSHOT_DIR 47 | env > envfile 48 | sudo criu restore -d -vvv -o ./restore.log --images-dir ./test-state/snapshot && echo 'OK' 49 | } 50 | 51 | clean 52 | create_snap 53 | restore 54 | # This sleep is needed for criu as otherwise criu will try to reuse the PID that the previous process just used 55 | # but the PID is not freed up by the previous process yet. 56 | sleep 0.1 57 | #truncate --size=100 test-state/stdout 58 | sudo criu restore -d -vvv -o ./restore.log --images-dir ./test-state/snapshot2 && echo 'OK' -------------------------------------------------------------------------------- /tests/afl-showmap_test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | # Make was annoying with errors/env and I was too tired for learning it atm 3 | clean(){ 4 | rm -rf envfile 5 | unset LETS_DO_THE_TIMEWARP_AGAIN 6 | unset TESTENV 7 | unset AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES 8 | unset AFL_SKIP_CPUFREQ 9 | unset AFL_DEBUG_CHILD_OUTPUT 10 | 11 | rm -rf /tmp/criu_snapshot/ 12 | rm -rf /tmp/log 13 | rm -rf states/test 14 | } 15 | 16 | create_snap(){ 17 | export LETS_DO_THE_TIMEWARP_AGAIN=1 18 | export AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 19 | export AFL_SKIP_CPUFREQ=1 20 | export AFL_DEBUG_CHILD_OUTPUT=1 21 | export AFL_SKIP_BIN_CHECK=1 22 | 23 | old_pwd=$PWD 24 | state_dir=$(pwd)/states/test 25 | export CRIU_SNAPSHOT_DIR=$state_dir/snapshot 26 | mkdir -p $CRIU_SNAPSHOT_DIR 27 | cd $state_dir 28 | mkdir out 29 | mkdir fd 30 | touch out/.cur_input 31 | chmod 600 out/.cur_input 32 | touch stderr 33 | touch stdout 34 | # This throws a weird error(?) but seems to work: 35 | # test.sh: line 15: 608344 Killed setsid stdbuf -oL AFLplusplus/afl-qemu-trace test/pseudoserver < /dev/null &> /tmp/log 36 | setsid stdbuf -oL ../../AFLplusplus/afl-qemu-trace ../../test/pseudoserver < /dev/null 1> ./stdout 2> ./stderr && echo "Initial snap successful" 37 | } 38 | 39 | fuzz_snap(){ 40 | unset LETS_DO_THE_TIMEWARP_AGAIN 41 | sudo rm -f out/* &> /dev/null || echo "rm failed" 42 | mkdir -p "in" "out" "fd" &> /dev/null || echo "mkdir failed" 43 | echo "RI" > "in/foobar" 44 | echo "supertest" > "in/eintest" 45 | backup_snap 46 | sudo -E ../../AFLplusplus/afl-showmap -i ./in -o ./out -m none -Q -- sh ../../restore.sh ./test @@ 47 | } 48 | 49 | backup_snap(){ 50 | sudo rm -rf /tmp/test 51 | sudo cp -r $state_dir /tmp/test 52 | } 53 | 54 | clean 55 | create_snap 56 | fuzz_snap 57 | -------------------------------------------------------------------------------- /tests/targets/custom/pseudoclient_simple.c: -------------------------------------------------------------------------------- 1 | 2 | // Client side C/C++ program to demonstrate Socket programming 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #define PORT 8080 10 | 11 | int main() 12 | { 13 | int sock = 0; 14 | struct sockaddr_in serv_addr; 15 | char stack_buf[64]; 16 | char *msg = "R"; 17 | char *buffer = (char *)calloc(100, 1); 18 | if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) 19 | { 20 | printf("\n Socket creation error \n"); 21 | return -1; 22 | } 23 | 24 | serv_addr.sin_family = AF_INET; 25 | serv_addr.sin_port = htons(PORT); 26 | 27 | // Convert IPv4 and IPv6 addresses from text to binary form 28 | if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0) 29 | { 30 | printf("\nInvalid address / Address not supported \n"); 31 | return -1; 32 | } 33 | 34 | if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) 35 | { 36 | printf("\nConnection Failed \n"); 37 | return -1; 38 | } 39 | printf("client booted successfully\n"); 40 | 41 | // TODO: This is a quick fix for our init_run being developed only with server binaries in mind 42 | // I am not sure atm if `recv` is the right point to snapshot the client. 43 | send(sock , msg , strlen(msg) , 0 ); 44 | printf("client sent: %s\n", msg); 45 | 46 | recv(sock, buffer, 100, 0); 47 | printf("client recv #1: %s\n", buffer); 48 | 49 | char *new_msg = "Need more state!\n"; 50 | send(sock, new_msg, strlen(new_msg), 0); 51 | printf("client send #2: %s\n", new_msg); 52 | 53 | free(buffer); 54 | 55 | recv(sock, stack_buf, 100, 0); 56 | printf("client recv #2: %s\n", buffer); 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /tests/snapshot_test.rs: -------------------------------------------------------------------------------- 1 | use fitm::FITMSnapshot; 2 | mod common; 3 | 4 | use crate::common::teardown; 5 | use fitm::utils; 6 | use std::time::Duration; 7 | 8 | static SERVER_BIN: &str = "./tests/targets/pseudoserver_simple"; 9 | #[allow(dead_code)] 10 | static CLIENT_BIN: &str = "./tests/targets/pseudoclient_simple"; 11 | 12 | #[test] 13 | fn repeated_cmin_test_() { 14 | common::setup(); 15 | 16 | let server0: FITMSnapshot = FITMSnapshot::new( 17 | 1, 18 | 0, 19 | SERVER_BIN.to_string(), 20 | Duration::from_secs(2), 21 | "".to_string(), 22 | true, 23 | false, 24 | None, 25 | ); 26 | 27 | server0 28 | .init_run(false, true, &[]) 29 | .expect("[!] Init run on server0 failed"); 30 | 31 | // =========== snapshot on gen1 ============= 32 | let file_name = "tmp-input-0"; 33 | std::fs::write(file_name, "R").expect("[!] Writing Test payload to tmp file failed"); 34 | 35 | let input_path = std::path::Path::new(file_name) 36 | .canonicalize() 37 | .expect("[!] Could not canonicalize tmp-input path"); 38 | 39 | let server1 = server0 40 | .create_next_snapshot(0, input_path.to_str().unwrap()) 41 | .expect("[!] Create_next_snapshot for server0 failed") 42 | .unwrap(); 43 | 44 | // =========== snapshot on gen3 ============= 45 | let file_name = "tmp-input-1"; 46 | std::fs::write(file_name, "ACK!").expect("[!] Writing Test payload to tmp file failed"); 47 | 48 | let input_path = std::path::Path::new(file_name) 49 | .canonicalize() 50 | .expect("[!] Could not canonicalize tmp-input path"); 51 | 52 | let _server2 = server1 53 | .create_next_snapshot(0, input_path.to_str().unwrap()) 54 | .expect("[!] Create_next_snapshot for server0 failed"); 55 | 56 | teardown(); 57 | } 58 | -------------------------------------------------------------------------------- /fitm-qemu/libcompcov/compcovtest.cc: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////// 2 | // 3 | // Author: Mateusz Jurczyk (mjurczyk@google.com) 4 | // 5 | // Copyright 2019-2020 Google LLC 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // https://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // 19 | 20 | // solution: echo -ne 'The quick brown fox jumps over the lazy 21 | // dog\xbe\xba\xfe\xca\xbe\xba\xfe\xca\xde\xc0\xad\xde\xef\xbe' | ./compcovtest 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | int main() { 29 | 30 | char buffer[44] = {/* zero padding */}; 31 | fread(buffer, 1, sizeof(buffer) - 1, stdin); 32 | 33 | if (memcmp(&buffer[0], "The quick brown fox ", 20) != 0 || 34 | strncmp(&buffer[20], "jumps over ", 11) != 0 || 35 | strcmp(&buffer[31], "the lazy dog") != 0) { 36 | 37 | return 1; 38 | 39 | } 40 | 41 | uint64_t x = 0; 42 | fread(&x, sizeof(x), 1, stdin); 43 | if (x != 0xCAFEBABECAFEBABE) { return 2; } 44 | 45 | uint32_t y = 0; 46 | fread(&y, sizeof(y), 1, stdin); 47 | if (y != 0xDEADC0DE) { return 3; } 48 | 49 | uint16_t z = 0; 50 | fread(&z, sizeof(z), 1, stdin); 51 | 52 | switch (z) { 53 | 54 | case 0xBEEF: 55 | break; 56 | 57 | default: 58 | return 4; 59 | 60 | } 61 | 62 | printf("Puzzle solved, congrats!\n"); 63 | abort(); 64 | return 0; 65 | 66 | } 67 | 68 | -------------------------------------------------------------------------------- /tests/output_test.rs: -------------------------------------------------------------------------------- 1 | // use fitm::FITMSnapshot; 2 | // use std::fs; 3 | 4 | mod common; 5 | 6 | // init_run_test should check if a snapshot could be successfully be created. 7 | // As the test does not have access to criu server responses or other logs it 8 | // relies on the correct creation of various files 9 | 10 | #[test] 11 | fn create_outputs_test() { 12 | // pwd == root dir of repo 13 | common::setup(); 14 | 15 | // Needs refactoring 16 | /* 17 | let afl_client: AFLRun = AFLRun::new( 18 | 0, 19 | 0, 20 | "tests/targets/echo_server".to_string(), 21 | 1, 22 | "".to_string(), 23 | "".to_string(), 24 | false, 25 | false, 26 | ); 27 | 28 | // tested function 29 | afl_client.init_run(); 30 | 31 | // populate in folder here 32 | let first = "a simple string"; 33 | let second = "message 1, upcoming linebreak now:\nmessage 2"; 34 | let third = "foo\tbar"; 35 | fs::write("./active-state/fitm-gen0-state0/in/first_case.txt", first) 36 | .expect("Could not write first input file"); 37 | fs::write("./active-state/fitm-gen0-state0/in/second_case.txt", second) 38 | .expect("Could not write second input file"); 39 | fs::write("./active-state/fitm-gen0-state0/in/third_case.txt", third) 40 | .expect("Could not write third input file"); 41 | 42 | afl_client.create_outputs(); 43 | 44 | for path in 45 | fs::read_dir("./active-state/fitm-gen0-state0/outputs").expect("Couldn't read outputs dir") 46 | { 47 | let file_path = path.as_ref().unwrap().path(); 48 | let file_content = std::fs::read_to_string(&file_path) 49 | .expect(format!("{} file missing", &file_path.display()).as_str()); 50 | // holy cow 51 | let mut file_name = path.unwrap().file_name().into_string().unwrap(); 52 | file_name.truncate(2); 53 | match file_name.as_str() { 54 | "0_" => assert_eq!(file_content, first), 55 | "1_" => assert_eq!(file_content, second), 56 | "2_" => assert_eq!(file_content, third), 57 | _ => assert_eq!(0, 1), 58 | } 59 | } 60 | */ 61 | 62 | common::teardown(); 63 | } 64 | -------------------------------------------------------------------------------- /tests/pipe_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "../qemu/qemu/linux-user/fitm.h" 7 | 8 | 9 | void child(int *pipefd) { 10 | char *buff = calloc(100, 1); 11 | char buf[100]; 12 | 13 | int pid = getpid(); 14 | sprintf(buff, "ls /proc/%d/fd", pid); 15 | printf("* child pid: %d\n", pid); 16 | char* msg = "foo"; 17 | write(pipefd[1], msg, strlen(msg)); 18 | 19 | recv(100, buf, 200, 0); 20 | msg = "bar"; 21 | write(pipefd[1], msg, strlen(msg)); 22 | memset(buff, 100, 0); 23 | 24 | 25 | sprintf(buff, "ls /proc/%d/fd", getpid()); 26 | // system(buff); 27 | } 28 | 29 | 30 | int main() { 31 | char buf[100]; 32 | 33 | char *buff = calloc(100, 1); 34 | 35 | int pipefd[2]; 36 | pipe(pipefd); 37 | int childPid = fork(); 38 | if (childPid == -1) { 39 | } else if ( childPid == 0) { 40 | char *msg = calloc(100, 1); 41 | read(pipefd[0], msg, 100); 42 | printf("+ msg from pipe: %s\n", msg); 43 | // close(pipefd[1]); 44 | int pid = getpid(); 45 | sprintf(buff, "ls /proc/%d/fd", pid); 46 | system(buff); 47 | printf("+ parent pid: %d\n", pid); 48 | 49 | sleep(2); 50 | char *criu_call = calloc(150, 1); 51 | strcpy(criu_call, "sudo /home/hirnheiner/repos/criu/criu/criu restore -d -vvv -o restore.log --images-dir /tmp/criu_snapshot --inherit-fd "); 52 | char* path = calloc(100, 1); 53 | sprintf(path, "/proc/self/fd/%i", pipefd[1]); 54 | 55 | char* ls = calloc(100, 1); 56 | sprintf(ls, "ls /proc/self/fd"); 57 | system(ls); 58 | 59 | char* pipename = calloc(100, 1); 60 | readlink(path, pipename, 100); 61 | printf("%s\n", path); 62 | printf("%s\n", pipename); 63 | 64 | char* inheritfd_arg = calloc(100, 1); 65 | sprintf(inheritfd_arg, "fd[%i]:%s", pipefd[1], pipename); 66 | strncat(criu_call, inheritfd_arg, strlen(inheritfd_arg)); 67 | system(criu_call); 68 | while(1){ 69 | read(pipefd[0], msg, 100); 70 | printf("+ msg from pipe: %s\n", msg); 71 | }; 72 | } else { 73 | close(pipefd[0]); 74 | child(pipefd); 75 | } 76 | 77 | return 0; 78 | } 79 | -------------------------------------------------------------------------------- /tests/targets/custom/pseudoclient_complex.c: -------------------------------------------------------------------------------- 1 | 2 | // Client side C/C++ program to demonstrate Socket programming 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #define PORT 8080 10 | 11 | int main() 12 | { 13 | int sock = 0; 14 | struct sockaddr_in serv_addr; 15 | char *msg = "R"; 16 | char stack_buf[8]; 17 | char *buffer = (char *)calloc(100, 1); 18 | if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) 19 | { 20 | printf("\n Socket creation error \n"); 21 | return -1; 22 | } 23 | 24 | serv_addr.sin_family = AF_INET; 25 | serv_addr.sin_port = htons(PORT); 26 | 27 | // Convert IPv4 and IPv6 addresses from text to binary form 28 | if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0) 29 | { 30 | printf("\nInvalid address / Address not supported \n"); 31 | return -1; 32 | } 33 | 34 | if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) 35 | { 36 | printf("\nConnection Failed \n"); 37 | return -1; 38 | } 39 | // TODO: This is a quick fix for our init_run being developed only with server binaries in mind 40 | // I am not sure atm if `recv` is the right point to snapshot the client. 41 | send(sock , msg , strlen(msg) , 0 ); 42 | printf("client sent: %s\n", msg); 43 | recv(sock, buffer, 100, 0); 44 | printf("client recv #1: %s\n", buffer); 45 | if(!strcmp(buffer, "ACK! Got correct init signal\n")) { 46 | char *new_msg = "Need more state!\n"; 47 | send(sock, new_msg, strlen(new_msg), 0); 48 | printf("client send #2: %s\n", new_msg); 49 | 50 | free(buffer); 51 | buffer = (char *) calloc(100, 1); 52 | 53 | recv(sock, buffer, 100, 0); 54 | printf("client recv #2: %s\n", buffer); 55 | if (!strcmp(buffer, "make client go b000000000000m.\n")) { 56 | printf("dingdingding, client goes bum"); 57 | char a = *(char*)NULL; 58 | } else { 59 | printf("client did not go bum\n"); 60 | } 61 | } else { 62 | printf("client got incorrect init response\n"); 63 | recv(sock, buffer, 100, 0); 64 | printf("this is the wrong recv\n"); 65 | } 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /create_restore.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from sys import argv 3 | from os import getcwd, chmod 4 | from subprocess import call 5 | 6 | import re 7 | import json 8 | 9 | # execute from fitm/ 10 | def main(): 11 | open_fds = "" 12 | lines = [x.strip("\n") for x in open("./restore.sh.tmp", "r").readlines()] 13 | cur_state = f"{getcwd()}/active-state"[1:] 14 | if argv[1]: 15 | base_state_saved = f"{getcwd()}/saved-states/{argv[1]}"[1:] 16 | base_state_active = f"{getcwd()}/active-state"[1:] 17 | 18 | call( 19 | f"./criu/crit/crit-python3 decode -i /{base_state_saved}/snapshot/files.img --pretty -o ./file".split() 20 | ) 21 | call( 22 | f"./criu/crit/crit-python3 decode -i /{base_state_saved}/snapshot/fdinfo-2.img --pretty -o ./fdinfo".split() 23 | ) 24 | 25 | file_info = json.load(open("./file", "r")) 26 | fd_info = json.load(open("./fdinfo", "r")) 27 | 28 | files = filter( 29 | lambda x: "reg" in x.keys() and "/fd/" in x["reg"]["name"], 30 | file_info["entries"], 31 | ) 32 | files = map(lambda x: (x["id"], x["reg"]["name"]), files) 33 | 34 | mapping = [] 35 | 36 | for f in files: 37 | fd = list(filter(lambda x: x["id"] == f[0], fd_info["entries"]))[0] 38 | mapping.append((fd["fd"], f[1])) 39 | 40 | lines.append(f' --inherit-fd "fd[1]:{base_state_active}/stdout" \\') 41 | lines.append(f' --inherit-fd "fd[2]:{base_state_active}/stderr" \\') 42 | 43 | open_fds += f"exec 1>> /{cur_state}/stdout\n" 44 | open_fds += f"exec 2>> /{cur_state}/stderr\n" 45 | 46 | # open_fds += f"exec 1<> /{cur_state}/stdout\n" 47 | # open_fds += f"exec 2<> /{cur_state}/stderr\n" 48 | 49 | for m in mapping: 50 | open_fds += ( 51 | f"exec {m[0]}<> {re.sub(r'fitm-c[0-9]+s[0-9]+', argv[2], m[1])}\n" 52 | ) 53 | lines.append(f' --inherit-fd "fd[{m[0]}]:{m[1][1:]}" \\') 54 | 55 | lines.append(" && echo 'OK'") 56 | 57 | open(f"/{cur_state}/restore.sh", "w").write( 58 | "\n".join(lines).replace("## TEMPLATE ##", open_fds) 59 | ) 60 | # Make file world executable 61 | chmod(f"/{cur_state}/restore.sh", 0o755) 62 | 63 | 64 | if __name__ == "__main__": 65 | main() 66 | -------------------------------------------------------------------------------- /tests/targets/custom/pseudoserver_simple.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main() { 9 | printf("Starting server..\n"); 10 | 11 | // open socket 12 | int server_fd, new_socket; 13 | struct sockaddr_in address; 14 | int addrlen = sizeof(address); 15 | int opt = 1; 16 | if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) 17 | { 18 | perror("socket failed"); 19 | exit(EXIT_FAILURE); 20 | } 21 | 22 | // Forcefully attaching socket to the port 8080 23 | if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, 24 | &opt, sizeof(opt))) 25 | { 26 | perror("setsockopt"); 27 | exit(EXIT_FAILURE); 28 | } 29 | address.sin_family = AF_INET; 30 | address.sin_addr.s_addr = INADDR_ANY; 31 | address.sin_port = htons( 8080 ); 32 | 33 | // Forcefully attaching socket to the port 8080 34 | if (bind(server_fd, (struct sockaddr *)&address, 35 | sizeof(address))<0) 36 | { 37 | perror("bind failed"); 38 | exit(EXIT_FAILURE); 39 | } 40 | if (listen(server_fd, 3) < 0) 41 | { 42 | perror("listen"); 43 | exit(EXIT_FAILURE); 44 | } 45 | printf("server pre accept\n"); 46 | if ((new_socket = accept(server_fd, (struct sockaddr *)&address, 47 | (socklen_t*)&addrlen))<0) 48 | { 49 | perror("accept"); 50 | exit(EXIT_FAILURE); 51 | } 52 | printf("server booted successfully\n"); 53 | 54 | // recv 1 55 | char *buf = (char *)calloc(100, 1); 56 | recv(new_socket, buf, 100, 0); 57 | printf("server recv #1: %s\n", buf); 58 | 59 | // send 1 60 | free(buf); 61 | buf = (char *)calloc(100, 1); 62 | char *new_msg = "ACK! Got correct init signal\n"; 63 | send(new_socket , new_msg , strlen(new_msg) , 0 ); 64 | printf("server send #1: %s\n", new_msg); 65 | 66 | // recv 2 67 | free(buf); 68 | buf = (char *)calloc(100, 1); 69 | recv(new_socket, buf, 100, 0); 70 | printf("server recv #2: %s\n", buf); 71 | 72 | // send 2 73 | new_msg = "make client go b0000000000000000000000000000m.\n"; 74 | send(new_socket , new_msg , strlen(new_msg) , 0 ); 75 | printf("server send #2: %s\n", new_msg); 76 | 77 | free(buf); 78 | return 0; 79 | } 80 | -------------------------------------------------------------------------------- /tests/targets/custom/pseudoserver_complex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main() { 9 | 10 | // open socket 11 | int server_fd, new_socket; 12 | struct sockaddr_in address; 13 | int addrlen = sizeof(address); 14 | int opt = 1; 15 | if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) 16 | { 17 | perror("socket failed"); 18 | exit(EXIT_FAILURE); 19 | } 20 | 21 | // Forcefully attaching socket to the port 8080 22 | if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, 23 | &opt, sizeof(opt))) 24 | { 25 | perror("setsockopt"); 26 | exit(EXIT_FAILURE); 27 | } 28 | address.sin_family = AF_INET; 29 | address.sin_addr.s_addr = INADDR_ANY; 30 | address.sin_port = htons( 8080 ); 31 | 32 | // Forcefully attaching socket to the port 8080 33 | if (bind(server_fd, (struct sockaddr *)&address, 34 | sizeof(address))<0) 35 | { 36 | perror("bind failed"); 37 | exit(EXIT_FAILURE); 38 | } 39 | if (listen(server_fd, 3) < 0) 40 | { 41 | perror("listen"); 42 | exit(EXIT_FAILURE); 43 | } 44 | if ((new_socket = accept(server_fd, (struct sockaddr *)&address, 45 | (socklen_t*)&addrlen))<0) 46 | { 47 | perror("accept"); 48 | exit(EXIT_FAILURE); 49 | } 50 | 51 | char *buf = (char *)calloc(100, 1); 52 | // client sends first... 53 | // send(new_socket, "RI\0", 3, 0); 54 | 55 | // recv 1 56 | recv(new_socket, buf, 100, 0); 57 | printf("server recv #1: %s\n", buf); 58 | 59 | if(buf[0] == 'R') { 60 | free(buf); 61 | buf = (char *)calloc(100, 1); 62 | 63 | // send 1 64 | char *new_msg = "ACK! Got correct init signal\n"; 65 | send(new_socket , new_msg , strlen(new_msg) , 0 ); 66 | printf("server send #1: %s\n", new_msg); 67 | 68 | free(buf); 69 | buf = (char *)calloc(100, 1); 70 | // recv 2 71 | recv(new_socket, buf, 100, 0); 72 | printf("server recv #2: %s\n", buf); 73 | if (!strcmp(buf, "Need more state!\n")) { 74 | 75 | // send 2 76 | new_msg = "make client go b0000000000m.\n"; 77 | send(new_socket , new_msg , strlen(new_msg) , 0 ); 78 | printf("server send #2: %s\n", new_msg); 79 | 80 | free(buf); 81 | } else { 82 | printf("server didn't get more state\n"); 83 | } 84 | } else { 85 | printf("server got incorrect init signal...aborting\n"); 86 | } 87 | return 0; 88 | } 89 | -------------------------------------------------------------------------------- /misc/print_connection.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | This script outputs a whole connection, from beginning to end, 5 | If you want, you can add `-v` to get delimeters, like 6 | >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>NEXt>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 7 | """ 8 | # fitm-gen22-state0/envfile | grep INPUT_FILENAME 9 | # INPUT_FILENAME=/path/to/FitM/saved-states/fitm-gen20-state3/out/main/queue/id:000269,src:000248,time:104360,op:havoc,rep:2 10 | 11 | import os 12 | import sys 13 | 14 | NEXT = ( 15 | ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>NEXT>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" 16 | ) 17 | # ENVFILE = "envfile" 18 | # IF_TOK = "INPUT_FILENAME=" 19 | PREV_INPUT_FILE = "prev_input" 20 | PREV_INPUT_PATH = "prev_input_path" 21 | 22 | 23 | if len(sys.argv) < 2: 24 | raise Exception("Usage: [-v] ./path/to/fitm-genX-stateY") 25 | 26 | 27 | def faux_print(*args, **kwargs): 28 | pass 29 | 30 | 31 | # -r => raw message, don't print information. 32 | if len(sys.argv) > 2 and sys.argv[1] == "-v": 33 | current_state = sys.argv[2] 34 | # to silence the linter 35 | print = print 36 | else: 37 | current_state = sys.argv[1] 38 | print = faux_print 39 | 40 | connection_files = [] 41 | 42 | if not os.path.exists(current_state): 43 | raise Exception( 44 | f"Could not open initial state {current_state}, make sure you have the proper access rights!" 45 | ) 46 | 47 | # Walk backwards though the linked file list 48 | while current_state: 49 | try: 50 | prev_file = os.path.join(current_state, PREV_INPUT_FILE) 51 | if not os.path.exists(prev_file): 52 | print(f"finished in gen {prev_file}") 53 | break 54 | 55 | connection_files.insert(0, os.path.join(current_state, PREV_INPUT_FILE)) 56 | 57 | with open(prev_file) as f: 58 | prev = f.read() 59 | 60 | # "cd" out ouf /out/main/filename 61 | current_state = os.path.dirname(prev) 62 | for i in range(3): 63 | current_state = os.path.dirname(current_state) 64 | 65 | except Exception as ex: 66 | print(f"Initial handling finished: {ex} ({current_state})") 67 | current_state = None 68 | 69 | stdout = os.fdopen(sys.stdout.fileno(), "wb") 70 | 71 | for con_file in connection_files: 72 | print(NEXT) 73 | print(con_file) 74 | 75 | try: 76 | with open(con_file, "rb") as f: 77 | content = ( 78 | f.read() 79 | ) # errors='surrogateescape')#.decode("utf-8",errors='surrogatepass') 80 | print(f"DBG: len={len(content)}") 81 | except Exception as ex: 82 | content = b"" 83 | print(f"File missing (cmin killed it?)") # {ex}") 84 | 85 | print(NEXT) 86 | stdout.write(content) # .encode("utf-8", errors="surrogateescape")) 87 | stdout.flush() 88 | print("") 89 | -------------------------------------------------------------------------------- /tests/self-dump_test.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by hirnheiner on 11.05.20. 3 | // Checkout the Makefile 4 | 5 | #include "rpc.pb-c.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define MAX_MSG_SIZE 1024 16 | 17 | static int send_req(int socket_fd, CriuReq *req) 18 | { 19 | unsigned char buf[MAX_MSG_SIZE]; 20 | int len; 21 | 22 | len = criu_req__get_packed_size(req); 23 | 24 | if (criu_req__pack(req, buf) != len) { 25 | perror("Failed packing request"); 26 | return -1; 27 | } 28 | 29 | if (write(socket_fd, buf, len) == -1) { 30 | perror("Can't send request"); 31 | return -1; 32 | } 33 | 34 | return 0; 35 | } 36 | 37 | int do_criu(){ 38 | CriuReq req = CRIU_REQ__INIT; 39 | CriuResp *resp = NULL; 40 | int fd, dir_fd; 41 | int ret = 0; 42 | struct sockaddr_un addr; 43 | socklen_t addr_len; 44 | 45 | dir_fd = open("/tmp/criu_snapshot", O_DIRECTORY); 46 | if (dir_fd == -1) { 47 | perror("Can't open /tmp/criu_snapshot dir"); 48 | return -1; 49 | } 50 | 51 | req.type = CRIU_REQ_TYPE__DUMP; 52 | req.opts = malloc(sizeof(CriuOpts)); 53 | if (!req.opts) { 54 | perror("Can't allocate memory for dump request"); 55 | return -1; 56 | } 57 | 58 | criu_opts__init(req.opts); 59 | req.opts->images_dir_fd = dir_fd; 60 | req.opts->log_level = 4; 61 | req.opts->shell_job = 1; 62 | 63 | fd = socket(AF_LOCAL, SOCK_SEQPACKET, 0); 64 | if (fd == -1) { 65 | perror("Can't create socket"); 66 | return -1; 67 | } 68 | 69 | memset(&addr, 0, sizeof(addr)); 70 | addr.sun_family = AF_LOCAL; 71 | 72 | strcpy(addr.sun_path, "/tmp/criu_service.socket"); 73 | 74 | addr_len = strlen(addr.sun_path) + sizeof(addr.sun_family); 75 | 76 | ret = connect(fd, (struct sockaddr *) &addr, addr_len); 77 | if (ret == -1) { 78 | perror("Can't connect to socket"); 79 | goto exit; 80 | } 81 | 82 | /* 83 | * Send request 84 | */ 85 | ret = send_req(fd, &req); 86 | if (ret == -1) { 87 | perror("Can't send request"); 88 | goto exit; 89 | } 90 | 91 | 92 | exit: 93 | // Closing the socket FD before the process is dumped breaks CRIU 94 | // close(fd); 95 | // close(dir_fd); 96 | if (resp) 97 | criu_resp__free_unpacked(resp, NULL); 98 | return ret; 99 | } 100 | 101 | 102 | int main(int argc, char *argv[]) { 103 | int state = 0; 104 | while(state < 2){ 105 | printf("%i\n", state++); 106 | sleep(1); 107 | } 108 | puts("requesting process dump"); 109 | 110 | // request selfdump 111 | do_criu(); 112 | 113 | puts("continue..."); 114 | while(state < 10){ 115 | printf("%i\n", state++); 116 | sleep(1); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | all: lightftp live555 pseudotest_simple kamailio pjproject teamspeak echo_loop 2 | 3 | ./targets/pjproject/pjsip-apps/bin/pjsua-x86_64-unknown-linux-gnu: 4 | cd ./targets/pjproject && ./configure && $(MAKE) dep && $(MAKE) clean && $(MAKE) && cd ../../ 5 | 6 | pjproject: ./targets/pjproject/pjsip-apps/bin/pjsua-x86_64-unknown-linux-gnu 7 | 8 | ./targets/kamailio/src/kamailio: 9 | $(MAKE) -C ./targets/kamailio cfg 10 | $(MAKE) -C ./targets/kamailio all 11 | 12 | kamailio: ./targets/kamailio/src/kamailio 13 | 14 | ./targets/teamspeak3-server_linux_amd64/ts3server: 15 | ./download_teamspeak.sh 16 | 17 | teamspeak: ./targets/teamspeak3-server_linux_amd64/ts3server 18 | 19 | lightftp: 20 | $(MAKE) -C ./targets/LightFTP/Source/Release clean all 21 | 22 | live555: 23 | cd ./targets/live555 && ./genMakefiles linux-64bit 24 | $(MAKE) -C ./targets/live555 25 | $(MAKE) -C ./targets/live555/testProgs 26 | 27 | live555-client: 28 | ./targets/live555/testProgs/testRTSPClient rtsp://192.168.178.27:8554/wavAudioTest 29 | 30 | live555-server: 31 | cd ./targets/live555/testProgs && ./testOnDemandRTSPServer 8854 && cd ../../ 32 | 33 | selfdump: symlink 34 | mkdir -p /tmp/criu_snapshot 35 | protoc-c --proto_path=. --c_out=. rpc.proto 36 | cc -g -Wall -I. -c -o self-dump.o self-dump.c 37 | cc -g -Wall -I. -c -o rpc.pb-c.o rpc.pb-c.c 38 | cc self-dump.o rpc.pb-c.o -lprotobuf-c -o self-dump 39 | 40 | syscall: 41 | gcc -o syscall syscall_test.c 42 | 43 | clean: 44 | rm -rf self-dump self-dump.o rpc.pb-c.o rpc.pb-c.c rpc.pb-c.h \ 45 | forkserver_test rpc.proto syscall getenv_from_file_test pipe_test \ 46 | targets/custom/pseudoclient targets/custom/pseudoserver targets/custom/snapshot_creation 47 | 48 | test_getenv: clean 49 | gcc -o getenv_from_file_test getenv_from_file_test.c 50 | ./getenv_from_file_test 51 | 52 | test_pipe: clean 53 | gcc -o pipe_test pipe_test.c 54 | setsid stdbuf -oL ../AFLplusplus/afl-qemu-trace ./pipe_test < /dev/null &> pipe_test.out 55 | 56 | snapshot_creation: 57 | gcc -o targets/custom/snapshot_creation targets/custom/snapshot_creation.c 58 | 59 | echo_server: 60 | gcc -o targets/custom/echo_server targets/custom/echo_server.c 61 | 62 | pseudotest_simple: pseudoserver_simple pseudoclient_simple 63 | 64 | pseudoserver_simple: clean 65 | gcc -o targets/custom/pseudoserver_simple targets/custom/pseudoserver_simple.c 66 | 67 | pseudoclient_simple: clean 68 | gcc -o targets/custom/pseudoclient_simple targets/custom/pseudoclient_simple.c 69 | 70 | pseudotest_complex: pseudoserver_complex pseudoclient_complex 71 | 72 | pseudoserver_complex: clean 73 | gcc -o targets/custom/pseudoserver_complex targets/custom/pseudoserver_complex.c 74 | 75 | pseudoclient_complex: clean 76 | gcc -o targets/custom/pseudoclient_complex targets/custom/pseudoclient_complex.c 77 | 78 | # Invoke with: make symlink CRIUPATH=/home/hirnheiner/repos/criu 79 | symlink: 80 | ln -s $(CRIUPATH)/images/rpc.proto || true 81 | 82 | ./targets/custom/echo_loop: ./targets/custom/echo_loop.c 83 | gcc -O3 -o targets/custom/echo_loop $< 84 | 85 | echo_loop: ./targets/custom/echo_loop 86 | -------------------------------------------------------------------------------- /tests/syscall_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void do_syscall(void){ 9 | // Should generate file with name at /fds/ and return FD 10 | printf("SOCKET: %d\n", socket(0, 0, 0)); 11 | // Should always return 0 12 | printf("BIND: %d\n", bind(0, 0, 0)); 13 | // Should always return 0 14 | printf("CONNECT: %d\n", connect(0, 0, 0)); 15 | // Should always return 0 16 | printf("SETSOCKOPT: %d\n", setsockopt(0, 0, 0, 0, 0)); 17 | // Should always return 0 18 | printf("GETSOCKOPT: %d\n", getsockopt(0, 0, 0, 0, 0)); 19 | // Should generate file with name at /fds/ and return FD 20 | // Maybe we need to handle a connection queue or copy stuff to peer adr. 21 | printf("ACCEPT: %d\n", accept(0, 0, 0)); 22 | // Write to the given FD (a local file if everything works out) and set the "sent flag" 23 | printf("SEND: %d\n", send(0, 0, 0, 0)); 24 | // Read from stdin. Trigger snapshot if we've sent previously in this session 25 | printf("RECV: %d\n", recv(0, 0, 0, 0)); 26 | // Should always return 0 27 | printf("LISTEN: %d\n", listen(0, 0)); 28 | } 29 | 30 | void run_server(void){ 31 | int server_fd, new_socket, valread; 32 | struct sockaddr_in address; 33 | int opt = 1; 34 | int addrlen = sizeof(address); 35 | char buffer[1024] = {0}; 36 | char *hello = "Hello from server"; 37 | 38 | // Creating socket file descriptor 39 | if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) 40 | { 41 | perror("socket failed"); 42 | exit(EXIT_FAILURE); 43 | } 44 | 45 | // Forcefully attaching socket to the port 8080 46 | if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) 47 | { 48 | perror("setsockopt"); 49 | exit(EXIT_FAILURE); 50 | } 51 | address.sin_family = AF_INET; 52 | address.sin_addr.s_addr = INADDR_ANY; 53 | address.sin_port = htons( 8080 ); 54 | 55 | // Forcefully attaching socket to the port 8080 56 | if (bind(server_fd, (struct sockaddr *)&address, 57 | sizeof(address))<0) 58 | { 59 | perror("bind failed"); 60 | exit(EXIT_FAILURE); 61 | } 62 | if (listen(server_fd, 3) < 0) 63 | { 64 | perror("listen"); 65 | exit(EXIT_FAILURE); 66 | } 67 | if ((new_socket = accept(server_fd, (struct sockaddr *)&address, 68 | (socklen_t*)&addrlen))<0) 69 | { 70 | perror("accept"); 71 | exit(EXIT_FAILURE); 72 | } 73 | valread = read( new_socket , buffer, 1024); 74 | printf("%s\n",buffer ); 75 | send(new_socket , hello , strlen(hello) , 0 ); 76 | printf("Hello message sent\n"); 77 | } 78 | 79 | int main(void) { 80 | int state = 0; 81 | while(state < 2){ 82 | printf("%i\n", state); 83 | state++; 84 | sleep(1); 85 | } 86 | run_server(); 87 | while(state < 6){ 88 | printf("%i\n", state); 89 | state++; 90 | sleep(1); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /fitm-qemu/libqasan/map_macro.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 William Swanson 3 | * 4 | * Permission is hereby granted, free of charge, to any person 5 | * obtaining a copy of this software and associated documentation 6 | * files (the "Software"), to deal in the Software without 7 | * restriction, including without limitation the rights to use, copy, 8 | * modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 20 | * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | * 23 | * Except as contained in this notice, the names of the authors or 24 | * their institutions shall not be used in advertising or otherwise to 25 | * promote the sale, use or other dealings in this Software without 26 | * prior written authorization from the authors. 27 | */ 28 | 29 | #ifndef MAP_H_INCLUDED 30 | #define MAP_H_INCLUDED 31 | 32 | #define EVAL0(...) __VA_ARGS__ 33 | #define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__))) 34 | #define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__))) 35 | #define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) 36 | #define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__))) 37 | #define EVAL(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__))) 38 | 39 | #define MAP_END(...) 40 | #define MAP_OUT 41 | #define MAP_COMMA , 42 | 43 | #define MAP_GET_END2() 0, MAP_END 44 | #define MAP_GET_END1(...) MAP_GET_END2 45 | #define MAP_GET_END(...) MAP_GET_END1 46 | #define MAP_NEXT0(test, next, ...) next MAP_OUT 47 | #define MAP_NEXT1(test, next) MAP_NEXT0(test, next, 0) 48 | #define MAP_NEXT(test, next) MAP_NEXT1(MAP_GET_END test, next) 49 | 50 | #define MAP0(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP1)(f, peek, __VA_ARGS__) 51 | #define MAP1(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP0)(f, peek, __VA_ARGS__) 52 | 53 | #define MAP_LIST_NEXT1(test, next) MAP_NEXT0(test, MAP_COMMA next, 0) 54 | #define MAP_LIST_NEXT(test, next) MAP_LIST_NEXT1(MAP_GET_END test, next) 55 | 56 | #define MAP_LIST0(f, x, peek, ...) \ 57 | f(x) MAP_LIST_NEXT(peek, MAP_LIST1)(f, peek, __VA_ARGS__) 58 | #define MAP_LIST1(f, x, peek, ...) \ 59 | f(x) MAP_LIST_NEXT(peek, MAP_LIST0)(f, peek, __VA_ARGS__) 60 | 61 | /** 62 | * Applies the function macro `f` to each of the remaining parameters. 63 | */ 64 | #define MAP(f, ...) EVAL(MAP1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) 65 | 66 | /** 67 | * Applies the function macro `f` to each of the remaining parameters and 68 | * inserts commas between the results. 69 | */ 70 | #define MAP_LIST(f, ...) \ 71 | EVAL(MAP_LIST1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) 72 | 73 | #endif 74 | 75 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use std::collections::HashMap; 3 | use std::fs; 4 | use std::path::{Path, PathBuf}; 5 | use std::process; 6 | use std::{env, time::Duration}; 7 | 8 | #[derive(Serialize, Deserialize)] 9 | struct RunArgs { 10 | /// The client target binary 11 | client: String, 12 | client_args: Vec, 13 | client_envs: HashMap, 14 | client_files: Vec, 15 | /// The client target binary 16 | server: String, 17 | server_args: Vec, 18 | server_envs: HashMap, 19 | server_files: Vec, 20 | /// run time in secs 21 | run_time: u64, 22 | // Still needs an echo binary or a binary producing a short output, as client 23 | // Just fuzzes the client for 100 millis. 24 | /// Enable protocol discovery (server_only) 25 | server_only: bool, 26 | } 27 | 28 | fn is_root() { 29 | match env::var("SUDO_USER") { 30 | Ok(_) => {} 31 | Err(_) => { 32 | println!( 33 | "Please execute FitM as root as it is needed for criu. \ 34 | For reference please visit \ 35 | https://criu.org/Self_dump#Difficulties" 36 | ); 37 | process::exit(1); 38 | } 39 | } 40 | } 41 | 42 | fn setup_env() { 43 | let idc = "AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES"; 44 | let cpu = "AFL_SKIP_CPUFREQ"; 45 | // let debug = "AFL_DEBUG_CHILD_OUTPUT"; 46 | let debug = "AFL_QUIET"; 47 | 48 | env::set_var(idc, "1"); 49 | env::set_var(cpu, "1"); 50 | env::set_var(debug, "1"); 51 | } 52 | 53 | #[allow(dead_code)] 54 | fn load_args(path: PathBuf) -> RunArgs { 55 | match fs::read_to_string(path) { 56 | Ok(args_json) => match serde_json::from_str(&args_json) { 57 | Ok(run_args) => run_args, 58 | Err(e) => panic!("[!] Error parsing fitm-args.json: {:?}", e), 59 | }, 60 | Err(e) => panic!("[!] Error reading fitm-args.json: {:?}", e), 61 | } 62 | } 63 | 64 | fn ensure_saved_states() { 65 | if !Path::new("saved-states").exists() && fs::create_dir("saved-states").is_err() { 66 | println!("Could not create saved-states dir, aborting!"); 67 | process::exit(0); 68 | }; 69 | } 70 | 71 | fn main() { 72 | is_root(); 73 | 74 | setup_env(); 75 | 76 | ensure_saved_states(); 77 | 78 | println!("cwd: {:?}", std::env::current_dir().unwrap()); 79 | 80 | let config_path: PathBuf = std::env::args() 81 | .nth(1) 82 | .expect("No config path given") 83 | .into(); 84 | let args = load_args(config_path); 85 | 86 | // TODO: use argv to fill these 87 | // Paths are relative to ACTIVE_DIR 88 | if let Err(e) = fitm::run( 89 | &args.client, 90 | &args.client_args, 91 | &args.client_envs, 92 | &args.client_files, 93 | &args.server, 94 | &args.server_args, 95 | &args.server_envs, 96 | &args.server_files, 97 | &Duration::from_secs(args.run_time), 98 | args.server_only, 99 | ) { 100 | println!("Error {:?}", e); 101 | }; 102 | } 103 | -------------------------------------------------------------------------------- /fitm-qemu/libqasan/libqasan.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2019-2020, Andrea Fioraldi 3 | 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | *******************************************************************************/ 25 | 26 | #include "libqasan.h" 27 | 28 | #ifdef DEBUG 29 | int __qasan_debug; 30 | #endif 31 | int __qasan_log; 32 | 33 | void __libqasan_print_maps(void) { 34 | 35 | int fd = open("/proc/self/maps", O_RDONLY); 36 | char buf[4096] = {0}; 37 | 38 | read(fd, buf, 4095); 39 | close(fd); 40 | 41 | size_t len = strlen(buf); 42 | 43 | QASAN_LOG("Guest process maps:\n"); 44 | int i; 45 | char *line = NULL; 46 | for (i = 0; i < len; i++) { 47 | 48 | if (!line) line = &buf[i]; 49 | if (buf[i] == '\n') { 50 | 51 | buf[i] = 0; 52 | QASAN_LOG("%s\n", line); 53 | line = NULL; 54 | 55 | } 56 | 57 | } 58 | 59 | if (line) QASAN_LOG("%s\n", line); 60 | QASAN_LOG("\n"); 61 | 62 | } 63 | 64 | /*__attribute__((constructor))*/ void __libqasan_init() { 65 | 66 | __libqasan_init_hooks(); 67 | 68 | #ifdef DEBUG 69 | __qasan_debug = getenv("QASAN_DEBUG") != NULL; 70 | #endif 71 | __qasan_log = getenv("QASAN_LOG") != NULL; 72 | 73 | QASAN_LOG("QEMU-AddressSanitizer (v%s)\n", QASAN_VERSTR); 74 | QASAN_LOG( 75 | "Copyright (C) 2019-2021 Andrea Fioraldi \n"); 76 | QASAN_LOG("\n"); 77 | 78 | if (__qasan_log) __libqasan_print_maps(); 79 | 80 | } 81 | 82 | int __libc_start_main(int (*main)(int, char **, char **), int argc, char **argv, 83 | int (*init)(int, char **, char **), void (*fini)(void), 84 | void (*rtld_fini)(void), void *stack_end) { 85 | 86 | typeof(&__libc_start_main) orig = dlsym(RTLD_NEXT, "__libc_start_main"); 87 | 88 | __libqasan_init(); 89 | if (getenv("AFL_INST_LIBS")) __libqasan_hotpatch(); 90 | 91 | return orig(main, argc, argv, init, fini, rtld_fini, stack_end); 92 | 93 | } 94 | 95 | -------------------------------------------------------------------------------- /tests/gen_afl_maps_test.rs: -------------------------------------------------------------------------------- 1 | // use fitm::{origin_state, FITMSnapshot}; 2 | // use std::fs; 3 | 4 | mod common; 5 | 6 | // init_run_test should check if a snapshot could be successfully be created. 7 | // As the test does not have access to criu server responses or other logs it 8 | // relies on the correct creation of various files 9 | 10 | /* 11 | 12 | fitm-gen0-state0 -> snapshot() at initial recv 13 | --> outputs send stuff 14 | fitm-gen1-state0 -> snapshot() at initial recv (server should not send earlier (for now)) 15 | 16 | fuzz fitm-gen1-state0 -> c0s1(fitm-gen0-state0[send stuff]) 17 | --> outputs c0s1stuff[testcase][u8] 18 | 19 | for testcase in c0s1stuff 20 | fuzz fitm-gen0-state0 -> c1s1(c0s1[testcase]) 21 | 22 | fitm-gen0-state0: origin_state(client) 23 | fitm-gen1-state0: origin_state(server), necessary for criu right now 24 | - server_run0 (c0s1) 25 | - client_run0 (c1s1) 26 | - server_run0 (c1s2) 27 | - server_run1 (c1s3) < base state here <<----. 28 | - client_run0 (c2s3) | 29 | - server_run0 (c2s5) ---------------^ << counter for c, s are global 30 | - client_run1 (c3s3) 31 | - server_run2 (c1s4) 32 | - client_run1 (c2s4) 33 | 34 | numbers are continouus 35 | 36 | Scripted client, wants to CWD, DELE, MODE 37 | 38 | FTP Example 39 | Base snapshot: 40 | fitm-gen0-state0: sent CWD, rady to recv 41 | fitm-gen1-state0: ready to recv 42 | 43 | step 1: fuzz the server (fitm-gen1-state0). 44 | Client => CWD 45 | server: CWD, CWX, DWD, FXX, PORT, ... 46 | if new testcase: snapshot(c0s1..c0sn) 47 | 48 | step 2: fuzz the client (fitm-gen0-state0). 49 | Server => [CWD, PORT] 50 | client: Unknown command: XOXO -> DELE, CWD with what it expected -> PLZ send file, PORT -> Exit 51 | 52 | step 3: fuzz all servers (c0s1) 53 | 54 | 55 | */ 56 | #[test] 57 | fn gen_afl_maps_test() { 58 | // pwd == root dir of repo 59 | common::setup(); 60 | 61 | // Needs refactoring 62 | /* 63 | let afl_server: FITMSnapshot = FITMSnapshot::new( 64 | 1, 65 | 0, 66 | "tests/targets/echo_server".to_string(), 67 | 1, 68 | origin_state(true).to_string(), 69 | true, 70 | true, 71 | ); 72 | 73 | afl_server.init_run(); 74 | 75 | std::fs::remove_dir_all(format!("active-state/{}", &afl_server.state_path)).expect( 76 | format!( 77 | "Could not remove '{}' in gen_afl_maps_test", 78 | &afl_server.state_path 79 | ) 80 | .as_str(), 81 | ); 82 | 83 | // populate in folder here 84 | let first = "a simple string"; 85 | let second = "message 1, upcoming linebreak now:\nmessage 2"; 86 | let third = "foo\tbar"; 87 | fs::create_dir_all("./saved-states/fitm-gen1-state0/out/main/queue/") 88 | .expect("Could not create queue folder"); 89 | fs::write( 90 | "./saved-states/fitm-gen1-state0/out/main/queue/first_case.txt", 91 | first, 92 | ) 93 | .expect("Could not write first input file"); 94 | fs::write( 95 | "./saved-states/fitm-gen1-state0/out/main/queue/second_case.txt", 96 | second, 97 | ) 98 | .expect("Could not write second input file"); 99 | fs::write( 100 | "./saved-states/fitm-gen1-state0/out/main/queue/third_case.txt", 101 | third, 102 | ) 103 | .expect("Could not write third input file"); 104 | 105 | // tested function 106 | afl_server 107 | .gen_afl_maps() 108 | .expect("Couldn't generate afl maps"); 109 | 110 | let map1 = fs::read_to_string("./active-state/fitm-gen1-state0/out/maps/first_case.txt"); 111 | let map2 = fs::read_to_string("./active-state/fitm-gen1-state0/out/maps/second_case.txt"); 112 | let map3 = fs::read_to_string("./active-state/fitm-gen1-state0/out/maps/third_case.txt"); 113 | 114 | // Can't check for exact content since the addresses change depending on the compiler/architecture used for building the tested binary 115 | assert!(map1.unwrap().contains(":")); 116 | assert!(map2.unwrap().contains(":")); 117 | assert!(map3.unwrap().contains(":"));*/ 118 | 119 | // break here and inspect `active-state/stdout-afl` to see breaking 120 | // forkserver 121 | common::teardown(); 122 | } 123 | -------------------------------------------------------------------------------- /fitm-qemu/libqasan/uninstrument.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This code is DEPRECATED! 4 | I'm keeping it here cause maybe the uninstrumentation of a function is needed 5 | for some strange reason. 6 | 7 | */ 8 | 9 | /******************************************************************************* 10 | Copyright (c) 2019-2020, Andrea Fioraldi 11 | 12 | 13 | Redistribution and use in source and binary forms, with or without 14 | modification, are permitted provided that the following conditions are met: 15 | 16 | 1. Redistributions of source code must retain the above copyright notice, this 17 | list of conditions and the following disclaimer. 18 | 2. Redistributions in binary form must reproduce the above copyright notice, 19 | this list of conditions and the following disclaimer in the documentation 20 | and/or other materials provided with the distribution. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 23 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 26 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 29 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | *******************************************************************************/ 33 | 34 | #include "libqasan.h" 35 | #include "map_macro.h" 36 | #include 37 | #include 38 | 39 | #define X_GET_FNPAR(type, name) name 40 | #define GET_FNPAR(x) X_GET_FNPAR x 41 | #define X_GET_FNTYPE(type, name) type 42 | #define GET_FNTYPE(x) X_GET_FNTYPE x 43 | #define X_GET_FNDECL(type, name) type name 44 | #define GET_FNDECL(x) X_GET_FNDECL x 45 | 46 | #define HOOK_UNINSTRUMENT(rettype, name, ...) \ 47 | rettype (*__lq_libc_##name)(MAP_LIST(GET_FNTYPE, __VA_ARGS__)); \ 48 | rettype name(MAP_LIST(GET_FNDECL, __VA_ARGS__)) { \ 49 | \ 50 | if (!(__lq_libc_##name)) __lq_libc_##name = ASSERT_DLSYM(name); \ 51 | int state = QASAN_SWAP(QASAN_DISABLED); \ 52 | rettype r = __lq_libc_##name(MAP_LIST(GET_FNPAR, __VA_ARGS__)); \ 53 | QASAN_SWAP(state); \ 54 | \ 55 | return r; \ 56 | \ 57 | } 58 | 59 | HOOK_UNINSTRUMENT(char *, getenv, (const char *, name)) 60 | 61 | /* 62 | HOOK_UNINSTRUMENT(char*, setlocale, (int, category), (const char *, locale)) 63 | HOOK_UNINSTRUMENT(int, setenv, (const char *, name), (const char *, value), 64 | (int, overwrite)) HOOK_UNINSTRUMENT(char*, getenv, (const char *, name)) 65 | HOOK_UNINSTRUMENT(char*, bindtextdomain, (const char *, domainname), (const char 66 | *, dirname)) HOOK_UNINSTRUMENT(char*, bind_textdomain_codeset, (const char *, 67 | domainname), (const char *, codeset)) HOOK_UNINSTRUMENT(char*, gettext, (const 68 | char *, msgid)) HOOK_UNINSTRUMENT(char*, dgettext, (const char *, domainname), 69 | (const char *, msgid)) HOOK_UNINSTRUMENT(char*, dcgettext, (const char *, 70 | domainname), (const char *, msgid), (int, category)) HOOK_UNINSTRUMENT(int, 71 | __gen_tempname, (char, *tmpl), (int, suffixlen), (int, flags), (int, kind)) 72 | HOOK_UNINSTRUMENT(int, mkstemp, (char *, template)) 73 | HOOK_UNINSTRUMENT(int, mkostemp, (char *, template), (int, flags)) 74 | HOOK_UNINSTRUMENT(int, mkstemps, (char *, template), (int, suffixlen)) 75 | HOOK_UNINSTRUMENT(int, mkostemps, (char *, template), (int, suffixlen), (int, 76 | flags)) HOOK_UNINSTRUMENT(struct passwd *, getpwnam, (const char *, name)) 77 | HOOK_UNINSTRUMENT(struct passwd *, getpwuid, (uid_t, uid)) 78 | HOOK_UNINSTRUMENT(int, getpwnam_r, (const char *, name), (struct passwd *, pwd), 79 | (char *, buf), (size_t, buflen), (struct passwd **, result)) 80 | HOOK_UNINSTRUMENT(int, getpwuid_r, (uid_t, uid), (struct passwd *, pwd), (char 81 | *, buf), (size_t, buflen), (struct passwd **, result)) 82 | */ 83 | 84 | -------------------------------------------------------------------------------- /misc/fitm_pseudocode.py: -------------------------------------------------------------------------------- 1 | def afl_fuzz(snap, inputs, run_time): 2 | """ 3 | Runs afl fuzz 4 | """ 5 | fuzz_result = some_magic(snap, inputs, run_time) 6 | return fuzz_result 7 | 8 | 9 | def afl_cmin(snap, inputs): 10 | """ 11 | Minimizes the inputs 12 | """ 13 | minimized_inputs = some_magic(snap, inputs) 14 | return minimized_inputs 15 | 16 | 17 | def process_stage(current_snaps, current_inputs, next_inputs, nextnext_snaps): 18 | """ 19 | Run afl_fuzz for each snapshot with all inputs for the current gen 20 | @param current_snaps: list of snapshots for this stage 21 | @param current_inputs: list of inputs for this stage 22 | @param next_inputs: (out) list of inputs for the next stage (client->server//server->client) 23 | @param nextnext_snaps: (out) list of snapshots based off of this base snap (client->client, server->server) 24 | @return: False, if we didn't advance to the next generation (no more output) 25 | """ 26 | for snap in current_snaps: 27 | # Not necessary but could be nice: ignore inputs that don't yield new cov for fuzzing 28 | inputs = afl_cmin(snap, current_inputs) 29 | 30 | fuzz_result = afl_fuzz(snap, inputs) 31 | # Post-processing: prune queue entries that don't yield new cov. 32 | minimized_queue = afl_cmin(snap, fuzz_result.queue) 33 | 34 | # Get all outputs for the nth client run 35 | for queue_entry in minimized_queue: 36 | output = snap.restore().input(queue_entry).run_to_recv().output() 37 | if output: 38 | next_gen_valid = True 39 | next_inputs.append(output) 40 | 41 | # Get all snapshots for the n+1 server run (later) 42 | # This could also be done at a later time. 43 | for queue_entry in minimized_queue: 44 | nextnext_snaps.append( 45 | snap.restore().input(queue_entry).run_to_recv().snapshot() 46 | ) 47 | 48 | 49 | def gen_is_client(gen_id): 50 | """ 51 | We begin fuzzing with the server (gen == 0), then client (gen == 1), etc. 52 | So every odd numbered is a client. 53 | """ 54 | return (gen_id % 2) == 1 55 | 56 | 57 | def main(): 58 | 59 | client_binary = "testclient" 60 | server_binary = "testserver" 61 | 62 | snapshots = [] 63 | generation_inputs = [] 64 | 65 | # The initial server is right after the initial recv 66 | fitm_server = server_binary.run_to_recv().snapshot() 67 | snapshots[0] = [fitm_server] 68 | 69 | # The initial client is the snapshot right after the inital recv (after n sends) 70 | fitm_client = client_binary.run_to_recv().snapshot() 71 | snapshots[1] = [fitm_client] 72 | 73 | # The initial server input is the initial client output, of all sends before the recv 74 | initial_input = client_binary.run_to_recv().output() 75 | if not initial_input: 76 | raise Exception( 77 | "Uh oh, client misbehaved! No initial input for server generated!" 78 | ) 79 | generation_inputs[0] = [initial_input] 80 | 81 | current_gen = 0 82 | 83 | while True: 84 | 85 | if gen_is_client(current_gen): 86 | print(f"Fuzzing client (gen {current_gen})") 87 | else: 88 | print(f"Fuzzing server (gen {current_gen})") 89 | 90 | # server -> client or client -> server 91 | next_gen = current_gen + 1 92 | # client -> client or server -> server 93 | nextnext_gen = current_gen + 2 94 | 95 | # make sure the next stage inputs list exist 96 | if not hasattr(generation_inputs, next_gen): 97 | generation_inputs[next_gen] = [] 98 | # make sure the nextnext stage snapshot exists 99 | if not hasattr(snapshots, nextnext_gen): 100 | generation_inputs[nextnext_gen] = [] 101 | 102 | process_stage( 103 | snapshots[current_gen], 104 | generation_inputs[current_gen], 105 | generation_inputs[next_gen], 106 | snapshots[nextnext_gen], 107 | ) 108 | 109 | # Continue with the next gen (server->client or vice versa) 110 | current_gen += 1 111 | 112 | # ... Unless we did not create any inputs for the next gen (our sends were all empty or we crashed... 113 | # In that case: restart fuzzin from gen 0. :) 114 | if len(generation_inputs[current_gen]) == 0: 115 | current_gen = 0 116 | -------------------------------------------------------------------------------- /fitm-qemu/libqasan/libqasan.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2019-2020, Andrea Fioraldi 3 | 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | *******************************************************************************/ 25 | 26 | #ifndef __LIBQASAN_H__ 27 | #define __LIBQASAN_H__ 28 | 29 | #define _GNU_SOURCE 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include "qasan.h" 43 | 44 | #define QASAN_LOG(msg...) \ 45 | do { \ 46 | \ 47 | if (__qasan_log) { \ 48 | \ 49 | fprintf(stderr, "==%d== ", getpid()); \ 50 | fprintf(stderr, msg); \ 51 | \ 52 | } \ 53 | \ 54 | } while (0) 55 | 56 | #ifdef DEBUG 57 | #define QASAN_DEBUG(msg...) \ 58 | do { \ 59 | \ 60 | if (__qasan_debug) { \ 61 | \ 62 | fprintf(stderr, "==%d== ", getpid()); \ 63 | fprintf(stderr, msg); \ 64 | \ 65 | } \ 66 | \ 67 | } while (0) 68 | 69 | #else 70 | #define QASAN_DEBUG(msg...) \ 71 | do { \ 72 | \ 73 | } while (0) 74 | #endif 75 | 76 | #define ASSERT_DLSYM(name) \ 77 | ({ \ 78 | \ 79 | void *a = (void *)dlsym(RTLD_NEXT, #name); \ 80 | if (!a) { \ 81 | \ 82 | fprintf(stderr, \ 83 | "FATAL ERROR: failed dlsym of " #name " in libqasan!\n"); \ 84 | abort(); \ 85 | \ 86 | } \ 87 | a; \ 88 | \ 89 | }) 90 | 91 | extern int __qasan_debug; 92 | extern int __qasan_log; 93 | 94 | void __libqasan_init_hooks(void); 95 | void __libqasan_init_malloc(void); 96 | 97 | void __libqasan_hotpatch(void); 98 | 99 | size_t __libqasan_malloc_usable_size(void *ptr); 100 | void * __libqasan_malloc(size_t size); 101 | void __libqasan_free(void *ptr); 102 | void * __libqasan_calloc(size_t nmemb, size_t size); 103 | void * __libqasan_realloc(void *ptr, size_t size); 104 | int __libqasan_posix_memalign(void **ptr, size_t align, size_t len); 105 | void * __libqasan_memalign(size_t align, size_t len); 106 | void * __libqasan_aligned_alloc(size_t align, size_t len); 107 | 108 | void * __libqasan_memcpy(void *dest, const void *src, size_t n); 109 | void * __libqasan_memmove(void *dest, const void *src, size_t n); 110 | void * __libqasan_memset(void *s, int c, size_t n); 111 | void * __libqasan_memchr(const void *s, int c, size_t n); 112 | void * __libqasan_memrchr(const void *s, int c, size_t n); 113 | size_t __libqasan_strlen(const char *s); 114 | size_t __libqasan_strnlen(const char *s, size_t len); 115 | int __libqasan_strcmp(const char *str1, const char *str2); 116 | int __libqasan_strncmp(const char *str1, const char *str2, size_t len); 117 | int __libqasan_strcasecmp(const char *str1, const char *str2); 118 | int __libqasan_strncasecmp(const char *str1, const char *str2, size_t len); 119 | int __libqasan_memcmp(const void *mem1, const void *mem2, size_t len); 120 | int __libqasan_bcmp(const void *mem1, const void *mem2, size_t len); 121 | char * __libqasan_strstr(const char *haystack, const char *needle); 122 | char * __libqasan_strcasestr(const char *haystack, const char *needle); 123 | void * __libqasan_memmem(const void *haystack, size_t haystack_len, 124 | const void *needle, size_t needle_len); 125 | char * __libqasan_strchr(const char *s, int c); 126 | char * __libqasan_strrchr(const char *s, int c); 127 | size_t __libqasan_wcslen(const wchar_t *s); 128 | wchar_t *__libqasan_wcscpy(wchar_t *d, const wchar_t *s); 129 | int __libqasan_wcscmp(const wchar_t *s1, const wchar_t *s2); 130 | 131 | #endif 132 | 133 | -------------------------------------------------------------------------------- /fitm-qemu/libqasan/patch.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2019-2020, Andrea Fioraldi 3 | 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | *******************************************************************************/ 25 | 26 | #include "libqasan.h" 27 | #include 28 | 29 | #ifdef __x86_64__ 30 | 31 | uint8_t *__libqasan_patch_jump(uint8_t *addr, uint8_t *dest) { 32 | 33 | // mov rax, dest 34 | addr[0] = 0x48; 35 | addr[1] = 0xb8; 36 | *(uint8_t **)&addr[2] = dest; 37 | 38 | // jmp rax 39 | addr[10] = 0xff; 40 | addr[11] = 0xe0; 41 | 42 | return &addr[12]; 43 | 44 | } 45 | 46 | #elif __i386__ 47 | 48 | uint8_t *__libqasan_patch_jump(uint8_t *addr, uint8_t *dest) { 49 | 50 | // mov eax, dest 51 | addr[0] = 0xb8; 52 | *(uint8_t **)&addr[1] = dest; 53 | 54 | // jmp eax 55 | addr[5] = 0xff; 56 | addr[6] = 0xe0; 57 | 58 | return &addr[7]; 59 | 60 | } 61 | 62 | #elif __arm__ 63 | 64 | // in ARM, r12 is a scratch register used by the linker to jump, 65 | // so let's use it in our stub 66 | 67 | uint8_t *__libqasan_patch_jump(uint8_t *addr, uint8_t *dest) { 68 | 69 | // ldr r12, OFF 70 | addr[0] = 0x0; 71 | addr[1] = 0xc0; 72 | addr[2] = 0x9f; 73 | addr[3] = 0xe5; 74 | 75 | // add pc, pc, r12 76 | addr[4] = 0xc; 77 | addr[5] = 0xf0; 78 | addr[6] = 0x8f; 79 | addr[7] = 0xe0; 80 | 81 | // OFF: .word dest 82 | *(uint32_t *)&addr[8] = (uint32_t)dest; 83 | 84 | return &addr[12]; 85 | 86 | } 87 | 88 | #elif __aarch64__ 89 | 90 | // in ARM64, x16 is a scratch register used by the linker to jump, 91 | // so let's use it in our stub 92 | 93 | uint8_t *__libqasan_patch_jump(uint8_t *addr, uint8_t *dest) { 94 | 95 | // ldr x16, OFF 96 | addr[0] = 0x50; 97 | addr[1] = 0x0; 98 | addr[2] = 0x0; 99 | addr[3] = 0x58; 100 | 101 | // br x16 102 | addr[4] = 0x0; 103 | addr[5] = 0x2; 104 | addr[6] = 0x1f; 105 | addr[7] = 0xd6; 106 | 107 | // OFF: .dword dest 108 | *(uint64_t *)&addr[8] = (uint64_t)dest; 109 | 110 | return &addr[16]; 111 | 112 | } 113 | 114 | #else 115 | 116 | #define CANNOT_HOTPATCH 117 | 118 | #endif 119 | 120 | #ifdef CANNOT_HOTPATCH 121 | 122 | void __libqasan_hotpatch(void) { 123 | 124 | } 125 | 126 | #else 127 | 128 | static void *libc_start, *libc_end; 129 | int libc_perms; 130 | 131 | static void find_libc(void) { 132 | 133 | FILE * fp; 134 | char * line = NULL; 135 | size_t len = 0; 136 | ssize_t read; 137 | 138 | fp = fopen("/proc/self/maps", "r"); 139 | if (fp == NULL) return; 140 | 141 | while ((read = getline(&line, &len, fp)) != -1) { 142 | 143 | int fields, dev_maj, dev_min, inode; 144 | uint64_t min, max, offset; 145 | char flag_r, flag_w, flag_x, flag_p; 146 | char path[512] = ""; 147 | fields = sscanf(line, 148 | "%" PRIx64 "-%" PRIx64 " %c%c%c%c %" PRIx64 149 | " %x:%x %d" 150 | " %512s", 151 | &min, &max, &flag_r, &flag_w, &flag_x, &flag_p, &offset, 152 | &dev_maj, &dev_min, &inode, path); 153 | 154 | if ((fields < 10) || (fields > 11)) continue; 155 | 156 | if (flag_x == 'x' && (__libqasan_strstr(path, "/libc.so") || 157 | __libqasan_strstr(path, "/libc-"))) { 158 | 159 | libc_start = (void *)min; 160 | libc_end = (void *)max; 161 | 162 | libc_perms = PROT_EXEC; 163 | if (flag_w == 'w') libc_perms |= PROT_WRITE; 164 | if (flag_r == 'r') libc_perms |= PROT_READ; 165 | 166 | break; 167 | 168 | } 169 | 170 | } 171 | 172 | free(line); 173 | fclose(fp); 174 | 175 | } 176 | 177 | /* Why this shit? https://twitter.com/andreafioraldi/status/1227635146452541441 178 | Unfortunatly, symbol override with LD_PRELOAD is not enough to prevent libc 179 | code to call this optimized XMM-based routines. 180 | We patch them at runtime to call our unoptimized version of the same routine. 181 | */ 182 | 183 | void __libqasan_hotpatch(void) { 184 | 185 | find_libc(); 186 | 187 | if (!libc_start) return; 188 | 189 | if (mprotect(libc_start, libc_end - libc_start, 190 | PROT_READ | PROT_WRITE | PROT_EXEC) < 0) 191 | return; 192 | 193 | void *libc = dlopen("libc.so.6", RTLD_LAZY); 194 | 195 | #define HOTPATCH(fn) \ 196 | uint8_t *p_##fn = (uint8_t *)dlsym(libc, #fn); \ 197 | if (p_##fn) __libqasan_patch_jump(p_##fn, (uint8_t *)&(fn)); 198 | 199 | HOTPATCH(memcmp) 200 | HOTPATCH(memmove) 201 | 202 | uint8_t *p_memcpy = (uint8_t *)dlsym(libc, "memcpy"); 203 | // fuck you libc 204 | if (p_memcpy && p_memmove != p_memcpy) 205 | __libqasan_patch_jump(p_memcpy, (uint8_t *)&memcpy); 206 | 207 | HOTPATCH(memchr) 208 | HOTPATCH(memrchr) 209 | HOTPATCH(memmem) 210 | #ifndef __BIONIC__ 211 | HOTPATCH(bzero) 212 | HOTPATCH(explicit_bzero) 213 | HOTPATCH(mempcpy) 214 | HOTPATCH(bcmp) 215 | #endif 216 | 217 | HOTPATCH(strchr) 218 | HOTPATCH(strrchr) 219 | HOTPATCH(strcasecmp) 220 | HOTPATCH(strncasecmp) 221 | HOTPATCH(strcat) 222 | HOTPATCH(strcmp) 223 | HOTPATCH(strncmp) 224 | HOTPATCH(strcpy) 225 | HOTPATCH(strncpy) 226 | HOTPATCH(stpcpy) 227 | HOTPATCH(strdup) 228 | HOTPATCH(strlen) 229 | HOTPATCH(strnlen) 230 | HOTPATCH(strstr) 231 | HOTPATCH(strcasestr) 232 | HOTPATCH(wcslen) 233 | HOTPATCH(wcscpy) 234 | HOTPATCH(wcscmp) 235 | 236 | #undef HOTPATCH 237 | 238 | mprotect(libc_start, libc_end - libc_start, libc_perms); 239 | 240 | } 241 | 242 | #endif 243 | 244 | -------------------------------------------------------------------------------- /fitm-qemu/README.persistent.md: -------------------------------------------------------------------------------- 1 | # How to use the persistent mode in AFL++'s QEMU mode 2 | 3 | ## 1) Introduction 4 | 5 | Persistent mode lets you fuzz your target persistently between two 6 | addresses - without forking for every fuzzing attempt. 7 | This increases the speed by a factor between x2 and x5, hence it is 8 | very, very valuable. 9 | 10 | The persistent mode is currently only available for x86/x86_64, arm 11 | and aarch64 targets. 12 | 13 | ## 2) How use the persistent mode 14 | 15 | ### 2.1) The START address 16 | 17 | The start of the persistent loop has to be set with env var AFL_QEMU_PERSISTENT_ADDR. 18 | 19 | This address can be the address of whatever instruction. 20 | Setting this address to the start of a function makes the usage simple. 21 | If the address is however within a function, either RET, OFFSET or EXITS 22 | (see below in 2.2, 2.3, 2.6) have to be set. 23 | This address (as well as the RET address, see below) has to be defined in 24 | hexadecimal with the 0x prefix or as a decimal value. 25 | 26 | If both RET and EXITS are not set, QEMU will assume that START points to a 27 | function and will patch the return address (on stack or in the link register) 28 | to return to START (like WinAFL). 29 | 30 | *Note:* If the target is compiled with position independant code (PIE/PIC) 31 | qemu loads these to a specific base address. 32 | For 64 bit you have to add 0x4000000000 (9 zeroes) and for 32 bit 0x40000000 33 | (7 zeroes) to the address. 34 | On strange setups the base address set by QEMU for PIE executable may change, 35 | you can check it printing the process map using 36 | `AFL_QEMU_DEBUG_MAPS=1 afl-qemu-trace TARGET-BINARY` 37 | 38 | If this address is not valid, afl-fuzz will error during startup with the 39 | message that the forkserver was not found. 40 | 41 | ### 2.2) The RET address 42 | 43 | The RET address is the last instruction of the persistent loop. 44 | The emulator will emit a jump to START when translating the instruction at RET. 45 | It is optional, and only needed if the return should not be 46 | at the end of the function to which the START address points into, but earlier. 47 | 48 | It is defined by setting AFL_QEMU_PERSISTENT_RET, and too 0x4000000000 has to 49 | be set if the target is position independant. 50 | 51 | ### 2.3) The OFFSET 52 | 53 | This option is valid only for x86/x86_64 only, arm/aarch64 do not save the 54 | return address on stack. 55 | 56 | If the START address is *not* the beginning of a function, and *no* RET has 57 | been set (so the end of the loop will be at the end of the function but START 58 | will not be at the beginning of it), we need an offset from the ESP pointer 59 | to locate the return address to patch. 60 | 61 | The value by which the ESP pointer has to be corrected has to be set in the 62 | variable AFL_QEMU_PERSISTENT_RETADDR_OFFSET. 63 | 64 | Now to get this value right here is some help: 65 | 1. use gdb on the target 66 | 2. set a breakpoint to "main" (this is required for PIE/PIC binaries so the 67 | addresses are set up) 68 | 3. "run" the target with a valid commandline 69 | 4. set a breakpoint to the function in which START is contained 70 | 5. set a breakpoint to your START address 71 | 6. "continue" to the function start breakpoint 72 | 6. print the ESP value with `print $esp` and take note of it 73 | 7. "continue" the target until the second breakpoint 74 | 8. again print the ESP value 75 | 9. calculate the difference between the two values - and this is the offset 76 | 77 | ### 2.4) Resetting the register state 78 | 79 | It is very, very likely you need to restore the general purpose registers state 80 | when starting a new loop. Because of this 99% of the time you should set 81 | 82 | AFL_QEMU_PERSISTENT_GPR=1 83 | 84 | An example is when you want to use main() as persistent START: 85 | 86 | ```c 87 | int main(int argc, char **argv) { 88 | 89 | if (argc < 2) return 1; 90 | 91 | // do stuff 92 | 93 | } 94 | ``` 95 | 96 | If you don't save and restore the registers in x86_64, the parameter `argc` 97 | will be lost at the second execution of the loop. 98 | 99 | ### 2.5) Resetting the memory state 100 | 101 | This option restores the memory state using the AFL++ Snapshot LKM if loaded. 102 | Otherwise, all the writeable pages are restored. 103 | 104 | To enable this option, set AFL_QEMU_PERSISTENT_MEM=1. 105 | 106 | ### 2.6) Reset on exit() 107 | 108 | The user can force QEMU to set the program counter to START instead of executing 109 | the exit_group syscall and exit the program. 110 | 111 | The env variable is AFL_QEMU_PERSISTENT_EXITS. 112 | 113 | ### 2.7) Snapshot 114 | 115 | AFL_QEMU_SNAPSHOT=address is just a "syntactical sugar" env variable that is equivalent to 116 | the following set of variables: 117 | 118 | ``` 119 | AFL_QEMU_PERSISTENT_ADDR=address 120 | AFL_QEMU_PERSISTENT_GPR=1 121 | AFL_QEMU_PERSISTENT_MEM=1 122 | AFL_QEMU_PERSISTENT_EXITS=1 123 | ``` 124 | 125 | ## 3) Optional parameters 126 | 127 | ### 3.1) Loop counter value 128 | 129 | The more stable your loop in the target, the longer you can run it, the more 130 | unstable it is the lower the loop count should be. A low value would be 100, 131 | the maximum value should be 10000. The default is 1000. 132 | This value can be set with AFL_QEMU_PERSISTENT_CNT 133 | 134 | This is the same concept as in the llvm_mode persistent mode with __AFL_LOOP(). 135 | 136 | ### 3.2) A hook for in-memory fuzzing 137 | 138 | You can increase the speed of the persistent mode even more by bypassing all 139 | the reading of the fuzzing input via a file by reading directly into the 140 | memory address space of the target process. 141 | 142 | All this needs is that the START address has a register that can reach the 143 | memory buffer or that the memory buffer is at a known location. You probably need 144 | the value of the size of the buffer (maybe it is in a register when START is 145 | hit). 146 | 147 | The persistent hook will execute a function on every persistent iteration 148 | (at the start START) defined in a shared object specified with 149 | AFL_QEMU_PERSISTENT_HOOK=/path/to/hook.so. 150 | 151 | The signature is: 152 | 153 | ```c 154 | void afl_persistent_hook(struct ARCH_regs *regs, 155 | uint64_t guest_base, 156 | uint8_t *input_buf, 157 | uint32_t input_buf_len); 158 | ``` 159 | 160 | Where ARCH is one of x86, x86_64, arm or arm64. 161 | You have to include `path/to/qemuafl/qemuafl/api.h`. 162 | 163 | In this hook, you can inspect and change the saved GPR state at START. 164 | 165 | You can also initialize your data structures when QEMU loads the shared object 166 | with: 167 | 168 | `int afl_persistent_hook_init(void);` 169 | 170 | If this routine returns true, the shared mem fuzzing feature of AFL++ is used 171 | and so the input_buf variables of the hook becomes meaningful. Otherwise, 172 | you have to read the input from a file like stdin. 173 | 174 | An example that you can use with little modification for your target can 175 | be found here: [utils/qemu_persistent_hook](../utils/qemu_persistent_hook) 176 | -------------------------------------------------------------------------------- /debug/namespacing/src/main.rs: -------------------------------------------------------------------------------- 1 | use libc::{self}; 2 | use std::{ 3 | env, 4 | ffi::CString, 5 | fs::{File, OpenOptions}, 6 | io::{Error, Read, Seek, SeekFrom, Write}, 7 | process::{id, Command, Stdio}, 8 | time::Duration, 9 | }; 10 | 11 | use std::os::unix::fs::OpenOptionsExt; 12 | use std::os::unix::io::AsRawFd; 13 | 14 | fn system(command: &str) -> i32 { 15 | let command = CString::new(command).unwrap(); 16 | unsafe { libc::system(command.as_ptr()) } 17 | } 18 | 19 | fn main() { 20 | println!("PID: {}", id()); 21 | println!("SID: {}", unsafe { libc::getsid(id() as _) }); 22 | println!("UID: {}", unsafe { libc::getuid() }); 23 | 24 | if let Some(val) = env::args().nth(1) { 25 | if val == "spawn" { 26 | let unshare_result = 27 | unsafe { libc::unshare(libc::CLONE_NEWPID | libc::CLONE_NEWNS | libc::CLONE_FS) }; 28 | println!("UNSHARE: {}", unshare_result); 29 | 30 | let foo = Command::new("./target/debug/Namespace-testing") 31 | .stdin(Stdio::null()) 32 | //.stdout(Stdio::from(File::create("stdout").unwrap())) 33 | //.stderr(Stdio::from(File::create("stderr").unwrap())) 34 | .stdout(Stdio::piped()) 35 | .stderr(Stdio::piped()) 36 | .spawn() 37 | .expect("failed to spawn"); 38 | 39 | println!("CHILD PID: {}", foo.id()); 40 | let out = foo.wait_with_output().unwrap(); 41 | println!("====== STDOUT =======\n{}", String::from_utf8(out.stdout).unwrap()); 42 | println!("====== STDERR =======\n{}", String::from_utf8(out.stderr).unwrap()); 43 | } 44 | } 45 | 46 | if id() == 1 { 47 | // remount proc 48 | unsafe { 49 | // WIP assume mounts are successful 50 | // libc::mount(b"none\0".as_ptr() as _, b"/\0".as_ptr() as _, 0 as _, libc::MS_REC | libc::MS_PRIVATE, 0 as _); 51 | libc::mount( 52 | b"none\0".as_ptr() as _, 53 | b"/proc\0".as_ptr() as _, 54 | 0 as _, 55 | libc::MS_REC | libc::MS_PRIVATE, 56 | 0 as _, 57 | ); 58 | libc::mount( 59 | b"proc\0".as_ptr() as _, 60 | b"/proc\0".as_ptr() as _, 61 | b"proc\0".as_ptr() as _, 62 | libc::MS_NOSUID | libc::MS_NODEV | libc::MS_NOEXEC, 63 | 0 as _, 64 | ); 65 | 66 | // Were the init-process -- HEREBY I PROCLAIM THAT I'M A SESSION LEADER 67 | libc::setsid(); 68 | } 69 | 70 | println!("NEW SID: {}", unsafe { libc::getsid(id() as _) }); 71 | system("ps -aux"); 72 | let criu_srv = Command::new("../../criu/criu/criu") 73 | .arg("service") 74 | .arg("-v4") 75 | .arg("--address") 76 | .arg("/tmp/criu_service.socket") 77 | .spawn() 78 | .unwrap(); 79 | 80 | let mut file = OpenOptions::new() 81 | .read(true) 82 | .write(true) 83 | .create(true) 84 | .mode(0o644) 85 | .open("/proc/sys/kernel/ns_last_pid") 86 | .expect("Failed to open ns_last_pid"); 87 | 88 | println!("locking"); 89 | unsafe { 90 | if libc::flock(file.as_raw_fd() as _, libc::LOCK_EX) != 0 { 91 | panic!("LOCKING FAILED") 92 | } 93 | } 94 | 95 | let mut contents = String::new(); 96 | file.read_to_string(&mut contents).unwrap(); 97 | println!("last pid: [{}]", contents.trim()); 98 | 99 | file.seek(SeekFrom::Start(0)).unwrap(); 100 | unsafe { libc::ftruncate(file.as_raw_fd(), 0) }; 101 | file.write(1336.to_string().as_bytes()).unwrap(); 102 | 103 | println!("FORKING"); 104 | 105 | let new_pid = unsafe { libc::fork() }; 106 | if new_pid == 0 { 107 | drop(file); 108 | // I bim jetzt mein eigener session-leader 109 | eprintln!("setsid result: {}", unsafe { libc::setsid() }); 110 | 111 | println!("CHILD SPEAKING I have pid {}", id()); 112 | //std::thread::sleep(Duration::from_secs(1)); 113 | for _ in 0..3 { 114 | std::thread::sleep(Duration::from_secs(1)); 115 | } 116 | eprintln!("Returned from sleep"); 117 | 118 | let mut file = File::create("out").unwrap(); 119 | file.write(b"testtest123"); 120 | return; 121 | } else { 122 | println!("PARENT REPORTING TARGET PID: 1337 resulting: {}", new_pid) 123 | } 124 | 125 | std::thread::sleep(Duration::from_millis(100)); 126 | 127 | system("mkdir dump"); 128 | let command = format!( 129 | "criu dump -t {} --images-dir dump/ --leave-running", 130 | new_pid 131 | ); 132 | system(&command); 133 | 134 | 135 | // [DBG] CHECK CHILD-FDs 136 | let command = format!( 137 | "ls /proc/{}/fd", 138 | new_pid 139 | ); 140 | system(&command); 141 | 142 | 143 | unsafe { 144 | if libc::flock(file.as_raw_fd(), libc::LOCK_UN) != 0 { 145 | panic!("UNLOCKING FAILED") 146 | } 147 | } 148 | drop(file); 149 | 150 | // env::set_current_dir("active-state").unwrap(); 151 | // let (stdout, stderr) = ( 152 | // File::create("stdout-afl").unwrap(), 153 | // File::create("stderr-afl").unwrap(), 154 | // ); 155 | 156 | // system("../afl-init.sh"); 157 | // let mut child = Command::new("./restore.sh").spawn().unwrap(); 158 | // let mut afl = Command::new("../../../AFLplusplus/afl-fuzz") 159 | // .args(&[ 160 | // format!("-i"), 161 | // format!("./in"), 162 | // format!("-o"), 163 | // format!("./out"), 164 | // // No mem limit 165 | // format!("-m"), 166 | // format!("none"), 167 | // // Fuzzing as main node 168 | // format!("-M"), 169 | // format!("main"), 170 | // format!("-d"), 171 | // // At what time to stop this afl run 172 | // format!("-V"), 173 | // format!("{}", 30), 174 | // // Timeout per individual execution 175 | // format!("-t"), 176 | // format!("{}", 1000), 177 | // format!("--"), 178 | // format!("bash"), 179 | // // Our restore script 180 | // format!("./restore.sh"), 181 | // // The fuzzer input file 182 | // format!("@@"), 183 | // ]) 184 | // .stdout(Stdio::from(stdout)) 185 | // .stderr(Stdio::from(stderr)) 186 | // // In case we already started the fuzz run earlier, resume it here. 187 | // .env("AFL_AUTORESUME", "1") 188 | // .env("CRIU_SNAPSHOT_DIR", "./snapshot") 189 | // // We launch sh first, which is (hopefully) not instrumented 190 | // .env("AFL_SKIP_BIN_CHECK", "1") 191 | // .env("AFL_NO_UI", "1") 192 | // // Give criu forkserver up to a minute to spawn 193 | // .env("AFL_FORKSRV_INIT_TMOUT", "60000") 194 | // .spawn().expect("Failed to spawn child"); 195 | 196 | for _i in 0..1 { 197 | println!(""); 198 | system("ps -aux"); 199 | } 200 | 201 | std::thread::sleep(Duration::from_secs(10)); 202 | // child.wait().unwrap(); 203 | // let exit_status = afl.wait().expect("waitpid failed"); 204 | // println!("AFL-EXIT: {}", exit_status); 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /fitm-qemu/libqasan/string.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2019-2020, Andrea Fioraldi 3 | 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | *******************************************************************************/ 25 | 26 | #include "libqasan.h" 27 | #include 28 | 29 | void *__libqasan_memcpy(void *dest, const void *src, size_t n) { 30 | 31 | unsigned char * d = dest; 32 | const unsigned char *s = src; 33 | 34 | if (!n) return dest; 35 | 36 | while (n--) { 37 | 38 | *d = *s; 39 | ++d; 40 | ++s; 41 | 42 | } 43 | 44 | return dest; 45 | 46 | } 47 | 48 | void *__libqasan_memmove(void *dest, const void *src, size_t n) { 49 | 50 | unsigned char * d = dest; 51 | const unsigned char *s = src; 52 | 53 | if (!n) return dest; 54 | 55 | if (!((d + n) >= s && d <= (s + n))) // do not overlap 56 | return __libqasan_memcpy(dest, src, n); 57 | 58 | d = __libqasan_malloc(n); 59 | __libqasan_memcpy(d, src, n); 60 | __libqasan_memcpy(dest, d, n); 61 | 62 | __libqasan_free(d); 63 | 64 | return dest; 65 | 66 | } 67 | 68 | void *__libqasan_memset(void *s, int c, size_t n) { 69 | 70 | unsigned char *b = s; 71 | while (n--) 72 | *(b++) = (unsigned char)c; 73 | return s; 74 | 75 | } 76 | 77 | void *__libqasan_memchr(const void *s, int c, size_t n) { 78 | 79 | unsigned char *m = (unsigned char *)s; 80 | size_t i; 81 | for (i = 0; i < n; ++i) 82 | if (m[i] == (unsigned char)c) return &m[i]; 83 | return NULL; 84 | 85 | } 86 | 87 | void *__libqasan_memrchr(const void *s, int c, size_t n) { 88 | 89 | unsigned char *m = (unsigned char *)s; 90 | long i; 91 | for (i = n; i >= 0; --i) 92 | if (m[i] == (unsigned char)c) return &m[i]; 93 | return NULL; 94 | 95 | } 96 | 97 | size_t __libqasan_strlen(const char *s) { 98 | 99 | const char *i = s; 100 | while (*(i++)) 101 | ; 102 | return i - s - 1; 103 | 104 | } 105 | 106 | size_t __libqasan_strnlen(const char *s, size_t len) { 107 | 108 | size_t r = 0; 109 | while (len-- && *(s++)) 110 | ++r; 111 | return r; 112 | 113 | } 114 | 115 | int __libqasan_strcmp(const char *str1, const char *str2) { 116 | 117 | while (1) { 118 | 119 | const unsigned char c1 = *str1, c2 = *str2; 120 | 121 | if (c1 != c2) return c1 - c2; 122 | if (!c1) return 0; 123 | str1++; 124 | str2++; 125 | 126 | } 127 | 128 | return 0; 129 | 130 | } 131 | 132 | int __libqasan_strncmp(const char *str1, const char *str2, size_t len) { 133 | 134 | while (len--) { 135 | 136 | unsigned char c1 = *str1, c2 = *str2; 137 | 138 | if (c1 != c2) return c1 - c2; 139 | if (!c1) return 0; 140 | str1++; 141 | str2++; 142 | 143 | } 144 | 145 | return 0; 146 | 147 | } 148 | 149 | int __libqasan_strcasecmp(const char *str1, const char *str2) { 150 | 151 | while (1) { 152 | 153 | const unsigned char c1 = tolower(*str1), c2 = tolower(*str2); 154 | 155 | if (c1 != c2) return c1 - c2; 156 | if (!c1) return 0; 157 | str1++; 158 | str2++; 159 | 160 | } 161 | 162 | return 0; 163 | 164 | } 165 | 166 | int __libqasan_strncasecmp(const char *str1, const char *str2, size_t len) { 167 | 168 | while (len--) { 169 | 170 | const unsigned char c1 = tolower(*str1), c2 = tolower(*str2); 171 | 172 | if (c1 != c2) return c1 - c2; 173 | if (!c1) return 0; 174 | str1++; 175 | str2++; 176 | 177 | } 178 | 179 | return 0; 180 | 181 | } 182 | 183 | int __libqasan_memcmp(const void *mem1, const void *mem2, size_t len) { 184 | 185 | const char *strmem1 = (const char *)mem1; 186 | const char *strmem2 = (const char *)mem2; 187 | 188 | while (len--) { 189 | 190 | const unsigned char c1 = *strmem1, c2 = *strmem2; 191 | if (c1 != c2) return (c1 > c2) ? 1 : -1; 192 | strmem1++; 193 | strmem2++; 194 | 195 | } 196 | 197 | return 0; 198 | 199 | } 200 | 201 | int __libqasan_bcmp(const void *mem1, const void *mem2, size_t len) { 202 | 203 | const char *strmem1 = (const char *)mem1; 204 | const char *strmem2 = (const char *)mem2; 205 | 206 | while (len--) { 207 | 208 | int diff = *strmem1 ^ *strmem2; 209 | if (diff != 0) return 1; 210 | strmem1++; 211 | strmem2++; 212 | 213 | } 214 | 215 | return 0; 216 | 217 | } 218 | 219 | char *__libqasan_strstr(const char *haystack, const char *needle) { 220 | 221 | do { 222 | 223 | const char *n = needle; 224 | const char *h = haystack; 225 | 226 | while (*n && *h && *n == *h) 227 | n++, h++; 228 | 229 | if (!*n) return (char *)haystack; 230 | 231 | } while (*(haystack++)); 232 | 233 | return 0; 234 | 235 | } 236 | 237 | char *__libqasan_strcasestr(const char *haystack, const char *needle) { 238 | 239 | do { 240 | 241 | const char *n = needle; 242 | const char *h = haystack; 243 | 244 | while (*n && *h && tolower(*n) == tolower(*h)) 245 | n++, h++; 246 | 247 | if (!*n) return (char *)haystack; 248 | 249 | } while (*(haystack++)); 250 | 251 | return 0; 252 | 253 | } 254 | 255 | void *__libqasan_memmem(const void *haystack, size_t haystack_len, 256 | const void *needle, size_t needle_len) { 257 | 258 | const char *n = (const char *)needle; 259 | const char *h = (const char *)haystack; 260 | if (haystack_len < needle_len) return 0; 261 | if (needle_len == 0) return (void *)haystack; 262 | if (needle_len == 1) return memchr(haystack, *n, haystack_len); 263 | 264 | const char *end = h + (haystack_len - needle_len); 265 | 266 | do { 267 | 268 | if (*h == *n) { 269 | 270 | if (memcmp(h, n, needle_len) == 0) return (void *)h; 271 | 272 | } 273 | 274 | } while (++h <= end); 275 | 276 | return 0; 277 | 278 | } 279 | 280 | char *__libqasan_strchr(const char *s, int c) { 281 | 282 | while (*s != (char)c) 283 | if (!*s++) return 0; 284 | return (char *)s; 285 | 286 | } 287 | 288 | char *__libqasan_strrchr(const char *s, int c) { 289 | 290 | char *r = NULL; 291 | do 292 | if (*s == (char)c) r = (char *)s; 293 | while (*s++); 294 | 295 | return r; 296 | 297 | } 298 | 299 | size_t __libqasan_wcslen(const wchar_t *s) { 300 | 301 | size_t len = 0; 302 | 303 | while (s[len] != L'\0') { 304 | 305 | if (s[++len] == L'\0') return len; 306 | if (s[++len] == L'\0') return len; 307 | if (s[++len] == L'\0') return len; 308 | ++len; 309 | 310 | } 311 | 312 | return len; 313 | 314 | } 315 | 316 | wchar_t *__libqasan_wcscpy(wchar_t *d, const wchar_t *s) { 317 | 318 | wchar_t *a = d; 319 | while ((*d++ = *s++)) 320 | ; 321 | return a; 322 | 323 | } 324 | 325 | int __libqasan_wcscmp(const wchar_t *s1, const wchar_t *s2) { 326 | 327 | wchar_t c1, c2; 328 | do { 329 | 330 | c1 = *s1++; 331 | c2 = *s2++; 332 | if (c2 == L'\0') return c1 - c2; 333 | 334 | } while (c1 == c2); 335 | 336 | return c1 < c2 ? -1 : 1; 337 | 338 | } 339 | 340 | -------------------------------------------------------------------------------- /fitm-qemu/libcompcov/pmparser.h: -------------------------------------------------------------------------------- 1 | /* 2 | @Author : ouadimjamal@gmail.com 3 | @date : December 2015 4 | 5 | Permission to use, copy, modify, distribute, and sell this software and its 6 | documentation for any purpose is hereby granted without fee, provided that 7 | the above copyright notice appear in all copies and that both that 8 | copyright notice and this permission notice appear in supporting 9 | documentation. No representations are made about the suitability of this 10 | software for any purpose. It is provided "as is" without express or 11 | implied warranty. 12 | 13 | */ 14 | 15 | #ifndef H_PMPARSER 16 | #define H_PMPARSER 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | // maximum line length in a procmaps file 28 | #define PROCMAPS_LINE_MAX_LENGTH (PATH_MAX + 100) 29 | /** 30 | * procmaps_struct 31 | * @desc hold all the information about an area in the process's VM 32 | */ 33 | typedef struct procmaps_struct { 34 | 35 | void * addr_start; //< start address of the area 36 | void * addr_end; //< end address 37 | unsigned long length; //< size of the range 38 | 39 | char perm[5]; //< permissions rwxp 40 | short is_r; //< rewrote of perm with short flags 41 | short is_w; 42 | short is_x; 43 | short is_p; 44 | 45 | long offset; //< offset 46 | char dev[12]; //< dev major:minor 47 | int inode; //< inode of the file that backs the area 48 | 49 | char pathname[600]; //< the path of the file that backs the area 50 | // chained list 51 | struct procmaps_struct *next; //= 0) { 113 | 114 | sprintf(maps_path, "/proc/%d/maps", pid); 115 | 116 | } else { 117 | 118 | sprintf(maps_path, "/proc/self/maps"); 119 | 120 | } 121 | 122 | FILE *file = fopen(maps_path, "r"); 123 | if (!file) { 124 | 125 | fprintf(stderr, "pmparser : cannot open the memory maps, %s\n", 126 | strerror(errno)); 127 | return NULL; 128 | 129 | } 130 | 131 | procmaps_iterator *maps_it = malloc(sizeof(procmaps_iterator)); 132 | int ind = 0; 133 | char buf[PROCMAPS_LINE_MAX_LENGTH]; 134 | // int c; 135 | procmaps_struct *list_maps = NULL; 136 | procmaps_struct *tmp; 137 | procmaps_struct *current_node = list_maps; 138 | char addr1[20], addr2[20], perm[8], offset[20], dev[10], inode[30], 139 | pathname[PATH_MAX]; 140 | while (!feof(file)) { 141 | 142 | fgets(buf, PROCMAPS_LINE_MAX_LENGTH, file); 143 | // allocate a node 144 | tmp = (procmaps_struct *)malloc(sizeof(procmaps_struct)); 145 | // fill the node 146 | _pmparser_split_line(buf, addr1, addr2, perm, offset, dev, inode, pathname); 147 | // printf("#%s",buf); 148 | // printf("%s-%s %s %s %s 149 | // %s\t%s\n",addr1,addr2,perm,offset,dev,inode,pathname); addr_start & 150 | // addr_end unsigned long l_addr_start; 151 | sscanf(addr1, "%lx", (long unsigned *)&tmp->addr_start); 152 | sscanf(addr2, "%lx", (long unsigned *)&tmp->addr_end); 153 | // size 154 | tmp->length = (unsigned long)(tmp->addr_end - tmp->addr_start); 155 | // perm 156 | strcpy(tmp->perm, perm); 157 | tmp->is_r = (perm[0] == 'r'); 158 | tmp->is_w = (perm[1] == 'w'); 159 | tmp->is_x = (perm[2] == 'x'); 160 | tmp->is_p = (perm[3] == 'p'); 161 | 162 | // offset 163 | sscanf(offset, "%lx", &tmp->offset); 164 | // device 165 | strcpy(tmp->dev, dev); 166 | // inode 167 | tmp->inode = atoi(inode); 168 | // pathname 169 | strcpy(tmp->pathname, pathname); 170 | tmp->next = NULL; 171 | // attach the node 172 | if (ind == 0) { 173 | 174 | list_maps = tmp; 175 | list_maps->next = NULL; 176 | current_node = list_maps; 177 | 178 | } 179 | 180 | current_node->next = tmp; 181 | current_node = tmp; 182 | ind++; 183 | // printf("%s",buf); 184 | 185 | } 186 | 187 | // close file 188 | fclose(file); 189 | 190 | // g_last_head=list_maps; 191 | maps_it->head = list_maps; 192 | maps_it->current = list_maps; 193 | return maps_it; 194 | 195 | } 196 | 197 | procmaps_struct *pmparser_next(procmaps_iterator *p_procmaps_it) { 198 | 199 | if (p_procmaps_it->current == NULL) return NULL; 200 | procmaps_struct *p_current = p_procmaps_it->current; 201 | p_procmaps_it->current = p_procmaps_it->current->next; 202 | return p_current; 203 | /* 204 | if(g_current==NULL){ 205 | 206 | g_current=g_last_head; 207 | 208 | }else 209 | 210 | g_current=g_current->next; 211 | 212 | return g_current; 213 | */ 214 | 215 | } 216 | 217 | void pmparser_free(procmaps_iterator *p_procmaps_it) { 218 | 219 | procmaps_struct *maps_list = p_procmaps_it->head; 220 | if (maps_list == NULL) return; 221 | procmaps_struct *act = maps_list; 222 | procmaps_struct *nxt = act->next; 223 | while (act != NULL) { 224 | 225 | free(act); 226 | act = nxt; 227 | if (nxt != NULL) nxt = nxt->next; 228 | 229 | } 230 | 231 | } 232 | 233 | void _pmparser_split_line(char *buf, char *addr1, char *addr2, char *perm, 234 | char *offset, char *device, char *inode, 235 | char *pathname) { 236 | 237 | // 238 | int orig = 0; 239 | int i = 0; 240 | // addr1 241 | while (buf[i] != '-') { 242 | 243 | addr1[i - orig] = buf[i]; 244 | i++; 245 | 246 | } 247 | 248 | addr1[i] = '\0'; 249 | i++; 250 | // addr2 251 | orig = i; 252 | while (buf[i] != '\t' && buf[i] != ' ') { 253 | 254 | addr2[i - orig] = buf[i]; 255 | i++; 256 | 257 | } 258 | 259 | addr2[i - orig] = '\0'; 260 | 261 | // perm 262 | while (buf[i] == '\t' || buf[i] == ' ') 263 | i++; 264 | orig = i; 265 | while (buf[i] != '\t' && buf[i] != ' ') { 266 | 267 | perm[i - orig] = buf[i]; 268 | i++; 269 | 270 | } 271 | 272 | perm[i - orig] = '\0'; 273 | // offset 274 | while (buf[i] == '\t' || buf[i] == ' ') 275 | i++; 276 | orig = i; 277 | while (buf[i] != '\t' && buf[i] != ' ') { 278 | 279 | offset[i - orig] = buf[i]; 280 | i++; 281 | 282 | } 283 | 284 | offset[i - orig] = '\0'; 285 | // dev 286 | while (buf[i] == '\t' || buf[i] == ' ') 287 | i++; 288 | orig = i; 289 | while (buf[i] != '\t' && buf[i] != ' ') { 290 | 291 | device[i - orig] = buf[i]; 292 | i++; 293 | 294 | } 295 | 296 | device[i - orig] = '\0'; 297 | // inode 298 | while (buf[i] == '\t' || buf[i] == ' ') 299 | i++; 300 | orig = i; 301 | while (buf[i] != '\t' && buf[i] != ' ') { 302 | 303 | inode[i - orig] = buf[i]; 304 | i++; 305 | 306 | } 307 | 308 | inode[i - orig] = '\0'; 309 | // pathname 310 | pathname[0] = '\0'; 311 | while (buf[i] == '\t' || buf[i] == ' ') 312 | i++; 313 | orig = i; 314 | while (buf[i] != '\t' && buf[i] != ' ' && buf[i] != '\n') { 315 | 316 | pathname[i - orig] = buf[i]; 317 | i++; 318 | 319 | } 320 | 321 | pathname[i - orig] = '\0'; 322 | 323 | } 324 | 325 | #endif 326 | 327 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FitM, the Fuzzer in the Middle 2 | 3 |

4 | 5 |

6 | 7 | FitM, the Fuzzer-in-the-Middle, is a AFL++-based coverage-guided fuzzer for stateful, binary-only client-server applications. 8 | It can be used in situations where you would normally turn to grammar-based fuzzers or start patching your target. With FitM you can explore the communication between client and server by fuzzing them at the same time. 9 | It builds on top of [qemuafl](https://github.com/AFLplusplus/qemuafl) for emulation and [CRIU](https://criu.org/Main_Page) for userspace snapshots. No source code needed! 10 | 11 | ## How it works 12 | 13 | The FitM tool uses [FitM-qemu](https://github.com/fgsect/FitM-qemu) for instrumentation. 14 | FitM-qemu extends qemuafl with a network emulation layer on the syscall level. 15 | With it, we can fuzz two targets (binary A & B) at the same time, usually a server and a client and schedule different snapshots of these processes ("generations") we collect while exploring the protocol. 16 | Each generation represents a stage in the protocol's communication levels that the client-server pair speaks. 17 | The fuzzer starts in generation 0 with binary A. This binary should produce some output without needing any input. 18 | Using the initial output of binary A as seed, FitM will fuzz binary B for a while. 19 | Afterwards, FitM creates a set of new snapshots of B, generation 3, for later. 20 | Next, the snapshot of generation 0 (binary A) is restored, seeded with generation 1's output and fuzzed (generation 2). 21 | A snapshot is always created during a receive call that followed a send call until we fully explore the client-server interaction. 22 | The below figure depicts this cycle. 23 | 24 | 25 |

26 | Overview over the different stages of FitM, see paper 27 |

28 | 29 | See [our paper](fitm.pdf) for technical explanations, benchmarks, and further details. 30 | 31 | ## Getting Started 32 | 33 | This module uses submodules. Clone with `--recurse-submodules`. 34 | 35 | Alternatively, run the following after cloning: 36 | ``` 37 | git submodule init 38 | git submodule update 39 | ``` 40 | 41 | ## Building 42 | 43 | ``` 44 | vagrant up 45 | vagrant ssh 46 | cd /vagrant 47 | make 48 | ``` 49 | 50 | If you don't want to use the provided Vagrantfile you can read the provided `provision.sh` script to understand the necessary dependencies. In general you will need packages to build [QEMU](https://github.com/AFLplusplus/qemuafl/blob/master/README.rst) and [Criu](https://criu.org/Installation#Installing_build_dependencies). Additionally, you will need a stable [rust toolchain](https://www.rust-lang.org/tools/install). 51 | 52 | ## Running 53 | Run this: `FITM_ARGS=config/fitm-args.ftp.json make run` 54 | 55 | The fuzzer will create the folders `active-state`, `saved-states` and `cmin-tmp`. 56 | Whenever afl-cmin is used the inputs that should be fed into cmin are put into `cmin-tmp`. 57 | `active-state` holds the necessary folder/files for FitM's operation and the restored snapshot's files. 58 | The structure is as follows: 59 | 60 | - `fd`: files that are used by the process. You will find current output here. 61 | - `in`, `out`: afl's `in`/`out` folders. 62 | - `next_snapshot`: populated during `create_next_snapshot()` with the files produced by criu. Renamed to snapshot and eventually copied to `saved-states`. 63 | - `out_postrun`: the content of the `out` folder after fuzzing. 64 | - `outputs`: folder with "persisted" outputs. Generally, output is written to files in the `fd` folder, but since those files (and the folder) need to be returned to the state they were in before restoring in order to snapshot the next state we collect outputs in an extra step `create_outputs()` and store them in the outputs folder. 65 | - `snapshot`: serialized process data, i.e. the snapshot. The criu docs are helpful here. 66 | - `envfile`: env for target process. Read by `getenv_from_file()` (see `./fitm-qemu/FitM-qemu/qemuafl/fitm.h`) in QEMU syscall translation layer. 67 | - `pipes`: names of forkserver pipes. Needed to reconnect pipes in restored snapshot to pipes from forkserver. Done with the `--inherit-fd` argument in `./active-state/restore.sh`. 68 | - `prev_input` / `prev_input_path`: input and path to input file that was used to generate current snapshot. 69 | - `restore.log`: criu output of the snapshot restore process. 70 | - `run-info`: serialized FITMSnapshot object for the active state. Helps to know where you are. 71 | - `snapshot_map`: afl-map output for the snapshot with prev_input. 72 | - `stdout`/`stderr`: stdout/err of the target process. 73 | - `stdout-afl`/`stderr-afl`: stdout/err from the AFL process. 74 | 75 | Each folder in `saved-states` represents one snapshot plus FitM-related files. Some of the files, e.g. `pipes`, related to each snapshot are specific to the snapshots state and thus have to be managed for every state. 76 | In general, criu breaks very quickly if a process had a handle to a file while it ran, but the file changes (contents, path, size, metadata) in any way between snapshot and restore. 77 | 78 | Apart from the above three folder you will find the following temporary files in the repo's root folder after running FitM: 79 | 80 | - `criu_stdout`/`criu_stderr`: stdout/err of the criu server process. To create snapshots each target process communicates with a separate criu process, the criu server. 81 | - `fdinfo`, `file`: Criu has a tool called "crit" that can be used to parse the binary files that are part of a snapshot folder. We use crit to parse the open files in the target process and attach them accordingly. The parsing code can be found in `create_restore.py`. This script create a bash script `restore.sh` based on the `restore.sh.tmp` template for each state. The `restore.sh` script is the target given to AFL when starting another fuzz run. The script will call `criu restore` and by using the [--restore-detached](https://criu.org/Tree_after_restore#Detached) flag we make sure that the target process ends up as a child of AFL after criu has exited. 82 | 83 | ## Special Files 84 | ### fitm-args.json 85 | 86 | This file is used to configure FitM. The meaning of each key is as follows: 87 | 88 | - `client`: path to the binary that should be gen0. 89 | - `client_args`: command-line arguments for the client binary. 90 | - `client_envs`: environment variables that will be available to the client binary. 91 | - `client_files`: currently unused. 92 | - `server`: path to the binary that should be gen1. 93 | - `server_args`: command-line arguments for the server binary. 94 | - `server_envs`: environment variables that will be available to the server binary. 95 | - `server_files`: currently unused. 96 | - `run_time`: time spent fuzzing each generation in seconds. 97 | - `server_only`: boolean to indicate that we only want to fuzz the server. The client is only fuzzed for 100ms and it's output is disregarded. 98 | 99 | ### fitm-state.json 100 | 101 | A JSON file used to save state information from previous runs. This allows us to abort fuzzing at any point, introduce changes and then reuse the accumulated states in `./saved-states`. The file holds a serialized form of the `generation_snaps` variable. This variable holds a list of generations that need to be fuzzed, each generation being another list of `FITMSnapshot` objects (`[[gen0_snap0, gen0_snap1, .., gen0_snapN], [gen1_snap0, .. gen1_snapN], .., [genN_snap0, .., genN_snapN]]`). 102 | 103 | ## Debugging 104 | 105 | When using FitM with a new target you will probably investigate weird behaviour sooner or later. 106 | You will generally want to check `./active-state/restore.log` and see the log end with a message similar to this one: 107 | ``` 108 | (00.052019) Running pre-resume scripts 109 | (00.052043) Restore finished successfully. Tasks resumed. 110 | (00.052062) Writing stats 111 | (00.052705) Running post-resume scripts 112 | ``` 113 | At this point you know that the target was successfully restored by criu and any further fails come from the target misbehaving. 114 | You will want to check `./active-state/stdout` for the target's stdout, in case you are doing printf-debugging. 115 | When you set the `FITM_DEBUG` macro to 1 you will find a lot of debug prints there that might give you a first idea of where things are breaking. 116 | Next, you can add `QEMU_STRACE` with a value of 1 to the `client_envs`/`server_envs` in your fitm-args.json to get strace output from QEMU. 117 | You will see the strace output in `./active-state/stderr`. These are the syscalls that the emulated target sends to emulation layer. 118 | By diffing the syscall traces of the target with and without FITM enabled you should be able to learn where things break (see `FITM_DISABLED` in `./fitm-qemu/FitM-qemu/linux-user/syscall.c`). 119 | 120 | ## Paper / Citing / More Information 121 | 122 | The FitM paper 123 | 124 | For further details, see [our paper](fitm.pdf) at BAR 2022. 125 | 126 | To cite, use: 127 | ```bib 128 | @InProceedings{fitm, 129 | title = {FitM: Binary-Only Coverage-Guided Fuzzing for Stateful Network Protocols}, 130 | author = {Maier, Dominik and Bittner, Otto and Munier, Marc and Beier, Julian}, 131 | booktitle = {Workshop on Binary Analysis Research (BAR), 2022}, 132 | year = 2022, 133 | } 134 | ``` 135 | 136 | -------------------------------------------------------------------------------- /fitm-qemu/README.md: -------------------------------------------------------------------------------- 1 | # High-performance binary-only instrumentation for afl-fuzz 2 | 3 | (See ../README.md for the general instruction manual.) 4 | 5 | ## 1) Introduction 6 | 7 | The code in this directory allows you to build a standalone feature that 8 | leverages the QEMU "user emulation" mode and allows callers to obtain 9 | instrumentation output for black-box, closed-source binaries. This mechanism 10 | can be then used by afl-fuzz to stress-test targets that couldn't be built 11 | with afl-gcc. 12 | 13 | The usual performance cost is 2-5x, which is considerably better than 14 | seen so far in experiments with tools such as DynamoRIO and PIN. 15 | 16 | The idea and much of the initial implementation comes from Andrew Griffiths. 17 | The actual implementation on current QEMU (shipped as qemuafl) is from 18 | Andrea Fioraldi. Special thanks to abiondo that re-enabled TCG chaining. 19 | 20 | ## 2) How to use qemu_mode 21 | 22 | The feature is implemented with a patched QEMU. The simplest way 23 | to build it is to run ./build_qemu_support.sh. The script will download, 24 | configure, and compile the QEMU binary for you. 25 | 26 | QEMU is a big project, so this will take a while, and you may have to 27 | resolve a couple of dependencies (most notably, you will definitely need 28 | libtool and glib2-devel). 29 | 30 | Once the binaries are compiled, you can leverage the QEMU tool by calling 31 | afl-fuzz and all the related utilities with -Q in the command line. 32 | 33 | Note that QEMU requires a generous memory limit to run; somewhere around 34 | 200 MB is a good starting point, but considerably more may be needed for 35 | more complex programs. The default -m limit will be automatically bumped up 36 | to 200 MB when specifying -Q to afl-fuzz; be careful when overriding this. 37 | 38 | In principle, if you set CPU_TARGET before calling ./build_qemu_support.sh, 39 | you should get a build capable of running non-native binaries (say, you 40 | can try CPU_TARGET=arm). This is also necessary for running 32-bit binaries 41 | on a 64-bit system (CPU_TARGET=i386). If you're trying to run QEMU on a 42 | different architecture you can also set HOST to the cross-compiler prefix 43 | to use (for example HOST=arm-linux-gnueabi to use arm-linux-gnueabi-gcc). 44 | 45 | You can also compile statically-linked binaries by setting STATIC=1. This 46 | can be useful when compiling QEMU on a different system than the one you're 47 | planning to run the fuzzer on and is most often used with the HOST variable. 48 | 49 | Note: when targetting the i386 architecture, on some binaries the forkserver 50 | handshake may fail due to the lack of reserved memory. Fix it with 51 | 52 | export QEMU_RESERVED_VA=0x1000000 53 | 54 | Note: if you want the QEMU helper to be installed on your system for all 55 | users, you need to build it before issuing 'make install' in the parent 56 | directory. 57 | 58 | If you want to specify a different path for libraries (e.g. to run an arm64 59 | binary on x86_64) use QEMU_LD_PREFIX. 60 | 61 | ## 3) Deferred initialization 62 | 63 | As for LLVM mode (refer to its README.md for mode details) QEMU mode supports 64 | the deferred initialization. 65 | 66 | This can be enabled setting the environment variable AFL_ENTRYPOINT which allows 67 | to move the forkserver to a different part, e.g. just before the file is 68 | opened (e.g. way after command line parsing and config file loading, etc.) 69 | which can be a huge speed improvement. 70 | 71 | ## 4) Persistent mode 72 | 73 | AFL++'s QEMU mode now supports also persistent mode for x86, x86_64, arm 74 | and aarch64 targets. 75 | This increases the speed by several factors, however it is a bit of work to set 76 | up - but worth the effort. 77 | 78 | Please see the extra documentation for it: [README.persistent.md](README.persistent.md) 79 | 80 | ## 5) Snapshot mode 81 | 82 | As an extension to persistent mode, qemuafl can snapshot and restore the memory 83 | state and brk(). Details are in the persistent mode readme. 84 | 85 | The env var that enables the ready to use snapshot mode is AFL_QEMU_SNAPSHOT and 86 | takes a hex address as a value that is the snapshot entrypoint. 87 | 88 | Snapshot mode can work restoring all the writeable pages, that is typically slower than 89 | fork() mode but, on the other hand, it can scale better with multicore. 90 | If the AFL++ Snapshot kernel module is loaded, qemuafl will use it and, in this 91 | case, the speed is better than fork() and also the scaling capabilities. 92 | 93 | ## 6) Partial instrumentation 94 | 95 | You can tell QEMU to instrument only a part of the address space. 96 | 97 | Just set AFL_QEMU_INST_RANGES=A,B,C... 98 | 99 | The format of the items in the list is either a range of addresses like 0x123-0x321 100 | or a module name like module.so (that is matched in the mapped object filename). 101 | 102 | Alternatively you can tell QEMU to ignore part of an address space for instrumentation. 103 | 104 | Just set AFL_QEMU_EXCLUDE_RANGES=A,B,C... 105 | 106 | The format of the items on the list is the same as for AFL_QEMU_INST_RANGES, and excluding ranges 107 | takes priority over any included ranges or AFL_INST_LIBS. 108 | 109 | ## 7) CompareCoverage 110 | 111 | CompareCoverage is a sub-instrumentation with effects similar to laf-intel. 112 | 113 | The environment variable that enables QEMU CompareCoverage is AFL_COMPCOV_LEVEL. 114 | There is also ./libcompcov/ which implements CompareCoverage for *cmp functions 115 | (splitting memcmp, strncmp, etc. to make these conditions easier solvable by 116 | afl-fuzz). 117 | 118 | AFL_COMPCOV_LEVEL=1 is to instrument comparisons with only immediate 119 | values / read-only memory. AFL_COMPCOV_LEVEL=2 instruments all 120 | comparison instructions and memory comparison functions when libcompcov 121 | is preloaded. 122 | AFL_COMPCOV_LEVEL=3 has the same effects of AFL_COMPCOV_LEVEL=2 but enables also 123 | the instrumentation of the floating-point comparisons on x86 and x86_64 (experimental). 124 | 125 | Integer comparison instructions are currently instrumented only 126 | on the x86, x86_64, arm and aarch64 targets. 127 | 128 | Highly recommended. 129 | 130 | ## 8) CMPLOG mode 131 | 132 | Another new feature is CMPLOG, which is based on the redqueen project. 133 | Here all immediates in CMP instructions are learned and put into a dynamic 134 | dictionary and applied to all locations in the input that reached that 135 | CMP, trying to solve and pass it. 136 | This is a very effective feature and it is available for x86, x86_64, arm 137 | and aarch64. 138 | 139 | To enable it you must pass on the command line of afl-fuzz: 140 | -c /path/to/your/target 141 | 142 | ## 9) Wine mode 143 | 144 | AFL++ QEMU can use Wine to fuzz WIn32 PE binaries. Use the -W flag of afl-fuzz. 145 | 146 | Note that some binaries require user interaction with the GUI and must be patched. 147 | 148 | For examples look [here](https://github.com/andreafioraldi/WineAFLplusplusDEMO). 149 | 150 | ## 10) Notes on linking 151 | 152 | The feature is supported only on Linux. Supporting BSD may amount to porting 153 | the changes made to linux-user/elfload.c and applying them to 154 | bsd-user/elfload.c, but I have not looked into this yet. 155 | 156 | The instrumentation follows only the .text section of the first ELF binary 157 | encountered in the linking process. It does not trace shared libraries. In 158 | practice, this means two things: 159 | 160 | - Any libraries you want to analyze *must* be linked statically into the 161 | executed ELF file (this will usually be the case for closed-source 162 | apps). 163 | 164 | - Standard C libraries and other stuff that is wasteful to instrument 165 | should be linked dynamically - otherwise, AFL will have no way to avoid 166 | peeking into them. 167 | 168 | Setting AFL_INST_LIBS=1 can be used to circumvent the .text detection logic 169 | and instrument every basic block encountered. 170 | 171 | ## 11) Benchmarking 172 | 173 | If you want to compare the performance of the QEMU instrumentation with that of 174 | afl-gcc compiled code against the same target, you need to build the 175 | non-instrumented binary with the same optimization flags that are normally 176 | injected by afl-gcc, and make sure that the bits to be tested are statically 177 | linked into the binary. A common way to do this would be: 178 | 179 | CFLAGS="-O3 -funroll-loops" ./configure --disable-shared 180 | make clean all 181 | 182 | Comparative measurements of execution speed or instrumentation coverage will be 183 | fairly meaningless if the optimization levels or instrumentation scopes don't 184 | match. 185 | 186 | ## 12) Other features 187 | 188 | With `AFL_QEMU_FORCE_DFL` you force QEMU to ignore the registered signal 189 | handlers of the target. 190 | 191 | ## 13) Gotchas, feedback, bugs 192 | 193 | If you need to fix up checksums or do other cleanup on mutated test cases, see 194 | utils/custom_mutators/ for a viable solution. 195 | 196 | Do not mix QEMU mode with ASAN, MSAN, or the likes; QEMU doesn't appreciate 197 | the "shadow VM" trick employed by the sanitizers and will probably just 198 | run out of memory. 199 | 200 | Compared to fully-fledged virtualization, the user emulation mode is *NOT* a 201 | security boundary. The binaries can freely interact with the host OS. If you 202 | somehow need to fuzz an untrusted binary, put everything in a sandbox first. 203 | 204 | QEMU does not necessarily support all CPU or hardware features that your 205 | target program may be utilizing. In particular, it does not appear to have 206 | full support for AVX2 / FMA3. Using binaries for older CPUs, or recompiling them 207 | with -march=core2, can help. 208 | 209 | Beyond that, this is an early-stage mechanism, so fields reports are welcome. 210 | You can send them to . 211 | 212 | ## 14) Alternatives: static rewriting 213 | 214 | Statically rewriting binaries just once, instead of attempting to translate 215 | them at run time, can be a faster alternative. That said, static rewriting is 216 | fraught with peril, because it depends on being able to properly and fully model 217 | program control flow without actually executing each and every code path. 218 | 219 | Checkout the "Fuzzing binary-only targets" section in our main README.md and 220 | the docs/binaryonly_fuzzing.md document for more information and hints. 221 | -------------------------------------------------------------------------------- /fitm-qemu/libqasan/malloc.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2019-2020, Andrea Fioraldi 3 | 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | *******************************************************************************/ 25 | 26 | #include "libqasan.h" 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #define REDZONE_SIZE 128 34 | // 50 mb quarantine 35 | #define QUARANTINE_MAX_BYTES 52428800 36 | 37 | #if __STDC_VERSION__ < 201112L || \ 38 | (defined(__FreeBSD__) && __FreeBSD_version < 1200000) 39 | // use this hack if not C11 40 | typedef struct { 41 | 42 | long long __ll; 43 | long double __ld; 44 | 45 | } max_align_t; 46 | 47 | #endif 48 | 49 | #define ALLOC_ALIGN_SIZE (_Alignof(max_align_t)) 50 | 51 | struct chunk_begin { 52 | 53 | size_t requested_size; 54 | void * aligned_orig; // NULL if not aligned 55 | struct chunk_begin *next; 56 | struct chunk_begin *prev; 57 | char redzone[REDZONE_SIZE]; 58 | 59 | }; 60 | 61 | struct chunk_struct { 62 | 63 | struct chunk_begin begin; 64 | char redzone[REDZONE_SIZE]; 65 | size_t prev_size_padding; 66 | 67 | }; 68 | 69 | #ifdef __GLIBC__ 70 | 71 | void *(*__lq_libc_malloc)(size_t); 72 | void (*__lq_libc_free)(void *); 73 | #define backend_malloc __lq_libc_malloc 74 | #define backend_free __lq_libc_free 75 | 76 | #define TMP_ZONE_SIZE 4096 77 | static int __tmp_alloc_zone_idx; 78 | static unsigned char __tmp_alloc_zone[TMP_ZONE_SIZE]; 79 | 80 | #else 81 | 82 | // From dlmalloc.c 83 | void * dlmalloc(size_t); 84 | void dlfree(void *); 85 | #define backend_malloc dlmalloc 86 | #define backend_free dlfree 87 | 88 | #endif 89 | 90 | int __libqasan_malloc_initialized; 91 | 92 | static struct chunk_begin *quarantine_top; 93 | static struct chunk_begin *quarantine_end; 94 | static size_t quarantine_bytes; 95 | 96 | #ifdef __BIONIC__ 97 | static pthread_mutex_t quarantine_lock; 98 | #define LOCK_TRY pthread_mutex_trylock 99 | #define LOCK_INIT pthread_mutex_init 100 | #define LOCK_UNLOCK pthread_mutex_unlock 101 | #else 102 | static pthread_spinlock_t quarantine_lock; 103 | #define LOCK_TRY pthread_spin_trylock 104 | #define LOCK_INIT pthread_spin_init 105 | #define LOCK_UNLOCK pthread_spin_unlock 106 | #endif 107 | 108 | // need qasan disabled 109 | static int quanratine_push(struct chunk_begin *ck) { 110 | 111 | if (ck->requested_size >= QUARANTINE_MAX_BYTES) return 0; 112 | 113 | if (LOCK_TRY(&quarantine_lock)) return 0; 114 | 115 | while (ck->requested_size + quarantine_bytes >= QUARANTINE_MAX_BYTES) { 116 | 117 | struct chunk_begin *tmp = quarantine_end; 118 | quarantine_end = tmp->prev; 119 | 120 | quarantine_bytes -= tmp->requested_size; 121 | 122 | if (tmp->aligned_orig) 123 | backend_free(tmp->aligned_orig); 124 | else 125 | backend_free(tmp); 126 | 127 | } 128 | 129 | ck->next = quarantine_top; 130 | if (quarantine_top) quarantine_top->prev = ck; 131 | quarantine_top = ck; 132 | 133 | LOCK_UNLOCK(&quarantine_lock); 134 | 135 | return 1; 136 | 137 | } 138 | 139 | void __libqasan_init_malloc(void) { 140 | 141 | if (__libqasan_malloc_initialized) return; 142 | 143 | #ifdef __GLIBC__ 144 | __lq_libc_malloc = dlsym(RTLD_NEXT, "malloc"); 145 | __lq_libc_free = dlsym(RTLD_NEXT, "free"); 146 | #endif 147 | 148 | LOCK_INIT(&quarantine_lock, PTHREAD_PROCESS_PRIVATE); 149 | 150 | __libqasan_malloc_initialized = 1; 151 | QASAN_LOG("\n"); 152 | QASAN_LOG("Allocator initialization done.\n"); 153 | QASAN_LOG("\n"); 154 | 155 | } 156 | 157 | size_t __libqasan_malloc_usable_size(void *ptr) { 158 | 159 | char *p = ptr; 160 | p -= sizeof(struct chunk_begin); 161 | 162 | // Validate that the chunk marker is readable (a crude check 163 | // to verify that ptr is a valid malloc region before we dereference it) 164 | QASAN_LOAD(p, sizeof(struct chunk_begin) - REDZONE_SIZE); 165 | return ((struct chunk_begin *)p)->requested_size; 166 | 167 | } 168 | 169 | void *__libqasan_malloc(size_t size) { 170 | 171 | if (!__libqasan_malloc_initialized) { 172 | 173 | __libqasan_init_malloc(); 174 | 175 | #ifdef __GLIBC__ 176 | void *r = &__tmp_alloc_zone[__tmp_alloc_zone_idx]; 177 | 178 | if (size & (ALLOC_ALIGN_SIZE - 1)) 179 | __tmp_alloc_zone_idx += 180 | (size & ~(ALLOC_ALIGN_SIZE - 1)) + ALLOC_ALIGN_SIZE; 181 | else 182 | __tmp_alloc_zone_idx += size; 183 | 184 | return r; 185 | #endif 186 | 187 | } 188 | 189 | int state = QASAN_SWAP(QASAN_DISABLED); // disable qasan for this thread 190 | 191 | struct chunk_begin *p = backend_malloc(sizeof(struct chunk_struct) + size); 192 | 193 | QASAN_SWAP(state); 194 | 195 | if (!p) return NULL; 196 | 197 | QASAN_UNPOISON(p, sizeof(struct chunk_struct) + size); 198 | 199 | p->requested_size = size; 200 | p->aligned_orig = NULL; 201 | p->next = p->prev = NULL; 202 | 203 | QASAN_ALLOC(&p[1], (char *)&p[1] + size); 204 | QASAN_POISON(p->redzone, REDZONE_SIZE, ASAN_HEAP_LEFT_RZ); 205 | if (size & (ALLOC_ALIGN_SIZE - 1)) 206 | QASAN_POISON((char *)&p[1] + size, 207 | (size & ~(ALLOC_ALIGN_SIZE - 1)) + 8 - size + REDZONE_SIZE, 208 | ASAN_HEAP_RIGHT_RZ); 209 | else 210 | QASAN_POISON((char *)&p[1] + size, REDZONE_SIZE, ASAN_HEAP_RIGHT_RZ); 211 | 212 | __builtin_memset(&p[1], 0xff, size); 213 | 214 | return &p[1]; 215 | 216 | } 217 | 218 | void __libqasan_free(void *ptr) { 219 | 220 | if (!ptr) return; 221 | 222 | #ifdef __GLIBC__ 223 | if (ptr >= (void *)__tmp_alloc_zone && 224 | ptr < ((void *)__tmp_alloc_zone + TMP_ZONE_SIZE)) 225 | return; 226 | #endif 227 | 228 | struct chunk_begin *p = ptr; 229 | p -= 1; 230 | 231 | // Validate that the chunk marker is readable (a crude check 232 | // to verify that ptr is a valid malloc region before we dereference it) 233 | QASAN_LOAD(p, sizeof(struct chunk_begin) - REDZONE_SIZE); 234 | size_t n = p->requested_size; 235 | 236 | QASAN_STORE(ptr, n); 237 | int state = QASAN_SWAP(QASAN_DISABLED); // disable qasan for this thread 238 | 239 | if (!quanratine_push(p)) { 240 | 241 | if (p->aligned_orig) 242 | backend_free(p->aligned_orig); 243 | else 244 | backend_free(p); 245 | 246 | } 247 | 248 | QASAN_SWAP(state); 249 | 250 | if (n & (ALLOC_ALIGN_SIZE - 1)) 251 | n = (n & ~(ALLOC_ALIGN_SIZE - 1)) + ALLOC_ALIGN_SIZE; 252 | 253 | QASAN_POISON(ptr, n, ASAN_HEAP_FREED); 254 | QASAN_DEALLOC(ptr); 255 | 256 | } 257 | 258 | void *__libqasan_calloc(size_t nmemb, size_t size) { 259 | 260 | size *= nmemb; 261 | 262 | #ifdef __GLIBC__ 263 | if (!__libqasan_malloc_initialized) { 264 | 265 | void *r = &__tmp_alloc_zone[__tmp_alloc_zone_idx]; 266 | __tmp_alloc_zone_idx += size; 267 | return r; 268 | 269 | } 270 | 271 | #endif 272 | 273 | char *p = __libqasan_malloc(size); 274 | if (!p) return NULL; 275 | 276 | __builtin_memset(p, 0, size); 277 | 278 | return p; 279 | 280 | } 281 | 282 | void *__libqasan_realloc(void *ptr, size_t size) { 283 | 284 | char *p = __libqasan_malloc(size); 285 | if (!p) return NULL; 286 | 287 | if (!ptr) return p; 288 | 289 | size_t n = ((struct chunk_begin *)ptr)[-1].requested_size; 290 | if (size < n) n = size; 291 | 292 | __builtin_memcpy(p, ptr, n); 293 | 294 | __libqasan_free(ptr); 295 | return p; 296 | 297 | } 298 | 299 | int __libqasan_posix_memalign(void **ptr, size_t align, size_t len) { 300 | 301 | if ((align % 2) || (align % sizeof(void *))) return EINVAL; 302 | if (len == 0) { 303 | 304 | *ptr = NULL; 305 | return 0; 306 | 307 | } 308 | 309 | size_t rem = len % align; 310 | size_t size = len; 311 | if (rem) size += rem; 312 | 313 | int state = QASAN_SWAP(QASAN_DISABLED); // disable qasan for this thread 314 | 315 | char *orig = backend_malloc(sizeof(struct chunk_struct) + size); 316 | 317 | QASAN_SWAP(state); 318 | 319 | if (!orig) return ENOMEM; 320 | 321 | QASAN_UNPOISON(orig, sizeof(struct chunk_struct) + size); 322 | 323 | char *data = orig + sizeof(struct chunk_begin); 324 | data += align - ((uintptr_t)data % align); 325 | 326 | struct chunk_begin *p = (struct chunk_begin *)data - 1; 327 | 328 | p->requested_size = len; 329 | p->aligned_orig = orig; 330 | 331 | QASAN_ALLOC(data, data + len); 332 | QASAN_POISON(p->redzone, REDZONE_SIZE, ASAN_HEAP_LEFT_RZ); 333 | if (len & (ALLOC_ALIGN_SIZE - 1)) 334 | QASAN_POISON( 335 | data + len, 336 | (len & ~(ALLOC_ALIGN_SIZE - 1)) + ALLOC_ALIGN_SIZE - len + REDZONE_SIZE, 337 | ASAN_HEAP_RIGHT_RZ); 338 | else 339 | QASAN_POISON(data + len, REDZONE_SIZE, ASAN_HEAP_RIGHT_RZ); 340 | 341 | __builtin_memset(data, 0xff, len); 342 | 343 | *ptr = data; 344 | 345 | return 0; 346 | 347 | } 348 | 349 | void *__libqasan_memalign(size_t align, size_t len) { 350 | 351 | void *ret = NULL; 352 | 353 | __libqasan_posix_memalign(&ret, align, len); 354 | 355 | return ret; 356 | 357 | } 358 | 359 | void *__libqasan_aligned_alloc(size_t align, size_t len) { 360 | 361 | void *ret = NULL; 362 | 363 | if ((len % align)) return NULL; 364 | 365 | __libqasan_posix_memalign(&ret, align, len); 366 | 367 | return ret; 368 | 369 | } 370 | 371 | -------------------------------------------------------------------------------- /fitm-qemu/build_qemu_support.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # american fuzzy lop++ - QEMU build script 4 | # -------------------------------------- 5 | # 6 | # Originally written by Andrew Griffiths and 7 | # Michal Zalewski 8 | # 9 | # TCG instrumentation and block chaining support by Andrea Biondo 10 | # 11 | # 12 | # QEMU 3.1.1 port, TCG thread-safety, CompareCoverage and NeverZero 13 | # counters by Andrea Fioraldi 14 | # 15 | # Copyright 2015, 2016, 2017 Google Inc. All rights reserved. 16 | # Copyright 2019-2020 AFLplusplus Project. All rights reserved. 17 | # 18 | # Licensed under the Apache License, Version 2.0 (the "License"); 19 | # you may not use this file except in compliance with the License. 20 | # You may obtain a copy of the License at: 21 | # 22 | # http://www.apache.org/licenses/LICENSE-2.0 23 | # 24 | # This script downloads, patches, and builds a version of QEMU with 25 | # minor tweaks to allow non-instrumented binaries to be run under 26 | # afl-fuzz. 27 | # 28 | # The modifications reside in patches/*. The standalone QEMU binary 29 | # will be written to ../afl-qemu-trace. 30 | # 31 | 32 | QEMUAFL_VERSION="$(cat ./QEMUAFL_VERSION)" 33 | 34 | echo "=================================================" 35 | echo " FitM QEMU build script" 36 | echo "=================================================" 37 | echo 38 | 39 | echo "[*] Performing basic sanity checks..." 40 | 41 | if [ ! "`uname -s`" = "Linux" ]; then 42 | 43 | echo "[-] Error: QEMU instrumentation is supported only on Linux." 44 | exit 0 45 | 46 | fi 47 | 48 | if [ ! -f "../AFLplusplus/include/config.h" ]; then 49 | 50 | echo "[-] Error: key files not found - wrong working directory? AFLplusplus submodule checked out?" 51 | exit 1 52 | 53 | fi 54 | 55 | if echo "$CC" | grep -qF /afl-; then 56 | 57 | echo "[-] Error: do not use afl-gcc or afl-clang to compile this tool." 58 | exit 1 59 | 60 | fi 61 | 62 | echo "[+] All checks passed!" 63 | 64 | echo "[*] Making sure FitM-qemu is checked out" 65 | 66 | test -d FitM-qemu || { echo "[-] Not checked out, please checkout submodules using 'git submodule update --init'." ; exit 1 ; } 67 | echo "[+] Got FitM-qemu." 68 | 69 | cd "FitM-qemu" || exit 1 70 | #if [ -n "$NO_CHECKOUT" ]; then 71 | # echo "[*] Skipping checkout to $QEMUAFL_VERSION" 72 | #else 73 | # echo "[*] Checking out $QEMUAFL_VERSION" 74 | # sh -c 'git stash' 1>/dev/null 2>/dev/null 75 | # git checkout "$QEMUAFL_VERSION" || echo Warning: could not check out to commit $QEMUAFL_VERSION 76 | #fi 77 | 78 | echo "[*] Making sure imported headers matches" 79 | cp "../../AFLplusplus/include/config.h" "./qemuafl/imported/" || exit 1 80 | cp "../../AFLplusplus/include/cmplog.h" "./qemuafl/imported/" || exit 1 81 | cp "../../AFLplusplus/include/snapshot-inl.h" "./qemuafl/imported/" || exit 1 82 | cp "../../AFLplusplus/include/types.h" "./qemuafl/imported/" || exit 1 83 | 84 | if [ -n "$HOST" ]; then 85 | echo "[+] Configuring host architecture to $HOST..." 86 | CROSS_PREFIX=$HOST- 87 | else 88 | CROSS_PREFIX= 89 | fi 90 | 91 | echo "[*] Configuring QEMU for $CPU_TARGET..." 92 | 93 | ORIG_CPU_TARGET="$CPU_TARGET" 94 | 95 | if [ "$ORIG_CPU_TARGET" = "" ]; then 96 | CPU_TARGET="`uname -m`" 97 | test "$CPU_TARGET" = "i686" && CPU_TARGET="i386" 98 | test "$CPU_TARGET" = "arm64v8" && CPU_TARGET="aarch64" 99 | case "$CPU_TARGET" in 100 | *arm*) 101 | CPU_TARGET="arm" 102 | ;; 103 | esac 104 | fi 105 | 106 | echo "Building for CPU target $CPU_TARGET" 107 | 108 | # --enable-pie seems to give a couple of exec's a second performance 109 | # improvement, much to my surprise. Not sure how universal this is.. 110 | QEMU_CONF_FLAGS=" \ 111 | --audio-drv-list= \ 112 | --disable-blobs \ 113 | --disable-bochs \ 114 | --disable-brlapi \ 115 | --disable-bsd-user \ 116 | --disable-bzip2 \ 117 | --disable-cap-ng \ 118 | --disable-cloop \ 119 | --disable-curl \ 120 | --disable-curses \ 121 | --disable-dmg \ 122 | --disable-fdt \ 123 | --disable-gcrypt \ 124 | --disable-glusterfs \ 125 | --disable-gnutls \ 126 | --disable-gtk \ 127 | --disable-guest-agent \ 128 | --disable-iconv \ 129 | --disable-libiscsi \ 130 | --disable-libnfs \ 131 | --disable-libssh \ 132 | --disable-libusb \ 133 | --disable-linux-aio \ 134 | --disable-live-block-migration \ 135 | --disable-lzo \ 136 | --disable-nettle \ 137 | --disable-numa \ 138 | --disable-opengl \ 139 | --disable-parallels \ 140 | --disable-plugins \ 141 | --disable-qcow1 \ 142 | --disable-qed \ 143 | --disable-rbd \ 144 | --disable-rdma \ 145 | --disable-replication \ 146 | --disable-sdl \ 147 | --disable-seccomp \ 148 | --disable-sheepdog \ 149 | --disable-smartcard \ 150 | --disable-snappy \ 151 | --disable-spice \ 152 | --disable-system \ 153 | --disable-tools \ 154 | --disable-tpm \ 155 | --disable-usb-redir \ 156 | --disable-vde \ 157 | --disable-vdi \ 158 | --disable-vhost-crypto \ 159 | --disable-vhost-kernel \ 160 | --disable-vhost-net \ 161 | --disable-vhost-scsi \ 162 | --disable-vhost-user \ 163 | --disable-vhost-vdpa \ 164 | --disable-vhost-vsock \ 165 | --disable-virglrenderer \ 166 | --disable-virtfs \ 167 | --disable-vnc \ 168 | --disable-vnc-jpeg \ 169 | --disable-vnc-png \ 170 | --disable-vnc-sasl \ 171 | --disable-vte \ 172 | --disable-vvfat \ 173 | --disable-xen \ 174 | --disable-xen-pci-passthrough \ 175 | --disable-xfsctl \ 176 | --target-list="${CPU_TARGET}-linux-user" \ 177 | --without-default-devices \ 178 | " 179 | 180 | if [ -n "${CROSS_PREFIX}" ]; then 181 | 182 | QEMU_CONF_FLAGS="$QEMU_CONF_FLAGS --cross-prefix=$CROSS_PREFIX" 183 | 184 | fi 185 | 186 | if [ "$STATIC" = "1" ]; then 187 | 188 | echo Building STATIC binary 189 | 190 | QEMU_CONF_FLAGS="$QEMU_CONF_FLAGS \ 191 | --static \ 192 | --extra-cflags=-DAFL_QEMU_STATIC_BUILD=1 \ 193 | " 194 | 195 | else 196 | 197 | QEMU_CONF_FLAGS="${QEMU_CONF_FLAGS} --enable-pie " 198 | 199 | fi 200 | 201 | if [ "$DEBUG" = "1" ]; then 202 | 203 | echo Building DEBUG binary 204 | 205 | # --enable-gcov might go here but incurs a mesonbuild error on meson 206 | # versions prior to 0.56: 207 | # https://github.com/qemu/meson/commit/903d5dd8a7dc1d6f8bef79e66d6ebc07c 208 | QEMU_CONF_FLAGS="$QEMU_CONF_FLAGS \ 209 | --disable-strip \ 210 | --enable-debug \ 211 | --enable-debug-info \ 212 | --enable-debug-mutex \ 213 | --enable-debug-stack-usage \ 214 | --enable-debug-tcg \ 215 | --enable-qom-cast-debug \ 216 | --enable-werror \ 217 | " 218 | 219 | else 220 | 221 | QEMU_CONF_FLAGS="$QEMU_CONF_FLAGS \ 222 | --disable-debug-info \ 223 | --disable-debug-mutex \ 224 | --disable-debug-tcg \ 225 | --disable-qom-cast-debug \ 226 | --disable-stack-protector \ 227 | --disable-werror \ 228 | " 229 | 230 | fi 231 | 232 | if [ "$PROFILING" = "1" ]; then 233 | 234 | echo Building PROFILED binary 235 | 236 | QEMU_CONF_FLAGS="$QEMU_CONF_FLAGS \ 237 | --enable-gprof \ 238 | --enable-profiler \ 239 | " 240 | 241 | fi 242 | 243 | # shellcheck disable=SC2086 244 | ./configure $QEMU_CONF_FLAGS || exit 1 245 | 246 | echo "[+] Configuration complete." 247 | 248 | echo "[*] Attempting to build QEMU (fingers crossed!)..." 249 | 250 | make -j `nproc` || exit 1 251 | 252 | echo "[+] Build process successful!" 253 | 254 | echo "[*] Copying binary..." 255 | 256 | cp -f "build/${CPU_TARGET}-linux-user/qemu-${CPU_TARGET}" "../../fitm-qemu-trace" || exit 1 257 | 258 | cd .. 259 | ls -l ../fitm-qemu-trace || exit 1 260 | 261 | echo "[+] Successfully created '../fitm-qemu-trace'." 262 | 263 | if [ "$ORIG_CPU_TARGET" = "" ]; then 264 | 265 | echo "[*] Testing the build..." 266 | 267 | cd ../AFLplusplus 268 | make afl-showmap 269 | 270 | make >/dev/null || exit 1 271 | 272 | cc test-instr.c -o test-instr || exit 1 273 | 274 | unset AFL_INST_RATIO 275 | export ASAN_OPTIONS=detect_leaks=0 276 | 277 | echo "[*] Comparing two afl-showmap outputs..." 278 | echo 0 | ./afl-showmap -m none -q -o .test-instr0 -- ../fitm-qemu-trace ./test-instr || exit 1 279 | echo 1 | ./afl-showmap -m none -q -o .test-instr1 -- ../fitm-qemu-trace ./test-instr || exit 1 280 | 281 | rm -f test-instr 282 | 283 | cmp -s .test-instr0 .test-instr1 284 | DR="$?" 285 | 286 | rm -f .test-instr0 .test-instr1 287 | 288 | if [ "$DR" = "0" ]; then 289 | 290 | echo "[-] Error: fitm-qemu-trace instrumentation doesn't seem to work!" 291 | exit 1 292 | 293 | fi 294 | 295 | echo "[+] Instrumentation tests passed. " 296 | echo "[+] All set, you can now use the -Q mode in afl-fuzz!" 297 | 298 | cd ../fitm-qemu || exit 1 299 | 300 | else 301 | 302 | echo "[!] Note: can't test instrumentation when CPU_TARGET set." 303 | echo "[+] All set, you can now (hopefully) use the -Q mode in afl-fuzz!" 304 | 305 | fi 306 | 307 | ORIG_CROSS="$CROSS" 308 | 309 | if [ "$ORIG_CROSS" = "" ]; then 310 | CROSS=$CPU_TARGET-linux-gnu-gcc 311 | if ! command -v "$CROSS" > /dev/null 312 | then # works on Arch Linux 313 | CROSS=$CPU_TARGET-pc-linux-gnu-gcc 314 | fi 315 | if ! command -v "$CROSS" > /dev/null && [ "$CPU_TARGET" = "i386" ] 316 | then 317 | CROSS=i686-linux-gnu-gcc 318 | if ! command -v "$CROSS" > /dev/null 319 | then # works on Arch Linux 320 | CROSS=i686-pc-linux-gnu-gcc 321 | fi 322 | if ! command -v "$CROSS" > /dev/null && [ "`uname -m`" = "x86_64" ] 323 | then # set -m32 324 | test "$CC" = "" && CC="gcc" 325 | CROSS="$CC" 326 | CROSS_FLAGS=-m32 327 | fi 328 | fi 329 | fi 330 | 331 | if ! command -v "$CROSS" > /dev/null ; then 332 | if [ "$CPU_TARGET" = "$(uname -m)" ] ; then 333 | echo "[+] Building afl++ qemu support libraries with CC=$CC" 334 | echo "[+] Building libcompcov ..." 335 | make -C libcompcov && echo "[+] libcompcov ready" 336 | echo "[+] Building unsigaction ..." 337 | make -C unsigaction && echo "[+] unsigaction ready" 338 | echo "[+] Building libqasan ..." 339 | make -C libqasan && echo "[+] unsigaction ready" 340 | else 341 | echo "[!] Cross compiler $CROSS could not be found, cannot compile libcompcov libqasan and unsigaction" 342 | fi 343 | else 344 | echo "[+] Building afl++ qemu support libraries with CC=\"$CROSS $CROSS_FLAGS\"" 345 | echo "[+] Building libcompcov ..." 346 | make -C libcompcov CC="$CROSS $CROSS_FLAGS" && echo "[+] libcompcov ready" 347 | echo "[+] Building unsigaction ..." 348 | make -C unsigaction CC="$CROSS $CROSS_FLAGS" && echo "[+] unsigaction ready" 349 | echo "[+] Building libqasan ..." 350 | make -C libqasan CC="$CROSS $CROSS_FLAGS" && echo "[+] unsigaction ready" 351 | fi 352 | 353 | echo "[+] All done for qemu_mode, enjoy!" 354 | 355 | exit 0 356 | -------------------------------------------------------------------------------- /fitm-qemu/libcompcov/libcompcov.so.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | american fuzzy lop++ - strcmp() / memcmp() CompareCoverage library 4 | ------------------------------------------------------------------ 5 | 6 | Written and maintained by Andrea Fioraldi 7 | 8 | Copyright 2019-2020 AFLplusplus Project. All rights reserved. 9 | 10 | Licensed under the Apache License, Version 2.0 (the "License"); 11 | you may not use this file except in compliance with the License. 12 | You may obtain a copy of the License at: 13 | 14 | http://www.apache.org/licenses/LICENSE-2.0 15 | 16 | This Linux-only companion library allows you to instrument strcmp(), 17 | memcmp(), and related functions to get compare coverage. 18 | See README.md for more info. 19 | 20 | */ 21 | 22 | #ifndef _GNU_SOURCE 23 | #define _GNU_SOURCE 24 | #endif 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "types.h" 36 | #include "config.h" 37 | 38 | #include "pmparser.h" 39 | 40 | #ifndef __linux__ 41 | #error "Sorry, this library is Linux-specific for now!" 42 | #endif /* !__linux__ */ 43 | 44 | /* Change this value to tune the compare coverage */ 45 | 46 | #define MAX_CMP_LENGTH 32 47 | 48 | static void *__compcov_code_start, *__compcov_code_end; 49 | 50 | static u8 *__compcov_afl_map; 51 | 52 | static u32 __compcov_level; 53 | 54 | static int (*__libc_strcmp)(const char *, const char *); 55 | static int (*__libc_strncmp)(const char *, const char *, size_t); 56 | static int (*__libc_strcasecmp)(const char *, const char *); 57 | static int (*__libc_strncasecmp)(const char *, const char *, size_t); 58 | static int (*__libc_memcmp)(const void *, const void *, size_t); 59 | 60 | static int debug_fd = -1; 61 | 62 | #define MAX_MAPPINGS 1024 63 | 64 | static struct mapping { void *st, *en; } __compcov_ro[MAX_MAPPINGS]; 65 | 66 | static u32 __compcov_ro_cnt; 67 | 68 | /* Check an address against the list of read-only mappings. */ 69 | 70 | static u8 __compcov_is_ro(const void *ptr) { 71 | 72 | u32 i; 73 | 74 | for (i = 0; i < __compcov_ro_cnt; i++) 75 | if (ptr >= __compcov_ro[i].st && ptr <= __compcov_ro[i].en) return 1; 76 | 77 | return 0; 78 | 79 | } 80 | 81 | static size_t __strlen2(const char *s1, const char *s2, size_t max_length) { 82 | 83 | // from https://github.com/googleprojectzero/CompareCoverage 84 | 85 | size_t len = 0; 86 | for (; len < max_length && s1[len] != '\0' && s2[len] != '\0'; len++) {} 87 | return len; 88 | 89 | } 90 | 91 | /* Identify the binary boundaries in the memory mapping */ 92 | 93 | static void __compcov_load(void) { 94 | 95 | __libc_strcmp = dlsym(RTLD_NEXT, "strcmp"); 96 | __libc_strncmp = dlsym(RTLD_NEXT, "strncmp"); 97 | __libc_strcasecmp = dlsym(RTLD_NEXT, "strcasecmp"); 98 | __libc_strncasecmp = dlsym(RTLD_NEXT, "strncasecmp"); 99 | __libc_memcmp = dlsym(RTLD_NEXT, "memcmp"); 100 | 101 | if (getenv("AFL_QEMU_COMPCOV")) { __compcov_level = 1; } 102 | if (getenv("AFL_COMPCOV_LEVEL")) { 103 | 104 | __compcov_level = atoi(getenv("AFL_COMPCOV_LEVEL")); 105 | 106 | } 107 | 108 | char *id_str = getenv(SHM_ENV_VAR); 109 | int shm_id; 110 | 111 | if (id_str) { 112 | 113 | shm_id = atoi(id_str); 114 | __compcov_afl_map = shmat(shm_id, NULL, 0); 115 | 116 | if (__compcov_afl_map == (void *)-1) exit(1); 117 | 118 | } else { 119 | 120 | __compcov_afl_map = calloc(1, MAP_SIZE); 121 | 122 | } 123 | 124 | if (getenv("AFL_INST_LIBS")) { 125 | 126 | __compcov_code_start = (void *)0; 127 | __compcov_code_end = (void *)-1; 128 | return; 129 | 130 | } 131 | 132 | char *bin_name = getenv("AFL_COMPCOV_BINNAME"); 133 | 134 | procmaps_iterator *maps = pmparser_parse(-1); 135 | procmaps_struct * maps_tmp = NULL; 136 | 137 | while ((maps_tmp = pmparser_next(maps)) != NULL) { 138 | 139 | /* If AFL_COMPCOV_BINNAME is not set pick the first executable segment */ 140 | if (!bin_name || strstr(maps_tmp->pathname, bin_name) != NULL) { 141 | 142 | if (maps_tmp->is_x) { 143 | 144 | if (!__compcov_code_start) __compcov_code_start = maps_tmp->addr_start; 145 | if (!__compcov_code_end) __compcov_code_end = maps_tmp->addr_end; 146 | 147 | } 148 | 149 | } 150 | 151 | if ((maps_tmp->is_w && !maps_tmp->is_r) || __compcov_ro_cnt == MAX_MAPPINGS) 152 | continue; 153 | 154 | __compcov_ro[__compcov_ro_cnt].st = maps_tmp->addr_start; 155 | __compcov_ro[__compcov_ro_cnt].en = maps_tmp->addr_end; 156 | ++__compcov_ro_cnt; 157 | 158 | } 159 | 160 | pmparser_free(maps); 161 | 162 | } 163 | 164 | static void __compcov_trace(uintptr_t cur_loc, const u8 *v0, const u8 *v1, 165 | size_t n) { 166 | 167 | size_t i; 168 | 169 | if (debug_fd != 1) { 170 | 171 | char debugbuf[4096]; 172 | snprintf(debugbuf, sizeof(debugbuf), "0x%" PRIxPTR " %s %s %zu\n", cur_loc, 173 | v0 == NULL ? "(null)" : (char *)v0, 174 | v1 == NULL ? "(null)" : (char *)v1, n); 175 | write(debug_fd, debugbuf, strlen(debugbuf)); 176 | 177 | } 178 | 179 | for (i = 0; i < n && v0[i] == v1[i]; ++i) { 180 | 181 | __compcov_afl_map[cur_loc + i]++; 182 | 183 | } 184 | 185 | } 186 | 187 | /* Check an address against the list of read-only mappings. */ 188 | 189 | static u8 __compcov_is_in_bound(const void *ptr) { 190 | 191 | return ptr >= __compcov_code_start && ptr < __compcov_code_end; 192 | 193 | } 194 | 195 | /* Replacements for strcmp(), memcmp(), and so on. Note that these will be used 196 | only if the target is compiled with -fno-builtins and linked dynamically. */ 197 | 198 | #undef strcmp 199 | 200 | int strcmp(const char *str1, const char *str2) { 201 | 202 | void *retaddr = __builtin_return_address(0); 203 | 204 | if (__compcov_is_in_bound(retaddr) && 205 | !(__compcov_level < 2 && !__compcov_is_ro(str1) && 206 | !__compcov_is_ro(str2))) { 207 | 208 | size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH + 1); 209 | 210 | if (n <= MAX_CMP_LENGTH) { 211 | 212 | uintptr_t cur_loc = (uintptr_t)retaddr; 213 | cur_loc = (cur_loc >> 4) ^ (cur_loc << 8); 214 | cur_loc &= MAP_SIZE - 1; 215 | 216 | __compcov_trace(cur_loc, str1, str2, n); 217 | 218 | } 219 | 220 | } 221 | 222 | return __libc_strcmp(str1, str2); 223 | 224 | } 225 | 226 | #undef strncmp 227 | 228 | int strncmp(const char *str1, const char *str2, size_t len) { 229 | 230 | void *retaddr = __builtin_return_address(0); 231 | 232 | if (__compcov_is_in_bound(retaddr) && 233 | !(__compcov_level < 2 && !__compcov_is_ro(str1) && 234 | !__compcov_is_ro(str2))) { 235 | 236 | size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH + 1); 237 | n = MIN(n, len); 238 | 239 | if (n <= MAX_CMP_LENGTH) { 240 | 241 | uintptr_t cur_loc = (uintptr_t)retaddr; 242 | cur_loc = (cur_loc >> 4) ^ (cur_loc << 8); 243 | cur_loc &= MAP_SIZE - 1; 244 | 245 | __compcov_trace(cur_loc, str1, str2, n); 246 | 247 | } 248 | 249 | } 250 | 251 | return __libc_strncmp(str1, str2, len); 252 | 253 | } 254 | 255 | #undef strcasecmp 256 | 257 | int strcasecmp(const char *str1, const char *str2) { 258 | 259 | void *retaddr = __builtin_return_address(0); 260 | 261 | if (__compcov_is_in_bound(retaddr) && 262 | !(__compcov_level < 2 && !__compcov_is_ro(str1) && 263 | !__compcov_is_ro(str2))) { 264 | 265 | /* Fallback to strcmp, maybe improve in future */ 266 | 267 | size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH + 1); 268 | 269 | if (n <= MAX_CMP_LENGTH) { 270 | 271 | uintptr_t cur_loc = (uintptr_t)retaddr; 272 | cur_loc = (cur_loc >> 4) ^ (cur_loc << 8); 273 | cur_loc &= MAP_SIZE - 1; 274 | 275 | __compcov_trace(cur_loc, str1, str2, n); 276 | 277 | } 278 | 279 | } 280 | 281 | return __libc_strcasecmp(str1, str2); 282 | 283 | } 284 | 285 | #undef strncasecmp 286 | 287 | int strncasecmp(const char *str1, const char *str2, size_t len) { 288 | 289 | void *retaddr = __builtin_return_address(0); 290 | 291 | if (__compcov_is_in_bound(retaddr) && 292 | !(__compcov_level < 2 && !__compcov_is_ro(str1) && 293 | !__compcov_is_ro(str2))) { 294 | 295 | /* Fallback to strncmp, maybe improve in future */ 296 | 297 | size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH + 1); 298 | n = MIN(n, len); 299 | 300 | if (n <= MAX_CMP_LENGTH) { 301 | 302 | uintptr_t cur_loc = (uintptr_t)retaddr; 303 | cur_loc = (cur_loc >> 4) ^ (cur_loc << 8); 304 | cur_loc &= MAP_SIZE - 1; 305 | 306 | __compcov_trace(cur_loc, str1, str2, n); 307 | 308 | } 309 | 310 | } 311 | 312 | return __libc_strncasecmp(str1, str2, len); 313 | 314 | } 315 | 316 | #undef memcmp 317 | 318 | int memcmp(const void *mem1, const void *mem2, size_t len) { 319 | 320 | void *retaddr = __builtin_return_address(0); 321 | 322 | if (__compcov_is_in_bound(retaddr) && 323 | !(__compcov_level < 2 && !__compcov_is_ro(mem1) && 324 | !__compcov_is_ro(mem2))) { 325 | 326 | size_t n = len; 327 | 328 | if (n <= MAX_CMP_LENGTH) { 329 | 330 | uintptr_t cur_loc = (uintptr_t)retaddr; 331 | cur_loc = (cur_loc >> 4) ^ (cur_loc << 8); 332 | cur_loc &= MAP_SIZE - 1; 333 | 334 | __compcov_trace(cur_loc, mem1, mem2, n); 335 | 336 | } 337 | 338 | } 339 | 340 | return __libc_memcmp(mem1, mem2, len); 341 | 342 | } 343 | 344 | // TODO bcmp 345 | 346 | /* Common libraries wrappers (from honggfuzz) */ 347 | 348 | /* 349 | * Apache's httpd wrappers 350 | */ 351 | int ap_cstr_casecmp(const char *s1, const char *s2) { 352 | 353 | return strcasecmp(s1, s2); 354 | 355 | } 356 | 357 | int ap_cstr_casecmpn(const char *s1, const char *s2, size_t n) { 358 | 359 | return strncasecmp(s1, s2, n); 360 | 361 | } 362 | 363 | int apr_cstr_casecmp(const char *s1, const char *s2) { 364 | 365 | return strcasecmp(s1, s2); 366 | 367 | } 368 | 369 | int apr_cstr_casecmpn(const char *s1, const char *s2, size_t n) { 370 | 371 | return strncasecmp(s1, s2, n); 372 | 373 | } 374 | 375 | /* 376 | * *SSL wrappers 377 | */ 378 | int CRYPTO_memcmp(const void *m1, const void *m2, size_t len) { 379 | 380 | return memcmp(m1, m2, len); 381 | 382 | } 383 | 384 | int OPENSSL_memcmp(const void *m1, const void *m2, size_t len) { 385 | 386 | return memcmp(m1, m2, len); 387 | 388 | } 389 | 390 | int OPENSSL_strcasecmp(const char *s1, const char *s2) { 391 | 392 | return strcasecmp(s1, s2); 393 | 394 | } 395 | 396 | int OPENSSL_strncasecmp(const char *s1, const char *s2, size_t len) { 397 | 398 | return strncasecmp(s1, s2, len); 399 | 400 | } 401 | 402 | int32_t memcmpct(const void *s1, const void *s2, size_t len) { 403 | 404 | return memcmp(s1, s2, len); 405 | 406 | } 407 | 408 | /* 409 | * libXML wrappers 410 | */ 411 | int xmlStrncmp(const char *s1, const char *s2, int len) { 412 | 413 | if (len <= 0) { return 0; } 414 | if (s1 == s2) { return 0; } 415 | if (s1 == NULL) { return -1; } 416 | if (s2 == NULL) { return 1; } 417 | return strncmp(s1, s2, (size_t)len); 418 | 419 | } 420 | 421 | int xmlStrcmp(const char *s1, const char *s2) { 422 | 423 | if (s1 == s2) { return 0; } 424 | if (s1 == NULL) { return -1; } 425 | if (s2 == NULL) { return 1; } 426 | return strcmp(s1, s2); 427 | 428 | } 429 | 430 | int xmlStrEqual(const char *s1, const char *s2) { 431 | 432 | if (s1 == s2) { return 1; } 433 | if (s1 == NULL) { return 0; } 434 | if (s2 == NULL) { return 0; } 435 | if (strcmp(s1, s2) == 0) { return 1; } 436 | return 0; 437 | 438 | } 439 | 440 | int xmlStrcasecmp(const char *s1, const char *s2) { 441 | 442 | if (s1 == s2) { return 0; } 443 | if (s1 == NULL) { return -1; } 444 | if (s2 == NULL) { return 1; } 445 | return strcasecmp(s1, s2); 446 | 447 | } 448 | 449 | int xmlStrncasecmp(const char *s1, const char *s2, int len) { 450 | 451 | if (len <= 0) { return 0; } 452 | if (s1 == s2) { return 0; } 453 | if (s1 == NULL) { return -1; } 454 | if (s2 == NULL) { return 1; } 455 | return strncasecmp(s1, s2, (size_t)len); 456 | 457 | } 458 | 459 | const char *xmlStrcasestr(const char *haystack, const char *needle) { 460 | 461 | if (haystack == NULL) { return NULL; } 462 | if (needle == NULL) { return NULL; } 463 | return strcasestr(haystack, needle); 464 | 465 | } 466 | 467 | /* 468 | * Samba wrappers 469 | */ 470 | int memcmp_const_time(const void *s1, const void *s2, size_t n) { 471 | 472 | return memcmp(s1, s2, n); 473 | 474 | } 475 | 476 | bool strcsequal(const void *s1, const void *s2) { 477 | 478 | if (s1 == s2) { return true; } 479 | if (!s1 || !s2) { return false; } 480 | return (strcmp(s1, s2) == 0); 481 | 482 | } 483 | 484 | /* Init code to open init the library. */ 485 | 486 | __attribute__((constructor)) void __compcov_init(void) { 487 | 488 | if (getenv("AFL_QEMU_COMPCOV_DEBUG") != NULL) 489 | debug_fd = 490 | open("compcov.debug", O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, 0644); 491 | 492 | __compcov_load(); 493 | 494 | } 495 | 496 | -------------------------------------------------------------------------------- /src/namespacing.rs: -------------------------------------------------------------------------------- 1 | use libc::{self, pid_t}; 2 | use std::process::ExitStatus; 3 | use std::{ 4 | ffi::CString, 5 | fmt::Debug, 6 | io::{self, Write}, 7 | }; 8 | 9 | fn mount( 10 | src: &str, 11 | target: &str, 12 | fstype: Option<&str>, 13 | flags: u64, 14 | data: Option<&str>, 15 | ) -> io::Result<()> { 16 | let src = CString::new(src)?; 17 | let target = CString::new(target)?; 18 | 19 | let fstype_buf; 20 | let fstype_ptr = match fstype { 21 | Some(val) => { 22 | fstype_buf = CString::new(val)?; 23 | fstype_buf.as_ptr() 24 | } 25 | None => 0 as _, 26 | }; 27 | 28 | let data_buf; 29 | let data_ptr = match data { 30 | Some(val) => { 31 | data_buf = CString::new(val)?; 32 | data_buf.as_ptr() 33 | } 34 | None => 0 as _, 35 | }; 36 | 37 | if -1 38 | == unsafe { 39 | libc::mount( 40 | src.as_ptr(), 41 | target.as_ptr(), 42 | fstype_ptr, 43 | flags, 44 | data_ptr as _, 45 | ) 46 | } 47 | { 48 | Err(io::Error::last_os_error()) 49 | } else { 50 | Ok(()) 51 | } 52 | } 53 | 54 | unsafe fn sys_clone(flags: libc::c_int) -> io::Result> { 55 | let ret: pid_t = libc::syscall(libc::SYS_clone, flags as libc::c_int, 0, 0, 0, 0) as _; 56 | 57 | match ret { 58 | 0 => Ok(None), 59 | x if x > 0 => Ok(Some(x)), 60 | x => { 61 | *libc::__errno_location() = -x; 62 | Err(io::Error::last_os_error()) 63 | } 64 | } 65 | } 66 | 67 | pub struct NamespaceContext { 68 | pub init_fn: Box, 69 | } 70 | 71 | impl NamespaceContext { 72 | pub fn new() -> Self { 73 | NamespaceContext { 74 | init_fn: Box::new(|| { 75 | // mount("none","/", None, libc::MS_REC | libc::MS_PRIVATE, None); // Make / private (meaning changes wont propagate to the default namespace) 76 | mount("none", "/proc", None, libc::MS_REC | libc::MS_PRIVATE, None) 77 | .expect("mounting proc private failed"); 78 | mount( 79 | "proc", 80 | "/proc", 81 | Some("proc"), 82 | libc::MS_NOSUID | libc::MS_NODEV | libc::MS_NOEXEC, 83 | None, 84 | ) 85 | .expect("proc remounting failed"); 86 | 87 | unsafe { libc::setsid() }; 88 | 89 | let _ = io::stdout().flush(); 90 | // std::process::Command::new("stat").arg("/proc/self/ns/pid").status().unwrap(); 91 | // std::process::Command::new("ps").arg("-aux").status().unwrap(); 92 | }), 93 | } 94 | } 95 | 96 | pub fn execute(self, f: T) -> io::Result 97 | where 98 | T: FnOnce() -> Result, 99 | E: Debug, 100 | { 101 | // Clone-process with namespaceing flags 102 | // let clone_result = unsafe { 103 | // let args = clone_args { 104 | // flags: (libc::CLONE_NEWPID | libc::CLONE_NEWNS) as _, 105 | // pidfd: 0, 106 | // parent_tid: 0, 107 | // child_tid: 0, 108 | // exit_signal: libc::SIGCHLD as _, 109 | // stack: 0, 110 | // stack_size: 0, 111 | // tls: 0, 112 | // }; 113 | // clone3(&args)? 114 | // }; 115 | 116 | let clone_result = 117 | unsafe { sys_clone(libc::CLONE_NEWPID | libc::CLONE_NEWNS | libc::SIGCHLD)? }; 118 | 119 | Ok(match clone_result { 120 | Some(child_pid) => Namespace { 121 | init_pid: child_pid, 122 | status: None, 123 | }, 124 | None => { 125 | (self.init_fn)(); 126 | let res = f(); 127 | std::thread::sleep(std::time::Duration::from_millis(5)); 128 | std::process::Command::new("sync") 129 | .arg("-f") 130 | .status() 131 | .unwrap(); 132 | let _ = io::stdout().flush(); 133 | match res { 134 | Ok(val) => std::process::exit(val), 135 | Err(e) => panic!("Namespace call failed with error {:?}", e), 136 | } 137 | } 138 | }) 139 | } 140 | } 141 | 142 | impl Default for NamespaceContext { 143 | fn default() -> Self { 144 | Self::new() 145 | } 146 | } 147 | 148 | pub struct Namespace { 149 | pub init_pid: pid_t, 150 | pub status: Option, 151 | } 152 | 153 | impl Namespace { 154 | pub fn wait(&mut self) -> io::Result { 155 | if let Some(status) = self.status { 156 | return Ok(status); 157 | } 158 | 159 | let mut status = 0_i32; 160 | loop { 161 | let result = unsafe { libc::waitpid(self.init_pid, &mut status, 0) }; 162 | if result == -1 { 163 | let e = io::Error::last_os_error(); 164 | if e.kind() != io::ErrorKind::Interrupted { 165 | return Err(e); 166 | } 167 | } else { 168 | break; 169 | } 170 | } 171 | // Casting the waitpid return value to automatically interpret ExistStatus flags 172 | let exit_status: ExitStatus = unsafe { std::mem::transmute(status) }; 173 | self.status = Some(exit_status); 174 | Ok(exit_status) 175 | } 176 | } 177 | 178 | /* 179 | #[repr(align(8))] 180 | #[repr(C)] 181 | struct clone_args { 182 | flags: u64, /* Flags bit mask */ 183 | pidfd: u64, /* Where to store PID file descriptor (pid_t *) */ 184 | child_tid: u64, /* Where to store child TID:u64, in child's memory (pid_t *) */ 185 | parent_tid: u64, /* Where to store child TID, in parent's memory (int *) */ 186 | exit_signal: u64, /* Signal to deliver to parent on child termination */ 187 | stack: u64, /* Pointer to lowest byte of stack */ 188 | stack_size: u64, /* Size of stack */ 189 | tls: u64, /* Location of new TLS */ 190 | } 191 | */ 192 | 193 | // Das ist V2 aber mein kernel is zu alt ... 194 | #[repr(align(8))] 195 | #[repr(C)] 196 | #[allow(dead_code)] 197 | struct clone_argsV2 { 198 | flags: u64, /* Flags bit mask */ 199 | pidfd: u64, /* Where to store PID file descriptor (pid_t *) */ 200 | child_tid: u64, /* Where to store child TID:u64, in child's memory (pid_t *) */ 201 | parent_tid: u64, /* Where to store child TID, in parent's memory (int *) */ 202 | exit_signal: u64, /* Signal to deliver to parent on child termination */ 203 | stack: u64, /* Pointer to lowest byte of stack */ 204 | stack_size: u64, /* Size of stack */ 205 | tls: u64, /* Location of new TLS */ 206 | set_tid: u64, /* Pointer to a pid_t array */ 207 | set_tid_size: u64, /* Number of elements in set_tid */ 208 | } 209 | 210 | /* 211 | unsafe fn clone3(args: &clone_args) -> io::Result> { 212 | let ret: pid_t = libc::syscall( 213 | libc::SYS_clone3, 214 | args as *const _, 215 | std::mem::size_of::(), 216 | ) as _; 217 | // asm!( 218 | // "syscall", 219 | // in("rax") libc::SYS_clone3, 220 | // in("rdi") args as *const _, 221 | // in("rsi") std::mem::size_of::(), 222 | // out("rdx") _, 223 | // out("rcx") _, 224 | // out("r11") _, 225 | // lateout("rax") ret, 226 | // ); 227 | 228 | match ret { 229 | 0 => Ok(None), 230 | x if x > 0 => Ok(Some(x)), 231 | x => { 232 | *libc::__errno_location() = -x; 233 | Err(io::Error::last_os_error()) 234 | } 235 | } 236 | } 237 | */ 238 | 239 | #[cfg(test)] 240 | mod tests { 241 | use super::*; 242 | use std::fs::{File, OpenOptions}; 243 | use std::io::{Read, Seek, SeekFrom, Write}; 244 | use std::os::unix::{fs::OpenOptionsExt, io::AsRawFd}; 245 | use std::path::Path; 246 | use std::process::{id, Command, Stdio}; 247 | 248 | use crate::FITMSnapshot; 249 | 250 | fn system(command: &str) -> i32 { 251 | let command = CString::new(command).unwrap(); 252 | unsafe { libc::system(command.as_ptr()) } 253 | } 254 | 255 | #[test] 256 | fn test_namespacing1() { 257 | let mut child = NamespaceContext::new() 258 | .execute(|| -> io::Result { 259 | println!("PID: {}", id()); 260 | println!("SID: {}", unsafe { libc::getsid(id() as _) }); 261 | println!("UID: {}", unsafe { libc::getuid() }); 262 | 263 | // TODO spawn this process with a specific PID 264 | Command::new("setsid") 265 | .arg("bash") 266 | .arg("-c") 267 | .arg("sleep 5s; echo 'hi'") 268 | .stdin(Stdio::piped()) 269 | .stdout(Stdio::piped()) 270 | .stderr(Stdio::piped()) 271 | .spawn()?; 272 | 273 | system("ps -aux"); 274 | system("criu dump -t 2 -v -o dump.log --images-dir dump/ --leave-running"); 275 | system("chown 1000:1000 dump/dump.log"); 276 | 277 | std::thread::sleep(std::time::Duration::from_secs(10)); 278 | Ok(10) 279 | }) 280 | .unwrap(); 281 | 282 | let ret = child.wait().unwrap(); 283 | println!("{:?}", ret); 284 | } 285 | 286 | #[test] 287 | fn test_snapshot_init() { 288 | NamespaceContext::new() 289 | .execute(|| -> io::Result { 290 | println!("PID: {}", id()); 291 | println!("SID: {}", unsafe { libc::getsid(id() as _) }); 292 | println!("UID: {}", unsafe { libc::getuid() }); 293 | 294 | let _criu_srv = Command::new("criu") 295 | .args(&[ 296 | "service", 297 | "-v4", 298 | "--address", 299 | "/tmp/criu_service.socket", 300 | "-o", 301 | "dump.log", 302 | "-vv", 303 | ]) 304 | .spawn()?; 305 | 306 | let mut file = OpenOptions::new() 307 | .read(true) 308 | .write(true) 309 | .create(true) 310 | .mode(0o644) 311 | .open("/proc/sys/kernel/ns_last_pid") 312 | .expect("Failed to open ns_last_pid"); 313 | 314 | println!("locking"); 315 | unsafe { 316 | if libc::flock(file.as_raw_fd() as _, libc::LOCK_EX) != 0 { 317 | panic!("LOCKING FAILED") 318 | } 319 | } 320 | 321 | let mut contents = String::new(); 322 | file.read_to_string(&mut contents).unwrap(); 323 | println!("last pid: [{}]", contents.trim()); 324 | 325 | let (stdout, stderr) = (File::create("stdout-afl")?, File::create("stderr-afl")?); 326 | 327 | let stdin_path = "/dev/null"; 328 | let stdin_file = File::open(stdin_path)?; 329 | 330 | file.seek(SeekFrom::Start(0)).unwrap(); 331 | unsafe { libc::ftruncate(file.as_raw_fd(), 0) }; 332 | file.write(311336.to_string().as_bytes())?; 333 | 334 | let mut child = Command::new("setsid") 335 | .args(&[ 336 | format!("stdbuf"), 337 | format!("-oL"), 338 | format!("./fitm-qemu-trace"), 339 | format!("{}", "tests/targets/pseudoserver_simple"), 340 | format!("{}", "/dev/null"), 341 | ]) 342 | .stdin(Stdio::from(stdin_file)) 343 | .stdout(Stdio::from(stdout)) 344 | .stderr(Stdio::from(stderr)) 345 | .env("LETS_DO_THE_TIMEWARP_AGAIN", "1") 346 | .env("FITM_CREATE_OUTPUTS", "1") 347 | .env("CRIU_SNAPSHOT_DIR", "./snapshot") 348 | .env("AFL_NO_UI", "1") 349 | .spawn() 350 | .expect("[!] Could not spawn snapshot run"); 351 | 352 | println!("CHILD PID: {}", child.id()); 353 | unsafe { 354 | if libc::flock(file.as_raw_fd(), libc::LOCK_UN) != 0 { 355 | panic!("UNLOCKING FAILED") 356 | } 357 | } 358 | drop(file); 359 | 360 | child 361 | .wait() 362 | .expect("[!] Could not wait for child to finish"); 363 | std::process::exit(1); 364 | }) 365 | .unwrap() 366 | .wait() 367 | .unwrap(); 368 | } 369 | 370 | #[test] 371 | fn test_snapshot_init2() { 372 | if !Path::new("saved-states").exists() && std::fs::create_dir("saved-states").is_err() { 373 | println!("Could not create saved-states dir, aborting!"); 374 | std::process::exit(0); 375 | } 376 | 377 | let afl_server_snap: FITMSnapshot = FITMSnapshot::new( 378 | 1, 379 | 0, 380 | "tests/targets/pseudoserver_simple".to_string(), 381 | std::time::Duration::from_secs(5), 382 | "".to_string(), 383 | true, 384 | false, 385 | None, 386 | ); 387 | 388 | afl_server_snap 389 | .init_run(false, true, &[], &[], &[]) 390 | .unwrap(); 391 | 392 | std::fs::write("./saved-states/fitm-gen1-state0/in/testinp", "ulullulul") 393 | .expect("failed to create test-input"); 394 | 395 | afl_server_snap 396 | .create_outputs( 397 | "./saved-states/fitm-gen1-state0/in", 398 | "./saved-states/fitm-gen1-state0/outputs", 399 | ) 400 | .unwrap(); 401 | } 402 | } 403 | -------------------------------------------------------------------------------- /fitm-qemu/libqasan/hooks.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Copyright (c) 2019-2020, Andrea Fioraldi 3 | 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | *******************************************************************************/ 25 | 26 | #include "libqasan.h" 27 | #include "map_macro.h" 28 | 29 | ssize_t (*__lq_libc_write)(int, const void *, size_t); 30 | ssize_t (*__lq_libc_read)(int, void *, size_t); 31 | char *(*__lq_libc_fgets)(char *, int, FILE *); 32 | int (*__lq_libc_atoi)(const char *); 33 | long (*__lq_libc_atol)(const char *); 34 | long long (*__lq_libc_atoll)(const char *); 35 | 36 | void __libqasan_init_hooks(void) { 37 | 38 | __libqasan_init_malloc(); 39 | 40 | __lq_libc_write = ASSERT_DLSYM(write); 41 | __lq_libc_read = ASSERT_DLSYM(read); 42 | __lq_libc_fgets = ASSERT_DLSYM(fgets); 43 | __lq_libc_atoi = ASSERT_DLSYM(atoi); 44 | __lq_libc_atol = ASSERT_DLSYM(atol); 45 | __lq_libc_atoll = ASSERT_DLSYM(atoll); 46 | 47 | } 48 | 49 | ssize_t write(int fd, const void *buf, size_t count) { 50 | 51 | void *rtv = __builtin_return_address(0); 52 | 53 | QASAN_DEBUG("%14p: write(%d, %p, %zu)\n", rtv, fd, buf, count); 54 | QASAN_LOAD(buf, count); 55 | ssize_t r = __lq_libc_write(fd, buf, count); 56 | QASAN_DEBUG("\t\t = %zd\n", r); 57 | 58 | return r; 59 | 60 | } 61 | 62 | ssize_t read(int fd, void *buf, size_t count) { 63 | 64 | void *rtv = __builtin_return_address(0); 65 | 66 | QASAN_DEBUG("%14p: read(%d, %p, %zu)\n", rtv, fd, buf, count); 67 | QASAN_STORE(buf, count); 68 | ssize_t r = __lq_libc_read(fd, buf, count); 69 | QASAN_DEBUG("\t\t = %zd\n", r); 70 | 71 | return r; 72 | 73 | } 74 | 75 | #ifdef __ANDROID__ 76 | size_t malloc_usable_size(const void *ptr) { 77 | 78 | #else 79 | size_t malloc_usable_size(void *ptr) { 80 | 81 | #endif 82 | 83 | void *rtv = __builtin_return_address(0); 84 | 85 | QASAN_DEBUG("%14p: malloc_usable_size(%p)\n", rtv, ptr); 86 | size_t r = __libqasan_malloc_usable_size((void *)ptr); 87 | QASAN_DEBUG("\t\t = %zu\n", r); 88 | 89 | return r; 90 | 91 | } 92 | 93 | void *malloc(size_t size) { 94 | 95 | void *rtv = __builtin_return_address(0); 96 | 97 | QASAN_DEBUG("%14p: malloc(%zu)\n", rtv, size); 98 | void *r = __libqasan_malloc(size); 99 | QASAN_DEBUG("\t\t = %p\n", r); 100 | 101 | return r; 102 | 103 | } 104 | 105 | void *calloc(size_t nmemb, size_t size) { 106 | 107 | void *rtv = __builtin_return_address(0); 108 | 109 | QASAN_DEBUG("%14p: calloc(%zu, %zu)\n", rtv, nmemb, size); 110 | void *r = __libqasan_calloc(nmemb, size); 111 | QASAN_DEBUG("\t\t = %p\n", r); 112 | 113 | return r; 114 | 115 | } 116 | 117 | void *realloc(void *ptr, size_t size) { 118 | 119 | void *rtv = __builtin_return_address(0); 120 | 121 | QASAN_DEBUG("%14p: realloc(%p, %zu)\n", rtv, ptr, size); 122 | void *r = __libqasan_realloc(ptr, size); 123 | QASAN_DEBUG("\t\t = %p\n", r); 124 | 125 | return r; 126 | 127 | } 128 | 129 | int posix_memalign(void **memptr, size_t alignment, size_t size) { 130 | 131 | void *rtv = __builtin_return_address(0); 132 | 133 | QASAN_DEBUG("%14p: posix_memalign(%p, %zu, %zu)\n", rtv, memptr, alignment, 134 | size); 135 | int r = __libqasan_posix_memalign(memptr, alignment, size); 136 | QASAN_DEBUG("\t\t = %d [*memptr = %p]\n", r, *memptr); 137 | 138 | return r; 139 | 140 | } 141 | 142 | void *memalign(size_t alignment, size_t size) { 143 | 144 | void *rtv = __builtin_return_address(0); 145 | 146 | QASAN_DEBUG("%14p: memalign(%zu, %zu)\n", rtv, alignment, size); 147 | void *r = __libqasan_memalign(alignment, size); 148 | QASAN_DEBUG("\t\t = %p\n", r); 149 | 150 | return r; 151 | 152 | } 153 | 154 | void *aligned_alloc(size_t alignment, size_t size) { 155 | 156 | void *rtv = __builtin_return_address(0); 157 | 158 | QASAN_DEBUG("%14p: aligned_alloc(%zu, %zu)\n", rtv, alignment, size); 159 | void *r = __libqasan_aligned_alloc(alignment, size); 160 | QASAN_DEBUG("\t\t = %p\n", r); 161 | 162 | return r; 163 | 164 | } 165 | 166 | void *valloc(size_t size) { 167 | 168 | void *rtv = __builtin_return_address(0); 169 | 170 | QASAN_DEBUG("%14p: valloc(%zu)\n", rtv, size); 171 | void *r = __libqasan_memalign(sysconf(_SC_PAGESIZE), size); 172 | QASAN_DEBUG("\t\t = %p\n", r); 173 | 174 | return r; 175 | 176 | } 177 | 178 | void *pvalloc(size_t size) { 179 | 180 | void *rtv = __builtin_return_address(0); 181 | 182 | QASAN_DEBUG("%14p: pvalloc(%zu)\n", rtv, size); 183 | size_t page_size = sysconf(_SC_PAGESIZE); 184 | size = (size & (page_size - 1)) + page_size; 185 | void *r = __libqasan_memalign(page_size, size); 186 | QASAN_DEBUG("\t\t = %p\n", r); 187 | 188 | return r; 189 | 190 | } 191 | 192 | void free(void *ptr) { 193 | 194 | void *rtv = __builtin_return_address(0); 195 | 196 | QASAN_DEBUG("%14p: free(%p)\n", rtv, ptr); 197 | __libqasan_free(ptr); 198 | 199 | } 200 | 201 | char *fgets(char *s, int size, FILE *stream) { 202 | 203 | void *rtv = __builtin_return_address(0); 204 | 205 | QASAN_DEBUG("%14p: fgets(%p, %d, %p)\n", rtv, s, size, stream); 206 | QASAN_STORE(s, size); 207 | #ifndef __ANDROID__ 208 | QASAN_LOAD(stream, sizeof(FILE)); 209 | #endif 210 | char *r = __lq_libc_fgets(s, size, stream); 211 | QASAN_DEBUG("\t\t = %p\n", r); 212 | 213 | return r; 214 | 215 | } 216 | 217 | int memcmp(const void *s1, const void *s2, size_t n) { 218 | 219 | void *rtv = __builtin_return_address(0); 220 | 221 | QASAN_DEBUG("%14p: memcmp(%p, %p, %zu)\n", rtv, s1, s2, n); 222 | QASAN_LOAD(s1, n); 223 | QASAN_LOAD(s2, n); 224 | int r = __libqasan_memcmp(s1, s2, n); 225 | QASAN_DEBUG("\t\t = %d\n", r); 226 | 227 | return r; 228 | 229 | } 230 | 231 | void *memcpy(void *dest, const void *src, size_t n) { 232 | 233 | void *rtv = __builtin_return_address(0); 234 | 235 | QASAN_DEBUG("%14p: memcpy(%p, %p, %zu)\n", rtv, dest, src, n); 236 | QASAN_LOAD(src, n); 237 | QASAN_STORE(dest, n); 238 | void *r = __libqasan_memcpy(dest, src, n); 239 | QASAN_DEBUG("\t\t = %p\n", r); 240 | 241 | return r; 242 | 243 | } 244 | 245 | void *mempcpy(void *dest, const void *src, size_t n) { 246 | 247 | void *rtv = __builtin_return_address(0); 248 | 249 | QASAN_DEBUG("%14p: mempcpy(%p, %p, %zu)\n", rtv, dest, src, n); 250 | QASAN_LOAD(src, n); 251 | QASAN_STORE(dest, n); 252 | void *r = (uint8_t *)__libqasan_memcpy(dest, src, n) + n; 253 | QASAN_DEBUG("\t\t = %p\n", r); 254 | 255 | return r; 256 | 257 | } 258 | 259 | void *memmove(void *dest, const void *src, size_t n) { 260 | 261 | void *rtv = __builtin_return_address(0); 262 | 263 | QASAN_DEBUG("%14p: memmove(%p, %p, %zu)\n", rtv, dest, src, n); 264 | QASAN_LOAD(src, n); 265 | QASAN_STORE(dest, n); 266 | void *r = __libqasan_memmove(dest, src, n); 267 | QASAN_DEBUG("\t\t = %p\n", r); 268 | 269 | return r; 270 | 271 | } 272 | 273 | void *memset(void *s, int c, size_t n) { 274 | 275 | void *rtv = __builtin_return_address(0); 276 | 277 | QASAN_DEBUG("%14p: memset(%p, %d, %zu)\n", rtv, s, c, n); 278 | QASAN_STORE(s, n); 279 | void *r = __libqasan_memset(s, c, n); 280 | QASAN_DEBUG("\t\t = %p\n", r); 281 | 282 | return r; 283 | 284 | } 285 | 286 | void *memchr(const void *s, int c, size_t n) { 287 | 288 | void *rtv = __builtin_return_address(0); 289 | 290 | QASAN_DEBUG("%14p: memchr(%p, %d, %zu)\n", rtv, s, c, n); 291 | void *r = __libqasan_memchr(s, c, n); 292 | if (r == NULL) 293 | QASAN_LOAD(s, n); 294 | else 295 | QASAN_LOAD(s, r - s); 296 | QASAN_DEBUG("\t\t = %p\n", r); 297 | 298 | return r; 299 | 300 | } 301 | 302 | void *memrchr(const void *s, int c, size_t n) { 303 | 304 | void *rtv = __builtin_return_address(0); 305 | 306 | QASAN_DEBUG("%14p: memrchr(%p, %d, %zu)\n", rtv, s, c, n); 307 | QASAN_LOAD(s, n); 308 | void *r = __libqasan_memrchr(s, c, n); 309 | QASAN_DEBUG("\t\t = %p\n", r); 310 | 311 | return r; 312 | 313 | } 314 | 315 | void *memmem(const void *haystack, size_t haystacklen, const void *needle, 316 | size_t needlelen) { 317 | 318 | void *rtv = __builtin_return_address(0); 319 | 320 | QASAN_DEBUG("%14p: memmem(%p, %zu, %p, %zu)\n", rtv, haystack, haystacklen, 321 | needle, needlelen); 322 | QASAN_LOAD(haystack, haystacklen); 323 | QASAN_LOAD(needle, needlelen); 324 | void *r = __libqasan_memmem(haystack, haystacklen, needle, needlelen); 325 | QASAN_DEBUG("\t\t = %p\n", r); 326 | 327 | return r; 328 | 329 | } 330 | 331 | #ifndef __BIONIC__ 332 | void bzero(void *s, size_t n) { 333 | 334 | void *rtv = __builtin_return_address(0); 335 | 336 | QASAN_DEBUG("%14p: bzero(%p, %zu)\n", rtv, s, n); 337 | QASAN_STORE(s, n); 338 | __libqasan_memset(s, 0, n); 339 | 340 | } 341 | 342 | #endif 343 | 344 | void explicit_bzero(void *s, size_t n) { 345 | 346 | void *rtv = __builtin_return_address(0); 347 | 348 | QASAN_DEBUG("%14p: bzero(%p, %zu)\n", rtv, s, n); 349 | QASAN_STORE(s, n); 350 | __libqasan_memset(s, 0, n); 351 | 352 | } 353 | 354 | int bcmp(const void *s1, const void *s2, size_t n) { 355 | 356 | void *rtv = __builtin_return_address(0); 357 | 358 | QASAN_DEBUG("%14p: bcmp(%p, %p, %zu)\n", rtv, s1, s2, n); 359 | QASAN_LOAD(s1, n); 360 | QASAN_LOAD(s2, n); 361 | int r = __libqasan_bcmp(s1, s2, n); 362 | QASAN_DEBUG("\t\t = %d\n", r); 363 | 364 | return r; 365 | 366 | } 367 | 368 | char *strchr(const char *s, int c) { 369 | 370 | void *rtv = __builtin_return_address(0); 371 | 372 | QASAN_DEBUG("%14p: strchr(%p, %d)\n", rtv, s, c); 373 | size_t l = __libqasan_strlen(s); 374 | QASAN_LOAD(s, l + 1); 375 | void *r = __libqasan_strchr(s, c); 376 | QASAN_DEBUG("\t\t = %p\n", r); 377 | 378 | return r; 379 | 380 | } 381 | 382 | char *strrchr(const char *s, int c) { 383 | 384 | void *rtv = __builtin_return_address(0); 385 | 386 | QASAN_DEBUG("%14p: strrchr(%p, %d)\n", rtv, s, c); 387 | size_t l = __libqasan_strlen(s); 388 | QASAN_LOAD(s, l + 1); 389 | void *r = __libqasan_strrchr(s, c); 390 | QASAN_DEBUG("\t\t = %p\n", r); 391 | 392 | return r; 393 | 394 | } 395 | 396 | int strcasecmp(const char *s1, const char *s2) { 397 | 398 | void *rtv = __builtin_return_address(0); 399 | 400 | QASAN_DEBUG("%14p: strcasecmp(%p, %p)\n", rtv, s1, s2); 401 | size_t l1 = __libqasan_strlen(s1); 402 | QASAN_LOAD(s1, l1 + 1); 403 | size_t l2 = __libqasan_strlen(s2); 404 | QASAN_LOAD(s2, l2 + 1); 405 | int r = __libqasan_strcasecmp(s1, s2); 406 | QASAN_DEBUG("\t\t = %d\n", r); 407 | 408 | return r; 409 | 410 | } 411 | 412 | int strncasecmp(const char *s1, const char *s2, size_t n) { 413 | 414 | void *rtv = __builtin_return_address(0); 415 | 416 | QASAN_DEBUG("%14p: strncasecmp(%p, %p, %zu)\n", rtv, s1, s2, n); 417 | size_t l1 = __libqasan_strnlen(s1, n); 418 | QASAN_LOAD(s1, l1); 419 | size_t l2 = __libqasan_strnlen(s2, n); 420 | QASAN_LOAD(s2, l2); 421 | int r = __libqasan_strncasecmp(s1, s2, n); 422 | QASAN_DEBUG("\t\t = %d\n", r); 423 | 424 | return r; 425 | 426 | } 427 | 428 | char *strcat(char *dest, const char *src) { 429 | 430 | void *rtv = __builtin_return_address(0); 431 | 432 | QASAN_DEBUG("%14p: strcat(%p, %p)\n", rtv, dest, src); 433 | size_t l2 = __libqasan_strlen(src); 434 | QASAN_LOAD(src, l2 + 1); 435 | size_t l1 = __libqasan_strlen(dest); 436 | QASAN_STORE(dest, l1 + l2 + 1); 437 | __libqasan_memcpy(dest + l1, src, l2); 438 | dest[l1 + l2] = 0; 439 | void *r = dest; 440 | QASAN_DEBUG("\t\t = %p\n", r); 441 | 442 | return r; 443 | 444 | } 445 | 446 | int strcmp(const char *s1, const char *s2) { 447 | 448 | void *rtv = __builtin_return_address(0); 449 | 450 | QASAN_DEBUG("%14p: strcmp(%p, %p)\n", rtv, s1, s2); 451 | size_t l1 = __libqasan_strlen(s1); 452 | QASAN_LOAD(s1, l1 + 1); 453 | size_t l2 = __libqasan_strlen(s2); 454 | QASAN_LOAD(s2, l2 + 1); 455 | int r = __libqasan_strcmp(s1, s2); 456 | QASAN_DEBUG("\t\t = %d\n", r); 457 | 458 | return r; 459 | 460 | } 461 | 462 | int strncmp(const char *s1, const char *s2, size_t n) { 463 | 464 | void *rtv = __builtin_return_address(0); 465 | 466 | QASAN_DEBUG("%14p: strncmp(%p, %p, %zu)\n", rtv, s1, s2, n); 467 | size_t l1 = __libqasan_strnlen(s1, n); 468 | QASAN_LOAD(s1, l1); 469 | size_t l2 = __libqasan_strnlen(s2, n); 470 | QASAN_LOAD(s2, l2); 471 | int r = __libqasan_strncmp(s1, s2, n); 472 | QASAN_DEBUG("\t\t = %d\n", r); 473 | 474 | return r; 475 | 476 | } 477 | 478 | char *strcpy(char *dest, const char *src) { 479 | 480 | void *rtv = __builtin_return_address(0); 481 | 482 | QASAN_DEBUG("%14p: strcpy(%p, %p)\n", rtv, dest, src); 483 | size_t l = __libqasan_strlen(src) + 1; 484 | QASAN_LOAD(src, l); 485 | QASAN_STORE(dest, l); 486 | void *r = __libqasan_memcpy(dest, src, l); 487 | QASAN_DEBUG("\t\t = %p\n", r); 488 | 489 | return r; 490 | 491 | } 492 | 493 | char *strncpy(char *dest, const char *src, size_t n) { 494 | 495 | void *rtv = __builtin_return_address(0); 496 | 497 | QASAN_DEBUG("%14p: strncpy(%p, %p, %zu)\n", rtv, dest, src, n); 498 | size_t l = __libqasan_strnlen(src, n); 499 | QASAN_STORE(dest, n); 500 | void *r; 501 | if (l < n) { 502 | 503 | QASAN_LOAD(src, l + 1); 504 | r = __libqasan_memcpy(dest, src, l + 1); 505 | 506 | } else { 507 | 508 | QASAN_LOAD(src, n); 509 | r = __libqasan_memcpy(dest, src, n); 510 | 511 | } 512 | 513 | QASAN_DEBUG("\t\t = %p\n", r); 514 | 515 | return r; 516 | 517 | } 518 | 519 | char *stpcpy(char *dest, const char *src) { 520 | 521 | void *rtv = __builtin_return_address(0); 522 | 523 | QASAN_DEBUG("%14p: stpcpy(%p, %p)\n", rtv, dest, src); 524 | size_t l = __libqasan_strlen(src) + 1; 525 | QASAN_LOAD(src, l); 526 | QASAN_STORE(dest, l); 527 | char *r = __libqasan_memcpy(dest, src, l) + (l - 1); 528 | QASAN_DEBUG("\t\t = %p\n", r); 529 | 530 | return r; 531 | 532 | } 533 | 534 | char *strdup(const char *s) { 535 | 536 | void *rtv = __builtin_return_address(0); 537 | 538 | QASAN_DEBUG("%14p: strdup(%p)\n", rtv, s); 539 | size_t l = __libqasan_strlen(s); 540 | QASAN_LOAD(s, l + 1); 541 | void *r = __libqasan_malloc(l + 1); 542 | __libqasan_memcpy(r, s, l + 1); 543 | QASAN_DEBUG("\t\t = %p\n", r); 544 | 545 | return r; 546 | 547 | } 548 | 549 | size_t strlen(const char *s) { 550 | 551 | void *rtv = __builtin_return_address(0); 552 | 553 | QASAN_DEBUG("%14p: strlen(%p)\n", rtv, s); 554 | size_t r = __libqasan_strlen(s); 555 | QASAN_LOAD(s, r + 1); 556 | QASAN_DEBUG("\t\t = %zu\n", r); 557 | 558 | return r; 559 | 560 | } 561 | 562 | size_t strnlen(const char *s, size_t n) { 563 | 564 | void *rtv = __builtin_return_address(0); 565 | 566 | QASAN_DEBUG("%14p: strnlen(%p, %zu)\n", rtv, s, n); 567 | size_t r = __libqasan_strnlen(s, n); 568 | QASAN_LOAD(s, r); 569 | QASAN_DEBUG("\t\t = %zu\n", r); 570 | 571 | return r; 572 | 573 | } 574 | 575 | char *strstr(const char *haystack, const char *needle) { 576 | 577 | void *rtv = __builtin_return_address(0); 578 | 579 | QASAN_DEBUG("%14p: strstr(%p, %p)\n", rtv, haystack, needle); 580 | size_t l = __libqasan_strlen(haystack) + 1; 581 | QASAN_LOAD(haystack, l); 582 | l = __libqasan_strlen(needle) + 1; 583 | QASAN_LOAD(needle, l); 584 | void *r = __libqasan_strstr(haystack, needle); 585 | QASAN_DEBUG("\t\t = %p\n", r); 586 | 587 | return r; 588 | 589 | } 590 | 591 | char *strcasestr(const char *haystack, const char *needle) { 592 | 593 | void *rtv = __builtin_return_address(0); 594 | 595 | QASAN_DEBUG("%14p: strcasestr(%p, %p)\n", rtv, haystack, needle); 596 | size_t l = __libqasan_strlen(haystack) + 1; 597 | QASAN_LOAD(haystack, l); 598 | l = __libqasan_strlen(needle) + 1; 599 | QASAN_LOAD(needle, l); 600 | void *r = __libqasan_strcasestr(haystack, needle); 601 | QASAN_DEBUG("\t\t = %p\n", r); 602 | 603 | return r; 604 | 605 | } 606 | 607 | int atoi(const char *nptr) { 608 | 609 | void *rtv = __builtin_return_address(0); 610 | 611 | QASAN_DEBUG("%14p: atoi(%p)\n", rtv, nptr); 612 | size_t l = __libqasan_strlen(nptr) + 1; 613 | QASAN_LOAD(nptr, l); 614 | int r = __lq_libc_atoi(nptr); 615 | QASAN_DEBUG("\t\t = %d\n", r); 616 | 617 | return r; 618 | 619 | } 620 | 621 | long atol(const char *nptr) { 622 | 623 | void *rtv = __builtin_return_address(0); 624 | 625 | QASAN_DEBUG("%14p: atol(%p)\n", rtv, nptr); 626 | size_t l = __libqasan_strlen(nptr) + 1; 627 | QASAN_LOAD(nptr, l); 628 | long r = __lq_libc_atol(nptr); 629 | QASAN_DEBUG("\t\t = %ld\n", r); 630 | 631 | return r; 632 | 633 | } 634 | 635 | long long atoll(const char *nptr) { 636 | 637 | void *rtv = __builtin_return_address(0); 638 | 639 | QASAN_DEBUG("%14p: atoll(%p)\n", rtv, nptr); 640 | size_t l = __libqasan_strlen(nptr) + 1; 641 | QASAN_LOAD(nptr, l); 642 | long long r = __lq_libc_atoll(nptr); 643 | QASAN_DEBUG("\t\t = %lld\n", r); 644 | 645 | return r; 646 | 647 | } 648 | 649 | size_t wcslen(const wchar_t *s) { 650 | 651 | void *rtv = __builtin_return_address(0); 652 | 653 | QASAN_DEBUG("%14p: wcslen(%p)\n", rtv, s); 654 | size_t r = __libqasan_wcslen(s); 655 | QASAN_LOAD(s, sizeof(wchar_t) * (r + 1)); 656 | QASAN_DEBUG("\t\t = %zu\n", r); 657 | 658 | return r; 659 | 660 | } 661 | 662 | wchar_t *wcscpy(wchar_t *dest, const wchar_t *src) { 663 | 664 | void *rtv = __builtin_return_address(0); 665 | 666 | QASAN_DEBUG("%14p: wcscpy(%p, %p)\n", rtv, dest, src); 667 | size_t l = __libqasan_wcslen(src) + 1; 668 | QASAN_LOAD(src, l * sizeof(wchar_t)); 669 | QASAN_STORE(dest, l * sizeof(wchar_t)); 670 | void *r = __libqasan_wcscpy(dest, src); 671 | QASAN_DEBUG("\t\t = %p\n", r); 672 | 673 | return r; 674 | 675 | } 676 | 677 | int wcscmp(const wchar_t *s1, const wchar_t *s2) { 678 | 679 | void *rtv = __builtin_return_address(0); 680 | 681 | QASAN_DEBUG("%14p: wcscmp(%p, %p)\n", rtv, s1, s2); 682 | size_t l1 = __libqasan_wcslen(s1); 683 | QASAN_LOAD(s1, sizeof(wchar_t) * (l1 + 1)); 684 | size_t l2 = __libqasan_wcslen(s2); 685 | QASAN_LOAD(s2, sizeof(wchar_t) * (l2 + 1)); 686 | int r = __libqasan_wcscmp(s1, s2); 687 | QASAN_DEBUG("\t\t = %d\n", r); 688 | 689 | return r; 690 | 691 | } 692 | 693 | --------------------------------------------------------------------------------