diff options
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | README.md | 9 | ||||
| -rw-r--r-- | cover.c | 4 | ||||
| -rw-r--r-- | fix.m4 | 93 | ||||
| -rw-r--r-- | jump.c | 4 | ||||
| -rw-r--r-- | reach | 27 | 
6 files changed, 101 insertions, 38 deletions
| diff --git a/Makefile b/Makefile index 3b1e5f5..d707904 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ PREFIX ?= /usr/local BIN_PREFIX ::= $(DESTDIR)$(PREFIX)/bin/taosc- DATA_DIR ::= $(DESTDIR)$(PREFIX)/share/taosc -BIN ::= fix measure-stack scout synth trace-call +BIN ::= fix measure-stack reach scout synth trace-call DATA ::= collect cover jump patch all: $(BIN) $(DATA) diff --git a/README.md b/README.md index b1d40e9..7a91a02 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,9 @@ Taosc is an automated makeshift patcher for binary programs. ## Installation -Taosc depends on GDB, [Dyninst] and [E9Patch] and POSIX utilities. -To build taosc, you need [GNU M4] and a compiler for C++23 and [Zig] 0.15: +Taosc depends on GDB, [Dyninst], [E9Patch], [FUZZOLIC], [GNU Parallel] +and POSIX utilities. To build taosc, you need [GNU M4] and a compiler +for C++23 and [Zig] 0.15: make -j$(nproc) @@ -15,7 +16,7 @@ To install taosc to `$prefix`, you'll also need `install(1p)`: ## Usage - taosc-fix WORKDIR EXECUTABLE OPTION... + taosc-fix WORKDIR TIMEOUT EXECUTABLE PROOFS-OF-CONCEPT [OPTION]... ## Copying @@ -26,5 +27,7 @@ or (at your option) any later version. [Dyninst]: https://github.com/dyninst/dyninst [E9Patch]: https://github.com/gjduck/e9patch +[FUZZOLIC]: https://season-lab.github.io/fuzzolic [GNU M4]: https://www.gnu.org/software/m4 +[GNU Parallel]: https://www.gnu.org/software/parallel [Zig]: https://ziglang.org diff --git a/cover.c b/cover.c index 3061646..591224c 100644 --- a/cover.c +++ b/cover.c @@ -1,6 +1,6 @@ /* - * Live variable collector - * Copyright (C) 2024-2025 Nguyễn Gia Phong + * Patch for coverage reporting + * Copyright (C) 2025 Nguyễn Gia Phong * * This file is part of taosc. * diff --git a/fix.m4 b/fix.m4 index 065f91e..3425e53 100644 --- a/fix.m4 +++ b/fix.m4 @@ -20,30 +20,40 @@ set -eux -o pipefail save_exit_code() { set +e - # TODO: make timeout configurable - timeout -k 1 5 $@ 2>&1 1>/dev/null + timeout -k 1 $1 ${@:2} 1>/dev/null 2>&1 exit_code=$? set -e } -if test $# -lt 3 +bad() { + save_exit_code $@ + test $exit_code -gt 128 || + test $exit_code -ge 124 -a $exit_code -le 127 # timeout +} + +if test $# -lt 4 then - echo Usage: taosc-fix WORKDIR EXECUTABLE OPTION... + echo Usage: taosc-fix WORKDIR TIMEOUT EXECUTABLE PROOFS-OF-CONCEPT [OPTION]... exit 1 fi wd="$(realpath $1)" test -d "$wd" -bin="$wd/$(basename $2)" -binary="$(realpath $2)" +timeout=$2 +bin="$wd/$(basename $3)" +binary="$(realpath $3)" test -x "$binary" -opts="${@:3}" # TODO: interpolation +poc="$(realpath $4)" +test -d "$poc" +test "$(ls -A "$poc")" +options="${@:5}" # TODO: interpolation -test -d "$wd/exploits" -test -n "$(ls -A "$wd/exploits")" -for exploit in "$wd/exploits"/* +mkdir -p "$wd" +rm -fr "$wd/poc" +cp -r "$poc" "$wd/poc" +for exploit in "$wd"/poc/* do gdb --batch --ex run --ex backtrace --args\ - "$binary" "$opts" "$exploit" 2>/dev/null | + "$binary" $options "$exploit" 2>/dev/null | grep '^#[0-9]\+ \+0x[0-9a-f]\+' | awk '!$7 || $7 == bin {print $1, $2}' "bin=$binary" | sed 's/^#//' @@ -58,8 +68,8 @@ grep -v '^0 0x[0-9a-f]\+$' "$wd/stack-trace" | taosc-trace-call "$binary" >> "$wd/call-trace" rm -f "$wd/patch-location" -pushd DATA_DIR > /dev/null -trap 'popd > /dev/null' EXIT +pushd DATA_DIR 1>/dev/null +trap 'popd 1>/dev/null' EXIT taosc-scout "$binary" < "$wd/call-trace" | while read loc destinations && test ! -f "$wd/patch-location" do @@ -67,11 +77,12 @@ taosc-scout "$binary" < "$wd/call-trace" | rm -f "$wd/destinations" for dest in $destinations do - for exploit in "$wd/exploits"/* + # In case $wd/poc got poluted + rm -fr "$wd/poc" + cp -r "$poc" "$wd/poc" + for exploit in "$wd/poc"/* do - save_exit_code env TAOSC_DEST=0x$dest "$bin.jump" "$opts" "$exploit" - if test $exit_code -gt 128 || - test $exit_code -ge 124 -a $exit_code -le 127 # timeout + if bad $timeout env TAOSC_DEST=0x$dest "$bin.jump" $options "$exploit" then continue 2 # next destination fi @@ -79,27 +90,49 @@ taosc-scout "$binary" < "$wd/call-trace" | echo $loc > "$wd/patch-location" echo $dest >> "$wd/destinations" done - done 2>&1 1>/dev/null + done 1>/dev/null 2>&1 test -s "$wd/patch-location" test -s "$wd/destinations" +stack_size=$(taosc-measure-stack "$binary" < "$wd/patch-location") patch_loc=0x$(< "$wd/patch-location") e9tool -100 -M addr=$patch_loc -P 'report()@cover' -o "$bin.covered" "$binary" e9tool -100 -M addr=$patch_loc -P 'log(state)@collect'\ -o "$bin.collect" "$binary" e9tool -100 -M addr=$patch_loc -P 'if dest(state)@patch goto'\ -o "$bin.patched" "$binary" -exit -stack_size=$(taosc-measure-stack "$binary" < "$wd/patch-location") -afl-dyninst -x "$binary" "$bin.fuzzee" -install -Dm 644 DATA_DIR/collection "$wd/vars/list" -# TODO: augment number of executions -afl-dyninst-env afl-fuzz -i "$wd/fuzz/exploits" -o "$wd/fuzz/crashes"\ - -CE 10000 -- "$bin.fuzzee" $opts @@ -install -d "$wd/vars/neg" -find "$wd/fuzz/crashes/default/crashes" -name id:* | parallel\ - TAOSC_OUTPUT="$wd/vars/neg/"'$(basename {})' "$bin.collect" $opts {} || true -taosc-synth "$wd/vars" > "$wd/predicates" -taosc-scout "$binary" "$address" > "$wd/destinations" +# TODO: FUZZOLIC's options +fuzzolic -kmprst 90000 -i "$poc" -o "$wd/fuzzolic" -- "$binary" $option @@ +rm -fr "$wd/input" +mkdir -p "$wd/input/benign" +cp -r "$poc" "$wd/input/malicious" +# TODO: use parallel +for dat in "$wd"/fuzzolic/fuzzolic-*/test_case_*.dat +do + if taosc-reach $timeout "$bin.covered" $options "$dat" 1>/dev/null 2>&1 + then + if bad $timeout "$binary" $options "$dat" + then + cp $dat "$wd/input/malicious" + else + cp $dat "$wd/input/benign" + fi + fi +done + +rm -fr "$wd/values" +for input_dir in "$wd"/input/* +do + output_dir="$wd/values/$(basename "$input_dir")" + mkdir -p "$output_dir" + # TODO: use parallel + for input in "$input_dir"/* + do + output="$output_dir/$(basename "$input")" + save_exit_code $timeout\ + env TAOSC_STACK_SIZE=$stack_size TAOSC_OUTPUT=$output\ + "$bin.collect" $options "$input" + done +done # vim: filetype=sh.m4 diff --git a/jump.c b/jump.c index 794d79d..5110ae9 100644 --- a/jump.c +++ b/jump.c @@ -1,5 +1,5 @@ /* - * TODO + * Dynamic patch to jump to bailout action * Copyright (C) 2024-2025 Nguyễn Gia Phong * * This file is part of taosc. @@ -26,7 +26,7 @@ static const void *destination; * Get an environment variable and parse as a number. * Return 0 on error. */ -uint64_t getenvull(const char *name) +static uint64_t getenvull(const char *name) { const char *const s = getenv(name); if (s == NULL) diff --git a/reach b/reach new file mode 100644 index 0000000..1827fc5 --- /dev/null +++ b/reach @@ -0,0 +1,27 @@ +#!/bin/sh +# Patch location coverage checker +# Copyright (C) 2025 Nguyễn Gia Phong +# +# This file is part of taosc. +# +# Taosc is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Taosc is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with taosc. If not, see <https://www.gnu.org/licenses/>. + +set -u +test $# -lt 2 && echo Usage: taosc-reach DURATION COMMAND [ARG]... && exit 1 +reached=$(mktemp -u) +TAOSC_OUTPUT=$reached timeout -k 0 $1 ${@:2} +test -f $reached || exit 2 +trap "rm $reached" EXIT +test -s $reached && exit 3 +exit 0 | 
