def execute_message_call(laser_evm, callee_address: str) -> None: """Executes a message call transaction from all open states. :param laser_evm: :param callee_address: """ # TODO: Resolve circular import between .transaction and ..svm to import LaserEVM here open_states = laser_evm.open_states[:] del laser_evm.open_states[:] for open_world_state in open_states: if open_world_state[callee_address].deleted: log.debug("Can not execute dead contract, skipping.") continue next_transaction_id = get_next_transaction_id() transaction = MessageCallTransaction( 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), caller=symbol_factory.BitVecVal(ATTACKER_ADDRESS, 256), callee_account=open_world_state[callee_address], call_data=SymbolicCalldata(next_transaction_id), call_value=symbol_factory.BitVecSym( "call_value{}".format(next_transaction_id), 256), ) _setup_global_state_for_execution(laser_evm, transaction) laser_evm.exec()
def get_call_data( global_state: GlobalState, memory_start: Union[int, BitVec], memory_size: Union[int, BitVec], ): """Gets call_data from the global_state. :param global_state: state to look in :param memory_start: Start index :param memory_size: Size :return: Tuple containing: call_data array from memory or empty array if symbolic, type found """ state = global_state.mstate transaction_id = "{}_internalcall".format(global_state.current_transaction.id) memory_start = cast( BitVec, ( symbol_factory.BitVecVal(memory_start, 256) if isinstance(memory_start, int) else memory_start ), ) memory_size = cast( BitVec, ( symbol_factory.BitVecVal(memory_size, 256) if isinstance(memory_size, int) else memory_size ), ) uses_entire_calldata = simplify( memory_size == global_state.environment.calldata.calldatasize ) if is_true(uses_entire_calldata): return global_state.environment.calldata try: calldata_from_mem = state.memory[ util.get_concrete_int(memory_start) : util.get_concrete_int( memory_start + memory_size ) ] return ConcreteCalldata(transaction_id, calldata_from_mem) except TypeError: log.debug( "Unsupported symbolic memory offset %s size %s", memory_start, memory_size ) return SymbolicCalldata(transaction_id)
def test_symbolic_calldata_equal_indices(): calldata = SymbolicCalldata(0) index_a = BitVec("index_a", 256) index_b = BitVec("index_b", 256) # Act a = calldata[index_a] b = calldata[index_b] s = Solver() s.append(index_a == index_b) s.append(a != b) # Assert assert unsat == s.check()
def test_symbolic_calldata_constrain_index(): # Arrange calldata = SymbolicCalldata(0) solver = Solver() # Act value = calldata[51] constraints = [value == 1, calldata.calldatasize == 50] solver.add(constraints) result = solver.check() # Assert assert str(result) == "unsat"
def __init__( self, world_state: WorldState, callee_account: Account = None, caller: ExprRef = None, call_data=None, identifier: Optional[str] = None, gas_price=None, gas_limit=None, origin=None, code=None, call_value=None, init_call_data=True, static=False, ) -> None: assert isinstance(world_state, WorldState) self.world_state = world_state self.id = identifier or get_next_transaction_id() self.gas_price = (gas_price if gas_price is not None else symbol_factory.BitVecSym( "gasprice{}".format(identifier), 256)) self.gas_limit = gas_limit self.origin = (origin if origin is not None else symbol_factory.BitVecSym( "origin{}".format(identifier), 256)) self.code = code self.caller = caller self.callee_account = callee_account if call_data is None and init_call_data: self.call_data = SymbolicCalldata(self.id) # type: BaseCalldata else: self.call_data = (call_data if isinstance(call_data, BaseCalldata) else ConcreteCalldata(self.id, [])) self.call_value = (call_value if call_value is not None else symbol_factory.BitVecSym( "callvalue{}".format(identifier), 256)) self.static = static self.return_data = None # type: str
def get_call_data( global_state: GlobalState, memory_start: Union[int, ExprRef], memory_size: Union[int, ExprRef], ): """ Gets call_data from the global_state :param global_state: state to look in :param memory_start: Start index :param memory_size: Size :return: Tuple containing: call_data array from memory or empty array if symbolic, type found """ state = global_state.mstate transaction_id = "{}_internalcall".format( global_state.current_transaction.id) try: # TODO: This only allows for either fully concrete or fully symbolic calldata. # Improve management of memory and callata to support a mix between both types. calldata_from_mem = state.memory[util.get_concrete_int( memory_start):util.get_concrete_int(memory_start + memory_size)] i = 0 starting_calldata = [] while i < len(calldata_from_mem): elem = calldata_from_mem[i] if isinstance(elem, int): starting_calldata.append(elem) i += 1 else: # BitVec for j in range(0, elem.size(), 8): starting_calldata.append(Extract(j + 7, j, elem)) i += 1 call_data = ConcreteCalldata(transaction_id, starting_calldata) call_data_type = CalldataType.CONCRETE logging.debug("Calldata: " + str(call_data)) except TypeError: logging.debug("Unsupported symbolic calldata offset") call_data_type = CalldataType.SYMBOLIC call_data = SymbolicCalldata("{}_internalcall".format(transaction_id)) return call_data, call_data_type
def execute_message_call(laser_evm, callee_address: str, priority=None) -> None: """ Executes a message call transaction from all open states """ # TODO: Resolve circular import between .transaction and ..svm to import LaserEVM here # TODO: if the function of openstate.node.funcname is not in priority list, dont add it # TODO: This is for deleting repeated variables read # copy the open states from last iteration to this iteration # The working list is always empty when an iteration is done open_states = laser_evm.open_states[:] del laser_evm.open_states[:] for open_world_state in open_states: if open_world_state[callee_address].deleted: debug("Can not execute dead contract, skipping.") continue next_transaction_id = get_next_transaction_id() transaction = MessageCallTransaction( world_state=open_world_state, identifier=next_transaction_id, gas_price=BitVec("gas_price{}".format(next_transaction_id), 256), gas_limit=8000000, # block gas limit origin=BitVec("origin{}".format(next_transaction_id), 256), caller=BitVecVal(ATTACKER_ADDRESS, 256), callee_account=open_world_state[callee_address], call_data=SymbolicCalldata(next_transaction_id), call_data_type=CalldataType.SYMBOLIC, call_value=BitVec("call_value{}".format(next_transaction_id), 256), ) # the open states from last iterations are appended to work list here _setup_global_state_for_execution(laser_evm, transaction, open_world_state.node.function_name) laser_evm.exec(priority=None)
def heuristic_message_call_helper(laser_evm, callee_address: str, priority=None): jump = False open_states_copy = copy(laser_evm.open_states) for open_state in open_states_copy: name = open_state.node.function_name for priority_list in priority['RAW']: if name == priority_list.first.function_name: laser_evm.first_order_work_list.append(open_state) laser_evm.open_states.remove(open_state) jump = True break if jump: jump = False continue for priority_list in priority['WAR']: if name == priority_list.first.function_name: laser_evm.second_order_work_list.append(open_state) laser_evm.open_states.remove(open_state) jump = True break if jump: jump = False continue for priority_list in priority['WAW']: if name == priority_list.first.function_name: laser_evm.third_order_work_list.append(open_state) laser_evm.open_states.remove(open_state) jump = True break if jump: jump = False continue for priority_list in priority['RAR']: if name == priority_list.first.function_name: laser_evm.forth_order_work_list.append(open_state) laser_evm.open_states.remove(open_state) jump = True break if jump: jump = False continue laser_evm.ranking.append(laser_evm.first_order_work_list) laser_evm.ranking.append(laser_evm.second_order_work_list) laser_evm.ranking.append(laser_evm.third_order_work_list) laser_evm.ranking.append(laser_evm.forth_order_work_list) del laser_evm.open_states[:] for items in laser_evm.ranking: title = items[0] list1 = items[1:] for open_world_state in list1: if open_world_state[callee_address].deleted: debug("Can not execute dead contract, skipping.") continue last_func_called = open_world_state.node.function_name next_transaction_id = get_next_transaction_id() transaction = MessageCallTransaction( world_state=open_world_state, callee_account=open_world_state[callee_address], caller=BitVecVal(ATTACKER_ADDRESS, 256), identifier=next_transaction_id, call_data=SymbolicCalldata(next_transaction_id), gas_price=BitVec("gas_price{}".format(next_transaction_id), 256), call_value=BitVec("call_value{}".format(next_transaction_id), 256), origin=BitVec("origin{}".format(next_transaction_id), 256), call_data_type=CalldataType.SYMBOLIC, gas_limit=8000000, # block gas limit ) # the open states from last iterations are appended to work list here _setup_global_state_for_execution(laser_evm, transaction, last_func_called) laser_evm.exec(priority=priority, title=title, laser_obj=laser_evm) # Execute the new open states added to the work list in Instruction.jumpi_ function if title == 'RAW': for gs in laser_evm.second_work_list: laser_evm.work_list.append(gs) laser_evm.exec(priority=priority, title=title, laser_obj=laser_evm) elif title == 'WAR': for gs in laser_evm.third_work_list: laser_evm.work_list.append(gs) laser_evm.exec(priority=priority, title=title, laser_obj=laser_evm) elif title == 'WAW': for gs in laser_evm.forth_work_list: laser_evm.work_list.append(gs) laser_evm.exec(priority=priority, title=title, laser_obj=laser_evm)