Exemplo n.º 1
0
def malloc_summary(name, argbytes, esp_p, esp_v, size_arg):
    arg_p, arg_v = (esp_p + (4 * size_arg) + 4, esp_v + (4 * size_arg) + 4)
    # We replace each call to malloc with a summary function that
    # fixes the stack (since the Windows API uses callee-cleanup)
    # and inserts an IFLO_MALLOC pseudo-op into the trace.
    summary = [
        TraceEntry(('IFLO_TB_HEAD_EIP', [name])),
        TraceEntry(('IFLO_GET_ARG', [size_arg, arg_p, arg_v])),
        TraceEntry(('IFLO_MALLOC', [])),
    ] + ret(esp_p, esp_v, argbytes)

    return summary
Exemplo n.º 2
0
def fix_reps(trace):
    counter=[0] # work around python's lack of proper closures
    def labelmaker():
        label = "label%d" % counter[0]
        counter[0] += 1
        return label
    labels = defaultdict(labelmaker)

    edits = []
    current_tb = 'START'
    for i in range(len(trace)):
        t = trace[i]
        if t.op == 'IFLO_TB_HEAD_EIP':
            current_tb = t.args[0]
        elif t.op == 'IFLO_INSN_BYTES' and is_rep(t) and trace[i-1].op != 'IFLO_TB_HEAD_EIP':
            new_te = TraceEntry(('IFLO_TB_HEAD_EIP', [t.args[0]]))
            new_t = TraceEntry((t.op,t.args))

            if trace[i+1].op == 'IFLO_SET_CC_OP':
                cc_te = TraceEntry((trace[i+1].op, trace[i+1].args))
                edit = ((i, i+2), [cc_te, new_te, new_t])   # IFLO_INSN_BYTES, IFLO_SET_CC_OP => IFLO_SET_CC_OP, IFLO_TB_HEAD_EIP, IFLO_INSN_BYTES
            else:
                edit = ((i, i+1), [new_te, new_t])                      # IFLO_INSN_BYTES                 =>                 IFLO_TB_HEAD_EIP, IFLO_INSN_BYTES

            edits.append(edit)
            current_tb = t.args[0]
        elif t.op == 'IFLO_OPS_TEMPLATE_JNZ_ECX' and t.args[-1]:
            new_t = TraceEntry((t.op,t.args))
            new_te = TraceEntry(('IFLO_TB_HEAD_EIP', ["labels_%x" % current_tb]))
            edit = ((i,i+1), [new_t,new_te])

            edits.append(edit)
            current_tb = "labels_%x" % current_tb

    edits.sort()
    print "About to make %d edits to split TBs" % len(edits)

    if not edits: return remake_trace(trace)

    if not quiet:
        widgets = ['Fixing reps: ', Percentage(), ' ', Bar(marker=RotatingMarker()), ' ', ETA()]
        pbar = ProgressBar(widgets=widgets, maxval=len(edits)).start()

    i = 0
    while edits:
        i += 1
        if not quiet: pbar.update(i)
        (st, ed), repl = edits.pop()
        trace[st:ed] = repl

    return remake_trace(trace)
Exemplo n.º 3
0
def realloc_summary(name, argbytes, esp_p, esp_v, ptr_arg, size_arg):
    ptr_arg_p, ptr_arg_v = (esp_p + (4 * ptr_arg) + 4,
                            esp_v + (4 * ptr_arg) + 4)
    size_arg_p, size_arg_v = (esp_p + (4 * size_arg) + 4,
                              esp_v + (4 * size_arg) + 4)
    summary = [
        TraceEntry(('IFLO_TB_HEAD_EIP', [name])),
        TraceEntry(('IFLO_GET_ARG', [ptr_arg, ptr_arg_p, ptr_arg_v])),
        TraceEntry(('IFLO_MOVL_T0_ARG', [])),
        TraceEntry(('IFLO_GET_ARG', [size_arg, size_arg_p, size_arg_v])),
        TraceEntry(('IFLO_REALLOC', ['T0', 'ARG'])),
    ] + ret(esp_p, esp_v, argbytes)

    return summary
Exemplo n.º 4
0
def nop_functions(trace, tbs, tbdict, cfg):
    # No-ops. Format: address, number of argument bytes, name
    nops = all_nops[target_os]

    for n, argbytes, name in nops:
        if n not in tbdict: continue
        surgery_sites = []
        summary_name = "NOP_%d" % argbytes
        for tb in tbdict[n]:
            callsite = tb.prev
            retaddr = find_retaddr(callsite)
            esp_p, esp_v = get_callsite_esp(callsite)

            retsite = min((t for t in tbdict[retaddr]), key=lambda x: len(trace) if x.start < callsite.end else x.start - callsite.end)

            print "Call to %#x (%s) at callsite %s.%d returns to %s.%d" % (n, name, callsite._label_str(), callsite.start, retsite._label_str(), retsite.start)
            remove_start, remove_end = callsite.end, retsite.start
            surgery_sites.append( (remove_start, remove_end, esp_p, esp_v, argbytes, summary_name) )

            # Alter the callsite so that it goes to the redirected function
            for i, te in reversed(callsite.body):
                if te.op == 'IFLO_JMP_T0':
                    new_te = TraceEntry(('IFLO_CALL', [summary_name]))
                    trace[i] = new_te
                    break

        surgery_sites.sort()
        while surgery_sites:
            st, ed, esp_p, esp_v, argbytes, summary_name = surgery_sites.pop()
            summary = null_summary(summary_name, argbytes, esp_p, esp_v)
            trace[st:ed] =  summary

        trace, tbs, tbdict, cfg = remake_trace(trace)

    return trace, tbs, tbdict, cfg
Exemplo n.º 5
0
def split_fastcall(trace):
    edits = []
    for i,e in enumerate(trace):
        if e.op == 'IFLO_SYSENTER':
            edits.append( (i, [
                TraceEntry(('IFLO_SYSENTER_DATA',[])),
                TraceEntry(('IFLO_SYSENTER_CONTROL',[]))
            ]))
        elif e.op == 'IFLO_SYSEXIT':
            edits.append( (i, [
                TraceEntry(('IFLO_SYSEXIT_DATA',[])),
                TraceEntry(('IFLO_SYSEXIT_CONTROL',[]))
            ]))

    edits.sort()
    while edits:
        i, rep = edits.pop()
        trace[i:i+1] = rep

    return remake_trace(trace)
Exemplo n.º 6
0
def ret(esp_p, esp_v, argbytes):
    return [
        TraceEntry(('IFLO_OPREG_TEMPL_MOVL_A0_R', [4])),
        TraceEntry(('IFLO_OPS_MEM_LDL_T0_A0',
                    [2, esp_p, esp_v, invalid, invalid, invalid, 0])),
        TraceEntry(('IFLO_ADDL_ESP_IM', [argbytes + 4])),
        TraceEntry(('IFLO_JMP_T0', [])),
        TraceEntry(('IFLO_MOVL_T0_0', [])),
        TraceEntry(('IFLO_EXIT_TB', [])),
    ]
Exemplo n.º 7
0
def detect_reallocs(trace, tbs, tbdict, cfg):
    reallocs = all_reallocs[target_os]

    # Replace the reallocs with summary functions
    for m, argbytes, ptr_arg, size_arg, name in reallocs:
        if m not in tbdict: continue
        surgery_sites = []
        summary_name = "REALLOC_%d_%d" % (argbytes, size_arg)
        for tb in tbdict[m]:
            callsite = tb.prev
            
            # Find the return address and get its TB object
            # Note: this might fail if we have nested calls, but worry
            # about that later. The "right" solution is a shadow stack
            retaddr = find_retaddr(callsite)
            esp_p, esp_v = get_callsite_esp(callsite)
            
            # Find the closest return site instance to this callsite
            retsite = min((t for t in tbdict[retaddr]), key=lambda x: len(trace) if x.start < callsite.end else x.start - callsite.end)

            print "Call to %#x (%s) at callsite %s.%d returns to %s.%d" % (m, name, callsite._label_str(), callsite.start, retsite._label_str(), retsite.start)
            print "  `->    PTR: [%#x]" % get_function_arg(trace, ptr_arg, esp_v, callsite.end)
            print "  `->   SIZE: [%#x]" % get_function_arg(trace, size_arg, esp_v, callsite.end)
            print "  `-> RETURN: [%#x]" % get_tb_retval(retsite.prev)
            remove_start, remove_end = callsite.end, retsite.start
            surgery_sites.append( (remove_start, remove_end, esp_p, esp_v, argbytes, ptr_arg, size_arg, summary_name) )

            # Alter the callsite so that it goes to the redirected function
            for i, te in reversed(callsite.body):
                if te.op == 'IFLO_JMP_T0':
                    new_te = TraceEntry(('IFLO_CALL', [summary_name]))
                    trace[i] = new_te
                    break

        # By doing them from the bottom up we can defer recalculating
        # the indices until we're all done.
        surgery_sites.sort()
        while surgery_sites:
            st, ed, esp_p, esp_v, argbytes, ptr_arg, size_arg, summary_name = surgery_sites.pop()
            summary = realloc_summary(summary_name, argbytes, esp_p, esp_v, ptr_arg, size_arg)
            trace[st:ed] =  summary
        trace, tbs, tbdict, cfg = remake_trace(trace)

    return trace, tbs, tbdict, cfg
Exemplo n.º 8
0
def copyarg_summary(name, argbytes, esp_p, esp_v, src_arg, dst_arg, dst_ptr_p,
                    dst_ptr_v):
    src_p, src_v = (esp_p + (4 * src_arg) + 4, esp_v + (4 * src_arg) + 4)
    dst_p, dst_v = (esp_p + (4 * dst_arg) + 4, esp_v + (4 * dst_arg) + 4)
    summary = [
        TraceEntry(('IFLO_TB_HEAD_EIP', [name])),
        TraceEntry(('IFLO_GET_ARG', [src_arg, src_p, src_v])),
        TraceEntry(('IFLO_MOVL_T0_ARG', [])),
        TraceEntry(('IFLO_GET_ARG', [dst_arg, dst_p, dst_v])),
        TraceEntry(('IFLO_MOVL_A0_ARG', [])),
        TraceEntry(('IFLO_OPS_MEM_STL_T0_A0',
                    [2, dst_ptr_p, dst_ptr_v, invalid, invalid, invalid, 0])),
    ] + ret(esp_p, esp_v, argbytes)

    return summary
Exemplo n.º 9
0
def isolate_self_ints(trace):
    apic_base = 0xfee00000
    apic_tpr = apic_base + 0x80
    selfints = find_self_interrupts(trace)
    
    idt = {}
    int_edges = set()
    to_delete = []
    wlist = []
    for self_int, tpr_set, int_start, int_end in selfints:
        int_site = first(lambda x: x.op == 'IFLO_TB_HEAD_EIP', reversed(trace[int_start-50:int_start]))
        int_handler = trace[int_start+1]
        ret = first(lambda x: x.op == 'IFLO_TB_HEAD_EIP', reversed(trace[int_end-50:int_end]))
        retsite = first(lambda x: x.op == 'IFLO_TB_HEAD_EIP', trace[int_end:int_end+20])
        int_edges.add( (int_site.args[0], int_handler.args[0]) )
        int_edges.add( (ret.args[0], retsite.args[0]) )
        to_delete += [int_start, int_end]

        vec = trace[int_start].args[0]

        print "Detected self-interrupt, edges: %#x -> %#x, %#x -> %#x, vector: %#x (%#x)" % (
            int_site.args[0], int_handler.args[0],
            ret.args[0], retsite.args[0],
            vec, trace[self_int].args[-1] & 0xff,
        ) 

        idt[vec] = int_handler.args[0]

        # Gotta include the one right before the ICR set as well
        i = self_int
        while i > 0:
            e = trace[i]
            if e.op.startswith('IFLO_OPS_MEM_STL') and e.args[1] == apic_tpr:
                e.mark()
                wlist.append( (i, uses(e)) )
                break
            i -= 1

        # Noops used here as temporary placeholders. We will
        # delete them after slicing, but deletion now would mess
        # up our indices
        trace[int_start] = TraceEntry(('IFLO_NOOP', []))
        trace[int_end] = TraceEntry(('IFLO_NOOP', []))
        wlist.append( (self_int, uses(trace[self_int])) )
        wlist.append( (tpr_set, uses(trace[tpr_set])) )
        trace[self_int].mark()
        trace[tpr_set].mark()
        
    if wlist:
        multislice(trace, wlist)

    # Remove the actual interrupt/irets
    to_delete.sort()
    while to_delete:
        del trace[to_delete.pop()]

    trace, tbs, tbdict, cfg = remake_trace(trace)
    for s,d in int_edges:
        try:
            cfg[s].remove(d)
        except KeyError:
            pass
    
    return trace, tbs, tbdict, cfg, idt
Exemplo n.º 10
0
def set_input(trace, in_addr, idx):
    for var, i in trace.find_inputs(in_addr):
        #print "Replacing", `trace[i]`, "with SET_INPUT[%s,%d] at %d" % (var, idx, i)
        new_te = TraceEntry(("IFLO_SET_INPUT", [var, idx]))
        trace[i] = new_te
Exemplo n.º 11
0
    trace.append((last_op, last_args))

    # Find the inputs
    inbufs = []
    input_insns = []
    last_op, last_args = trace.pop(0)
    while last_op != 'IFLO_LABEL_INPUT':
        last_op, last_args = trace.pop(0)
    while last_op == 'IFLO_LABEL_INPUT':
        inbufs += memrange(last_args[0], last_args[1])
        input_insns.append((last_op, last_args))
        last_op, last_args = trace.pop(0)
    trace.insert(0, (last_op, last_args))

    # Turn the trace into a list of TraceEntry objects
    trace = list(enumerate(TraceEntry(t) for t in trace))

    # Set up the inputs
    set_input(trace, inbufs)

    slice = dynslice(trace, outbufs, output_track=True)

    print "# Sliced %d instructions down to %d" % (len(trace), len(slice))
    print "# Re-rolling loops..."
    for s in slice:
        print repr(s)
    newslice = reroll_loops(trace, slice, debug=True)
    print "# Slice went from %d instructions to %d (loops are counted as one insn)" % (
        len(slice), len(newslice))

    print "######## BEGIN GENERATED CODE ########"
Exemplo n.º 12
0
def null_summary(name, argbytes, esp_p, esp_v):
    summary = [
        TraceEntry(('IFLO_TB_HEAD_EIP', [name])),
    ] + ret(esp_p, esp_v, argbytes)

    return summary