def test_intercontract_call():
    # Arrange
    cfg.gbl_next_uid = 0

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

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

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

    accounts = {
        caller_address: caller_account,
        callee_address: callee_account
    }

    laser = svm.LaserEVM(accounts)

    # Act
    laser.sym_exec(caller_address)

    # 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
Exemple #2
0
    def test_laser_result(self):
        for input_file in TESTDATA_INPUTS_CONTRACTS.iterdir():
            if input_file.name == "weak_random.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 = svm.Account("0x0000000000000000000000000000000000000000",
                                  disassembly)
            accounts = {account.address: account}

            laser = svm.LaserEVM(accounts, max_depth=22)
            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()
Exemple #3
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),
                solc_binary=Mythril._init_solc_binary("0.5.0")).disassembly
            account = Account("0x0000000000000000000000000000000000000000",
                              disassembly)
            accounts = {account.address: account}

            laser = svm.LaserEVM(accounts, max_depth=22, transaction_count=1)
            laser.register_hooks(hook_type="post",
                                 hook_dict=get_detection_module_hooks())
            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()
Exemple #4
0
def AnalyseSherlockJson(fileAddress):

    startTime = time.time()  # pour calculer temps exécution

    accounts = {}
    contractAddress = ""
    contractName = ""
    firstContract = True

    with open(fileAddress) as json_file:
        data = json.load(json_file)
        #print (data)
        for p in data['contract']:
            contract = EVMContract(code=p['contractBytecode'],
                                   name=p['contractName'])
            address = p['contractAddress']
            account = svm.Account(address, contract.disassembly)
            accounts[address] = account
            if firstContract == True:
                contractAddress = p['contractAddress']
                contractName = p['contractName']
                firstContract = False
            print('contract name: ' + p['contractName'])
            print('contract address: ' + p['contractAddress'])
            #print('contract bytecote: ' + p['contractBytecode'])
            print('')

    laser = svm.LaserEVM(accounts,
                         max_depth=maxDept,
                         execution_timeout=executionTimeOut,
                         create_timeout=createTimeOut,
                         transaction_count=transactionCount)
    ExecutionSymbolique(laser, contractAddress, contractName, startTime)
Exemple #5
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)
        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")
Exemple #6
0
    def runTest(self):
        disassembly = SolidityContract('./tests/native_tests.sol').disassembly
        account = svm.Account("0x0000000000000000000000000000000000000000", disassembly)
        accounts = {account.address: account}

        laser = svm.LaserEVM(accounts, max_depth = 100)
        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')
Exemple #7
0
    def runTest():
        """"""
        disassembly = SolidityContract(
            "./tests/native_tests.sol", solc_binary=Mythril._init_solc_binary("0.5.0")
        ).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))

        _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")
Exemple #8
0
def AnalyseSherlockSolidity(nameContract, addressContract, locationContract):

    startTime = time.time()  # pour calculer temps exécution

    accounts = {}
    contractAddress = addressContract
    contractName = nameContract
    contract = SolidityContract(locationContract)

    account = svm.Account(contractAddress, contract.disassembly)
    accounts = {contractAddress: account}

    laser = svm.LaserEVM(accounts,
                         max_depth=maxDept,
                         execution_timeout=executionTimeOut,
                         create_timeout=createTimeOut,
                         transaction_count=transactionCount)
    ExecutionSymbolique(laser, contractAddress, contractName, startTime)
Exemple #9
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_create():
    contract = SolidityContract(
        str(tests.TESTDATA_INPUTS_CONTRACTS / "calls.sol"))

    laser_evm = svm.LaserEVM({})

    laser_evm.time = datetime.now()
    execute_contract_creation(laser_evm, contract.creation_code)

    resulting_final_state = laser_evm.open_states[0]

    for address, created_account in resulting_final_state.accounts.items():
        created_account_code = created_account.code
        actual_code = Disassembly(contract.code)

        for i in range(len(created_account_code.instruction_list)):
            found_instruction = created_account_code.instruction_list[i]
            actual_instruction = actual_code.instruction_list[i]

            assert found_instruction["opcode"] == actual_instruction["opcode"]
Exemple #11
0
    def __init__(
        self,
        contract,
        address,
        strategy,
        dynloader=None,
        max_depth=22,
        execution_timeout=None,
        create_timeout=None,
        max_transaction_count=3,
    ):

        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,
            max_transaction_count=max_transaction_count,
        )

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

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

        # 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
Exemple #12
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
Exemple #13
0
    def __init__(self,
                 contract,
                 address,
                 strategy,
                 dynloader=None,
                 max_depth=22,
                 execution_timeout=None):
        s_strategy = None
        if strategy == 'dfs':
            s_strategy = DepthFirstSearchStrategy
        elif strategy == 'bfs':
            s_strategy = BreadthFirstSearchStrategy
        else:
            raise ValueError("Invalid strategy argument supplied")

        account = svm.Account(address,
                              contract.disassembly,
                              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)

        self.laser.sym_exec(address)

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

        # 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()

                if instruction == None:
                    continue

                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
Exemple #14
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
Exemple #15
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
Exemple #16
0
    def __init__(
            self,
            contract,
            address,
            strategy,
            dynloader=None,
            max_depth=22,
            execution_timeout=None,
            create_timeout=None,
            transaction_count=2,
            modules=(),
            compulsory_statespace=True,
            enable_iprof=False,
    ):
        """

        :param contract:
        :param address:
        :param strategy:
        :param dynloader:
        :param max_depth:
        :param execution_timeout:
        :param create_timeout:
        :param transaction_count:
        :param modules:
        """
        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,
        )
        requires_statespace = (compulsory_statespace or
                               len(get_detection_modules("post", modules)) > 0)
        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,
            requires_statespace=requires_statespace,
            enable_iprof=enable_iprof,
        )

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

        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)
        elif isinstance(contract, EVMContract) and contract.creation_code:
            self.laser.sym_exec(creation_code=contract.creation_code,
                                contract_name=contract.name)
        else:
            self.laser.sym_exec(address)
        created_address = "0x" + str(
            mk_contract_address(CREATOR_ADDRESS, 0).hex())
        for key, value in self.laser.world_state.accounts.items():
            if created_address == value.address:
                contract.code = value.code.bytecode
                break

        if not requires_statespace:
            return

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

        # 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.copy(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