def do_equiv_instrs(instrs, gen_patched, all_diffs=None): """Check each instruction if it has an equivalent one and computes the changed bytes set. Optionally, it can generate a changed file with the equivalent instructions. Returns the list of changed bytes.""" changed_bytes = set() changed = [] for ins in instrs: if check_equiv(ins): changed.append(ins) diff = inp.get_diff(changed) if gen_patched: inp.patch(diff, "equiv") changed_bytes.update((ea for ea, orig, new in diff)) for ins in changed: if all_diffs != None: all_diffs.append(inp.get_diff([ins])) ins.reset_changed() return changed_bytes
def randomize(input_file): # get the changed byte sets functions = inp.get_functions(input_file) levels = func.classify_functions(functions) func.analyze_functions(functions, levels) global_diffs = [] changeable = 0 for f in filter(lambda x: x.level != -1, functions.itervalues()): # skip the SEH prolog and epilog functions .. they cause trouble if "_SEH_" in f.name: continue diffs = [] # swap (register reassignment with CFG): RUNTIME ERROR # This application has requested the Runtime to terminate it in an unsusal way. # Please Contact the application's support team for more information swap.liveness_analysis(f.code) live_regs = swap.get_reg_live_subsets(f.instrs, f.code, f.igraph) swaps = swap.get_reg_swaps(live_regs) swap.do_single_swaps(swaps, False, diffs) # preserv (reordering of register preservation): ERROR preservs, avail_regs = preserv.get_reg_preservations(f) preserv.do_reg_preservs(f.instrs, f.blocks, preservs, avail_regs, False, diffs) # equiv (automic instruction substitution): GOOD equiv.do_equiv_instrs(f.instrs, False, diffs) # reorder (intra basic block reordering): GOOD reorder.do_reordering(f.blocks, False, diffs) if diffs: changeable += len(list(itertools.chain(*diffs))) global_diffs.extend(random.choice(diffs)) inp.patch(global_diffs, "rand", False) print "changed %d bytes of at least %d changeable" % (len(global_diffs), changeable) print "(not counting all possible reorderings and preservations)"
def do_single_swaps(swaps, gen_patched, all_diffs=None): """Applies one swap at a time and optionally generates the patched .dll. Returns a set of the linear addresses of the changed bytes.""" changed_bytes = set() for i, swap in enumerate(swaps): success, changed = apply_swap_comb([swap]) if success: diff = inp.get_diff(changed) if gen_patched: inp.patch(diff, "swap-%06d" % i) if all_diffs != None: all_diffs.append(diff) changed_bytes.update((ea for ea, orig, curr in diff)) for ins in changed: ins.reset_changed() return changed_bytes
def do_reordering(blocks, gen_patched, all_diffs=None): """Reorders the instructions within the given blocks and optionally generates instances of the input file with these instructions reordered. Returns the changed bytes set (coverage evaluation).""" reordered = [] changed_bytes = set() diff = [] for block in blocks: dag = BuildBBDependenceDAG(block) block.rinstrs = ReorderGraph(dag) # update the address of reordered instrs for i, rins in enumerate(block.rinstrs): if i == 0: rins.raddr = block.begin else: rins.raddr = block.rinstrs[i - 1].raddr + len(block.rinstrs[i - 1].bytes) if rins.raddr != rins.addr and rins.inst_len > 4: reordered.append(rins) diff.extend(inp.get_block_diff(block)) reloc_diff = inp.get_reloc_diff(reordered) if gen_patched: inp.patch(diff + reloc_diff, "reorder") if all_diffs != None and len(reloc_diff) == 0: all_diffs.append(diff + reloc_diff) changed_bytes.update((ea for ea, orig, curr in diff)) return changed_bytes
def do_reg_preservs(instrs, blocks, preservs, avail_regs, gen_patched, all_diffs=None): """Changes the preserved registers within the given instructions and optionally generates instances of the input file with these permuted register preservations. Returns the changed bytes set.""" changed_bytes = set() #f_exit instructions (ret) implicitly use the preserved registers.. exclude them! implicit = reduce(lambda x, y: x | y, [i.implicit for i in instrs if not i.f_exit], set()) regs = [reg for reg, pushes, pops in preservs if reg not in implicit] if not preservs or (len(regs) == 1 and not avail_regs): return changed_bytes # preserved registers that are implicitly used should not be touched # hmm .. rotation should still happen even when all regs are implicitly used.. #if not regs: # return changed_bytes if len(regs) == 1: #we know we have available regs.append(avail_regs.pop()) if len(regs) >= 2: # we do need the combinations here, because we want to change as much # as possible in case a register cannot be swapped (e.g. weird modrm/sib) for r1, r2 in itertools.combinations(regs, 2): for ins in instrs: if not ins.swap_registers(r1, r2): #print "MAYBEBUG: broke for", ins.disas, "in register preservations!", r1, r2, ins.regs break else: #no break! diff = inp.get_diff(instrs) if gen_patched: inp.patch(diff, "preserv-%s-%s" % (r1, r2)) if all_diffs != None: all_diffs.append(diff) changed_bytes.update((ea for ea, orig, curr in diff)) for ins in instrs: ins.reset_changed() # the second part bellow can be useful in cases where global swap # cannot be applied due to implicit uses # group preservs in the same block and reorder them # augment each ins in the preservs with blocks for reg, pushes, pops in preservs: for ins in itertools.chain(pushes, pops): ins.block = next((b for b in blocks if ins in b.instrs), None) preservs_groups = [] for reg, pushes, pops in preservs: if len(preservs_groups) == 0: preservs_groups.append([[reg, pushes, pops]]) continue for group in preservs_groups: if (all(p1.block == p2.block for p1, p2 in zip(group[0][1], pushes)) and all(p1.block == p2.block for p1, p2 in zip(group[0][2], pops))): group.append([reg, pushes, pops]) break else: preservs_groups.append([[reg, pushes, pops]]) #print "grouping!:", '\n'.join((str(g) for g in preservs_groups)) # reorder (rotate) the pushes/pops by placing the last one in the # first's position and shifting all the other instrs down # TODO: breaks if esp is used within the reordered block! (BIB @ 070012F1) for group in (g for g in preservs_groups if len(g) > 1): # group them by block #pushes_by_block = {} ins_by_block = {} for reg, pushes, pops in group: for ins in itertools.chain(pushes, pops): if (ins.block, ins.mnem) not in ins_by_block: ins_by_block[(ins.block, ins.mnem)] = [] ins_by_block[(ins.block, ins.mnem)].append(ins) # in each block, move the latest ins to the ealiest's position for (block, mnem), instrs in ins_by_block.iteritems(): instrs.sort(key=lambda x: x.addr) first = block.instrs.index(instrs[0]) last = block.instrs.index(instrs[-1]) block.rinstrs = block.instrs[:first] block.rinstrs.append(block.instrs[last]) block.rinstrs.extend(block.instrs[first:last]) block.rinstrs.extend(block.instrs[last + 1:]) diff = inp.get_block_diff(block) changed_bytes.update((ea for ea, orig, curr in diff)) #XXX should also generate patched files ... return changed_bytes
def do_reg_preservs(instrs, blocks, preservs, avail_regs, gen_patched, all_diffs=None): """Changes the preserved registers within the given instructions and optionally generates instances of the input file with these permuted register preservations. Returns the changed bytes set.""" changed_bytes = set() # f_exit instructions (ret) implicitly use the preserved registers.. exclude them! implicit = reduce(lambda x, y: x | y, [i.implicit for i in instrs if not i.f_exit], set()) regs = [reg for reg, pushes, pops in preservs if reg not in implicit] if not preservs or (len(regs) == 1 and not avail_regs): return changed_bytes # preserved registers that are implicitly used should not be touched # hmm .. rotation should still happen even when all regs are implicitly used.. # if not regs: # return changed_bytes if len(regs) == 1: # we know we have available regs.append(avail_regs.pop()) if len(regs) >= 2: # we do need the combinations here, because we want to change as much # as possible in case a register cannot be swapped (e.g. weird modrm/sib) for r1, r2 in itertools.combinations(regs, 2): for ins in instrs: if not ins.swap_registers(r1, r2): # print "MAYBEBUG: broke for", ins.disas, "in register preservations!", r1, r2, ins.regs break else: # no break! diff = inp.get_diff(instrs) if gen_patched: inp.patch(diff, "preserv-%s-%s" % (r1, r2)) if all_diffs != None: all_diffs.append(diff) changed_bytes.update((ea for ea, orig, curr in diff)) for ins in instrs: ins.reset_changed() # the second part bellow can be useful in cases where global swap # cannot be applied due to implicit uses # group preservs in the same block and reorder them # augment each ins in the preservs with blocks for reg, pushes, pops in preservs: for ins in itertools.chain(pushes, pops): ins.block = next((b for b in blocks if ins in b.instrs), None) preservs_groups = [] for reg, pushes, pops in preservs: if len(preservs_groups) == 0: preservs_groups.append([[reg, pushes, pops]]) continue for group in preservs_groups: if (all(p1.block == p2.block for p1, p2 in zip(group[0][1], pushes)) and all(p1.block == p2.block for p1, p2 in zip(group[0][2], pops))): group.append([reg, pushes, pops]) break else: preservs_groups.append([[reg, pushes, pops]]) # print "grouping!:", '\n'.join((str(g) for g in preservs_groups)) # reorder (rotate) the pushes/pops by placing the last one in the # first's position and shifting all the other instrs down # TODO: breaks if esp is used within the reordered block! (BIB @ 070012F1) for group in (g for g in preservs_groups if len(g) > 1): # group them by block # pushes_by_block = {} ins_by_block = {} for reg, pushes, pops in group: for ins in itertools.chain(pushes, pops): if (ins.block, ins.mnem) not in ins_by_block: ins_by_block[(ins.block, ins.mnem)] = [] ins_by_block[(ins.block, ins.mnem)].append(ins) # in each block, move the latest ins to the ealiest's position for (block, mnem), instrs in ins_by_block.iteritems(): instrs.sort(key=lambda x: x.addr) first = block.instrs.index(instrs[0]) last = block.instrs.index(instrs[-1]) block.rinstrs = block.instrs[:first] block.rinstrs.append(block.instrs[last]) block.rinstrs.extend(block.instrs[first:last]) block.rinstrs.extend(block.instrs[last + 1:]) diff = inp.get_block_diff(block) changed_bytes.update((ea for ea, orig, curr in diff)) # XXX should also generate patched files ... return changed_bytes