Added instrumentation for CMOV instructions

This commit is contained in:
Your Name
2022-02-18 07:55:45 +00:00
parent dd8ad4dfa3
commit cb1256499f
15 changed files with 457 additions and 28 deletions

View File

@ -173,6 +173,8 @@ instances run CMPLOG mode and instrumentation of the binary is less frequent
* `AFL_FRIDA_INST_JIT` - Enable the instrumentation of Just-In-Time compiled * `AFL_FRIDA_INST_JIT` - Enable the instrumentation of Just-In-Time compiled
code. Code is considered to be JIT if the executable segment is not backed by code. Code is considered to be JIT if the executable segment is not backed by
a file. a file.
* `AFL_FRIDA_INST_NO_INSN` - Don't generate instrumentation for conditional
instructions (e.g. `CMOV` instructions on x64).
* `AFL_FRIDA_INST_NO_OPTIMIZE` - Don't use optimized inline assembly coverage * `AFL_FRIDA_INST_NO_OPTIMIZE` - Don't use optimized inline assembly coverage
instrumentation (the default where available). Required to use instrumentation (the default where available). Required to use
`AFL_FRIDA_INST_TRACE`. `AFL_FRIDA_INST_TRACE`.

View File

@ -15,6 +15,7 @@
js_api_set_instrument_debug_file; js_api_set_instrument_debug_file;
js_api_set_instrument_jit; js_api_set_instrument_jit;
js_api_set_instrument_libraries; js_api_set_instrument_libraries;
js_api_set_instrument_no_instructions;
js_api_set_instrument_no_optimize; js_api_set_instrument_no_optimize;
js_api_set_instrument_seed; js_api_set_instrument_seed;
js_api_set_instrument_trace; js_api_set_instrument_trace;

View File

@ -12,6 +12,7 @@ extern gboolean instrument_optimize;
extern gboolean instrument_unique; extern gboolean instrument_unique;
extern guint64 instrument_hash_zero; extern guint64 instrument_hash_zero;
extern char * instrument_coverage_unstable_filename; extern char * instrument_coverage_unstable_filename;
extern gboolean instrument_coverage_insn;
extern gboolean instrument_use_fixed_seed; extern gboolean instrument_use_fixed_seed;
extern guint64 instrument_fixed_seed; extern guint64 instrument_fixed_seed;
@ -33,6 +34,8 @@ gboolean instrument_is_coverage_optimize_supported(void);
void instrument_coverage_optimize_init(void); void instrument_coverage_optimize_init(void);
void instrument_coverage_optimize(const cs_insn * instr, void instrument_coverage_optimize(const cs_insn * instr,
GumStalkerOutput *output); GumStalkerOutput *output);
void instrument_coverage_optimize_insn(const cs_insn * instr,
GumStalkerOutput *output);
void instrument_debug_config(void); void instrument_debug_config(void);
void instrument_debug_init(void); void instrument_debug_init(void);

View File

@ -29,6 +29,7 @@ guint64 instrument_hash_seed = 0;
gboolean instrument_use_fixed_seed = FALSE; gboolean instrument_use_fixed_seed = FALSE;
guint64 instrument_fixed_seed = 0; guint64 instrument_fixed_seed = 0;
char * instrument_coverage_unstable_filename = NULL; char * instrument_coverage_unstable_filename = NULL;
gboolean instrument_coverage_insn = FALSE;
static GumStalkerTransformer *transformer = NULL; static GumStalkerTransformer *transformer = NULL;
@ -233,6 +234,12 @@ static void instrument_basic_block(GumStalkerIterator *iterator,
} }
if (instrument_coverage_insn) {
instrument_coverage_optimize_insn(instr, output);
}
instrument_debug_instruction(instr->address, instr->size, output); instrument_debug_instruction(instr->address, instr->size, output);
if (likely(!excluded)) { if (likely(!excluded)) {
@ -269,6 +276,7 @@ void instrument_config(void) {
instrument_fixed_seed = util_read_num("AFL_FRIDA_INST_SEED", 0); instrument_fixed_seed = util_read_num("AFL_FRIDA_INST_SEED", 0);
instrument_coverage_unstable_filename = instrument_coverage_unstable_filename =
(getenv("AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE")); (getenv("AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE"));
instrument_coverage_insn = (getenv("AFL_FRIDA_INST_NO_INSN") == NULL);
instrument_debug_config(); instrument_debug_config();
instrument_coverage_config(); instrument_coverage_config();

View File

@ -20,6 +20,15 @@ void instrument_coverage_optimize(const cs_insn * instr,
} }
void instrument_coverage_optimize_insn(const cs_insn * instr,
GumStalkerOutput *output) {
UNUSED_PARAMETER(instr);
UNUSED_PARAMETER(output);
FFATAL("Optimized coverage not supported on this architecture");
}
void instrument_coverage_optimize_init(void) { void instrument_coverage_optimize_init(void) {
FWARNF("Optimized coverage not supported on this architecture"); FWARNF("Optimized coverage not supported on this architecture");

View File

@ -341,6 +341,14 @@ void instrument_coverage_optimize(const cs_insn * instr,
} }
void instrument_coverage_optimize_insn(const cs_insn * instr,
GumStalkerOutput *output) {
UNUSED_PARAMETER(instr);
UNUSED_PARAMETER(output);
}
void instrument_coverage_optimize_init(void) { void instrument_coverage_optimize_init(void) {
char *shm_env = getenv(SHM_ENV_VAR); char *shm_env = getenv(SHM_ENV_VAR);

View File

@ -23,6 +23,40 @@
#if defined(__x86_64__) #if defined(__x86_64__)
enum jcc_opcodes {
OPC_JO = 0x70,
OPC_JNO = 0x71,
OPC_JB = 0x72,
OPC_JAE = 0x73,
OPC_JE = 0x74,
OPC_JNE = 0x75,
OPC_JBE = 0x76,
OPC_JA = 0x77,
OPC_JS = 0x78,
OPC_JNS = 0x79,
OPC_JP = 0x7a,
OPC_JNP = 0x7b,
OPC_JL = 0x7c,
OPC_JGE = 0x7d,
OPC_JLE = 0x7e,
OPC_JG = 0x7f,
};
typedef union {
struct {
uint8_t opcode;
uint8_t distance;
};
uint8_t bytes[0];
} jcc_insn;
static GHashTable *coverage_blocks = NULL; static GHashTable *coverage_blocks = NULL;
gboolean instrument_is_coverage_optimize_supported(void) { gboolean instrument_is_coverage_optimize_supported(void) {
@ -201,37 +235,15 @@ static void instrument_coverage_suppress_init(void) {
} }
void instrument_coverage_optimize(const cs_insn * instr, static void instrument_coverage_write(GumAddress address,
GumStalkerOutput *output) { GumStalkerOutput *output) {
afl_log_code code = {0}; afl_log_code code = {0};
GumX86Writer *cw = output->writer.x86; GumX86Writer *cw = output->writer.x86;
guint64 area_offset = instrument_get_offset_hash(GUM_ADDRESS(instr->address)); guint64 area_offset = instrument_get_offset_hash(address);
gsize map_size_pow2; gsize map_size_pow2;
gsize area_offset_ror; gsize area_offset_ror;
GumAddress code_addr = 0; GumAddress code_addr = cw->pc;
if (instrument_previous_pc_addr == NULL) {
GumAddressSpec spec = {.near_address = cw->code,
.max_distance = 1ULL << 30};
instrument_previous_pc_addr = gum_memory_allocate_near(
&spec, sizeof(guint64), 0x1000, GUM_PAGE_READ | GUM_PAGE_WRITE);
*instrument_previous_pc_addr = instrument_hash_zero;
FVERBOSE("instrument_previous_pc_addr: %p", instrument_previous_pc_addr);
FVERBOSE("code_addr: %p", cw->code);
}
instrument_coverage_suppress_init();
// gum_x86_writer_put_breakpoint(cw);
code_addr = cw->pc;
if (!g_hash_table_add(coverage_blocks, GSIZE_TO_POINTER(cw->code))) {
FATAL("Failed - g_hash_table_add");
}
code.code = template; code.code = template;
@ -313,6 +325,129 @@ void instrument_coverage_optimize(const cs_insn * instr,
} }
void instrument_coverage_optimize(const cs_insn * instr,
GumStalkerOutput *output) {
GumX86Writer *cw = output->writer.x86;
guint64 area_offset = instrument_get_offset_hash(GUM_ADDRESS(instr->address));
if (instrument_previous_pc_addr == NULL) {
GumAddressSpec spec = {.near_address = cw->code,
.max_distance = 1ULL << 30};
instrument_previous_pc_addr = gum_memory_allocate_near(
&spec, sizeof(guint64), 0x1000, GUM_PAGE_READ | GUM_PAGE_WRITE);
*instrument_previous_pc_addr = instrument_hash_zero;
FVERBOSE("instrument_previous_pc_addr: %p", instrument_previous_pc_addr);
FVERBOSE("code_addr: %p", cw->code);
}
instrument_coverage_suppress_init();
if (!g_hash_table_add(coverage_blocks, GSIZE_TO_POINTER(cw->code))) {
FATAL("Failed - g_hash_table_add");
}
instrument_coverage_write(GUM_ADDRESS(instr->address), output);
}
void instrument_coverage_optimize_insn(const cs_insn * instr,
GumStalkerOutput *output) {
GumX86Writer *cw = output->writer.x86;
jcc_insn taken, not_taken;
switch (instr->id) {
case X86_INS_CMOVA:
taken.opcode = OPC_JA;
not_taken.opcode = OPC_JBE;
break;
case X86_INS_CMOVAE:
taken.opcode = OPC_JAE;
not_taken.opcode = OPC_JB;
break;
case X86_INS_CMOVB:
taken.opcode = OPC_JB;
not_taken.opcode = OPC_JAE;
break;
case X86_INS_CMOVBE:
taken.opcode = OPC_JBE;
not_taken.opcode = OPC_JA;
break;
case X86_INS_CMOVE:
taken.opcode = OPC_JE;
not_taken.opcode = OPC_JNE;
break;
case X86_INS_CMOVG:
taken.opcode = OPC_JG;
not_taken.opcode = OPC_JLE;
break;
case X86_INS_CMOVGE:
taken.opcode = OPC_JGE;
not_taken.opcode = OPC_JL;
break;
case X86_INS_CMOVL:
taken.opcode = OPC_JL;
not_taken.opcode = OPC_JGE;
break;
case X86_INS_CMOVLE:
taken.opcode = OPC_JLE;
not_taken.opcode = OPC_JG;
break;
case X86_INS_CMOVNE:
taken.opcode = OPC_JNE;
not_taken.opcode = OPC_JE;
break;
case X86_INS_CMOVNO:
taken.opcode = OPC_JNO;
not_taken.opcode = OPC_JO;
break;
case X86_INS_CMOVNP:
taken.opcode = OPC_JNP;
not_taken.opcode = OPC_JP;
break;
case X86_INS_CMOVNS:
taken.opcode = OPC_JNS;
not_taken.opcode = OPC_JS;
break;
case X86_INS_CMOVO:
taken.opcode = OPC_JO;
not_taken.opcode = OPC_JNO;
break;
case X86_INS_CMOVP:
taken.opcode = OPC_JP;
not_taken.opcode = OPC_JNP;
break;
case X86_INS_CMOVS:
taken.opcode = OPC_JS;
not_taken.opcode = OPC_JNS;
break;
default:
return;
}
taken.distance = sizeof(afl_log_code);
not_taken.distance = sizeof(afl_log_code);
// gum_x86_writer_put_breakpoint(cw);
gum_x86_writer_put_bytes(cw, taken.bytes, sizeof(jcc_insn));
instrument_coverage_write(GUM_ADDRESS(instr->address), output);
gum_x86_writer_put_bytes(cw, not_taken.bytes, sizeof(jcc_insn));
instrument_coverage_write(GUM_ADDRESS(instr->address + instr->size), output);
FVERBOSE("Instrument - 0x%016lx: %s %s", instr->address, instr->mnemonic,
instr->op_str);
}
void instrument_flush(GumStalkerOutput *output) { void instrument_flush(GumStalkerOutput *output) {
gum_x86_writer_flush(output->writer.x86); gum_x86_writer_flush(output->writer.x86);

View File

@ -218,6 +218,14 @@ void instrument_coverage_optimize(const cs_insn * instr,
} }
void instrument_coverage_optimize_insn(const cs_insn * instr,
GumStalkerOutput *output) {
UNUSED_PARAMETER(instr);
UNUSED_PARAMETER(output);
}
void instrument_coverage_optimize_init(void) { void instrument_coverage_optimize_init(void) {
} }

View File

@ -125,6 +125,12 @@ class Afl {
static setInstrumentLibraries() { static setInstrumentLibraries() {
Afl.jsApiSetInstrumentLibraries(); Afl.jsApiSetInstrumentLibraries();
} }
/**
* See `AFL_FRIDA_INST_NO_INSN`
*/
static setInstrumentNoInstructions() {
Afl.jsApiSetInstrumentNoInstructions();
}
/** /**
* See `AFL_FRIDA_INST_NO_OPTIMIZE` * See `AFL_FRIDA_INST_NO_OPTIMIZE`
*/ */
@ -299,6 +305,7 @@ Afl.jsApiSetInstrumentCoverageFile = Afl.jsApiGetFunction("js_api_set_instrument
Afl.jsApiSetInstrumentDebugFile = Afl.jsApiGetFunction("js_api_set_instrument_debug_file", "void", ["pointer"]); Afl.jsApiSetInstrumentDebugFile = Afl.jsApiGetFunction("js_api_set_instrument_debug_file", "void", ["pointer"]);
Afl.jsApiSetInstrumentJit = Afl.jsApiGetFunction("js_api_set_instrument_jit", "void", []); Afl.jsApiSetInstrumentJit = Afl.jsApiGetFunction("js_api_set_instrument_jit", "void", []);
Afl.jsApiSetInstrumentLibraries = Afl.jsApiGetFunction("js_api_set_instrument_libraries", "void", []); Afl.jsApiSetInstrumentLibraries = Afl.jsApiGetFunction("js_api_set_instrument_libraries", "void", []);
Afl.jsApiSetInstrumentNoInstructions = Afl.jsApiGetFunction("js_api_set_instrument_no_instructions", "void", []);
Afl.jsApiSetInstrumentNoOptimize = Afl.jsApiGetFunction("js_api_set_instrument_no_optimize", "void", []); Afl.jsApiSetInstrumentNoOptimize = Afl.jsApiGetFunction("js_api_set_instrument_no_optimize", "void", []);
Afl.jsApiSetInstrumentSeed = Afl.jsApiGetFunction("js_api_set_instrument_seed", "void", ["uint64"]); Afl.jsApiSetInstrumentSeed = Afl.jsApiGetFunction("js_api_set_instrument_seed", "void", ["uint64"]);
Afl.jsApiSetInstrumentTrace = Afl.jsApiGetFunction("js_api_set_instrument_trace", "void", []); Afl.jsApiSetInstrumentTrace = Afl.jsApiGetFunction("js_api_set_instrument_trace", "void", []);

View File

@ -142,6 +142,13 @@ js_api_set_prefetch_backpatch_disable(void) {
} }
__attribute__((visibility("default"))) void
js_api_set_instrument_no_instructions(void) {
instrument_coverage_insn = FALSE;
}
__attribute__((visibility("default"))) void js_api_set_instrument_no_optimize( __attribute__((visibility("default"))) void js_api_set_instrument_no_optimize(
void) { void) {

View File

@ -0,0 +1,87 @@
PWD:=$(shell pwd)/
ROOT:=$(PWD)../../../
BUILD_DIR:=$(PWD)build/
TEST_CMOV_SRC:=$(PWD)cmov.c
TEST_CMOV_OBJ:=$(BUILD_DIR)cmov
TEST_DATA_DIR:=$(BUILD_DIR)in/
CMP_LOG_INPUT:=$(TEST_DATA_DIR)in
QEMU_OUT:=$(BUILD_DIR)qemu-out
FRIDA_OUT:=$(BUILD_DIR)frida-out
ADDR_BIN:=$(ROOT)frida_mode/build/addr
GET_SYMBOL_ADDR:=$(ROOT)frida_mode/util/get_symbol_addr.sh
AFLPP_FRIDA_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/frida_hook.so
AFL_FRIDA_BASE_ADDR:=$(shell $(ADDR_BIN))
AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_CMOV_OBJ) LLVMFuzzerTestOneInput $(AFL_FRIDA_BASE_ADDR))
DUMMY_DATA_FILE:=$(BUILD_DIR)dummy.dat
.PHONY: all 32 clean frida frida_noinst debug format
all: $(TEST_CMOV_OBJ)
make -C $(ROOT)frida_mode/
32:
CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
$(BUILD_DIR):
mkdir -p $@
$(TEST_DATA_DIR): | $(BUILD_DIR)
mkdir -p $@
$(CMP_LOG_INPUT): | $(TEST_DATA_DIR)
echo -n "ABC" > $@
$(TEST_CMOV_OBJ): $(TEST_CMOV_SRC) | $(BUILD_DIR)
$(CC) -g $(CFLAGS) $(LDFLAGS) $< -o $@
########## DUMMY #######
$(DUMMY_DATA_FILE): | $(BUILD_DIR)
dd if=/dev/zero bs=1048576 count=1 of=$@
frida: $(TEST_CMOV_OBJ) $(CMP_LOG_INPUT) $(DUMMY_DATA_FILE)
AFL_FRIDA_PERSISTENT_CNT=1000000 \
AFL_FRIDA_PERSISTENT_HOOK=$(AFLPP_FRIDA_DRIVER_HOOK_OBJ) \
AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR) \
AFL_ENTRYPOINT=$(AFL_FRIDA_PERSISTENT_ADDR) \
$(ROOT)afl-fuzz \
-O \
-i $(TEST_DATA_DIR) \
-o $(FRIDA_OUT) \
-Z \
-t 10000+ \
-- \
$(TEST_CMOV_OBJ) $(DUMMY_DATA_FILE)
frida_noinst: $(TEST_CMOV_OBJ) $(CMP_LOG_INPUT) $(DUMMY_DATA_FILE)
AFL_FRIDA_INST_NO_INSN=1 \
AFL_FRIDA_PERSISTENT_CNT=1000000 \
AFL_FRIDA_PERSISTENT_HOOK=$(AFLPP_FRIDA_DRIVER_HOOK_OBJ) \
AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR) \
AFL_ENTRYPOINT=$(AFL_FRIDA_PERSISTENT_ADDR) \
$(ROOT)afl-fuzz \
-O \
-i $(TEST_DATA_DIR) \
-o $(FRIDA_OUT) \
-Z \
-- \
$(TEST_CMOV_OBJ) $(DUMMY_DATA_FILE)
debug: $(TEST_CMOV_OBJ) $(CMP_LOG_INPUT)
gdb \
--ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \
--ex 'set disassembly-flavor intel' \
--ex 'r $(CMP_LOG_INPUT)' \
--args $(TEST_CMOV_OBJ) $(CMP_LOG_INPUT)
clean:
rm -rf $(BUILD_DIR)
format:
cd $(ROOT) && echo $(TEST_CMOV_SRC) | xargs -L1 ./.custom-format.py -i

View File

@ -0,0 +1,19 @@
all:
@echo trying to use GNU make...
@gmake all || echo please install GNUmake
32:
@echo trying to use GNU make...
@gmake 32 || echo please install GNUmake
clean:
@gmake clean
frida:
@gmake frida
format:
@gmake format
debug:
@gmake debug

122
frida_mode/test/cmov/cmov.c Normal file
View File

@ -0,0 +1,122 @@
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static bool cmov_test(char *x, char *y, size_t len) {
register char * __rdi __asm__("rdi") = x;
register char * __rsi __asm__("rsi") = y;
register size_t __rcx __asm__("rcx") = len;
register long __rax __asm__("rax");
__asm__ __volatile__(
"mov $0x1, %%rax\n"
"mov $0x0, %%r8\n"
"1:\n"
"mov (%%rsi), %%bl\n"
"mov (%%rdi), %%dl\n"
"cmp %%bl, %%dl\n"
"cmovne %%r8, %%rax\n"
"inc %%rsi\n"
"inc %%rdi\n"
"dec %%rcx\n"
"jnz 1b\n"
: "=r"(__rax)
: "r"(__rdi), "r"(__rsi)
: "r8", "bl", "dl", "memory");
return __rax;
}
void LLVMFuzzerTestOneInput(char *buf, int len) {
char match[] = "CBAABC";
if (len > sizeof(match)) { return; }
if (cmov_test(buf, match, sizeof(buf)) != 0) {
printf("Puzzle solved, congrats!\n");
abort();
}
}
int main(int argc, char **argv) {
char * file;
int fd = -1;
off_t len;
char * buf = NULL;
size_t n_read;
int result = -1;
if (argc != 2) { return 1; }
do {
file = argv[1];
dprintf(STDERR_FILENO, "Running: %s\n", file);
fd = open(file, O_RDONLY);
if (fd < 0) {
perror("open");
break;
}
len = lseek(fd, 0, SEEK_END);
if (len < 0) {
perror("lseek (SEEK_END)");
break;
}
if (lseek(fd, 0, SEEK_SET) != 0) {
perror("lseek (SEEK_SET)");
break;
}
buf = (char *)malloc(len);
if (buf == NULL) {
perror("malloc");
break;
}
n_read = read(fd, buf, len);
if (n_read != len) {
perror("read");
break;
}
dprintf(STDERR_FILENO, "Running: %s: (%zd bytes)\n", file, n_read);
LLVMFuzzerTestOneInput(buf, len);
dprintf(STDERR_FILENO, "Done: %s: (%zd bytes)\n", file, n_read);
result = 0;
} while (false);
if (buf != NULL) { free(buf); }
if (fd != -1) { close(fd); }
return result;
}

View File

@ -149,6 +149,13 @@ class Afl {
Afl.jsApiSetInstrumentLibraries(); Afl.jsApiSetInstrumentLibraries();
} }
/**
* See `AFL_FRIDA_INST_NO_INSN`
*/
public static setInstrumentNoInstructions(): void {
Afl.jsApiSetInstrumentNoInstructions();
}
/** /**
* See `AFL_FRIDA_INST_NO_OPTIMIZE` * See `AFL_FRIDA_INST_NO_OPTIMIZE`
*/ */
@ -377,6 +384,11 @@ class Afl {
"void", "void",
[]); []);
private static readonly jsApiSetInstrumentNoInstructions = Afl.jsApiGetFunction(
"js_api_set_instrument_no_instructions",
"void",
[]);
private static readonly jsApiSetInstrumentNoOptimize = Afl.jsApiGetFunction( private static readonly jsApiSetInstrumentNoOptimize = Afl.jsApiGetFunction(
"js_api_set_instrument_no_optimize", "js_api_set_instrument_no_optimize",
"void", "void",

View File

@ -60,6 +60,7 @@ static char *afl_environment_variables[] = {
"AFL_FRIDA_INST_COVERAGE_FILE", "AFL_FRIDA_INST_COVERAGE_FILE",
"AFL_FRIDA_INST_DEBUG_FILE", "AFL_FRIDA_INST_DEBUG_FILE",
"AFL_FRIDA_INST_JIT", "AFL_FRIDA_INST_JIT",
"AFL_FRIDA_INST_NO_INSN",
"AFL_FRIDA_INST_NO_OPTIMIZE", "AFL_FRIDA_INST_NO_OPTIMIZE",
"AFL_FRIDA_INST_NO_PREFETCH", "AFL_FRIDA_INST_NO_PREFETCH",
"AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH", "AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH",