#!/usr/bin/env python ''' Rough concolic execution implementation Limitations - tested only on the simpleassert example program in examples/ - only works for 3 ints of stdin Bugs - Will probably break if a newly discovered branch gets more input/does another read(2) - possibly unnecessary deepcopies ''' import sys import Queue import struct import itertools from manticore import Manticore from manticore.core.plugin import ExtendedTracer, Follower, Plugin from manticore.core.smtlib.constraints import ConstraintSet from manticore.core.smtlib import Z3Solver, solver from manticore.core.smtlib.visitors import pretty_print as pp import copy from manticore.core.smtlib.expression import * prog = '../linux/simpleassert' main_end = 0x400ae9 VERBOSITY = 0 def _partition(pred, iterable): t1, t2 = itertools.tee(iterable) return (list(itertools.ifilterfalse(pred, t1)), filter(pred, t2)) def log(s): print '[+]', s class TraceReceiver(Plugin): def __init__(self, tracer): self._trace = None self._tracer = tracer super(self.__class__, self).__init__() @property def trace(self): return self._trace def will_generate_testcase_callback(self, state, test_id, msg): self._trace = state.context[self._tracer.context_key] instructions, writes = _partition(lambda x: x['type'] == 'regs', self._trace) total = len(self._trace) log('Recorded concrete trace: {}/{} instructions, {}/{} writes'.format( len(instructions), total, len(writes), total)) def flip(constraint): ''' flips a constraint (Equal) (Equal (BitVecITE Cond IfC ElseC) IfC) -> (Equal (BitVecITE Cond IfC ElseC) ElseC) ''' equal = copy.deepcopy(constraint) assert len(equal.operands) == 2 # assume they are the equal -> ite form that we produce on standard branches ite, forcepc = equal.operands assert isinstance(ite, BitVecITE) and isinstance(forcepc, BitVecConstant) assert len(ite.operands) == 3 cond, iifpc, eelsepc = ite.operands assert isinstance(iifpc, BitVecConstant) and isinstance(eelsepc, BitVecConstant) equal.operands[1] = eelsepc if forcepc.value == iifpc.value else iifpc return equal def eq(a, b): # this ignores checking the conditions, only checks the 2 possible pcs # the one that it is forced to ite1, force1 = a.operands ite2, force2 = b.operands if force1.value != force2.value: return False _, first1, second1 = ite1.operands _, first2, second2 = ite1.operands if first1.value != first2.value: return False if second1.value != second2.value: return False return True def perm(lst, func): ''' Produce permutations of `lst`, where permutations are mutated by `func`. Used for flipping constraints. highly possible that returned constraints can be unsat this does it blindly, without any attention to the constraints themselves Considering lst as a list of constraints, e.g. [ C1, C2, C3 ] we'd like to consider scenarios of all possible permutations of flipped constraints, excluding the original list. So we'd like to generate: [ func(C1), C2 , C3 ], [ C1 , func(C2), C3 ], [ func(C1), func(C2), C3 ], [ C1 , C2 , func(C3)], .. etc This is effectively treating the list of constraints as a bitmask of width len(lst) and counting up, skipping the 0th element (unmodified array). The code below yields lists of constraints permuted as above by treating list indeces as bitmasks from 1 to 2**len(lst) and applying func to all the set bit offsets. ''' for i in range(1, 2**len(lst)): yield [func(item) if (1< 1: main_end = int(sys.argv[1], 0) log("Got end of main: {:x}".format(main_end)) q = Queue.Queue() # todo randomly generated concrete start stdin = ints2inp(0, 5, 0) log('seed input generated ({}), running initial concrete run.'.format(inp2ints(stdin))) to_queue, datas = concrete_input_to_constraints(stdin) for each in to_queue: q.put(each) # hmmm probably issues with the datas stuff here? probably need to store # the datas in the q or something. what if there was a new read(2) deep in the program, changing the datas while not q.empty(): log('get constraint set from queue, queue size: {}'.format(q.qsize())) cons = q.get() inp = input_from_cons(cons, datas) to_queue, new_datas = concrete_input_to_constraints(inp, cons) if len(new_datas) > 0: datas = new_datas for each in to_queue: q.put(each) log('paths found: {}'.format(len(traces))) if __name__=='__main__': main()