Ejemplo n.º 1
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
Ejemplo n.º 2
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"
    )
Ejemplo n.º 3
0
    def create_account(
        self,
        balance=0,
        address=None,
        concrete_storage=False,
        dynamic_loader=None,
        creator=None,
    ) -> Account:
        """Create non-contract account.

        :param address: The account's address
        :param balance: Initial balance for the account
        :param concrete_storage: Interpret account storage as concrete
        :param dynamic_loader: used for dynamically loading storage from the block chain
        :return: The new account
        """
        address = (symbol_factory.BitVecVal(address, 256)
                   if address else self._generate_new_address(creator))

        new_account = Account(
            address=address,
            balances=self.balances,
            dynamic_loader=dynamic_loader,
            concrete_storage=concrete_storage,
        )
        if balance:
            new_account.add_balance(symbol_factory.BitVecVal(balance, 256))

        self.put_account(new_account)
        return new_account
def _get_global_state():
    active_account = Account("0x0", code=Disassembly("60606040"))
    passive_account = Account("0x325345346564645654645",
                              code=Disassembly("6060604061626364"))
    environment = Environment(active_account, None, None, None, None, None)
    world_state = WorldState()
    world_state.put_account(active_account)
    world_state.put_account(passive_account)
    return GlobalState(world_state, environment, None,
                       MachineState(gas_limit=8000000))
Ejemplo n.º 5
0
def get_callee_account(
    global_state: GlobalState,
    callee_address: Union[str, BitVec],
    dynamic_loader: DynLoader,
):
    """Gets the callees account from the global_state.

    :param global_state: state to look in
    :param callee_address: address of the callee
    :param dynamic_loader: dynamic loader to use
    :return: Account belonging to callee
    """
    if isinstance(callee_address, BitVec):
        if callee_address.symbolic:
            return Account(callee_address,
                           balances=global_state.world_state.balances)
        else:
            callee_address = hex(callee_address.value)[2:]

    try:
        return global_state.accounts[int(callee_address, 16)]
    except KeyError:
        # We have a valid call address, but contract is not in the modules list
        log.debug("Module with address %s not loaded.", callee_address)

    if dynamic_loader is None:
        raise ValueError("dynamic_loader is None")

    log.debug("Attempting to load dependency")

    try:
        code = dynamic_loader.dynld(callee_address)
    except ValueError as error:
        log.debug("Unable to execute dynamic loader because: %s", error)
        raise error
    if code is None:
        log.debug("No code returned, not a contract account?")
        raise ValueError("No code returned")
    log.debug("Dependency loaded: " + callee_address)

    callee_account = Account(
        symbol_factory.BitVecVal(int(callee_address, 16), 256),
        code,
        callee_address,
        dynamic_loader=dynamic_loader,
        balances=global_state.world_state.balances,
    )
    global_state.accounts[int(callee_address, 16)] = callee_account

    return callee_account
Ejemplo n.º 6
0
 def create_initialized_contract_account(self, contract_code,
                                         storage) -> None:
     """
     Creates a new contract account, based on the contract code and storage provided
     The contract code only includes the runtime contract bytecode
     :param contract_code: Runtime bytecode for the contract
     :param storage: Initial storage for the contract
     :return: The new account
     """
     # TODO: Add type hints
     new_account = Account(self._generate_new_address(),
                           code=contract_code,
                           balance=0)
     new_account.storage = storage
     self._put_account(new_account)
Ejemplo n.º 7
0
    def test_laser_result(self):
        for input_file in TESTDATA_INPUTS_CONTRACTS.iterdir():
            if input_file.name in ["weak_random.sol", "environments.sol"]:
                continue
            output_expected = TESTDATA_OUTPUTS_EXPECTED_LASER_RESULT / (
                input_file.name + ".json"
            )
            output_current = TESTDATA_OUTPUTS_CURRENT_LASER_RESULT / (
                input_file.name + ".json"
            )

            disassembly = SolidityContract(str(input_file)).disassembly
            account = Account("0x0000000000000000000000000000000000000000", disassembly)
            accounts = {account.address: account}

            laser = svm.LaserEVM(accounts, max_depth=22, max_transaction_count=1)
            laser.sym_exec(account.address)
            laser_info = _all_info(laser)

            output_current.write_text(
                json.dumps(laser_info, cls=LaserEncoder, indent=4)
            )

            if not (output_expected.read_text() == output_expected.read_text()):
                self.found_changed_files(input_file, output_expected, output_current)

        self.assert_and_show_changed_files()
Ejemplo n.º 8
0
    def put_account(self, account: Account) -> None:
        """

        :param account:
        """
        self._accounts[account.address.value] = account
        account._balances = self.balances
Ejemplo n.º 9
0
def get_state():
    active_account = Account("0x0", code=Disassembly("60606040"))
    environment = Environment(active_account, None, None, None, None, None)
    state = GlobalState(None, environment, None, MachineState(gas_limit=8000000))
    state.transaction_stack.append(
        (MessageCallTransaction(world_state=WorldState(), gas_limit=8000000), None)
    )
    return state
Ejemplo n.º 10
0
def test_intercontract_call():
    # Arrange
    cfg.gbl_next_uid = 0

    caller_code = Disassembly(
        "6080604052348015600f57600080fd5b5073deadbeefdeadbeefdeadbeefdeadbeefdeadbeef73ffffffffffffffffffffffffffffffffffffffff166389627e13336040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050602060405180830381600087803b15801560be57600080fd5b505af115801560d1573d6000803e3d6000fd5b505050506040513d602081101560e657600080fd5b8101908080519060200190929190505050500000a165627a7a72305820fdb1e90f0d9775c94820e516970e0d41380a94624fa963c556145e8fb645d4c90029"
    )
    caller_address = "0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe"

    callee_code = Disassembly(
        "608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806389627e13146044575b600080fd5b348015604f57600080fd5b506082600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506084565b005b8073ffffffffffffffffffffffffffffffffffffffff166108fc3073ffffffffffffffffffffffffffffffffffffffff16319081150290604051600060405180830381858888f1935050505015801560e0573d6000803e3d6000fd5b50505600a165627a7a72305820a6b1335d6f994632bc9a7092d0eaa425de3dea05e015af8a94ad70b3969e117a0029"
    )
    callee_address = "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"

    world_state = WorldState()

    caller_account = Account(caller_address,
                             caller_code,
                             contract_name="Caller")
    callee_account = Account(callee_address,
                             callee_code,
                             contract_name="Callee")
    world_state.put_account(callee_account)
    world_state.put_account(caller_account)

    laser = svm.LaserEVM()

    # Act
    laser.sym_exec(world_state=world_state,
                   target_address=int(caller_address, 16))

    # Assert
    # Initial node starts in contract caller
    assert len(laser.nodes.keys()) > 0
    assert laser.nodes[0].contract_name == "Caller"

    # At one point we call into contract callee
    for node in laser.nodes.values():
        if node.contract_name == "Callee":
            assert len(node.states[0].transaction_stack) > 1
            return

    assert False
Ejemplo n.º 11
0
def test_symbolic_call_storage_to(mocker):
    # 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("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."
    )
Ejemplo n.º 12
0
    def __getitem__(self, item: BitVec) -> Account:
        """Gets an account from the worldstate using item as key.

        :param item: Address of the account to get
        :return: Account associated with the address
        """
        try:
            return self._accounts[item.value]
        except KeyError:
            new_account = Account(address=item,
                                  code=None,
                                  balances=self.balances)
            self._accounts[item.value] = new_account
            return new_account
Ejemplo n.º 13
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
Ejemplo n.º 14
0
    def runTest():
        disassembly = SolidityContract("./tests/native_tests.sol").disassembly
        account = Account("0x0000000000000000000000000000000000000000",
                          disassembly)
        accounts = {account.address: account}

        laser = svm.LaserEVM(accounts, max_depth=100, transaction_count=1)
        laser.sym_exec(account.address)

        laser_info = str(_all_info(laser))
        print("\n")

        _test_natives(laser_info, SHA256_TEST, "SHA256")
        _test_natives(laser_info, RIPEMD160_TEST, "RIPEMD160")
        _test_natives(laser_info, ECRECOVER_TEST, "ECRECOVER")
        _test_natives(laser_info, IDENTITY_TEST, "IDENTITY")
Ejemplo n.º 15
0
def test_staticness_call_symbolic(f1):
    # Arrange
    state = get_global_state()
    state.environment.static = True
    state.mstate.stack = []
    call_value = symbol_factory.BitVecSym("x", 256)
    code = Disassembly(code="616263")
    f1.return_value = ("0", Account(code=code,
                                    address="0x19"), 0, call_value, 0, 0, 0)
    instruction = Instruction("call", dynamic_loader=None)

    # Act and Assert
    with pytest.raises(TransactionStartSignal) as ts:
        instruction.evaluate(state)

    assert ts.value.transaction.static
    assert ts.value.global_state.mstate.constraints[-1] == (call_value == 0)
Ejemplo n.º 16
0
def test_staticness_call_concrete(f1, input, success):
    # Arrange
    state = get_global_state()
    state.environment.static = True
    state.mstate.stack = []
    code = Disassembly(code="616263")
    f1.return_value = ("0", Account(code=code,
                                    address="0x19"), 0, input, 0, 0, 0)
    instruction = Instruction("call", dynamic_loader=None)

    # Act and Assert
    if success:
        with pytest.raises(TransactionStartSignal) as ts:
            instruction.evaluate(state)
        assert ts.value.transaction.static
    else:
        with pytest.raises(WriteProtection):
            instruction.evaluate(state)
Ejemplo n.º 17
0
def get_callee_account(global_state: GlobalState, callee_address: str,
                       dynamic_loader: DynLoader):
    """Gets the callees account from the global_state.

    :param global_state: state to look in
    :param callee_address: address of the callee
    :param dynamic_loader: dynamic loader to use
    :return: Account belonging to callee
    """
    environment = global_state.environment
    accounts = global_state.accounts

    try:
        return global_state.accounts[int(callee_address, 16)]
    except KeyError:
        # We have a valid call address, but contract is not in the modules list
        log.debug("Module with address " + callee_address + " not loaded.")

    if dynamic_loader is None:
        raise ValueError()

    log.debug("Attempting to load dependency")

    try:
        code = dynamic_loader.dynld(callee_address)
    except ValueError as error:
        log.debug("Unable to execute dynamic loader because: {}".format(
            str(error)))
        raise error
    if code is None:
        log.debug("No code returned, not a contract account?")
        raise ValueError()
    log.debug("Dependency loaded: " + callee_address)

    callee_account = Account(
        symbol_factory.BitVecVal(int(callee_address, 16), 256),
        code,
        callee_address,
        dynamic_loader=dynamic_loader,
        balances=global_state.world_state.balances,
    )
    accounts[callee_address] = callee_account

    return callee_account
Ejemplo n.º 18
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_limit=8000000))
    og_state.transaction_stack.append(
        (MessageCallTransaction(world_state=WorldState(),
                                gas_limit=8000000), None))

    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
Ejemplo n.º 19
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_limit=8000000))
    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_limit=8000000))
    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_limit=8000000))
    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
Ejemplo n.º 20
0
    def runTest():
        """"""
        disassembly = SolidityContract(
            "./tests/native_tests.sol",
            solc_binary=MythrilDisassembler._init_solc_binary("0.5.3"),
        ).disassembly
        account = Account("0x0000000000000000000000000000000000000000",
                          disassembly)
        world_state = WorldState()
        world_state.put_account(account)
        laser = svm.LaserEVM(max_depth=100, transaction_count=1)
        laser.sym_exec(world_state=world_state,
                       target_address=account.address.value)

        laser_info = str(_all_info(laser))

        _test_natives(laser_info, SHA256_TEST, "SHA256")
        _test_natives(laser_info, RIPEMD160_TEST, "RIPEMD160")
        _test_natives(laser_info, ECRECOVER_TEST, "ECRECOVER")
        _test_natives(laser_info, IDENTITY_TEST, "IDENTITY")
Ejemplo n.º 21
0
def test_execute_message_call(mocked_setup: MagicMock):
    # Arrange
    laser_evm = LaserEVM({})

    world_state = WorldState()
    world_state.accounts["address"] = Account("address")

    laser_evm.open_states = [world_state]
    laser_evm.exec = MagicMock()

    mocked_setup.side_effect = _is_message_call

    # Act
    execute_message_call(laser_evm, "address")

    # Assert
    # laser_evm.exec.assert_called_once()
    assert laser_evm.exec.call_count == 1
    # mocked_setup.assert_called_once()
    assert mocked_setup.call_count == 1

    assert len(laser_evm.open_states) == 0
Ejemplo n.º 22
0
def test_execute_message_call(mocked_setup: MagicMock):
    # Arrange
    laser_evm = LaserEVM({})

    world_state = WorldState()
    world_state.put_account(Account("0x0"))

    laser_evm.open_states = [world_state]
    laser_evm.exec = MagicMock()

    mocked_setup.side_effect = _is_message_call

    # Act
    execute_message_call(laser_evm, symbol_factory.BitVecVal(0, 256))

    # Assert
    # laser_evm.exec.assert_called_once()
    assert laser_evm.exec.call_count == 1
    # mocked_setup.assert_called_once()
    assert mocked_setup.call_count == 1

    assert len(laser_evm.open_states) == 0
Ejemplo n.º 23
0
 def create_account(self,
                    balance=0,
                    address=None,
                    concrete_storage=False,
                    dynamic_loader=None) -> Account:
     """
     Create non-contract account
     :param address: The account's address
     :param balance: Initial balance for the account
     :param concrete_storage: Interpret account storage as concrete
     :param dynamic_loader: used for dynamically loading storage from the block chain
     :return: The new account
     """
     address = address if address else self._generate_new_address()
     new_account = Account(
         address,
         balance=balance,
         dynamic_loader=dynamic_loader,
         concrete_storage=concrete_storage,
     )
     self._put_account(new_account)
     return new_account
Ejemplo n.º 24
0
    def __init__(self,
                 contract,
                 address,
                 strategy,
                 dynloader=None,
                 max_depth=22,
                 execution_timeout=None,
                 create_timeout=None,
                 transaction_count=2,
                 priority=None):

        if strategy == "dfs":
            s_strategy = DepthFirstSearchStrategy
        elif strategy == "bfs":
            s_strategy = BreadthFirstSearchStrategy
        elif strategy == "naive-random":
            s_strategy = ReturnRandomNaivelyStrategy
        elif strategy == "weighted-random":
            s_strategy = ReturnWeightedRandomStrategy
        else:
            raise ValueError("Invalid strategy argument supplied")

        account = Account(
            address,
            contract.disassembly,
            dynamic_loader=dynloader,
            contract_name=contract.name,
        )

        self.accounts = {address: account}

        self.laser = svm.LaserEVM(
            self.accounts,
            dynamic_loader=dynloader,
            max_depth=max_depth,
            execution_timeout=execution_timeout,
            strategy=s_strategy,
            create_timeout=create_timeout,
            transaction_count=transaction_count,
        )
        self.laser.register_hooks(hook_type="pre",
                                  hook_dict=get_detection_module_hooks())

        if isinstance(contract, SolidityContract):
            self.laser.sym_exec(creation_code=contract.creation_code,
                                contract_name=contract.name,
                                priority=priority)
        elif isinstance(contract, EVMContract) and contract.creation_code:
            self.laser.sym_exec(creation_code=contract.creation_code,
                                contract_name=contract.name,
                                priority=priority)
        else:
            self.laser.sym_exec(address, priority=priority)

        self.nodes = self.laser.nodes
        self.edges = self.laser.edges

        print('total states: ' + str(self.laser.total_states))
        # Generate lists of interesting operations

        self.calls = []
        self.sstors = {}

        for key in self.nodes:

            state_index = 0

            for state in self.nodes[key].states:

                instruction = state.get_current_instruction()

                op = instruction["opcode"]

                if op in ("CALL", "CALLCODE", "DELEGATECALL", "STATICCALL"):

                    stack = state.mstate.stack

                    if op in ("CALL", "CALLCODE"):
                        gas, to, value, meminstart, meminsz, memoutstart, memoutsz = (
                            get_variable(stack[-1]),
                            get_variable(stack[-2]),
                            get_variable(stack[-3]),
                            get_variable(stack[-4]),
                            get_variable(stack[-5]),
                            get_variable(stack[-6]),
                            get_variable(stack[-7]),
                        )

                        if to.type == VarType.CONCRETE and to.val < 5:
                            # ignore prebuilts
                            continue

                        if (meminstart.type == VarType.CONCRETE
                                and meminsz.type == VarType.CONCRETE):
                            self.calls.append(
                                Call(
                                    self.nodes[key],
                                    state,
                                    state_index,
                                    op,
                                    to,
                                    gas,
                                    value,
                                    state.mstate.memory[meminstart.
                                                        val:meminsz.val * 4],
                                ))
                        else:
                            self.calls.append(
                                Call(
                                    self.nodes[key],
                                    state,
                                    state_index,
                                    op,
                                    to,
                                    gas,
                                    value,
                                ))
                    else:
                        gas, to, meminstart, meminsz, memoutstart, memoutsz = (
                            get_variable(stack[-1]),
                            get_variable(stack[-2]),
                            get_variable(stack[-3]),
                            get_variable(stack[-4]),
                            get_variable(stack[-5]),
                            get_variable(stack[-6]),
                        )

                        self.calls.append(
                            Call(self.nodes[key], state, state_index, op, to,
                                 gas))

                elif op == "SSTORE":
                    stack = copy.deepcopy(state.mstate.stack)
                    address = state.environment.active_account.address

                    index, value = stack.pop(), stack.pop()

                    try:
                        self.sstors[address]
                    except KeyError:
                        self.sstors[address] = {}

                    try:
                        self.sstors[address][str(index)].append(
                            SStore(self.nodes[key], state, state_index, value))
                    except KeyError:
                        self.sstors[address][str(index)] = [
                            SStore(self.nodes[key], state, state_index, value)
                        ]

                state_index += 1
Ejemplo n.º 25
0
def test_vmtest(test_name: str, pre_condition: dict, action: dict,
                post_condition: dict) -> None:
    # Arrange
    if test_name == "gasprice":
        return
    accounts = {}
    for address, details in pre_condition.items():
        account = Account(address)
        account.code = Disassembly(details["code"][2:])
        account.balance = int(details["balance"], 16)
        account.nonce = int(details["nonce"], 16)

        accounts[address] = account

    laser_evm = LaserEVM(accounts)

    # Act
    laser_evm.time = datetime.now()

    # TODO: move this line below and check for VmExceptions when gas has been implemented
    if post_condition == {}:
        return

    execute_message_call(
        laser_evm,
        callee_address=action["address"],
        caller_address=action["caller"],
        origin_address=binascii.a2b_hex(action["origin"][2:]),
        code=action["code"][2:],
        gas=action["gas"],
        data=binascii.a2b_hex(action["data"][2:]),
        gas_price=int(action["gasPrice"], 16),
        value=int(action["value"], 16),
    )

    # Assert

    assert len(laser_evm.open_states) == 1

    world_state = laser_evm.open_states[0]
    model = get_model(
        next(iter(laser_evm.nodes.values())).states[0].mstate.constraints)

    for address, details in post_condition.items():
        account = world_state[address]

        assert account.nonce == int(details["nonce"], 16)
        assert account.code.bytecode == details["code"][2:]

        for index, value in details["storage"].items():
            expected = int(value, 16)
            actual = account.storage[int(index, 16)]
            if isinstance(actual, ExprRef):
                actual = model.eval(actual)
                actual = (
                    1 if actual == True else 0 if actual == False else actual
                )  # Comparisons should be done with == than 'is' here as actual can be a BoolRef
            else:
                if type(actual) == bytes:
                    actual = int(binascii.b2a_hex(actual), 16)
                elif type(actual) == str:
                    actual = int(actual, 16)
            assert actual == expected
Ejemplo n.º 26
0
def test_vmtest(
    test_name: str,
    environment: dict,
    pre_condition: dict,
    action: dict,
    gas_used: int,
    post_condition: dict,
) -> None:
    # Arrange
    if test_name == "gasprice":
        return
    accounts = {}
    for address, details in pre_condition.items():
        account = Account(address)
        account.code = Disassembly(details["code"][2:])
        account.balance = int(details["balance"], 16)
        account.nonce = int(details["nonce"], 16)

        accounts[address] = account

    laser_evm = LaserEVM(accounts)

    # Act
    laser_evm.time = datetime.now()

    final_states = execute_message_call(
        laser_evm,
        callee_address=action["address"],
        caller_address=action["caller"],
        origin_address=binascii.a2b_hex(action["origin"][2:]),
        code=action["code"][2:],
        gas_limit=int(action["gas"], 16),
        data=binascii.a2b_hex(action["data"][2:]),
        gas_price=int(action["gasPrice"], 16),
        value=int(action["value"], 16),
        track_gas=True,
    )

    # Assert
    if gas_used is not None and gas_used < int(environment["currentGasLimit"],
                                               16):
        # avoid gas usage larger than block gas limit
        # this currently exceeds our estimations
        gas_min_max = [(s.mstate.min_gas_used, s.mstate.max_gas_used)
                       for s in final_states]
        gas_ranges = [g[0] <= gas_used for g in gas_min_max]
        assert all(map(lambda g: g[0] <= g[1], gas_min_max))
        assert any(gas_ranges)

    if any(
        (v in test_name for v in ["error", "oog"])) and post_condition == {}:
        # no more work to do if error happens or out of gas
        assert len(laser_evm.open_states) == 0
    else:
        assert len(laser_evm.open_states) == 1
        world_state = laser_evm.open_states[0]
        model = get_model(
            next(iter(laser_evm.nodes.values())).states[0].mstate.constraints,
            enforce_execution_time=False,
        )

        for address, details in post_condition.items():
            account = world_state[address]

            assert account.nonce == int(details["nonce"], 16)
            assert account.code.bytecode == details["code"][2:]

            for index, value in details["storage"].items():
                expected = int(value, 16)
                actual = account.storage[int(index, 16)]
                if isinstance(actual, Expression):
                    actual = actual.value
                    actual = 1 if actual is True else 0 if actual is False else actual
                else:
                    if type(actual) == bytes:
                        actual = int(binascii.b2a_hex(actual), 16)
                    elif type(actual) == str:
                        actual = int(actual, 16)
                assert actual == expected
Ejemplo n.º 27
0
    def __init__(
        self,
        contract,
        address: Union[int, str, BitVec],
        strategy,
        dynloader=None,
        max_depth=22,
        execution_timeout=None,
        loop_bound=2,
        create_timeout=None,
        transaction_count=2,
        modules=(),
        compulsory_statespace=True,
        enable_iprof=False,
        disable_dependency_pruning=False,
        run_analysis_modules=True,
    ):
        """

        :param contract:
        :param address:
        :param strategy:
        :param dynloader:
        :param max_depth:
        :param execution_timeout:
        :param create_timeout:
        :param transaction_count:
        :param modules:
        """
        if isinstance(address, str):
            address = symbol_factory.BitVecVal(int(address, 16), 256)
        if isinstance(address, int):
            address = symbol_factory.BitVecVal(address, 256)

        if strategy == "dfs":
            s_strategy = DepthFirstSearchStrategy  # type: Type[BasicSearchStrategy]
        elif strategy == "bfs":
            s_strategy = BreadthFirstSearchStrategy
        elif strategy == "naive-random":
            s_strategy = ReturnRandomNaivelyStrategy
        elif strategy == "weighted-random":
            s_strategy = ReturnWeightedRandomStrategy
        else:
            raise ValueError("Invalid strategy argument supplied")

        creator_account = Account(hex(CREATOR_ADDRESS),
                                  "",
                                  dynamic_loader=dynloader,
                                  contract_name=None)
        attacker_account = Account(hex(ATTACKER_ADDRESS),
                                   "",
                                   dynamic_loader=dynloader,
                                   contract_name=None)

        requires_statespace = (compulsory_statespace or
                               len(get_detection_modules("post", modules)) > 0)
        if not contract.creation_code:
            self.accounts = {hex(ATTACKER_ADDRESS): attacker_account}
        else:
            self.accounts = {
                hex(CREATOR_ADDRESS): creator_account,
                hex(ATTACKER_ADDRESS): attacker_account,
            }

        self.laser = svm.LaserEVM(
            dynamic_loader=dynloader,
            max_depth=max_depth,
            execution_timeout=execution_timeout,
            strategy=s_strategy,
            create_timeout=create_timeout,
            transaction_count=transaction_count,
            requires_statespace=requires_statespace,
            enable_iprof=enable_iprof,
        )

        if loop_bound is not None:
            self.laser.extend_strategy(BoundedLoopsStrategy, loop_bound)

        plugin_loader = LaserPluginLoader(self.laser)
        plugin_loader.load(PluginFactory.build_mutation_pruner_plugin())
        plugin_loader.load(PluginFactory.build_instruction_coverage_plugin())

        if not disable_dependency_pruning:
            plugin_loader.load(PluginFactory.build_dependency_pruner_plugin())

        world_state = WorldState()
        for account in self.accounts.values():
            world_state.put_account(account)

        if run_analysis_modules:
            self.laser.register_hooks(
                hook_type="pre",
                hook_dict=get_detection_module_hooks(modules, hook_type="pre"),
            )
            self.laser.register_hooks(
                hook_type="post",
                hook_dict=get_detection_module_hooks(modules,
                                                     hook_type="post"),
            )

        if isinstance(contract, SolidityContract):
            self.laser.sym_exec(
                creation_code=contract.creation_code,
                contract_name=contract.name,
                world_state=world_state,
            )
        elif isinstance(contract, EVMContract) and contract.creation_code:
            self.laser.sym_exec(
                creation_code=contract.creation_code,
                contract_name=contract.name,
                world_state=world_state,
            )
        else:
            account = Account(
                address,
                contract.disassembly,
                dynamic_loader=dynloader,
                contract_name=contract.name,
                concrete_storage=True if
                (dynloader is not None
                 and dynloader.storage_loading) else False,
            )
            world_state.put_account(account)
            self.laser.sym_exec(world_state=world_state,
                                target_address=address.value)

        if not requires_statespace:
            return

        self.nodes = self.laser.nodes
        self.edges = self.laser.edges

        # Parse calls to make them easily accessible

        self.calls = []  # type: List[Call]

        for key in self.nodes:

            state_index = 0

            for state in self.nodes[key].states:

                instruction = state.get_current_instruction()

                op = instruction["opcode"]

                if op in ("CALL", "CALLCODE", "DELEGATECALL", "STATICCALL"):

                    stack = state.mstate.stack

                    if op in ("CALL", "CALLCODE"):
                        gas, to, value, meminstart, meminsz, memoutstart, memoutsz = (
                            get_variable(stack[-1]),
                            get_variable(stack[-2]),
                            get_variable(stack[-3]),
                            get_variable(stack[-4]),
                            get_variable(stack[-5]),
                            get_variable(stack[-6]),
                            get_variable(stack[-7]),
                        )

                        if to.type == VarType.CONCRETE and to.val < 5:
                            # ignore prebuilts
                            continue

                        if (meminstart.type == VarType.CONCRETE
                                and meminsz.type == VarType.CONCRETE):
                            self.calls.append(
                                Call(
                                    self.nodes[key],
                                    state,
                                    state_index,
                                    op,
                                    to,
                                    gas,
                                    value,
                                    state.mstate.memory[meminstart.
                                                        val:meminsz.val * 4],
                                ))
                        else:
                            self.calls.append(
                                Call(
                                    self.nodes[key],
                                    state,
                                    state_index,
                                    op,
                                    to,
                                    gas,
                                    value,
                                ))
                    else:
                        gas, to, meminstart, meminsz, memoutstart, memoutsz = (
                            get_variable(stack[-1]),
                            get_variable(stack[-2]),
                            get_variable(stack[-3]),
                            get_variable(stack[-4]),
                            get_variable(stack[-5]),
                            get_variable(stack[-6]),
                        )

                        self.calls.append(
                            Call(self.nodes[key], state, state_index, op, to,
                                 gas))

                state_index += 1
Ejemplo n.º 28
0
                              None, None, None)
    world_state = WorldState()
    world_state.put_account(active_account)
    state = GlobalState(world_state, environment, None,
                        MachineState(gas_limit=8000000))
    state.transaction_stack.append(
        (MessageCallTransaction(world_state=world_state,
                                gas_limit=8000000), None))
    return state


@patch(
    "mythril.laser.ethereum.instructions.get_call_parameters",
    return_value=(
        "0",
        Account(code=Disassembly(code="0x00"), address="0x19"),
        0,
        0,
        0,
        0,
        0,
    ),
)
def test_staticcall(f1):
    # Arrange
    state = get_global_state()
    state.mstate.stack = [10, 10, 10, 10, 10, 10, 10, 10, 0]
    instruction = Instruction("staticcall", dynamic_loader=None)

    # Act and Assert
    with pytest.raises(TransactionStartSignal) as ts:
Ejemplo n.º 29
0
def test_vmtest(
    test_name: str,
    environment: dict,
    pre_condition: dict,
    action: dict,
    gas_used: int,
    post_condition: dict,
) -> None:

    # Arrange
    if test_name in ignored_test_names:
        return

    world_state = WorldState()

    for address, details in pre_condition.items():
        account = Account(address, concrete_storage=True)
        account.code = Disassembly(details["code"][2:])
        account.nonce = int(details["nonce"], 16)
        for key, value in details["storage"].items():
            account.storage[int(key, 16)] = int(value, 16)

        world_state.put_account(account)
        account.set_balance(int(details["balance"], 16))

    laser_evm = LaserEVM()
    laser_evm.open_states = [world_state]

    # Act
    laser_evm.time = datetime.now()

    final_states = execute_message_call(
        laser_evm,
        callee_address=symbol_factory.BitVecVal(int(action["address"], 16),
                                                256),
        caller_address=symbol_factory.BitVecVal(int(action["caller"], 16),
                                                256),
        origin_address=symbol_factory.BitVecVal(int(action["origin"], 16),
                                                256),
        code=action["code"][2:],
        gas_limit=int(action["gas"], 16),
        data=binascii.a2b_hex(action["data"][2:]),
        gas_price=int(action["gasPrice"], 16),
        value=int(action["value"], 16),
        track_gas=True,
    )

    # Assert
    if gas_used is not None and gas_used < int(environment["currentGasLimit"],
                                               16):
        # avoid gas usage larger than block gas limit
        # this currently exceeds our estimations
        gas_min_max = [(s.mstate.min_gas_used, s.mstate.max_gas_used)
                       for s in final_states]
        gas_ranges = [g[0] <= gas_used for g in gas_min_max]
        assert all(map(lambda g: g[0] <= g[1], gas_min_max))
        assert any(gas_ranges)

    if post_condition == {}:
        # no more work to do if error happens or out of gas
        assert len(laser_evm.open_states) == 0
    else:
        assert len(laser_evm.open_states) == 1
        world_state = laser_evm.open_states[0]

        for address, details in post_condition.items():
            account = world_state[symbol_factory.BitVecVal(
                int(address, 16), 256)]

            assert account.nonce == int(details["nonce"], 16)
            assert account.code.bytecode == details["code"][2:]

            for index, value in details["storage"].items():
                expected = int(value, 16)
                actual = account.storage[int(index, 16)]

                if isinstance(actual, Expression):
                    actual = actual.value
                    actual = 1 if actual is True else 0 if actual is False else actual
                else:
                    if type(actual) == bytes:
                        actual = int(binascii.b2a_hex(actual), 16)
                    elif type(actual) == str:
                        actual = int(actual, 16)
                assert actual == expected
Ejemplo n.º 30
0
    def __init__(
        self,
        contract,
        address: Union[int, str, BitVec],
        strategy: str,
        dynloader=None,
        max_depth: int = 22,
        execution_timeout: Optional[int] = None,
        loop_bound: int = 3,
        create_timeout: Optional[int] = None,
        transaction_count: int = 2,
        modules=(),
        compulsory_statespace: bool = True,
        iprof: Optional[InstructionProfiler] = None,
        disable_dependency_pruning: bool = False,
        run_analysis_modules: bool = True,
        enable_coverage_strategy: bool = False,
        custom_modules_directory: str = "",
    ):
        """

        :param contract: Contract to symbolically execute
        :param address: Address of the contract to symbolically execute
        :param strategy: Execution strategy to use (bfs, dfs, etc)
        :param dynloader: Dynamic Loader
        :param max_depth: Max analysis depth
        :param execution_timeout: Timeout for the entire analysis
        :param create_timeout: Timeout for the creation transaction
        :param transaction_count: Number of transactions to symbolically execute
        :param modules: Analysis modules to run during analysis
        :param compulsory_statespace: Boolean indicating whether or not the statespace should be saved
        :param iprof: Instruction Profiler
        :param disable_dependency_pruning: Boolean indicating whether dependency pruning should be disabled
        :param run_analysis_modules: Boolean indicating whether analysis modules should be executed
        :param enable_coverage_strategy: Boolean indicating whether the coverage strategy should be enabled
        :param custom_modules_directory: The directory to read custom analysis modules from
        """
        if isinstance(address, str):
            address = symbol_factory.BitVecVal(int(address, 16), 256)
        if isinstance(address, int):
            address = symbol_factory.BitVecVal(address, 256)

        if strategy == "dfs":
            s_strategy = DepthFirstSearchStrategy  # type: Type[BasicSearchStrategy]
        elif strategy == "bfs":
            s_strategy = BreadthFirstSearchStrategy
        elif strategy == "naive-random":
            s_strategy = ReturnRandomNaivelyStrategy
        elif strategy == "weighted-random":
            s_strategy = ReturnWeightedRandomStrategy
        else:
            raise ValueError("Invalid strategy argument supplied")

        creator_account = Account(
            hex(CREATOR_ADDRESS), "", dynamic_loader=dynloader, contract_name=None
        )
        attacker_account = Account(
            hex(ATTACKER_ADDRESS), "", dynamic_loader=dynloader, contract_name=None
        )

        requires_statespace = (
            compulsory_statespace
            or len(get_detection_modules("post", modules, custom_modules_directory)) > 0
        )
        if not contract.creation_code:
            self.accounts = {hex(ATTACKER_ADDRESS): attacker_account}
        else:
            self.accounts = {
                hex(CREATOR_ADDRESS): creator_account,
                hex(ATTACKER_ADDRESS): attacker_account,
            }

        instruction_laser_plugin = PluginFactory.build_instruction_coverage_plugin()

        self.laser = svm.LaserEVM(
            dynamic_loader=dynloader,
            max_depth=max_depth,
            execution_timeout=execution_timeout,
            strategy=s_strategy,
            create_timeout=create_timeout,
            transaction_count=transaction_count,
            requires_statespace=requires_statespace,
            iprof=iprof,
            enable_coverage_strategy=enable_coverage_strategy,
            instruction_laser_plugin=instruction_laser_plugin,
        )

        if loop_bound is not None:
            self.laser.extend_strategy(BoundedLoopsStrategy, loop_bound)

        plugin_loader = LaserPluginLoader(self.laser)
        plugin_loader.load(PluginFactory.build_mutation_pruner_plugin())
        plugin_loader.load(instruction_laser_plugin)

        if not disable_dependency_pruning:
            plugin_loader.load(PluginFactory.build_dependency_pruner_plugin())

        world_state = WorldState()
        for account in self.accounts.values():
            world_state.put_account(account)

        if run_analysis_modules:
            self.laser.register_hooks(
                hook_type="pre",
                hook_dict=get_detection_module_hooks(
                    modules,
                    hook_type="pre",
                    custom_modules_directory=custom_modules_directory,
                ),
            )
            self.laser.register_hooks(
                hook_type="post",
                hook_dict=get_detection_module_hooks(
                    modules,
                    hook_type="post",
                    custom_modules_directory=custom_modules_directory,
                ),
            )

        if isinstance(contract, SolidityContract):
            self.laser.sym_exec(
                creation_code=contract.creation_code,
                contract_name=contract.name,
                world_state=world_state,
            )
        elif isinstance(contract, EVMContract) and contract.creation_code:
            self.laser.sym_exec(
                creation_code=contract.creation_code,
                contract_name=contract.name,
                world_state=world_state,
            )
        else:
            account = Account(
                address,
                contract.disassembly,
                dynamic_loader=dynloader,
                contract_name=contract.name,
                concrete_storage=True
                if (dynloader is not None and dynloader.storage_loading)
                else False,
            )
            world_state.put_account(account)
            self.laser.sym_exec(world_state=world_state, target_address=address.value)

        if not requires_statespace:
            return

        self.nodes = self.laser.nodes
        self.edges = self.laser.edges

        # Parse calls to make them easily accessible

        self.calls = []  # type: List[Call]

        for key in self.nodes:

            state_index = 0

            for state in self.nodes[key].states:

                instruction = state.get_current_instruction()

                op = instruction["opcode"]

                if op in ("CALL", "CALLCODE", "DELEGATECALL", "STATICCALL"):

                    stack = state.mstate.stack

                    if op in ("CALL", "CALLCODE"):
                        gas, to, value, meminstart, meminsz, memoutstart, memoutsz = (
                            get_variable(stack[-1]),
                            get_variable(stack[-2]),
                            get_variable(stack[-3]),
                            get_variable(stack[-4]),
                            get_variable(stack[-5]),
                            get_variable(stack[-6]),
                            get_variable(stack[-7]),
                        )

                        if (
                            to.type == VarType.CONCRETE
                            and 0 < to.val <= PRECOMPILE_COUNT
                        ):
                            # ignore prebuilts
                            continue

                        if (
                            meminstart.type == VarType.CONCRETE
                            and meminsz.type == VarType.CONCRETE
                        ):
                            self.calls.append(
                                Call(
                                    self.nodes[key],
                                    state,
                                    state_index,
                                    op,
                                    to,
                                    gas,
                                    value,
                                    state.mstate.memory[
                                        meminstart.val : meminsz.val + meminstart.val
                                    ],
                                )
                            )
                        else:
                            self.calls.append(
                                Call(
                                    self.nodes[key],
                                    state,
                                    state_index,
                                    op,
                                    to,
                                    gas,
                                    value,
                                )
                            )
                    else:
                        gas, to, meminstart, meminsz, memoutstart, memoutsz = (
                            get_variable(stack[-1]),
                            get_variable(stack[-2]),
                            get_variable(stack[-3]),
                            get_variable(stack[-4]),
                            get_variable(stack[-5]),
                            get_variable(stack[-6]),
                        )

                        self.calls.append(
                            Call(self.nodes[key], state, state_index, op, to, gas)
                        )

                state_index += 1