예제 #1
0
    def _end_message_call(
        self,
        return_global_state: GlobalState,
        global_state: GlobalState,
        revert_changes=False,
        return_data=None,
    ) -> List[GlobalState]:
        # 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 = return_data
        if not revert_changes:
            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]

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

        # 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

        return new_global_states
예제 #2
0
def test_delegate_call(sym_mock, concrete_mock, curr_instruction):
    # arrange
    # sym_mock = mocker.patch.object(delegatecall, "_symbolic_call")
    # concrete_mock = mocker.patch.object(delegatecall, "_concrete_call")
    sym_mock.return_value = []
    concrete_mock.return_value = []
    curr_instruction.return_value = {"address": "0x10"}

    active_account = Account("0x10")
    active_account.code = Disassembly("00")

    environment = Environment(active_account, None, None, None, None, None)
    state = GlobalState(None, environment, Node)
    state.mstate.memory = ["placeholder", "calldata_bling_0"]
    state.mstate.stack = [1, 2, 3]
    assert state.get_current_instruction() == {"address": "0x10"}

    node = Node("example")
    node.contract_name = "the contract name"
    node.function_name = "fallback"

    to = Variable("storage_1", VarType.SYMBOLIC)
    call = Call(node, state, None, "DELEGATECALL", to, None)

    statespace = MagicMock()
    statespace.calls = [call]

    # act
    execute(statespace)

    # assert
    assert concrete_mock.call_count == 1
    assert sym_mock.call_count == 1
예제 #3
0
def test_execute_node(mocker):
    record = TaintRecord()
    record.stack = [True, True, False, False]

    state_1 = GlobalState(None, None, None)
    state_1.mstate.stack = [1, 2, 3, 1]
    state_1.mstate.pc = 1
    mocker.patch.object(state_1, 'get_current_instruction')
    state_1.get_current_instruction.return_value = {"opcode": "SWAP1"}

    state_2 = GlobalState(None, 1, None)
    state_2.mstate.stack = [1, 2, 4, 1]
    mocker.patch.object(state_2, 'get_current_instruction')
    state_2.get_current_instruction.return_value = {"opcode": "ADD"}

    node = Node("Test contract")
    node.states = [state_1, state_2]

    # Act
    records = TaintRunner.execute_node(node, record)

    # Assert
    assert len(records) == 2

    assert records[0].stack == [True, True, False, False]
    assert records[1].stack == [True, True, False]

    assert state_2 in records[0].states
    assert state_1 in record.states
예제 #4
0
    def execute_state(record: TaintRecord, state: GlobalState) -> TaintRecord:
        assert len(state.mstate.stack) == len(record.stack)
        """ Runs taint analysis on a state """
        record.add_state(state)
        new_record = record.clone()

        # Apply Change
        op = state.get_current_instruction()["opcode"]

        if op in TaintRunner.stack_taint_table.keys():
            mutator = TaintRunner.stack_taint_table[op]
            TaintRunner.mutate_stack(new_record, mutator)
        elif op.startswith("PUSH"):
            TaintRunner.mutate_push(op, new_record)
        elif op.startswith("DUP"):
            TaintRunner.mutate_dup(op, new_record)
        elif op.startswith("SWAP"):
            TaintRunner.mutate_swap(op, new_record)
        elif op is "MLOAD":
            TaintRunner.mutate_mload(new_record, state.mstate.stack[-1])
        elif op.startswith("MSTORE"):
            TaintRunner.mutate_mstore(new_record, state.mstate.stack[-1])
        elif op is "SLOAD":
            TaintRunner.mutate_sload(new_record, state.mstate.stack[-1])
        elif op is "SSTORE":
            TaintRunner.mutate_sstore(new_record, state.mstate.stack[-1])
        elif op.startswith("LOG"):
            TaintRunner.mutate_log(new_record, op)
        elif op in ("CALL", "CALLCODE", "DELEGATECALL", "STATICCALL"):
            TaintRunner.mutate_call(new_record, op)
        else:
            logging.debug("Unknown operation encountered: {}".format(op))

        return new_record
예제 #5
0
파일: svm.py 프로젝트: fodisi/mythril
    def sym_exec(self, main_address):
        logging.debug("Starting LASER execution")
        self.time = datetime.now()

        # Initialize the execution environment
        environment = Environment(
            self.accounts[main_address],
            BitVec("caller", 256),
            [],
            BitVec("gasprice", 256),
            BitVec("callvalue", 256),
            BitVec("origin", 256),
            calldata_type=CalldataType.SYMBOLIC,
        )

        self.instructions_covered = [
            False for _ in environment.code.instruction_list
        ]

        initial_node = Node(environment.active_account.contract_name)
        self.nodes[initial_node.uid] = initial_node

        global_state = GlobalState(self.accounts, environment, initial_node)
        global_state.environment.active_function_name = "fallback()"
        initial_node.states.append(global_state)

        # Empty the work_list before starting an execution
        self.work_list.append(global_state)
        self._sym_exec()

        logging.info("Execution complete")
        logging.info("Achieved {0:.3g}% coverage".format(self.coverage))
        logging.info("%d nodes, %d edges, %d total states", len(self.nodes),
                     len(self.edges), self.total_states)
예제 #6
0
def test_concrete_call():
    # arrange
    address = "0x10"

    state = GlobalState(None, None, None)
    state.mstate.memory = ["placeholder", "calldata_bling_0"]

    node = Node("example")
    node.contract_name = "the contract name"
    node.function_name = "the function name"

    to = Variable(1, VarType.CONCRETE)
    meminstart = Variable(1, VarType.CONCRETE)
    call = Call(node, state, None, None, to, None)

    # act
    issues = _concrete_call(call, state, address, meminstart)

    # assert
    issue = issues[0]
    assert issue.address == address
    assert issue.contract == node.contract_name
    assert issue.function == node.function_name
    assert issue.title == "Call data forwarded with delegatecall()"
    assert issue.type == 'Informational'
    assert issue.description == "This contract forwards its call data via DELEGATECALL in its fallback function." \
                                " This means that any function in the called contract can be executed." \
                                " Note that the callee contract will have access to the storage of the " \
                                "calling contract.\n DELEGATECALL target: 0x1"
예제 #7
0
    def sym_exec(self, main_address):
        logging.debug("Starting LASER execution")

        # Initialize the execution environment
        environment = Environment(
            self.accounts[main_address],
            BitVec("caller", 256),
            [],
            BitVec("gasprice", 256),
            BitVec("callvalue", 256),
            BitVec("origin", 256),
            calldata_type=CalldataType.SYMBOLIC,
        )

        # TODO: contact name fix
        initial_node = Node(environment.active_account.contract_name)
        self.nodes[initial_node.uid] = initial_node

        global_state = GlobalState(self.accounts, environment, initial_node)
        initial_node.states.append(global_state)

        # Empty the work_list before starting an execution
        self.work_list.append(global_state)
        self._sym_exec()

        logging.info("Execution complete")
        logging.info("%d nodes, %d edges, %d total states", len(self.nodes),
                     len(self.edges), self.total_states)
예제 #8
0
    def callcode_(self, global_state):
        instr = global_state.get_current_instruction()
        environment = global_state.environment

        try:
            callee_address, callee_account, call_data, value, call_data_type, gas, _, _ = get_call_parameters(
                global_state, self.dynamic_loader, True)
        except ValueError as e:
            logging.info(
                "Could not determine required parameters for call, putting fresh symbol on the stack. \n{}"
                .format(e))
            global_state.mstate.stack.append(
                BitVec("retval_" + str(instr['address']), 256))
            return [global_state]

        global_state.call_stack.append(instr['address'])

        environment = deepcopy(environment)

        environment.callvalue = value
        environment.caller = environment.address
        environment.calldata = call_data

        new_global_state = GlobalState(global_state.accounts, environment,
                                       MachineState(gas))
        new_global_state.mstate.depth = global_state.mstate.depth + 1
        new_global_state.mstate.constraints = copy(
            global_state.mstate.constraints)

        return [new_global_state]
예제 #9
0
    def run(self, open_world_states: list, evm):
        """ Runs this transaction on the evm starting from the open world states """
        # Consume the open states
        open_states = open_world_states[:]
        del open_world_states[:]

        for open_world_state in open_states:

            # Initialize the execution environment
            environment = Environment(
                open_world_state[self.callee_address],
                self.caller,
                [],
                self.gas_price,
                self.call_value,
                self.origin,
                calldata_type=CalldataType.SYMBOLIC,
            )
            new_node = Node(environment.active_account.contract_name)
            evm.instructions_covered = [False for _ in environment.code.instruction_list]

            evm.nodes[new_node.uid] = new_node
            if open_world_state.node:
                evm.edges.append(Edge(open_world_state.node.uid, new_node.uid, edge_type=JumpType.Transaction, condition=None))

            global_state = GlobalState(open_world_state.accounts, environment, new_node)
            global_state.environment.active_function_name = 'fallback'
            new_node.states.append(global_state)

            evm.work_list.append(global_state)

        evm.exec()
        logging.info("Execution complete")
        logging.info("Achieved {0:.3g}% coverage".format(evm.coverage))
예제 #10
0
def get_constr_glbstate(contract, address):

    mstate = MachineState(gas=10000000)

    minimal_const_byte_len = get_minimal_constructor_param_encoding_len(abi_json_to_abi(contract.abi))

    # better would be to append symbolic params to the bytecode such that the codecopy instruction that copies the
    # params into memory takes care of placing them onto the memory with the respective size.
    for i in range(int(minimal_const_byte_len / 32)):
        mstate.mem_extend(128 + 32 * i, 32)
        mstate.memory.insert(128 + 32 * i, BitVec('calldata_' + contract.name + '[' + str(i * 32)+ "]", 256))


    # Todo Replace pure placement of enough symbolic 32 Byte-words with placement of symbolic variables that contain
    # the name of the solidity variables

    accounts = {address: Account(address, contract.disassembly, contract_name=contract.name)}

    environment = Environment(
        accounts[address],
        BitVec("caller", 256),
        [],
        BitVec("gasprice", 256),
        BitVec("callvalue", 256),
        BitVec("origin", 256),
        calldata_type=CalldataType.SYMBOLIC,
    )

    # Todo find source for account info, maybe the std statespace?

    return GlobalState(accounts, environment, None, mstate)
예제 #11
0
def test_symbolic_call_calldata_to(mocker):
    # arrange
    address = "0x10"

    state = GlobalState(None, None, None)
    state.mstate.memory = ["placeholder", "calldata_bling_0"]

    node = Node("example")
    node.contract_name = "the contract name"
    node.function_name = "the function name"

    to = Variable("calldata", VarType.SYMBOLIC)
    call = Call(node, state, None, "Type: ", to, None)

    mocker.patch.object(SymExecWrapper, "__init__", lambda x, y: None)
    statespace = SymExecWrapper(1)

    mocker.patch.object(statespace, "find_storage_write")
    statespace.find_storage_write.return_value = "Function name"

    # act
    issues = _symbolic_call(call, state, address, statespace)

    # assert
    issue = issues[0]
    assert issue.address == address
    assert issue.contract == node.contract_name
    assert issue.function == node.function_name
    assert issue.title == "Type:  to a user-supplied address"
    assert issue.type == "Informational"
    assert (
        issue.description ==
        "This contract delegates execution to a contract address obtained from calldata. "
        "Be aware that the called contract gets unrestricted access to this contract's state."
    )
예제 #12
0
def test_concrete_call_symbolic_to():
    # arrange
    address = "0x10"

    active_account = Account(address)
    active_account.code = Disassembly("00")
    environment = Environment(active_account, None, None, None, None, None)
    state = GlobalState(None, environment, None)
    state.mstate.memory = ["placeholder", "calldata_bling_0"]

    node = Node("example")
    node.contract_name = "the contract name"
    node.function_name = "the function name"

    to = Variable("calldata_3", VarType.SYMBOLIC)
    meminstart = Variable(1, VarType.CONCRETE)
    call = Call(node, state, None, None, to, None)

    # act
    issues = _concrete_call(call, state, address, meminstart)

    # assert
    issue = issues[0]
    assert issue.address == address
    assert issue.contract == node.contract_name
    assert issue.function == node.function_name
    assert issue.title == "Call data forwarded with delegatecall()"
    assert issue.type == "Informational"
    assert (
        issue.description
        == "This contract forwards its call data via DELEGATECALL in its fallback function."
        " This means that any function in the called contract can be executed."
        " Note that the callee contract will have access to the storage of the "
        "calling contract.\n DELEGATECALL target: calldata_3"
    )
예제 #13
0
def test_concrete_call_not_calldata():
    # arrange
    state = GlobalState(None, None, None)
    state.mstate.memory = ["placeholder", "not_calldata"]
    meminstart = Variable(1, VarType.CONCRETE)

    # act
    issues = _concrete_call(None, state, None, meminstart)

    # assert
    assert issues == []
예제 #14
0
def test_execute(mocker):
    active_account = Account('0x00')
    environment = Environment(active_account, None, None, None, None, None)
    state_1 = GlobalState(None, environment, None, MachineState(gas=10000000))
    state_1.mstate.stack = [1, 2]
    mocker.patch.object(state_1, 'get_current_instruction')
    state_1.get_current_instruction.return_value = {"opcode": "PUSH"}

    state_2 = GlobalState(None, environment, None, MachineState(gas=10000000))
    state_2.mstate.stack = [1, 2, 3]
    mocker.patch.object(state_2, 'get_current_instruction')
    state_2.get_current_instruction.return_value = {"opcode": "ADD"}

    node_1 = Node("Test contract")
    node_1.states = [state_1, state_2]

    state_3 = GlobalState(None, environment, None, MachineState(gas=10000000))
    state_3.mstate.stack = [1, 2]
    mocker.patch.object(state_3, 'get_current_instruction')
    state_3.get_current_instruction.return_value = {"opcode": "ADD"}

    node_2 = Node("Test contract")
    node_2.states = [state_3]

    edge = Edge(node_1.uid, node_2.uid)

    statespace = LaserEVM(None)
    statespace.edges = [edge]
    statespace.nodes[node_1.uid] = node_1
    statespace.nodes[node_2.uid] = node_2

    # Act
    result = TaintRunner.execute(statespace, node_1, state_1, [True, True])

    # Assert
    print(result)
    assert len(result.records) == 3
    assert result.records[2].states == []
    assert state_3 in result.records[1].states
예제 #15
0
def test_result_no_state():
    # arrange
    taint_result = TaintResult()
    record = TaintRecord()
    state = GlobalState(2, None, None)
    state.mstate.stack = [1, 2, 3]

    # act
    taint_result.add_records([record])
    tainted = taint_result.check(state, 2)

    # assert
    assert tainted is None
    assert record in taint_result.records
예제 #16
0
def test_codecopy_concrete():
    # Arrange
    active_account = Account("0x0", code=Disassembly("60606040"))
    environment = Environment(active_account, None, None, None, None, None)
    og_state = GlobalState(None, environment, None, MachineState(gas=10000000))

    og_state.mstate.stack = [2, 2, 2]
    instruction = Instruction("codecopy", dynamic_loader=None)

    # Act
    new_state = instruction.evaluate(og_state)[0]

    # Assert
    assert new_state.mstate.memory[2] == 96
    assert new_state.mstate.memory[3] == 64
예제 #17
0
def test_execute_state(mocker):
    record = TaintRecord()
    record.stack = [True, False, True]

    state = GlobalState(None, None, None)
    state.mstate.stack = [1, 2, 3]
    mocker.patch.object(state, 'get_current_instruction')
    state.get_current_instruction.return_value = {"opcode": "ADD"}

    # Act
    new_record = TaintRunner.execute_state(record, state)

    # Assert
    assert new_record.stack == [True, True]
    assert record.stack == [True, False, True]
예제 #18
0
    def initial_global_state(self):
        # Initialize the execution environment
        environment = Environment(self.callee_account,
                                  self.caller,
                                  self.call_data,
                                  self.gas_price,
                                  self.call_value,
                                  self.origin,
                                  code=self.code or self.callee_account.code,
                                  calldata_type=self.call_data_type,
                                  can_write=self.can_write)

        global_state = GlobalState(self.world_state, environment, None)
        global_state.environment.active_function_name = 'fallback'

        return global_state
예제 #19
0
    def initial_global_state(self):
        # Initialize the execution environment
        environment = Environment(
            self.callee_account,
            self.caller,
            self.call_data,
            self.gas_price,
            self.call_value,
            self.origin,
            self.code,
            calldata_type=self.call_data_type,
        )
        if hasattr(self, "code_extension"):
            environment.code_extension = self.code_extension

        global_state = GlobalState(self.world_state, environment, None)
        global_state.environment.active_function_name = 'constructor'

        return global_state
예제 #20
0
    def _new_node_state(self,
                        state: GlobalState,
                        edge_type=JumpType.UNCONDITIONAL,
                        condition=None) -> None:
        new_node = Node(state.environment.active_account.contract_name)
        old_node = state.node
        state.node = new_node
        new_node.constraints = state.mstate.constraints
        self.nodes[new_node.uid] = new_node
        self.edges.append(
            Edge(old_node.uid,
                 new_node.uid,
                 edge_type=edge_type,
                 condition=condition))

        if edge_type == JumpType.RETURN:
            new_node.flags |= NodeFlags.CALL_RETURN
        elif edge_type == JumpType.CALL:
            try:
                if "retval" in str(state.mstate.stack[-1]):
                    new_node.flags |= NodeFlags.CALL_RETURN
                else:
                    new_node.flags |= NodeFlags.FUNC_ENTRY
            except StackUnderflowException:
                new_node.flags |= NodeFlags.FUNC_ENTRY
        address = state.environment.code.instruction_list[
            state.mstate.pc]["address"]

        environment = state.environment
        disassembly = environment.code
        if address in disassembly.address_to_function_name:
            # Enter a new function
            environment.active_function_name = disassembly.address_to_function_name[
                address]
            new_node.flags |= NodeFlags.FUNC_ENTRY

            logging.debug("- Entering function " +
                          environment.active_account.contract_name + ":" +
                          new_node.function_name)
        elif address == 0:
            environment.active_function_name = "fallback"

        new_node.function_name = environment.active_function_name
예제 #21
0
    def initial_global_state(self):
        # Initialize the execution environment
        environment = Environment(
            self.callee_account,
            self.caller,
            self.call_data,
            self.gas_price,
            self.call_value,
            self.origin,
            self.code,
            calldata_type=self.call_data_type,
        )

        global_state = GlobalState(self.world_state, environment, None)
        global_state.environment.active_function_name = "constructor"
        global_state.mstate.constraints.extend(
            global_state.environment.calldata.constraints)

        return global_state
예제 #22
0
def test_symbolic_call_storage_to(mocker):
    # arrange
    address = "0x10"

    active_account = Account(address)
    environment = Environment(active_account, None, None, None, None, None)
    state = GlobalState(None, environment, None)
    state.mstate.memory = ["placeholder", "calldata_bling_0"]


    node = Node("example")
    node.contract_name = "the contract name"
    node.function_name = "the function name"

    to = Variable("storage_1", VarType.SYMBOLIC)
    call = Call(node, state, None, "Type: ", to, None)


    mocker.patch.object(SymExecWrapper, "__init__", lambda x, y: None)
    statespace = SymExecWrapper(1)

    mocker.patch.object(statespace, 'find_storage_write')
    statespace.find_storage_write.return_value = "Function name"


    # act
    issues = _symbolic_call(call, state, address, statespace)

    # assert
    issue = issues[0]
    assert issue.address == address
    assert issue.contract == node.contract_name
    assert issue.function == node.function_name
    assert issue.title == 'Type:  to a user-supplied address'
    assert issue.type == 'Informational'
    assert issue.description == 'This contract delegates execution to a contract address in storage slot 1.' \
                                ' This storage slot can be written to by calling the function `Function name`. ' \
                                'Be aware that the called contract gets unrestricted access to this contract\'s state.'
예제 #23
0
    def call_(self, global_state):
        instr = global_state.get_current_instruction()
        environment = global_state.environment

        try:
            callee_address, callee_account, call_data, value, call_data_type, gas, memory_out_offset, memory_out_size = get_call_parameters(
                global_state, self.dynamic_loader, True)
        except ValueError as e:
            logging.info(
                "Could not determine required parameters for call, putting fresh symbol on the stack. \n{}"
                .format(e))
            # TODO: decide what to do in this case
            global_state.mstate.stack.append(
                BitVec("retval_" + str(instr['address']), 256))
            return [global_state]

        if 0 < int(callee_address, 16) < 5:
            logging.info("Native contract called: " + callee_address)
            if call_data == [] and call_data_type == CalldataType.SYMBOLIC:
                logging.debug("CALL with symbolic data not supported")
                global_state.mstate.stack.append(
                    BitVec("retval_" + str(instr['address']), 256))
                return [global_state]

            data = natives.native_contracts(int(callee_address, 16), call_data)
            try:
                mem_out_start = helper.get_concrete_int(memory_out_offset)
                mem_out_sz = memory_out_size.as_long()
            except AttributeError:
                logging.debug(
                    "CALL with symbolic start or offset not supported")
                global_state.mstate.stack.append(
                    BitVec("retval_" + str(instr['address']), 256))
                return [global_state]

            global_state.mstate.mem_extend(mem_out_start, mem_out_sz)
            try:
                for i in range(min(len(data), mem_out_sz)
                               ):  # If more data is used then it's chopped off
                    global_state.mstate.memory[mem_out_start + i] = data[i]
            except:
                global_state.mstate.memory[mem_out_start] = BitVec(data, 256)

            # TODO: maybe use BitVec here constrained to 1
            global_state.mstate.stack.append(
                BitVec("retval_" + str(instr['address']), 256))
            return [global_state]

        global_state.call_stack.append(instr['address'])
        callee_environment = Environment(
            callee_account,
            BitVecVal(int(environment.active_account.address, 16), 256),
            call_data,
            environment.gasprice,
            value,
            environment.origin,
            calldata_type=call_data_type)
        new_global_state = GlobalState(global_state.accounts,
                                       callee_environment, MachineState(gas))
        new_global_state.mstate.depth = global_state.mstate.depth + 1
        new_global_state.mstate.constraints = copy(
            global_state.mstate.constraints)
        return [global_state]