def validate_bil(program, flow): r""" Runs the concrete executor, validating the the results are consistent with the trace. Returns a tuple of (Errors, Warnings) Currently only supports ARM, x86, and x86-64 """ trace = program.traces[0] libraries = [(m[3],m[1]) for m in trace.mapped] registers = program.tregs[0] regsize = 8 * program.tregs[1] arch = program.tregs[-1] if arch == "arm": cpu_flags = ["ZF", "CF", "NF", "VF"] PC = "PC" elif arch == "i386": cpu_flags = ["CF", "PF", "AF", "ZF", "SF", "OF", "DF"] PC = "EIP" elif arch == "x86-64": cpu_flags = ["CF", "PF", "AF", "ZF", "SF", "OF", "DF"] PC = "RIP" else: print "Architecture not supported" return [],[] errors = [] warnings = [] def new_state_for_clnum(clnum, include_flags=True): flags = cpu_flags if include_flags else [] flagvalues = [0 for f in flags] varnames = registers + flags initial_regs = trace.db.fetch_registers(clnum) varvals = initial_regs + flagvalues varvals = map(lambda x: ConcreteBitVector(regsize, x), varvals) initial_vars = dict(zip(varnames, varvals)) initial_mem_get = partial(trace.fetch_raw_memory, clnum) return State(initial_vars, initial_mem_get) state = new_state_for_clnum(0) for (addr,data,clnum,ins) in flow: instr = program.static[addr]['instruction'] if not isinstance(instr, BapInsn): errors.append(Error(clnum, instr, "Could not make BAP instruction for %s" % str(instr))) state = new_state_for_clnum(clnum) else: bil_instrs = instr.insn.bil if bil_instrs is None: errors.append(Error(clnum, instr, "No BIL for instruction %s" % str(instr))) state = new_state_for_clnum(clnum) else: # this is bad.. fix this if arch == "arm": state[PC] += 8 #Qira PC is wrong executor = ConcreteExecutor(state, PC) try: adt.visit(executor, bil_instrs) except VariableException as e: errors.append(Error(clnum, instr, "No BIL variable %s!" % str(e.args[0]))) except MemoryException as e: errors.append(Error(clnum, instr, "Used invalid address %x." % e.args[0])) if not executor.jumped: if arch == "arm": state[PC] -= 4 elif arch == "i386" or arch == "x86-64": state[PC] += instr.size() validate = True PC_val = state[PC] if PC_val > 0xf0000000 or any([PC_val >= base and PC_val <= base+size for (base,size) in libraries]): # we are jumping into a library that we can't trace.. reset the state and continue warnings.append(Warning(clnum, instr, "Jumping into library. Cannot trace this")) state = new_state_for_clnum(clnum) continue error = False correct_regs = new_state_for_clnum(clnum, include_flags=False).variables for reg, correct in correct_regs.iteritems(): if state[reg] != correct: error = True errors.append(Error(clnum, instr, "%s was incorrect! (%x != %x)." % (reg, state[reg] , correct))) state[reg] = correct for (addr, val) in state.memory.items(): realval = trace.fetch_raw_memory(clnum, addr, 1) if len(realval) == 0 or len(val) == 0: errors.append(Error(clnum, instr, "Used invalid address %x." % addr)) # this is unfixable, reset state state = new_state_for_clnum(clnum) elif val != realval: error = True errors.append(Error(clnum, instr, "Value at address %x is wrong! (%x != %x)." % (addr, ord(val), ord(realval)))) state[addr] = realval return (errors, warnings)
def visit_While(self, op): while self.run(op.cond) == 1: adt.visit(self, op.stmts)
def visit_If(self, op): if self.run(op.cond) == 1: adt.visit(self, op.true) else: adt.visit(self, op.false)
def jumps(bil): return visit(Jmp_visitor(), bil).jumps
def accesses(bil): r = visit(Access_visitor(), bil) return (r.reads, r.writes)
def validate_bil(program, flow): r""" Runs the concrete executor, validating the the results are consistent with the trace. Returns a tuple of (Errors, Warnings) Currently only supports ARM, x86, and x86-64 """ trace = program.traces[0] libraries = [(m[3], m[1]) for m in trace.mapped] registers = program.tregs[0] regsize = 8 * program.tregs[1] arch = program.tregs[-1] if arch == "arm": cpu_flags = ["ZF", "CF", "NF", "VF"] PC = "PC" elif arch == "i386": cpu_flags = ["CF", "PF", "AF", "ZF", "SF", "OF", "DF"] PC = "EIP" elif arch == "x86-64": cpu_flags = ["CF", "PF", "AF", "ZF", "SF", "OF", "DF"] PC = "RIP" else: print "Architecture not supported" return [], [] errors = [] warnings = [] def new_state_for_clnum(clnum, include_flags=True): flags = cpu_flags if include_flags else [] flagvalues = [0 for f in flags] varnames = registers + flags initial_regs = trace.db.fetch_registers(clnum) varvals = initial_regs + flagvalues varvals = map(lambda x: ConcreteBitVector(regsize, x), varvals) initial_vars = dict(zip(varnames, varvals)) initial_mem_get = partial(trace.fetch_raw_memory, clnum) return State(initial_vars, initial_mem_get) state = new_state_for_clnum(0) for (addr, data, clnum, ins) in flow: instr = program.static[addr]['instruction'] if not isinstance(instr, BapInsn): errors.append( Error(clnum, instr, "Could not make BAP instruction for %s" % str(instr))) state = new_state_for_clnum(clnum) else: bil_instrs = instr.insn.bil if bil_instrs is None: errors.append( Error(clnum, instr, "No BIL for instruction %s" % str(instr))) state = new_state_for_clnum(clnum) else: # this is bad.. fix this if arch == "arm": state[PC] += 8 #Qira PC is wrong executor = ConcreteExecutor(state, PC) try: adt.visit(executor, bil_instrs) except VariableException as e: errors.append( Error(clnum, instr, "No BIL variable %s!" % str(e.args[0]))) except MemoryException as e: errors.append( Error(clnum, instr, "Used invalid address %x." % e.args[0])) if not executor.jumped: if arch == "arm": state[PC] -= 4 elif arch == "i386" or arch == "x86-64": state[PC] += instr.size() validate = True PC_val = state[PC] if PC_val > 0xf0000000 or any([ PC_val >= base and PC_val <= base + size for (base, size) in libraries ]): # we are jumping into a library that we can't trace.. reset the state and continue warnings.append( Warning(clnum, instr, "Jumping into library. Cannot trace this")) state = new_state_for_clnum(clnum) continue error = False correct_regs = new_state_for_clnum( clnum, include_flags=False).variables for reg, correct in correct_regs.iteritems(): if state[reg] != correct: error = True errors.append( Error( clnum, instr, "%s was incorrect! (%x != %x)." % (reg, state[reg], correct))) state[reg] = correct for (addr, val) in state.memory.items(): realval = trace.fetch_raw_memory(clnum, addr, 1) if len(realval) == 0 or len(val) == 0: errors.append( Error(clnum, instr, "Used invalid address %x." % addr)) # this is unfixable, reset state state = new_state_for_clnum(clnum) elif val != realval: error = True errors.append( Error( clnum, instr, "Value at address %x is wrong! (%x != %x)." % (addr, ord(val), ord(realval)))) state[addr] = realval return (errors, warnings)