diff --git a/afl_transforms/tools/zax/test/test_context.c b/afl_transforms/tools/zax/test/test_context.c new file mode 100644 index 0000000000000000000000000000000000000000..6e43960a11c49a0d468c728879f8bb2619e70ca2 --- /dev/null +++ b/afl_transforms/tools/zax/test/test_context.c @@ -0,0 +1,34 @@ +#include <stdio.h> +volatile int bar(int a) +{ + if (a == 2) + return 5; + else + return 3; +} + +volatile int foo(int a) +{ + if (a==3) + return bar(a); + else + return bar(2); + +} + +volatile int bob(int a) +{ + a = bar(7); + if (a==2) + return 1; + else return 2; + +} + +int main(int argc, char **argv) +{ + int x = foo(argc); + int y = bar(argc); + int z = bob(argc); + printf("out=%d\n", x+y+z); +} diff --git a/afl_transforms/tools/zax/test/test_context.sh b/afl_transforms/tools/zax/test/test_context.sh new file mode 100755 index 0000000000000000000000000000000000000000..7505a722b9b7c82cbbcaadc975e007deb7bea232 --- /dev/null +++ b/afl_transforms/tools/zax/test/test_context.sh @@ -0,0 +1,106 @@ +cd $(dirname $(realpath $0) ) + +PUT=test_context.exe +ZAFL_PUT="$PUT.zafl $PUT.zafl.context_sensitive" +MYARG="a" + +log_msg() +{ + echo "TEST PASS: $1" +} + +log_error() +{ + echo "TEST FAIL: $1" + exit 1 +} + +check_afl() +{ + which afl-showmap >/dev/null 2>&1 + if [ ! $? -eq 0 ]; then + log_error "AFL doesn't seem to be installed. Try: 'sudo apt install afl' before proceeding or download/build afl directly from source" + fi +} + +build_one() +{ + local orig=$1 + local zafl=$2 + shift + shift + zafl.sh $orig $zafl $@ + if [ $? -eq 0 ]; then + log_msg "build $zafl" + else + log_error "build $zafl" + fi +} + +build_all() +{ + g++ test_context.c -o $PUT +} + +zafl_all() +{ + for p in $* + do + build_one $p $p.zafl -v -t $p.analysis + build_one $p $p.zafl.context_sensitive --enable-context-sensitivity function -v -t $p.analysis.context_sensitive + done +} + +clean_all() +{ + rm -fr ${PUT}* +} + +verify_output() +{ + local arg=$1 + shift + local orig_zafl=$1 + shift + local all_configs=$* + + ./$orig_zafl $arg > $orig_zafl.output.orig + + for p in $all_configs + do + echo "Program under test: $p" + ./${p} $arg > $p.output + diff $orig_zafl.output.orig $p.output + if [ ! $? -eq 0 ]; then + log_error "output verification failure: $p.output" + fi + done + + log_msg "output verified for $orig_zafl" +} + +clean_all +check_afl + +build_all + +zafl_all $PUT +verify_output $MYARG $PUT $ZAFL_PUT + +zafl_map=${PUT}.zafl.map +afl-showmap -o $zafl_map -- ./${PUT}.zafl +count_zafl=$(wc -l ${zafl_map} | cut -d' ' -f1) + +zafl_cs_map=${PUT}.zafl.map +afl-showmap -o $zafl_cs_map -- ./${PUT}.zafl.context_sensitive +count_zafl_cs=$(wc -l ${zafl_cs_map} | cut -d' ' -f1) + +# difference must be exactly 2 +let diff=$count_zafl_cs-$count_zafl +if [ $diff -eq 2 ]; then + log_msg "context sensitive map has +2 entries over baseline zafl map" +else + log_error "context sensitive map does not have expected number of entries (should be +2): map_size(zafl):$count_zafl map_size(zafl_context_sensitive):$count_zafl_cs" +fi + +clean_all diff --git a/afl_transforms/tools/zax/zax.cpp b/afl_transforms/tools/zax/zax.cpp index 334894d3c4f2dde5f10b304b705fac86ddfcdc14..351671a2d8a7437007bddbdd220dc723f0c61369 100644 --- a/afl_transforms/tools/zax/zax.cpp +++ b/afl_transforms/tools/zax/zax.cpp @@ -73,15 +73,18 @@ void Zax_t::instrumentBasicBlock(BasicBlock_t *p_bb, const bool p_honorRedZone, char *reg_temp16 = NULL; char *reg_trace_map = NULL; char *reg_prev_id = NULL; + char *reg_context = NULL; + char *reg_context16 = NULL; auto save_temp = true; auto save_trace_map = true; auto save_prev_id = true; + auto save_context = (getContextSensitivity() != ContextSensitivity_None) ? true : false; auto block_record=BBRecord_t(); const auto trace_map_fixed_addr = getenv("ZAFL_TRACE_MAP_FIXED_ADDRESS"); const auto do_fixed_addr_optimization = (trace_map_fixed_addr!=nullptr); - const auto num_free_regs_desired = 3; + const auto num_free_regs_desired = save_context ? 4 : 3; auto instr = getInstructionToInstrument(p_bb, num_free_regs_desired); if (!instr) throw; @@ -97,8 +100,8 @@ void Zax_t::instrumentBasicBlock(BasicBlock_t *p_bb, const bool p_honorRedZone, { auto regset = getDeadRegs(instr); live_flags = regset.find(IRDB_SDK::rn_EFLAGS)==regset.end(); - const auto allowed_regs = RegisterSet_t({rn_RAX, rn_RBX, rn_RCX, rn_RDX, rn_R8, rn_R9, rn_R10, rn_R11, rn_R12, rn_R13, rn_R14, rn_R15}); + const auto allowed_regs = RegisterSet_t({rn_RAX, rn_RBX, rn_RCX, rn_RDX, rn_R8, rn_R9, rn_R10, rn_R11, rn_R12, rn_R13, rn_R14, rn_R15}); auto free_regs = getFreeRegs(regset, allowed_regs); for (auto r : regset) @@ -121,6 +124,13 @@ void Zax_t::instrumentBasicBlock(BasicBlock_t *p_bb, const bool p_honorRedZone, save_prev_id = false; free_regs.erase(r); } + else if (r == rn_R8 && save_context) + { + reg_context=strdup("r8"); + reg_context16=strdup("r8w"); + save_context = false; + free_regs.erase(r); + } } // if we failed to do the assignment, check for any other register to fill the assignment. @@ -151,6 +161,19 @@ void Zax_t::instrumentBasicBlock(BasicBlock_t *p_bb, const bool p_honorRedZone, save_prev_id = false; free_regs.erase(r); } + + if (getContextSensitivity() != ContextSensitivity_None) + { + if (free_regs.size() >= 1) + { + auto r = *free_regs.begin(); + auto r16 = convertRegisterTo16bit(r); + reg_context = strdup(registerToString(r).c_str()); + reg_context16 = strdup(registerToString(r16).c_str()); + save_context = false; + free_regs.erase(r); + } + } } // In the event we couldn't find a free register, or we aren't using stars @@ -161,13 +184,16 @@ void Zax_t::instrumentBasicBlock(BasicBlock_t *p_bb, const bool p_honorRedZone, if (!reg_temp16) reg_temp16 = strdup("ax"); if (!reg_trace_map) reg_trace_map = strdup("rcx"); if (!reg_prev_id) reg_prev_id = strdup("rdx"); + if (!reg_context) reg_context = strdup("r8"); + if (!reg_context16) reg_context16 = strdup("r8w"); if (m_verbose) { cout << "save_temp: " << save_temp << " save_trace_map: " << save_trace_map << " save_prev_id: " << save_prev_id << " live_flags: " << live_flags << endl; cout << "reg_temp: " << reg_temp << " " << reg_temp32 << " " << reg_temp16 << " reg_trace_map: " << reg_trace_map - << " reg_prev_id: " << reg_prev_id << endl; + << " reg_prev_id: " << reg_prev_id + << " reg_context: " << reg_context << endl; } // warning: first instrumentation must use insertAssemblyBefore @@ -205,10 +231,11 @@ void Zax_t::instrumentBasicBlock(BasicBlock_t *p_bb, const bool p_honorRedZone, // Omit saving any registers we don't need to save because we // were able to locate a free register. if (p_honorRedZone) do_insert("lea rsp, [rsp-128]"); - if (save_temp) do_insert("push rax"); - if (save_trace_map) do_insert("push rcx"); - if (save_prev_id) do_insert("push rdx"); - if (live_flags) do_insert("pushf"); + if (save_temp) do_insert("push rax"); + if (save_trace_map) do_insert("push rcx"); + if (save_prev_id) do_insert("push rdx"); + if (save_context) do_insert("push r8"); + if (live_flags) do_insert("pushf"); const auto live_flags_str = live_flags ? "live" : "dead"; if (m_verbose) cout << " flags are " << live_flags_str << endl; @@ -239,6 +266,17 @@ void Zax_t::instrumentBasicBlock(BasicBlock_t *p_bb, const bool p_honorRedZone, do_insert(buf); create_got_reloc(getFileIR(), m_prev_id, tmp); + + if (getContextSensitivity() != ContextSensitivity_None) + { + sprintf(buf, "C%d: mov %s, QWORD [rel C%d]", labelid, reg_context, labelid); + do_insert(buf); + create_got_reloc(getFileIR(), m_context_id, tmp); + + sprintf(buf,"mov %s, [%s]", reg_context, reg_context); + do_insert(buf); + } + // if we are using a variable address trace map, generate the address. if(!do_fixed_addr_optimization) { @@ -248,6 +286,7 @@ void Zax_t::instrumentBasicBlock(BasicBlock_t *p_bb, const bool p_honorRedZone, create_got_reloc(getFileIR(), m_trace_map, tmp); } + // compute index into trace map // do the calculation to has the previouus block ID with this block ID // in the faster or slower fashion depending on the requested technique. if (!p_collafl_optimization) @@ -260,6 +299,14 @@ void Zax_t::instrumentBasicBlock(BasicBlock_t *p_bb, const bool p_honorRedZone, sprintf(buf, "xor %s,0x%x", reg_temp16, blockid); do_insert(buf); + // hash with calling context value + if (getContextSensitivity() != ContextSensitivity_None) + { + // xor ax, <context_id_register> + sprintf(buf, "xor %s,%s", reg_temp16, reg_context16); + do_insert(buf); + } + // 15: movzx eax,ax sprintf(buf,"movzx %s,%s", reg_temp32, reg_temp16); do_insert(buf); @@ -298,16 +345,18 @@ void Zax_t::instrumentBasicBlock(BasicBlock_t *p_bb, const bool p_honorRedZone, sprintf(buf, "mov %s, 0x%x", reg_temp32, blockid >> 1); do_insert(buf); + // store prev_id // 23: mov WORD PTR [rdx],ax sprintf(buf, "mov WORD [%s], %s", reg_prev_id, reg_temp16); do_insert(buf); // finally, restore any flags/registers so that the program can execute. - if (live_flags) do_insert("popf"); - if (save_prev_id) do_insert("pop rdx"); - if (save_trace_map) do_insert("pop rcx"); - if (save_temp) do_insert("pop rax"); - if (p_honorRedZone) do_insert("lea rsp, [rsp+128]"); + if (live_flags) do_insert("popf"); + if (save_context) do_insert("pop r8"); + if (save_prev_id) do_insert("pop rdx"); + if (save_trace_map) do_insert("pop rcx"); + if (save_temp) do_insert("pop rax"); + if (p_honorRedZone) do_insert("lea rsp, [rsp+128]"); m_modifiedBlocks[blockid] = block_record; @@ -319,5 +368,7 @@ void Zax_t::instrumentBasicBlock(BasicBlock_t *p_bb, const bool p_honorRedZone, free(reg_temp16); free(reg_trace_map); free(reg_prev_id); + free(reg_context); + free(reg_context16); } diff --git a/afl_transforms/tools/zax/zax_base.cpp b/afl_transforms/tools/zax/zax_base.cpp index 90eb6f71ab83f022f8fe1f9f926b373f6957a975..bb723c66e1e915ab68ad709d549bba134b41e4b6 100644 --- a/afl_transforms/tools/zax/zax_base.cpp +++ b/afl_transforms/tools/zax/zax_base.cpp @@ -33,12 +33,14 @@ #include <irdb-deep> #include "zax_base.hpp" +#include "critical_edge_breaker.hpp" using namespace std; using namespace IRDB_SDK; using namespace Zafl; #define ALLOF(a) begin(a),end(a) +#define FIRSTOF(a) (*(begin(a))) void create_got_reloc(FileIR_t* fir, pair<DataScoop_t*,int> wrt, Instruction_t* i) { @@ -118,6 +120,7 @@ ZaxBase_t::ZaxBase_t(IRDB_SDK::pqxxDB_t &p_dbinterface, IRDB_SDK::FileIR_t *p_va m_plt_zafl_initAflForkServer=ed->appendPltEntry("zafl_initAflForkServer"); m_trace_map = ed->appendGotEntry("zafl_trace_map"); m_prev_id = ed->appendGotEntry("zafl_prev_id"); + m_context_id = ed->appendGotEntry("zafl_context"); // let's not instrument these functions ever // see isBlacklisted() for other blacklisted functions @@ -154,6 +157,10 @@ ZaxBase_t::ZaxBase_t(IRDB_SDK::pqxxDB_t &p_dbinterface, IRDB_SDK::FileIR_t *p_va m_verbose = false; m_bb_float_instrumentation = false; + setContextSensitivity(ContextSensitivity_None); + + m_entry_point = nullptr; + m_labelid = 0; m_blockid = 0; @@ -171,6 +178,7 @@ ZaxBase_t::ZaxBase_t(IRDB_SDK::pqxxDB_t &p_dbinterface, IRDB_SDK::FileIR_t *p_va m_num_exit_blocks_elided = 0; m_num_entry_blocks_elided = 0; m_num_single_block_function_elided = 0; + m_num_contexts = 0; } void ZaxBase_t::setVerbose(bool p_verbose) @@ -219,6 +227,28 @@ bool ZaxBase_t::getBasicBlockFloatingInstrumentation() const return m_bb_float_instrumentation; } +void ZaxBase_t::setContextSensitivity(ContextSensitivity_t p_context_style) +{ + m_context_sensitivity = p_context_style; + switch (m_context_sensitivity) + { + case ContextSensitivity_None: + cout << "disable context sensitivity" << endl; + break; + case ContextSensitivity_Function: + cout << "enable context sensitivity (style: function)" << endl; + break; + case ContextSensitivity_Callsite: + cout << "enable context sensitivity (style: callsite)" << endl; + break; + } +} + +ContextSensitivity_t ZaxBase_t::getContextSensitivity() const +{ + return m_context_sensitivity; +} + /* * Only allow instrumentation in whitelisted functions/instructions * Each line in file is either a function name or address @@ -266,6 +296,24 @@ ZaflBlockId_t ZaxBase_t::getBlockId(const unsigned p_max) return m_blockid; } +ZaflContextId_t ZaxBase_t::getContextId(const unsigned p_max) +{ + auto counter = 0; + auto contextid = 0; + + // only try getting new context id 100 times + // avoid returning duplicate if we can help it + while (counter++ < 100) { + contextid = rand() % p_max; + if (m_used_contextid.find(contextid) == m_used_contextid.end()) + { + m_used_contextid.insert(contextid); + return contextid; + } + } + return contextid; +} + void ZaxBase_t::insertExitPoint(Instruction_t *p_inst) { assert(p_inst->getAddress()->getVirtualOffset()); @@ -304,6 +352,8 @@ void ZaxBase_t::insertForkServer(Instruction_t* p_entry) cout << "Blacklisting entry point: " << ss.str() << endl; m_blacklist.insert(ss.str()); + m_entry_point = p_entry; + // insert the instrumentation auto tmp=p_entry; const auto regs = vector<string>({ "rdi", "rsi", "rbp", "rdx", "rcx", "rbx", "rax", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"}); @@ -474,7 +524,7 @@ bool ZaxBase_t::isBlacklisted(const Instruction_t *p_inst) const { stringstream ss; ss << "0x" << hex << p_inst->getAddress()->getVirtualOffset(); - return (m_blacklist.count(ss.str()) > 0 || isBlacklisted(p_inst->getFunction())); + return (m_blacklist.count(ss.str()) > 0 || isBlacklisted(p_inst->getFunction()) || p_inst == m_entry_point); } bool ZaxBase_t::isWhitelisted(const Instruction_t *p_inst) const @@ -768,6 +818,9 @@ Instruction_t* ZaxBase_t::getInstructionToInstrument(const BasicBlock_t *p_bb, c for (auto i : p_bb->getInstructions()) { + if (isBlacklisted(i)) + continue; + const auto dead_regs = getDeadRegs(i); const auto num_free_regs = getFreeRegs(dead_regs, allowed_regs).size(); @@ -805,6 +858,202 @@ Instruction_t* ZaxBase_t::getInstructionToInstrument(const BasicBlock_t *p_bb, c return best_i; } +void ZaxBase_t::dumpAttributes() +{ + cout << "#ATTRIBUTE num_bb=" << dec << m_num_bb << endl; + cout << "#ATTRIBUTE num_bb_instrumented=" << m_num_bb_instrumented << endl; + cout << "#ATTRIBUTE num_bb_skipped=" << m_num_bb_skipped << endl; + cout << "#ATTRIBUTE num_bb_skipped_pushjmp=" << m_num_bb_skipped_pushjmp << endl; + cout << "#ATTRIBUTE num_bb_skipped_nop_padding=" << m_num_bb_skipped_nop_padding << endl; + cout << "#ATTRIBUTE num_bb_float_instrumentation=" << m_num_bb_float_instrumentation << endl; + cout << "#ATTRIBUTE num_bb_float_register_saved=" << m_num_bb_float_regs_saved << endl; + cout << "#ATTRIBUTE graph_optimize=" << boolalpha << m_graph_optimize << endl; + cout << "#ATTRIBUTE num_bb_skipped_cond_branch=" << m_num_bb_skipped_cbranch << endl; + cout << "#ATTRIBUTE num_style_collafl=" << m_num_style_collafl << endl; + cout << "#ATTRIBUTE num_domgraph_blocks_elided=" << m_num_domgraph_blocks_elided << endl; + cout << "#ATTRIBUTE num_entry_blocks_elided=" << m_num_entry_blocks_elided << endl; + cout << "#ATTRIBUTE num_exit_blocks_elided=" << m_num_exit_blocks_elided << endl; + cout << "#ATTRIBUTE num_single_block_function_elided=" << m_num_single_block_function_elided << endl; + cout << "#ATTRIBUTE num_contexts=" << m_num_contexts << endl; +} + +// file dump of modified basic block info +void ZaxBase_t::dumpMap() +{ + getFileIR()->setBaseIDS(); // make sure instructions have IDs + getFileIR()->assembleRegistry(); // make sure to assemble all instructions + + std::ofstream mapfile("zax.map"); + + mapfile << "# BLOCK_ID ID_EP:size ID_OLDEP:size (ID_INSTRUMENTATION:size)*" << endl; + for (auto &mb : m_modifiedBlocks) + { + const auto blockid = mb.first; + mapfile << dec << blockid << " "; + for (auto &entry : mb.second) + { + mapfile << hex << entry->getBaseID() << ":" << dec << entry->getDataBits().size() << " "; + } + mapfile << endl; + } +} + +void ZaxBase_t::addContextSensitivity_Callsite(const ControlFlowGraph_t& cfg) +{ + assert(0); +} + +// update calling context hash at entry point +// revert calling context hash on exit +void ZaxBase_t::addContextSensitivity_Function(const ControlFlowGraph_t& cfg) +{ + bool inserted_before = false; + + // don't bother with single block functions + if (cfg.getBlocks().size() == 1) + return; + + m_num_contexts++; + + // + // entry_point + // context = prev_context % RANDOM_CONTEXT_ID + // + // exit point (returns) + // context = prev_context % RANDOM_CONTEXT_ID + // + const auto do_insert=[&](Instruction_t* instr, const string& insn_str) -> Instruction_t* + { + if (inserted_before) + { + instr = insertAssemblyAfter(instr, insn_str); + return instr; + } + else + { + insertAssemblyBefore(instr, insn_str); + inserted_before = true; + return instr; + } + }; + + + auto compute_hash_chain = [&](ZaflContextId_t contextid, Instruction_t * instr, string reg_context, string reg_temp) -> Instruction_t* + { + auto labelid = getLabelId(); + const auto hash_context_reloc = string("E") + to_string(labelid) + ": mov " + reg_context + ", QWORD [rel E" + to_string(labelid) + "]"; + auto tmp = instr; + tmp = do_insert(tmp, hash_context_reloc); + create_got_reloc(getFileIR(), m_context_id, tmp); + + const auto deref_context = string("mov ") + reg_temp + ", [" + reg_context + "]"; + tmp = do_insert(tmp, deref_context); + + const auto hash_chain = string("xor ") + reg_temp + ", " + to_string(contextid); + tmp = do_insert(tmp, hash_chain); + + const auto store_context = string("mov [") + reg_context + "]" + "," + reg_temp; + tmp = do_insert(tmp, store_context); + + // 5ae: 48 8b 05 23 0a 20 00 mov rax,QWORD PTR [rip+0x200a23] # 200fd8 <x> + // 5b5: c7 00 d2 04 00 00 mov DWORD PTR [rax],0x4d2 + return tmp; + }; + + auto add_hash_context_instrumentation = [&](ZaflContextId_t contextid, Instruction_t* i) + { + inserted_before = false; + + // look for instruction in entry block with at least 1 free reg + auto reg_context = string("r14"); + auto reg_temp = string("r15"); + bool save_context = false; + bool save_temp = false; + + const auto allowed_regs = RegisterSet_t({rn_RAX, rn_RBX, rn_RCX, rn_RDX, rn_R8, rn_R9, rn_R10, rn_R11, rn_R12, rn_R13, rn_R14, rn_R15}); + const auto dead_regs = getDeadRegs(i); + auto free_regs = getFreeRegs(dead_regs, allowed_regs); + + // @todo: red zone? + + if (free_regs.size() > 0) + { + reg_context = registerToString(FIRSTOF(free_regs)); + free_regs.erase(FIRSTOF(free_regs)); + } + else + { + save_context = true; + i = do_insert(i, "push " + reg_context); + } + + if (free_regs.size() > 0) + { + reg_temp = registerToString(FIRSTOF(free_regs)); + free_regs.erase(FIRSTOF(free_regs)); + } + else + { + save_temp = true; + i = do_insert(i, "push " + reg_temp); + } + + // compute new hash chain value + i = compute_hash_chain(contextid, i, reg_context, reg_temp); + + if (save_temp) + i = do_insert(i, "pop " + reg_temp); + + if (save_context) + i = do_insert(i, "pop " + reg_context); + + }; + + auto contextid = getContextId(); + auto entry_block = cfg.getEntry(); + inserted_before = false; + add_hash_context_instrumentation(contextid, entry_block->getInstructions()[0]); + + // find all exit blocks + auto find_exits = BasicBlockSet_t(); + copy_if(ALLOF(cfg.getBlocks()), inserter(find_exits, find_exits.begin()), [entry_block](BasicBlock_t* bb) { + if (bb == entry_block) return false; + if (!bb->getIsExitBlock()) return false; + // make sure it's a ret + const auto last_instruction_index = bb->getInstructions().size() - 1; + return (bb->getInstructions()[last_instruction_index]->getDisassembly().find("ret")!=string::npos); + }); + + for (const auto &bb: find_exits) + { + const auto last_instruction_index = bb->getInstructions().size()-1; + inserted_before = false; + add_hash_context_instrumentation(contextid, bb->getInstructions()[last_instruction_index]); + } +} + +void ZaxBase_t::addContextSensitivity(const ControlFlowGraph_t& cfg) +{ + if (m_entry_point && m_entry_point->getFunction()) + { + cout << "cfg.func: " << cfg.getFunction()->getName() << " ep.func: " << m_entry_point->getFunction()->getName() << endl; + if (m_entry_point->getFunction()->getName() == cfg.getFunction()->getName()) + { + cout << "Do not setup calling context in same function as entry point for fork server" << endl; + return; + } + } + + if (getContextSensitivity() == ContextSensitivity_Callsite) + addContextSensitivity_Callsite(cfg); + else if (getContextSensitivity() == ContextSensitivity_Function) + addContextSensitivity_Function(cfg); + else if (getContextSensitivity() == ContextSensitivity_None) + return; + else + throw; +} + /* * Execute the transform. * @@ -814,6 +1063,15 @@ Instruction_t* ZaxBase_t::getInstructionToInstrument(const BasicBlock_t *p_bb, c */ int ZaxBase_t::execute() { + if (m_breakupCriticalEdges) + { + CriticalEdgeBreaker_t ceb(getFileIR(), m_verbose); + cout << "#ATTRIBUTE num_bb_extra_blocks=" << ceb.getNumberExtraNodes() << endl; + + getFileIR()->setBaseIDS(); + getFileIR()->assembleRegistry(); + } + setup(); // for all functions @@ -931,6 +1189,16 @@ int ZaxBase_t::execute() m_num_bb_instrumented += keepers.size(); m_num_bb_skipped += (num_blocks_in_func - keepers.size()); + if (getContextSensitivity() != ContextSensitivity_None) + { + // this handles inserting the calling context sensitivity value + // at entry and exits of functions (todo: or at call sites a la Angora) + getFileIR()->assembleRegistry(); + getFileIR()->setBaseIDS(); + auto cs_cfg=ControlFlowGraph_t::factory(f); + addContextSensitivity(*cs_cfg); + } + if (m_verbose) { getFileIR()->assembleRegistry(); @@ -943,46 +1211,9 @@ int ZaxBase_t::execute() cout << "Function " << f->getName() << ": " << dec << keepers.size() << "/" << num_blocks_in_func << " basic blocks instrumented." << endl; }; + teardown(); return 1; } -void ZaxBase_t::dumpAttributes() -{ - cout << "#ATTRIBUTE num_bb=" << dec << m_num_bb << endl; - cout << "#ATTRIBUTE num_bb_instrumented=" << m_num_bb_instrumented << endl; - cout << "#ATTRIBUTE num_bb_skipped=" << m_num_bb_skipped << endl; - cout << "#ATTRIBUTE num_bb_skipped_pushjmp=" << m_num_bb_skipped_pushjmp << endl; - cout << "#ATTRIBUTE num_bb_skipped_nop_padding=" << m_num_bb_skipped_nop_padding << endl; - cout << "#ATTRIBUTE num_bb_float_instrumentation=" << m_num_bb_float_instrumentation << endl; - cout << "#ATTRIBUTE num_bb_float_register_saved=" << m_num_bb_float_regs_saved << endl; - cout << "#ATTRIBUTE graph_optimize=" << boolalpha << m_graph_optimize << endl; - cout << "#ATTRIBUTE num_bb_skipped_cond_branch=" << m_num_bb_skipped_cbranch << endl; - cout << "#ATTRIBUTE num_style_collafl=" << m_num_style_collafl << endl; - cout << "#ATTRIBUTE num_domgraph_blocks_elided=" << m_num_domgraph_blocks_elided << endl; - cout << "#ATTRIBUTE num_entry_blocks_elided=" << m_num_entry_blocks_elided << endl; - cout << "#ATTRIBUTE num_exit_blocks_elided=" << m_num_exit_blocks_elided << endl; - cout << "#ATTRIBUTE num_single_block_function_elided=" << m_num_single_block_function_elided << endl; -} - -// file dump of modified basic block info -void ZaxBase_t::dumpMap() -{ - getFileIR()->setBaseIDS(); // make sure instructions have IDs - getFileIR()->assembleRegistry(); // make sure to assemble all instructions - - std::ofstream mapfile("zax.map"); - - mapfile << "# BLOCK_ID ID_EP:size ID_OLDEP:size (ID_INSTRUMENTATION:size)*" << endl; - for (auto &mb : m_modifiedBlocks) - { - const auto blockid = mb.first; - mapfile << dec << blockid << " "; - for (auto &entry : mb.second) - { - mapfile << hex << entry->getBaseID() << ":" << dec << entry->getDataBits().size() << " "; - } - mapfile << endl; - } -} diff --git a/afl_transforms/tools/zax/zax_base.hpp b/afl_transforms/tools/zax/zax_base.hpp index b2314dda630831cbba3fb3868709bb97416fc6f3..8f2a66f15dbbcf794fbfd968a923724d52425cca 100644 --- a/afl_transforms/tools/zax/zax_base.hpp +++ b/afl_transforms/tools/zax/zax_base.hpp @@ -18,8 +18,10 @@ namespace Zafl using ZaflBlockId_t = uint32_t; using ZaflLabelId_t = uint32_t; + using ZaflContextId_t = uint32_t; using BBRecord_t = vector<Instruction_t*>; using RegisterSet_t = IRDB_SDK::RegisterIDSet_t; + enum ContextSensitivity_t {ContextSensitivity_None, ContextSensitivity_Callsite, ContextSensitivity_Function}; /* * Base class for afl-compatible instrumentation: @@ -41,11 +43,13 @@ namespace Zafl void setBasicBlockFloatingInstrumentation(bool); void setEnableForkServer(bool); void setBreakupCriticalEdges(bool); + void setContextSensitivity(ContextSensitivity_t); void filterPaddingNOP(BasicBlockSet_t& p_in_out); void filterBlocksByDomgraph(BasicBlockSet_t& in_out, const DominatorGraph_t * dg ); void filterConditionalBranches(BasicBlockSet_t& p_in_out); void filterEntryBlock(BasicBlockSet_t& in_out, BasicBlock_t* p_entry); void filterExitBlocks(BasicBlockSet_t& in_out); + void addContextSensitivity(const ControlFlowGraph_t&); protected: ZaxBase_t(pqxxDB_t &p_dbinterface, FileIR_t *p_variantIR, string p_entry, set<string> p_exits, bool p_use_stars=false, bool p_autozafl=false); @@ -54,6 +58,7 @@ namespace Zafl virtual ZaflBlockId_t getBlockId(const unsigned p_maxid=0xFFFF); virtual ZaflLabelId_t getLabelId(const unsigned p_maxid=0xFFFF); + virtual ZaflContextId_t getContextId(const unsigned p_maxid=0xFFFF); virtual BasicBlockSet_t getBlocksToInstrument (const ControlFlowGraph_t& cfg); virtual Instruction_t* getInstructionToInstrument(const BasicBlock_t *, const unsigned p_num_free_regs_desired = 0); virtual void setup(); @@ -73,10 +78,12 @@ namespace Zafl bool BB_isPushJmp(const BasicBlock_t *) const; bool BB_isPaddingNop(const BasicBlock_t *) const; bool getBasicBlockFloatingInstrumentation() const; + ContextSensitivity_t getContextSensitivity() const; bool hasLeafAnnotation(Function_t* fn) const; RegisterSet_t getDeadRegs(Instruction_t* insn) const; RegisterSet_t getFreeRegs(const RegisterSet_t& candidates, const RegisterSet_t& allowed) const; - + void addContextSensitivity_Callsite(const ControlFlowGraph_t&); + void addContextSensitivity_Function(const ControlFlowGraph_t&); protected: pqxxDB_t& m_dbinterface; @@ -91,13 +98,16 @@ namespace Zafl bool m_breakupCriticalEdges; bool m_bb_float_instrumentation; // skip basic blocks based on graph bool m_verbose; - pair<DataScoop_t*,int> m_trace_map; // afl shared memory trace map pair<DataScoop_t*,int> m_prev_id; // id of previous block + pair<DataScoop_t*,int> m_context_id; // calling context variable Instruction_t* m_plt_zafl_initAflForkServer; // plt entry for afl fork server initialization routine + set<ZaflContextId_t> m_used_contextid; // internal bookkeeping to keep track of used block ids map<ZaflBlockId_t, BBRecord_t> m_modifiedBlocks; // keep track of modified blocks + ContextSensitivity_t m_context_sensitivity; // none, @callsite, @function + // stats size_t m_num_bb; size_t m_num_bb_instrumented; @@ -112,6 +122,7 @@ namespace Zafl size_t m_num_entry_blocks_elided; size_t m_num_exit_blocks_elided; size_t m_num_single_block_function_elided; + size_t m_num_contexts; private: string m_fork_server_entry; // string to specify fork server entry point @@ -120,6 +131,7 @@ namespace Zafl set<string> m_blacklist; // blacklisted functions and/or instructions ZaflLabelId_t m_labelid; // internal bookkeeping to generate labels ZaflBlockId_t m_blockid; // internal bookkeeping to generate labels + Instruction_t* m_entry_point; // entry point where fork server was inserted }; } diff --git a/afl_transforms/tools/zax/zax_driver.cpp b/afl_transforms/tools/zax/zax_driver.cpp index 40ff623696b475370316d08e1ecdcfac45d9c1bf..6b3493bbbb6352848ca762165e59c6130bd4eef9 100644 --- a/afl_transforms/tools/zax/zax_driver.cpp +++ b/afl_transforms/tools/zax/zax_driver.cpp @@ -49,7 +49,9 @@ static void usage(char* name) cerr<<"\t[--enable-critical-edge-breakup|-c] Breakup critical edges"<<endl; cerr<<"\t[--disable-critical-edge-breakup|-C] Do not breakup critical edges (default)"<<endl; cerr<<"\t[--enable-floating-instrumentation|-i] Select best instrumentation within basic block"<<endl; - cerr<<"\t[--disable-floating-instrumentation|-I] Instrument firt instruction in basic blocks"<<endl; + cerr<<"\t[--disable-floating-instrumentation|-I] Instrument first instruction in basic blocks"<<endl; + cerr<<"\t[--enable-context-sensitivity <style>] Use calling context sensitivity, style={callsite,function}"<<endl; + cerr<<"\t[--disable-context-sensitivity] Disable calling context sensitivity"<<endl; } int main(int argc, char **argv) @@ -74,6 +76,7 @@ int main(int argc, char **argv) auto untracer_mode=false; auto breakup_critical_edges=false; auto floating_instrumentation=false; + auto context_sensitivity=ContextSensitivity_None; set<string> exitpoints; srand(getpid()+time(NULL)); @@ -100,9 +103,10 @@ int main(int argc, char **argv) {"disable-critical-edge-breakup", no_argument, 0, 'C'}, {"enable-floating-instrumentation", no_argument, 0, 'i'}, {"disable-floating-instrumentation", no_argument, 0, 'I'}, + {"enable-context-sensitivity", required_argument, 0, 'z'}, {0,0,0,0} }; - const char* short_opts="e:E:w:sv?hagGdDfFucCiI"; + const char* short_opts="z:e:E:w:sv?hagGdDfFucCiI"; while(true) { @@ -172,6 +176,14 @@ int main(int argc, char **argv) case 'I': floating_instrumentation=false; break; + case 'z': + if (optarg == string("callsite")) + context_sensitivity=ContextSensitivity_Callsite; // Angora fuzzer style + else if (optarg == string("function")) + context_sensitivity=ContextSensitivity_Function; + else + context_sensitivity=ContextSensitivity_None; + break; default: break; } @@ -231,6 +243,7 @@ int main(int argc, char **argv) zax->setBasicBlockFloatingInstrumentation(floating_instrumentation); zax->setEnableForkServer(forkserver_enabled); zax->setBreakupCriticalEdges(breakup_critical_edges); + zax->setContextSensitivity(context_sensitivity); int success=zax->execute(); diff --git a/afl_transforms/tools/zax/zuntracer.cpp b/afl_transforms/tools/zax/zuntracer.cpp index 2018809ea64c450419b0f2ea95772860df79e6ba..82cb301283b458de1c339445b32ed3e5452ff7c8 100644 --- a/afl_transforms/tools/zax/zuntracer.cpp +++ b/afl_transforms/tools/zax/zuntracer.cpp @@ -1,5 +1,4 @@ #include "zuntracer.hpp" -#include "critical_edge_breaker.hpp" using namespace std; using namespace IRDB_SDK; @@ -226,22 +225,3 @@ set<BasicBlock_t*> ZUntracer_t::getBlocksToInstrument(ControlFlowGraph_t &cfg) } return keepers; } - -// -// breakup critical edges -// use block-level instrumentation -// -int ZUntracer_t::execute() -{ - if (m_breakupCriticalEdges) - { - CriticalEdgeBreaker_t ceb(getFileIR(), m_verbose); - cout << "#ATTRIBUTE num_bb_extra_blocks=" << ceb.getNumberExtraNodes() << endl; - - getFileIR()->setBaseIDS(); - getFileIR()->assembleRegistry(); - } - - return ZaxBase_t::execute(); -} - diff --git a/afl_transforms/tools/zax/zuntracer.hpp b/afl_transforms/tools/zax/zuntracer.hpp index b122f1b847d4d773bf78a566da370395fb6d2ba8..eba33a8874d490d4fdaefd6f393e0e8676f50ecb 100644 --- a/afl_transforms/tools/zax/zuntracer.hpp +++ b/afl_transforms/tools/zax/zuntracer.hpp @@ -16,7 +16,6 @@ namespace Zafl ZUntracer_t(const ZUntracer_t&) = delete; ZUntracer_t(IRDB_SDK::pqxxDB_t &p_dbinterface, IRDB_SDK::FileIR_t *p_variantIR, string p_entry, set<string> p_exits, bool p_use_stars=false, bool p_autozafl=false); virtual ~ZUntracer_t() {}; - virtual int execute(); protected: virtual void instrumentBasicBlock(BasicBlock_t*, const bool p_hasLeafAnnotation, const bool p_collafl_optimization=false); diff --git a/bin/zafl.sh b/bin/zafl.sh index d29ef894cc8eb08f4fa5c36c7ef658a64aea6138..8af80beecec7773afe94257545943eb0952b9d10 100755 --- a/bin/zafl.sh +++ b/bin/zafl.sh @@ -36,15 +36,17 @@ usage() echo " -M, --disable-fixed-map Disable fixed address tracing map (default)" echo " -i, --enable-floating-instrumentation Select best instrumentation point within basic block (default)" echo " -I, --disable-floating-instrumentation Use first instruction for instrumentation in basic blocks" + echo " --enable-context-sensitivity <style> style={callsite,function} only function supported currently" echo " -v Verbose mode" echo } ida_or_rida_opt=" -c rida " stars_opt=" -o zax:--stars " -zax_opt=" " +zax_opt="" other_args="" float_opt=" -o zax:--enable-floating-instrumentation " +context_sensitivity_opt="" me=$(whoami) tmp_dir=/tmp/${me}/$$ @@ -180,13 +182,32 @@ parse_args() shift ;; -i | --enable-floating-instrumentation) - float_opt= " -o zax:--enable-floating-instrumentation " + float_opt=" -o zax:--enable-floating-instrumentation " shift ;; -I | --disable-floating-instrumentation) - float_opt= " -o zax:--disable-floating-instrumentation " + float_opt=" -o zax:--disable-floating-instrumentation " + shift + ;; + --enable-context-sensitivity) shift + case $1 in + function) + context_sensitivity_opt=" -o zax:\"--enable-context-sensitivity function\" " + shift + ;; + callsite) + context_sensitivity_opt=" -o zax:--enable-context-sensitivity callsite " + echo "Error: context sensitivity <callsite> currently unsupported" + exit 1 + ;; + *) + echo "Error: must specify function or callsite for context sensitivity" + exit 1 + ;; + esac ;; + -*|--*=) # unsupported flags echo "Error: Unsupported flag $1" >&2 exit 1 @@ -292,7 +313,7 @@ fi # log_msg "Transforming input binary $input_binary into $output_zafl_binary" -zax_opt=" $zax_opt $float_opt " +zax_opt=" $zax_opt $float_opt $context_sensitivity_opt " cmd="$ZAFL_TM_ENV $PSZ $input_binary $output_zafl_binary $ida_or_rida_opt -c move_globals=on -c zax=on -o move_globals:--elftables-only $stars_opt $zax_opt $verbose_opt $options $other_args" if [ ! -z "$ZAFL_TM_ENV" ]; then diff --git a/libzafl b/libzafl index 6f6f7350dc73d7305b58353b638be10148667015..97b561cb943dd9a15b3388cb19bd457eaf98fa37 160000 --- a/libzafl +++ b/libzafl @@ -1 +1 @@ -Subproject commit 6f6f7350dc73d7305b58353b638be10148667015 +Subproject commit 97b561cb943dd9a15b3388cb19bd457eaf98fa37