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
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()
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()
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)
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")
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')
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")
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)
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"]
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
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
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
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
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
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