Beispiel #1
0
    def sym_exec(self, main_address=None, creation_code=None, contract_name=None, code_extension=None):
        logging.debug("Starting LASER execution")
        self.time = datetime.now()

        if main_address:
            logging.info("Starting message call transaction to {}".format(main_address))
            execute_message_call(self, main_address)
        elif creation_code:
            logging.info("Starting contract creation transaction")
            created_account = execute_contract_creation(self, creation_code, contract_name=contract_name, code_extension=code_extension)
            logging.info("Finished contract creation, found {} open states".format(len(self.open_states)))
            if len(self.open_states) == 0:
                print("No contract was created during the execution of contract creation "
                      "Try to increase the resources for creation exection (max-depth or create_timeout)")
            printd("Finished outer creation")

            # Reset code coverage
            self.coverage = {}

            self.time = datetime.now()
            logging.info("Starting message call transaction")
            execute_message_call(self, created_account.address)

            #self.time = datetime.now()
            #execute_message_call(self, created_account.address)

        logging.info("Finished symbolic execution")
        logging.info("%d nodes, %d edges, %d total states", len(self.nodes), len(self.edges), self.total_states)
        for code, coverage in self.coverage.items():
            cov = reduce(lambda sum_, val: sum_ + 1 if val else sum_, coverage[1]) / float(coverage[0]) * 100
            logging.info("Achieved {} coverage for code: {}".format(cov, code))
Beispiel #2
0
    def build_annotated_contracts(self):
        for contract in self.contracts:
            if contract.name not in self.annotation_map:
                continue
            annotations = self.annotation_map[contract.name]
            sol_file = get_containing_file(contract)
            file_code = sol_file.data

            contract_range = find_contract_idx_range(contract)
            contract_code = file_code[contract_range[0]:(contract_range[2] + 1)]

            contract_prefix = file_code[:contract_range[0]]
            contract_suffix = file_code[(contract_range[2] + 1):]

            contract_prefix_as_rew = expand_rew("", contract_code, (contract_prefix, 0))

            rew_contract_code = contract_code

            rewritings = []

            for annotation in annotations:
                rewritings.extend(annotation.rewrite_code(file_code, contract_code, contract_range))

            rewriting_set = []
            for rewriting in rewritings:
                if rewriting not in rewriting_set:
                    rewriting_set.append(rewriting)
            rewritings = rewriting_set

            rewritings = sorted(rewritings, key=lambda rew:(rew.pos, rew.gname, get_sorting_priority(rew.text)))

            for rew_idx in range(len(rewritings)):
                rew = rewritings[rew_idx]
                rew_contract_code = apply_rewriting(rew_contract_code, rew)
                increase_rewritten_pos(rewritings, rewritings[rew_idx], get_newlinetype(file_code))

            increase_rewritten_pos(rewritings, contract_prefix_as_rew, get_newlinetype(file_code))

            rew_file_code = contract_prefix + rew_contract_code + contract_suffix

            printd("Rewritten code")
            printd(rew_file_code)
            write_code(sol_file.filename, rew_file_code)

            annotation_contract = SolidityContract(sol_file.filename, contract.name, solc_args=self.solc_args)
            annotation_contract.is_rew = True
            contract.is_rew = False
            augment_with_ast_info(annotation_contract)
            annotation_contract.rewritings = rewritings
            annotation_contract.origin_file_code = file_code
            annotation_contract.original_contract = contract
            self.annotated_contracts.append(annotation_contract)

            for annotation in self.annotation_map[contract.name]:
                annotation.set_annotation_contract(annotation_contract)

            write_code(sol_file.filename, file_code)
Beispiel #3
0
 def pp_trace(self):
     printd()
     printd("Trace lvl: {}".format(self.lvl))
     printd("Storage: {}".format({
         k: str(v.slot).replace("\n", " ")
         for k, v in self.storage.items()
     }))
     printd("Tran_constraints: {}".format(
         list(
             map(lambda x: str(x.constraint).replace("\n", " "),
                 self.tran_constraints))))
     printd()
Beispiel #4
0
def get_traces(statespace, contract):
    states = []

    for k in statespace.nodes:
        node = statespace.nodes[k]
        for state in node.states:
            state.contract = contract
            states.append(state)

    printd("Sequential Tracebuilding")
    results = [get_trace_for_state(state) for state in states]
    printd("Finished")

    return [trace for trace_type, trace in results if trace is not None and trace_type == "c"], \
            [trace for trace_type, trace in results if trace is not None and trace_type == "t"]
Beispiel #5
0
def get_construction_traces(statespace):
    printd("get_constructor_traces")
    num_elimi_traces= 0
    traces = []

    for k in statespace.nodes:
        node = statespace.nodes[k]
        for state in node.states:
            instruction = state.get_current_instruction()
            if instruction['opcode']  in ["STOP", "RETURN"]:
                storage = state.environment.active_account.storage
                if storage and not is_storage_primitive(storage) and are_z3_satisfiable(state.mstate.constraints):
                    traces.append(TransactionTrace(state))
                else:
                    num_elimi_traces += 1
    printd("Construction traces: " + str(len(traces)) + " eliminated: " + str(num_elimi_traces))
    return traces
Beispiel #6
0
    def check_annotations(self):

        self.build_annotated_contracts()
        self.build_traces_and_violations()


        printd("Chain_building")
        # Individual violation processing, set states and filter those where it is not decided for chaining

        # Chaining and status update until all have fixed states or a certain limit is reached
        for contract in self.annotated_contracts:
            annotations = self.annotation_map[contract.name]
            chain_strat = BackwardChainStrategy(contract.const_traces, contract.trans_traces, annotations, self.config)
            printd("Start")

            if self.config.mythril_depth > 0 and self.config.chain_verification:
                chain_strat.check_violations()

            for annotation in annotations:
                status = annotation.status
                for violation in annotation.violations:
                    if violation.status.value > status.value:
                        status = violation.status
                annotation.status = status

            printd("Stop")
        printt("CHECK ANNOTATONS")
Beispiel #7
0
    def execute_state(self, global_state):
        instructions = global_state.environment.code.instruction_list
        try:
            op_code = instructions[global_state.mstate.pc]['opcode']
        except IndexError:
            self.open_states.append(global_state.world_state)
            return [], None

        self._execute_pre_hook(op_code, global_state)
        try:
            self._measure_coverage(global_state)

            if self.prepostprocessor:
                global_state = self.prepostprocessor.preprocess(global_state)
                op_code = global_state.get_current_instruction()['opcode']

            new_global_states = Instruction(op_code, self.dynamic_loader).evaluate(global_state)

            if self.prepostprocessor:
                new_global_states = self.prepostprocessor.postprocess(global_state, new_global_states)

        except CreateNewContractSignal as c:
            laser_evm = LaserEVM(self.accounts, self.dynamic_loader, self.max_depth, self.execution_timeout, self.create_timeout,
                 strategy=DepthFirstSearchStrategy, prepostprocessor=self.prepostprocessor)

            laser_evm.open_states = [self.world_state]
            printd("Start subconstruction")
            code_extension = SymbolicCodeExtension("calldata", "Subcontract", c.extension_byte_size, c.predefined_map)
            created_account = execute_contract_creation(laser_evm, c.bytecode, contract_name="Subcontract",
                        code_extension=code_extension, callvalue=c.callvalue)

            if laser_evm.open_states:
                created_account = laser_evm.open_states[0].accounts[created_account.address]

            paused_state = c.paused_state
            paused_state.accounts[created_account.address] = created_account

            # Computing address from hex str returned to z3 BitVec representation
            address_as_int = 0
            new_address = created_account.address.replace("0x", "")
            i = 0
            while i < len(new_address):
                address_as_int *= 256
                address_as_int += int(new_address[i:i + 2], 16)
                i += 2


            paused_state.mstate.stack.append(BitVecVal(address_as_int, 256))
            paused_state.mstate.pc += 1 # Because the post code of instruction wrapper was not executed after Signal
            new_global_states = [paused_state]

            if self.prepostprocessor:
                new_global_states = self.prepostprocessor.postprocess(global_state, new_global_states)
            printd("End subcontruction")

        except TransactionStartSignal as e:
            printd("Transaction start")
            # Setup new global state
            new_global_state = e.transaction.initial_global_state()


            new_global_state.transaction_stack = copy(global_state.transaction_stack) + [(e.transaction, global_state)]
            new_global_state.node = global_state.node
            new_global_state.mstate.constraints = global_state.mstate.constraints

            return [new_global_state], op_code

        except TransactionEndSignal as e:
            printd("Transaction end")
            transaction, return_global_state = e.global_state.transaction_stack.pop()

            if return_global_state is None:
                if not isinstance(transaction, ContractCreationTransaction) or transaction.return_data:
                    e.global_state.world_state.node = global_state.node
                    self.open_states.append(e.global_state.world_state)
                new_global_states = []
            else: 
                # First execute the post hook for the transaction ending instruction
                self._execute_post_hook(op_code, [e.global_state])

                # Resume execution of the transaction initializing instruction
                op_code = return_global_state.environment.code.instruction_list[return_global_state.mstate.pc]['opcode']

                # Set execution result in the return_state
                return_global_state.last_return_data = transaction.return_data
                return_global_state.world_state = copy(global_state.world_state)
                return_global_state.environment.active_account = \
                    global_state.accounts[return_global_state.environment.active_account.address]
		
                #if self.prepostprocessor:
                #    global_state = self.prepostprocessor.preprocess(global_state)
                #    op_code = global_state.get_current_instruction()['opcode']

                # Execute the post instruction handler
                new_global_states = Instruction(op_code, self.dynamic_loader).evaluate(return_global_state, True)

                #if self.prepostprocessor:
                #    new_global_states = self.prepostprocessor.postprocess(global_state, new_global_states)

                # In order to get a nice call graph we need to set the nodes here
                for state in new_global_states:
                    state.node = global_state.node

        self._execute_post_hook(op_code, new_global_states) # Todo Maybe also filter here

        return new_global_states, op_code
    def check_violations(self):
        traces = self.const_traces + self.trans_traces

        while self.annotations:
            annotation = self.annotations.popleft()
            violations = deque(annotation.violations)
            printd(annotation.annotation_str)
            try:
                # Checks one violation at a time.
                while violations:
                    # True when a chain starting at a construction trace was found
                    constructor_chain = None
                    violation = violations.popleft()
                    # skips already verified violations
                    if violation.status == Status.VSINGLE:
                        continue
                    vts = deque([violation.trace])
                    try:
                        # Goes only until a preconfigured maximal transaction chain
                        for i in range(self.config.max_transaction_depth):
                            new_vs = []
                            while vts:
                                v = vts.popleft()
                                # Violation is finished when a chain or single transaction with non state referencing
                                # constraints was found
                                if violation.status in [
                                        Status.VSINGLE or Status.VCHAIN
                                ]:
                                    raise ViolationFinishedException
                                for t in traces:
                                    is_const = t in self.const_traces
                                    # Does not consider constructor traces, if such a trace was already found.
                                    if constructor_chain and is_const:
                                        continue
                                        # Only applies a trace to the current violation trace if it changes the
                                        # constraints
                                    if refines_constraints(
                                            t.storage, v.tran_constraints):
                                        vt_new = t.apply_trace(v)
                                        if not vt_new:  # If resulting trace contains not satisfiable constraints, the trace is skipped
                                            continue
                                        if is_const:
                                            # If the constructor is reached the evm read the def value 0 from storage
                                            zeroize_storage_vars(vt_new)
                                            # Satisfiability check already done when combining traces, only repeat
                                            # here because zeroizing might renderhe constraints unsatisfiable
                                            if not are_z3_satisfiable([
                                                    constraint.constraint
                                                    for constraint in
                                                    vt_new.tran_constraints
                                            ]):
                                                continue
                                        # No storage references implies that violating trace can be applied to any
                                        # storage state of the contract and be satifiable
                                        if not contains_storage_reference(
                                                vt_new):
                                            if self.config.search_for_indipendent_chain and is_const:
                                                # If a independent chain should be searched after a construction chain
                                                # was found the former is cached
                                                constructor_chain = (
                                                    vt_new, Status.VCHAIN)
                                            else:
                                                # Found the violating chain
                                                violation.trace = vt_new
                                                violation.status = Status.VCHAIN
                                                raise ViolationFinishedException(
                                                )
                                        else:
                                            # Add non construction chains to the worklist for the next depth
                                            if not is_const:
                                                new_vs.append(vt_new)
                            if not new_vs:
                                # if the space of trace chains was exhausted the algorithm returns with the found
                                # construction trace or the statement that the annotations holds
                                if constructor_chain:
                                    violation.trace = constructor_chain[0]
                                    violation.status = constructor_chain[1]
                                else:
                                    violation.trace = None
                                    violation.status = Status.HOLDS
                                raise ViolationFinishedException()
                            else:
                                vts = deque(new_vs)
                        # When the maximal exploration depth was reached the found constructor chain or the statement
                        # that the violation could not be confirmed is returned
                        if vts:
                            if constructor_chain:
                                violation.trace = constructor_chain[0]
                                violation.status = constructor_chain[1]
                            else:
                                violation.status = Status.VDEPTH
                                violation.trace = vts[0]
                    except ViolationFinishedException as v:
                        pass
            except AnnotationFinishedException as a:
                pass
            status = Status.HSINGLE
            # Sets the anntotation confidence level according to the most sever found violation
            for violation in annotation.violations:
                if status.value < violation.status.value:
                    status = violation.status
            annotation.status = status
Beispiel #9
0
    def postprocess(self, global_state, new_global_states):
        returnable_new_states = []

        if hasattr(global_state, 'ignore'):
            printd("carry")
            for new_state in new_global_states:
                new_state.ignore = global_state.ignore

        # Had to be added, because treatment of a single instruction does not carry over added attributes
        if hasattr(global_state, 'saved_state'):  # Carry
            for new_state in new_global_states:
                new_state.saved_state = global_state.saved_state
                new_state.id = global_state.id

        for new_state in new_global_states:
            if hasattr(new_state, "duplicate"):
                del new_state.duplicate

        for state_idx in range(len(new_global_states)):
            new_state = new_global_states[state_idx]
            instr = global_state.environment.code.instruction_list[
                global_state.mstate.pc]
            new_instr = self.get_context_instructions(new_state)[
                new_state.mstate.pc]

            if self.is_this_or_previouse_ignore_type(
                    new_state,
                    IType.ENTRY) and not hasattr(new_state, 'ignore'):
                printd("Duplicate new")
                skip_state = new_state  # Not using deepcopy here anymore, leeds to missing states in node in statespace
                new_global_states[state_idx] = None

                while self.is_this_or_previouse_ignore_type(
                        skip_state, IType.ENTRY):
                    printd(
                        str(
                            self.get_context_instructions(skip_state)[
                                skip_state.mstate.pc]))

                    # Leave a new state to start the execution of the part to be ignored by the rest
                    ign_state = deepcopy(skip_state)
                    ign_state.ignore = "ignore"
                    returnable_new_states.append(ign_state)
                    printd("Leave ignore state to process                " +
                           str(
                               self.get_context_instructions(ign_state)
                               [instr_index(
                                   self.get_context_instructions(ign_state),
                                   instr)]))

                    # set skip state to the next instruction that may again be a regular one
                    ign_exit_istr = self.get_ignore_tuple(
                        skip_state, IType.ENTRY)[IType.EXIT.value]
                    istr_idx = instr_index(
                        self.get_context_instructions(global_state),
                        ign_exit_istr)
                    skip_state.mstate.pc = istr_idx + 1
                # After skip state finally reached an instruction that is not another entry it is marked as dublicate
                skip_state.duplicate = "duplicate"

                printd("Final skip state" + str(
                    self.get_context_instructions(skip_state)[
                        skip_state.mstate.pc]))
                returnable_new_states.append(skip_state)

            # Is exit instruction
            if self.is_this_or_previouse_ignore_type(
                    global_state, IType.EXIT) and not hasattr(
                        global_state, "duplicate"
                    ) and not self.is_this_or_previouse_ignore_type(
                        global_state, IType.ENTRY
                    ):  # Changed here from "duplicate to fix false drop"
                printd("Drop at exit")
                return returnable_new_states  # Todo Drop only the ignored once at exit?
#                if hasattr(new_state, 'saved_state'):
#                    new_global_states[state_idx] = None
#                else:
#                    raise RuntimeError("No saved global state at encounter of exit instruction. This should not happen")

        returnable_new_states.extend(
            list(filter(lambda state: state is not None, new_global_states)))

        return returnable_new_states
Beispiel #10
0
    def preprocess(self, global_state):
        instructions = global_state.environment.code.instruction_list
        instr = instructions[global_state.mstate.pc]

        if hasattr(global_state, "duplicate"):
            printd("Pick up duplicate")

        message_type = "T"
        if isinstance(global_state.current_transaction,
                      ContractCreationTransaction):
            message_type = "C"

        context_instructions = self.get_context_instructions(global_state)
        context_istr_idx = instr_index(context_instructions, instr)
        if context_istr_idx is None:
            printd(message_type + " " + str(instr) + " stack: " +
                   str(global_state.mstate.stack).replace("\n", "") +
                   " constr: " +
                   str(global_state.mstate.constraints).replace("\n", ""))
            return global_state  # In delgate functions, no beginning of rewriting instruction blocks exist
        instruction = context_instructions[context_istr_idx]

        printd(message_type + " " + str(instruction) + " stack: " +
               str(global_state.mstate.stack).replace("\n", "") + " constr: " +
               str(global_state.mstate.constraints).replace("\n", ""))
        if self.is_this_or_previouse_ignore_type(global_state, IType.ENTRY):
            if hasattr(global_state, 'saved_state'):  # Skip
                printd("Skip")
                ign_exit_istr = self.get_ignore_tuple(
                    global_state, IType.ENTRY)[IType.EXIT.value]
                istr_idx = instr_index(instructions, ign_exit_istr)
                global_state.mstate.pc = istr_idx + 1
            else:  # Save
                printd("Save")
                helper_state_ref = global_state
                global_state = deepcopy(helper_state_ref)
                global_state.saved_state = helper_state_ref
                global_state.id = self.state_ctr
                self.state_ctr += 1

        if self.is_this_or_previouse_ignore_type(global_state,
                                                 IType.VIOLATION):  # violation
            printd("violation")

            #    global_state.mstate.constraints[idx] = simplify(global_state.mstate.constraints[idx])
            if True:  # are_z3_satisfiable(global_state.mstate.constraints):
                violating_state = deepcopy(global_state)
                if hasattr(violating_state, 'saved_state'):
                    del violating_state.saved_state  # Todo Why do we delete this here? I think we need the mark to ignore it in graph building

                self.add_violation(violating_state)

        return global_state
Beispiel #11
0
    def get_traces_and_build_violations(self, contract):
        dynloader = DynLoader(self.eth) if self.dynld else None

        # Building ignore lists for transactions and constructor executions
        create_ignore_list = []
        trans_ignore_list = []

        for annotation in self.annotation_map[contract.name]:
            create_ignore_list.extend(annotation.get_creation_ignore_list())
            trans_ignore_list.extend(annotation.get_trans_ignore_list())


        # Used to run annotations violation build in traces construction in parallel
        annotationsProcessor = None
        if any([len( annotation.viol_rew_instrs) > 0 for annotation in self.annotation_map[contract.name]]):
            annotationsProcessor = AnnotationProcessor(contract.creation_disassembly.instruction_list,
                                        contract.disassembly.instruction_list, create_ignore_list, trans_ignore_list,
                                        contract)

        printd("Constructor and Transaction")

        constr_calldata_len = get_minimal_constructor_param_encoding_len(abi_json_to_abi(contract.abi))
        sym_code_extension = SymbolicCodeExtension("calldata", contract.name, constr_calldata_len)

        global keccak_map
        keccak_map = {}
        if self.max_depth:
            self.config.mythril_depth = self.max_depth
        printd("Sym Exe: " + str(contract.name))


        start_meassure()
        sym_transactions = SymExecWrapper(contract, self.address, laser_strategy, dynloader, max_depth=self.config.mythril_depth,
                                          prepostprocessor=annotationsProcessor, code_extension=sym_code_extension)
        printt("Symbolic Execution")


        contract.states = []

        contract.states = sym_transactions.laser.state_id_assigner.states
        contract.config = self.config

        end = time.time()
        # print(end - start)
        if annotationsProcessor:
            printd("Construction Violations: " + str(len(reduce(lambda x, y: x + y, annotationsProcessor.create_violations, []))))
            printd("Transaction Violations: " + str(len(reduce(lambda x, y: x + y, annotationsProcessor.trans_violations, []))))

        # Add found violations to the annotations they violated
        for ign_idx in range(len(trans_ignore_list)):
            annotation = trans_ignore_list[ign_idx][4]
            annotation.add_violations(annotationsProcessor.trans_violations[ign_idx], contract, length=0,
                    vio_description="An assert with the annotations condition would fail here.")

        for ign_idx in range(len(create_ignore_list)):
            annotation = create_ignore_list[ign_idx][4]
            annotation.add_violations(annotationsProcessor.create_violations[ign_idx], contract, length=0,
                                      vio_description="An assert with the annotations condition would fail here.")

        for annotation in self.annotation_map[contract.name]:
            annotation.build_violations(sym_transactions)

        printt("BUILD VIOLATIONS")


        # Build traces from the regular annotation ignoring global states
        start_time = time.time()
        create_traces, trans_traces = get_traces(sym_transactions, contract)
        printt("BUILD TRACES")
        printd("--- %s seconds ---" % (time.time() - start_time))

        return create_traces, trans_traces