Beispiel #1
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
def test_execute_contract_creation(mocked_setup: MagicMock):
    # Arrange
    laser_evm = LaserEVM({})

    laser_evm.open_states = [WorldState(), WorldState()]
    laser_evm.exec = MagicMock()
    mocked_setup.side_effect = _is_contract_creation

    # Act
    execute_contract_creation(laser_evm, "606000")

    # Assert
    # mocked_setup.assert_called()
    assert mocked_setup.call_count >= 1
    # laser_evm.exec.assert_called_once()
    assert laser_evm.exec.call_count == 1
    assert len(laser_evm.open_states) == 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
Beispiel #4
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")
def test_codecopy_concrete():
    # Arrange
    world_state = WorldState()
    account = world_state.create_account(balance=10, address=101)
    account.code = Disassembly("60606040")
    environment = Environment(account, None, None, None, None, None)
    og_state = GlobalState(world_state, 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
Beispiel #6
0
    def __init__(
        self,
        world_state: WorldState,
        caller: ExprRef,
        identifier=None,
        callee_account=None,
        code=None,
        call_data=None,
        gas_price=None,
        call_value=None,
        origin=None,
        call_data_type=None,
    ):
        assert isinstance(world_state, WorldState)
        self.id = identifier or get_next_transaction_id()
        self.world_state = world_state
        # TODO: set correct balance for new account
        self.callee_account = (
            callee_account
            if callee_account
            else world_state.create_account(0, concrete_storage=True)
        )

        self.caller = caller

        self.gas_price = (
            BitVec("gasprice{}".format(identifier), 256)
            if gas_price is None
            else gas_price
        )
        self.call_value = (
            BitVec("callvalue{}".format(identifier), 256)
            if call_value is None
            else call_value
        )
        self.origin = (
            BitVec("origin{}".format(identifier), 256) if origin is None else origin
        )
        self.call_data_type = (
            BitVec("call_data_type{}".format(identifier), 256)
            if call_data_type is None
            else call_data_type
        )

        self.call_data = (
            Calldata(self.id, call_data)
            if not isinstance(call_data, Calldata)
            else call_data
        )
        self.origin = origin
        self.code = code
        self.return_data = None
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
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
Beispiel #9
0
def test_extcodecopy_fail():
    # Arrange
    new_world_state = WorldState()
    new_account = new_world_state.create_account(balance=10, address=101)
    new_account.code = Disassembly("60616240")
    new_environment = Environment(new_account, None, None, None, None, None)
    state = GlobalState(
        new_world_state, new_environment, None, MachineState(gas_limit=8000000)
    )
    state.transaction_stack.append(
        (MessageCallTransaction(world_state=WorldState(), gas_limit=8000000), None)
    )

    state.mstate.stack = [2, 2, 2, symbol_factory.BitVecSym("FAIL", 256)]
    instruction = Instruction("extcodecopy", dynamic_loader=None)

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

    # Assert
    assert new_state.mstate.stack == []
    assert new_state.mstate.memory._memory == state.mstate.memory._memory
Beispiel #10
0
    def __init__(
        self,
        accounts: Dict[str, Account],
        dynamic_loader=None,
        max_depth=float("inf"),
        execution_timeout=60,
        create_timeout=10,
        strategy=DepthFirstSearchStrategy,
        transaction_count=2,
    ):
        world_state = WorldState()
        world_state.accounts = accounts
        # this sets the initial world state
        self.world_state = world_state
        self.open_states = [world_state]

        self.nodes = {}
        self.edges = []
        self.coverage = {}

        self.total_states = 0
        self.dynamic_loader = dynamic_loader

        self.work_list = []
        self.strategy = strategy(self.work_list, max_depth)
        self.max_depth = max_depth
        self.transaction_count = transaction_count

        self.execution_timeout = execution_timeout
        self.create_timeout = create_timeout

        self.time = None

        self.pre_hooks = defaultdict(list)
        self.post_hooks = defaultdict(list)

        logging.info("LASER EVM initialized with dynamic loader: " +
                     str(dynamic_loader))
Beispiel #11
0
    def __init__(
        self,
        accounts: Dict[str, Account],
        dynamic_loader=None,
        max_depth=float("inf"),
        execution_timeout=60,
        create_timeout=10,
        strategy=DepthFirstSearchStrategy,
        max_transaction_count=3,
    ):
        world_state = WorldState()
        world_state.accounts = accounts
        # this sets the initial world state
        self.world_state = world_state
        self.open_states = [world_state]

        self.nodes = {}
        self.edges = []
        self.coverage = {}

        self.total_states = 0
        self.dynamic_loader = dynamic_loader
        self.graph = (SimpleGraph() if issubclass(
            strategy, BasicSearchStrategy) else Graph())
        self.strategy = strategy(self.graph, max_depth)
        self.max_depth = max_depth
        self.max_transaction_count = max_transaction_count

        self.execution_timeout = execution_timeout
        self.create_timeout = create_timeout

        self.time = None

        self.pre_hooks = {}
        self.post_hooks = {}

        logging.info("LASER EVM initialized with dynamic loader: " +
                     str(dynamic_loader))
Beispiel #12
0
def test_extcodecopy():
    # Arrange
    new_world_state = WorldState()
    new_account = new_world_state.create_account(balance=10, address=101)
    new_account.code = Disassembly("60616240")
    ext_account = new_world_state.create_account(balance=1000, address=121)
    ext_account.code = Disassembly("6040404040")

    new_environment = Environment(new_account, None, None, None, None, None)
    state = GlobalState(
        new_world_state, new_environment, None, MachineState(gas_limit=8000000)
    )
    state.transaction_stack.append(
        (MessageCallTransaction(world_state=WorldState(), gas_limit=8000000), None)
    )

    state.mstate.stack = [3, 0, 0, 121]
    instruction = Instruction("extcodecopy", dynamic_loader=None)

    # Act
    new_state = instruction.evaluate(state)[0]
    # Assert
    assert new_state.mstate.memory[0:3] == [96, 64, 64]
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))
Beispiel #14
0
def execute_contract_creation(
    laser_evm, contract_initialization_code, contract_name=None, world_state=None
) -> Account:
    """Executes a contract creation transaction from all open states.

    :param laser_evm:
    :param contract_initialization_code:
    :param contract_name:
    :return:
    """
    # TODO: Resolve circular import between .transaction and ..svm to import LaserEVM here
    del laser_evm.open_states[:]

    world_state = world_state or WorldState()
    open_states = [world_state]
    new_account = None
    for open_world_state in open_states:
        next_transaction_id = get_next_transaction_id()
        # call_data "should" be '[]', but it is easier to model the calldata symbolically
        # and add logic in codecopy/codesize/calldatacopy/calldatasize than to model code "correctly"
        transaction = ContractCreationTransaction(
            world_state=open_world_state,
            identifier=next_transaction_id,
            gas_price=symbol_factory.BitVecSym(
                "gas_price{}".format(next_transaction_id), 256
            ),
            gas_limit=8000000,  # block gas limit
            origin=symbol_factory.BitVecSym(
                "origin{}".format(next_transaction_id), 256
            ),
            code=Disassembly(contract_initialization_code),
            caller=symbol_factory.BitVecVal(CREATOR_ADDRESS, 256),
            contract_name=contract_name,
            call_data=None,
            call_value=symbol_factory.BitVecSym(
                "call_value{}".format(next_transaction_id), 256
            ),
        )
        _setup_global_state_for_execution(laser_evm, transaction)
        new_account = new_account or transaction.callee_account
    laser_evm.exec(True)

    return new_account
Beispiel #15
0
 def __init__(
     self,
     world_state: WorldState,
     caller: ExprRef = None,
     call_data=None,
     identifier: Optional[str] = None,
     gas_price=None,
     gas_limit=None,
     origin=None,
     code=None,
     call_value=None,
     contract_name=None,
     contract_address=None,
 ) -> None:
     self.prev_world_state = deepcopy(world_state)
     contract_address = (contract_address if isinstance(
         contract_address, int) else None)
     callee_account = world_state.create_account(0,
                                                 concrete_storage=True,
                                                 creator=caller.value,
                                                 address=contract_address)
     callee_account.contract_name = contract_name or callee_account.contract_name
     # init_call_data "should" be false, but it is easier to model the calldata symbolically
     # and add logic in codecopy/codesize/calldatacopy/calldatasize than to model code "correctly"
     super().__init__(
         world_state=world_state,
         callee_account=callee_account,
         caller=caller,
         call_data=call_data,
         identifier=identifier,
         gas_price=gas_price,
         gas_limit=gas_limit,
         origin=origin,
         code=code,
         call_value=call_value,
         init_call_data=True,
     )
Beispiel #16
0
from mythril.disassembler.disassembly import Disassembly
from mythril.laser.ethereum.state.environment import Environment
from mythril.laser.ethereum.state.account import Account
from mythril.laser.ethereum.state.machine_state import MachineState
from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.laser.ethereum.state.world_state import WorldState
from mythril.laser.ethereum.instructions import Instruction
from mythril.laser.ethereum.transaction.transaction_models import MessageCallTransaction

from mythril.support.support_utils import get_code_hash

from mythril.laser.smt import symbol_factory

# Arrange
world_state = WorldState()
account = world_state.create_account(balance=10, address=101)
account.code = Disassembly("60606040")
world_state.create_account(balance=10, address=1000)
environment = Environment(account, None, None, None, None, None)
og_state = GlobalState(world_state, environment, None,
                       MachineState(gas_limit=8000000))
og_state.transaction_stack.append(
    (MessageCallTransaction(world_state=WorldState(),
                            gas_limit=8000000), None))

instruction = Instruction("extcodehash", dynamic_loader=None)


def test_extcodehash_no_account():

    # If account does not exist, return 0
Beispiel #17
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
Beispiel #18
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
Beispiel #19
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
Beispiel #20
0
    def execute(
        self,
        timeout: Optional[float] = 60,
        max_depth: Optional[int] = 128,
        call_depth_limit: Optional[int] = 3,
        bounded_loops_limit: Optional[int] = 3,
        creation_code: Optional[Text] = None,
        target_address: Optional[Text] = None,
        dyn_loader: Optional[DynLoader] = None,
        contract_loader: Optional[Union[FileLoader, JsonRpcLoader]] = None
    ) -> Report:
        if contract_loader is not None:
            if isinstance(contract_loader, FileLoader):
                creation_code = contract_loader.contract(
                ).creation_disassembly.bytecode
            elif isinstance(contract_loader, JsonRpcLoader):
                target_address = contract_loader.address
                dyn_loader = contract_loader.dyn_loader
            else:
                raise ValueError('Invalid type for contract_loader parameter')

        world_state = None
        if creation_code is not None and target_address is None:
            log.info('Running symbolic execution in creation mode...')
            laser = svm.LaserEVM(execution_timeout=timeout,
                                 max_depth=max_depth,
                                 requires_statespace=False)
        elif creation_code is None and target_address is not None:
            assert dyn_loader is not None, "Dynamic Loader has not been provided"
            log.info('Running symbolic execution in existing mode...')
            laser = svm.LaserEVM(dynamic_loader=dyn_loader,
                                 execution_timeout=timeout,
                                 max_depth=max_depth,
                                 requires_statespace=False)
            world_state = WorldState()
            world_state.accounts_exist_or_load(target_address, dyn_loader)
        else:
            raise ValueError(
                'Either creation_code or target_address needs to be provided')

        for strategy in self.strategy_loader.get_strategies():
            for hook in strategy.pre_hooks:
                laser.register_hooks('pre', {hook: [strategy.execute]})
            for hook in strategy.post_hooks:
                laser.register_hooks('post', {hook: [strategy.execute]})

        # Load laser plugins
        laser.extend_strategy(BoundedLoopsStrategy, bounded_loops_limit)
        plugin_loader = LaserPluginLoader()
        plugin_loader.load(CoveragePluginBuilder())
        plugin_loader.load(MutationPrunerBuilder())
        # Temporarily disabled due to unhandled exception
        # plugin_loader.load(CallDepthLimitBuilder())
        plugin_loader.load(InstructionProfilerBuilder())
        plugin_loader.load(DependencyPrunerBuilder())
        # plugin_loader.add_args("call-depth-limit", call_depth_limit=call_depth_limit)
        plugin_loader.instrument_virtual_machine(laser, None)

        # Run symbolic execution
        start_time = time.time()
        laser.sym_exec(
            creation_code=creation_code,
            contract_name='Unknown',
            world_state=world_state,
            target_address=int(target_address, 16) if target_address else None)
        log.info('Symbolic execution finished in %.2f seconds.',
                 time.time() - start_time)

        report = Report(start_time=start_time, end_time=time.time())
        report.contract_code = creation_code
        report.contract_address = target_address
        for strategy in self.strategy_loader.get_strategies():
            report.add_report(strategy.generate_report())
        self._post_process_report(report, target_address, dyn_loader)
        return report
Beispiel #21
0
    def __init__(
        self,
        accounts: Dict[str, Account],
        dynamic_loader=None,
        max_depth=float("inf"),
        execution_timeout=60,
        create_timeout=10,
        strategy=DepthFirstSearchStrategy,
        transaction_count=2,
        requires_statespace=True,
        enable_iprof=False,
    ) -> None:
        """

        :param accounts:
        :param dynamic_loader:
        :param max_depth:
        :param execution_timeout:
        :param create_timeout:
        :param strategy:
        :param transaction_count:
        """
        world_state = WorldState()
        world_state.accounts = accounts
        # this sets the initial world state
        self.world_state = world_state
        self.open_states = [world_state]

        self.total_states = 0
        self.dynamic_loader = dynamic_loader

        self.work_list = []  # type: List[GlobalState]
        self.strategy = strategy(self.work_list, max_depth)
        self.max_depth = max_depth
        self.transaction_count = transaction_count

        self.execution_timeout = execution_timeout or 0
        self.create_timeout = create_timeout

        self.requires_statespace = requires_statespace
        if self.requires_statespace:
            self.nodes = {}  # type: Dict[int, Node]
            self.edges = []  # type: List[Edge]

        self.time = None  # type: datetime

        self.pre_hooks = defaultdict(
            list)  # type: DefaultDict[str, List[Callable]]
        self.post_hooks = defaultdict(
            list)  # type: DefaultDict[str, List[Callable]]

        self._add_world_state_hooks = []  # type: List[Callable]
        self._execute_state_hooks = []  # type: List[Callable]

        self._start_sym_trans_hooks = []  # type: List[Callable]
        self._stop_sym_trans_hooks = []  # type: List[Callable]

        self._start_sym_exec_hooks = []  # type: List[Callable]
        self._stop_sym_exec_hooks = []  # type: List[Callable]

        self.iprof = InstructionProfiler() if enable_iprof else None

        log.info("LASER EVM initialized with dynamic loader: " +
                 str(dynamic_loader))