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 __init__(self, code="", creation_code="", name="Unknown", enable_online_lookup=False): """Create a new contract. Workaround: We currently do not support compile-time linking. Dynamic contract addresses of the format __[contract-name]_____________ are replaced with a generic address Apply this for creation_code & code :param code: :param creation_code: :param name: :param enable_online_lookup: """ creation_code = re.sub(r"(_{2}.{38})", "aa" * 20, creation_code) code = re.sub(r"(_{2}.{38})", "aa" * 20, code) self.creation_code = creation_code self.name = name self.code = code self.disassembly = Disassembly( code, enable_online_lookup=enable_online_lookup) self.creation_disassembly = Disassembly( creation_code, enable_online_lookup=enable_online_lookup)
def decode(code): dis = Disassembly(code) asm = dis.get_easm() tmp = dict() for line in asm.splitlines(): lst = line.split() tmp[lst[0]] = ' '.join(lst[1:]) return (json.dumps(dis.address_to_function_name), json.dumps(tmp))
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))
def __init__(self, code, creation_code="", name="Unknown"): self.creation_code = creation_code self.name = name # Workaround: We currently do not support compile-time linking. # Dynamic contract addresses of the format __[contract-name]_____________ are replaced with a generic address code = re.sub(r'(_+.*_+)', 'aa' * 20, code) self.code = code self.disassembly = Disassembly(self.code)
def test_easm_from_solidity_files(self): for input_file in TESTDATA_INPUTS.iterdir(): output_expected = TESTDATA_OUTPUTS_EXPECTED / (input_file.name + ".easm") output_current = TESTDATA_OUTPUTS_CURRENT / (input_file.name + ".easm") code = input_file.read_text() disassembly = Disassembly(code) output_current.write_text(disassembly.get_easm()) if not (output_expected.read_text() == output_current.read_text()): self.found_changed_files(input_file, output_expected, output_current) self.assert_and_show_changed_files()
def dynld(self, contract_address, dependency_address): if not self.contract_loading: raise ValueError( "Cannot load contract when contract_loading flag is false") logging.debug("Dynld at contract " + contract_address + ": " + dependency_address) m = re.match(r"^(0x[0-9a-fA-F]{40})$", dependency_address) if m: dependency_address = m.group(1) else: return None logging.debug("Dependency address: " + dependency_address) code = self.eth.eth_getCode(dependency_address) if code == "0x": return None else: return Disassembly(code)
def dynld(self, contract_address, dependency_address): """ :param contract_address: :param dependency_address: :return: """ if not self.contract_loading: raise ValueError("Cannot load contract when contract_loading flag is false") log.debug("Dynld at contract " + contract_address + ": " + dependency_address) # Ensure that dependency_address is the correct length, with 0s prepended as needed. dependency_address = ( "0x" + "0" * (42 - len(dependency_address)) + dependency_address[2:] ) m = re.match(r"^(0x[0-9a-fA-F]{40})$", dependency_address) if m: dependency_address = m.group(1) else: return None log.debug("Dependency address: " + dependency_address) code = self.eth.eth_getCode(dependency_address) if code == "0x": return None else: return Disassembly(code)
def execute_message_call( laser_evm, callee_address, caller_address, origin_address, code, data, gas, gas_price, value, ): """ Executes a message call transaction from all open states """ open_states = laser_evm.open_states[:] del laser_evm.open_states[:] for open_world_state in open_states: next_transaction_id = get_next_transaction_id() transaction = MessageCallTransaction( identifier=next_transaction_id, world_state=open_world_state, callee_account=open_world_state[callee_address], caller=caller_address, call_data=Calldata(next_transaction_id, data), gas_price=gas_price, call_value=value, origin=origin_address, call_data_type=CalldataType.SYMBOLIC, code=Disassembly(code), ) _setup_global_state_for_execution(laser_evm, transaction) laser_evm.exec()
def execute_create(): global last_state global created_contract_account if not last_state and not created_contract_account: code_raw = [] for i in range(len(contract_init_code) // 2): code_raw.append(int(contract_init_code[2 * i:2 * (i + 1)], 16)) calldata = ConcreteCalldata(0, code_raw) world_state = WorldState() account = world_state.create_account(balance=1000000, address=101) account.code = Disassembly("60a760006000f000") environment = Environment(account, None, calldata, 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)) laser = LaserEVM() states = [og_state] last_state = og_state for state in states: new_states, op_code = laser.execute_state(state) last_state = state if op_code == "STOP": break states.extend(new_states) created_contract_address = last_state.mstate.stack[-1].value created_contract_account = last_state.world_state.accounts[ created_contract_address] return last_state, created_contract_account
def test_concrete_call_symbolic_to(): # arrange address = "0x10" active_account = Account(address) active_account.code = Disassembly("00") environment = Environment(active_account, None, None, None, None, None) state = GlobalState(None, environment, None) state.mstate.memory = ["placeholder", "calldata_bling_0"] node = Node("example") node.contract_name = "the contract name" node.function_name = "the function name" to = Variable("calldata_3", VarType.SYMBOLIC) meminstart = Variable(1, VarType.CONCRETE) call = Call(node, state, None, None, to, None) # act issues = _concrete_call(call, state, address, meminstart) # assert issue = issues[0] assert issue.address == address assert issue.contract == node.contract_name assert issue.function == node.function_name assert issue.title == "Call data forwarded with delegatecall()" assert issue.type == "Informational" assert ( issue.description == "This contract forwards its call data via DELEGATECALL in its fallback function." " This means that any function in the called contract can be executed." " Note that the callee contract will have access to the storage of the " "calling contract.\n DELEGATECALL target: calldata_3" )
def execute_contract_creation(laser_evm, contract_initialization_code, contract_name=None) -> Account: """ Executes a contract creation transaction from all open states""" # TODO: Resolve circular import between .transaction and ..svm to import LaserEVM here open_states = laser_evm.open_states[:] del laser_evm.open_states[:] new_account = laser_evm.world_state.create_account(0, concrete_storage=True, dynamic_loader=None) if contract_name: new_account.contract_name = contract_name for open_world_state in open_states: next_transaction_id = get_next_transaction_id() transaction = ContractCreationTransaction( open_world_state, BitVec("creator{}".format(next_transaction_id), 256), next_transaction_id, new_account, Disassembly(contract_initialization_code), [], BitVec("gas_price{}".format(next_transaction_id), 256), BitVec("call_value{}".format(next_transaction_id), 256), BitVec("origin{}".format(next_transaction_id), 256), CalldataType.SYMBOLIC, ) _setup_global_state_for_execution(laser_evm, transaction) laser_evm.exec(True) return new_account
def execute_contract_creation(laser_evm, contract_initialization_code, contract_name=None): """ Executes a contract creation transaction from all open states""" open_states = laser_evm.open_states[:] del laser_evm.open_states[:] new_account = laser_evm.world_state.create_account(0, concrete_storage=True, dynamic_loader=None) if contract_name: new_account.contract_name = contract_name for open_world_state in open_states: transaction = ContractCreationTransaction( open_world_state, BitVec("caller", 256), new_account, Disassembly(contract_initialization_code), [], BitVec("gas_price", 256), BitVec("call_value", 256), BitVec("origin", 256), CalldataType.SYMBOLIC ) _setup_global_state_for_execution(laser_evm, transaction) laser_evm.exec(True) return new_account
def execute_contract_creation(laser_evm, contract_initialization_code, contract_name=None) -> Account: """ Executes a contract creation transaction from all open states""" # TODO: Resolve circular import between .transaction and ..svm to import LaserEVM here open_states = laser_evm.open_states[:] del laser_evm.open_states[:] new_account = laser_evm.world_state.create_account(0, concrete_storage=True, dynamic_loader=None) if contract_name: new_account.contract_name = contract_name for open_world_state in open_states: next_transaction_id = get_next_transaction_id() transaction = ContractCreationTransaction( 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), code=Disassembly(contract_initialization_code), caller=BitVecVal(CREATOR_ADDRESS, 256), callee_account=new_account, call_data=[], call_data_type=CalldataType.SYMBOLIC, call_value=BitVec("call_value{}".format(next_transaction_id), 256), ) _setup_global_state_for_execution(laser_evm, transaction) laser_evm.exec(True) return new_account
def runTest(self): disassembly = Disassembly(self.code) self.assertEqual( len(disassembly.blocks), 162, 'Disassembler error: Incorrect number of blocks generated)')
def __init__( self, address: Union[BitVec, str], code=None, contract_name="unknown", balances: Array = None, concrete_storage=False, dynamic_loader=None, ) -> None: """Constructor for account. :param address: Address of the account :param code: The contract code of the account :param contract_name: The name associated with the account :param balance: The balance for the account :param concrete_storage: Interpret storage as concrete """ self.nonce = 0 self.code = code or Disassembly("") self.address = (address if isinstance(address, BitVec) else symbol_factory.BitVecVal(int(address, 16), 256)) self.storage = Storage(concrete_storage, address=self.address, dynamic_loader=dynamic_loader) # Metadata self.contract_name = contract_name self.deleted = False self._balances = balances self.balance = lambda: self._balances[self.address]
def __init__( self, address: str, code=None, contract_name="unknown", balance=None, concrete_storage=False, dynamic_loader=None, ): """ Constructor for account :param address: Address of the account :param code: The contract code of the account :param contract_name: The name associated with the account :param balance: The balance for the account :param concrete_storage: Interpret storage as concrete """ self.nonce = 0 self.code = code or Disassembly("") self.balance = balance if balance else BitVec("{}_balance".format(address), 256) self.storage = Storage( concrete_storage, address=address, dynamic_loader=dynamic_loader ) # Metadata self.address = address self.contract_name = contract_name self.deleted = False
def test_delegate_call(sym_mock, concrete_mock, curr_instruction): # arrange # sym_mock = mocker.patch.object(delegatecall, "_symbolic_call") # concrete_mock = mocker.patch.object(delegatecall, "_concrete_call") sym_mock.return_value = [] concrete_mock.return_value = [] curr_instruction.return_value = {"address": "0x10"} active_account = Account("0x10") active_account.code = Disassembly("00") environment = Environment(active_account, None, None, None, None, None) state = GlobalState(None, environment, Node) state.mstate.memory = ["placeholder", "calldata_bling_0"] state.mstate.stack = [1, 2, 3] assert state.get_current_instruction() == {"address": "0x10"} node = Node("example") node.contract_name = "the contract name" node.function_name = "fallback" to = Variable("storage_1", VarType.SYMBOLIC) call = Call(node, state, None, "DELEGATECALL", to, None) statespace = MagicMock() statespace.calls = [call] # act execute(statespace) # assert assert concrete_mock.call_count == 1 assert sym_mock.call_count == 1
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_vmtest( test_name: str, pre_condition: dict, action: dict, post_condition: dict ) -> None: # Arrange accounts = {} for address, details in pre_condition.items(): account = Account(address) account.code = Disassembly(details["code"][2:]) account.balance = int(details["balance"], 16) account.nonce = int(details["nonce"], 16) accounts[address] = account laser_evm = LaserEVM(accounts) # Act laser_evm.time = datetime.now() # TODO: move this line below and check for VmExceptions when gas has been implemented if post_condition == {}: return execute_message_call( laser_evm, callee_address=action["address"], caller_address=action["caller"], origin_address=action["origin"], code=action["code"][2:], gas=action["gas"], data=binascii.a2b_hex(action["data"][2:]), gas_price=int(action["gasPrice"], 16), value=int(action["value"], 16), ) # Assert assert len(laser_evm.open_states) == 1 world_state = laser_evm.open_states[0] model = get_model(next(iter(laser_evm.nodes.values())).states[0].mstate.constraints) for address, details in post_condition.items(): account = world_state[address] assert account.nonce == int(details["nonce"], 16) assert account.code.bytecode == details["code"][2:] for index, value in details["storage"].items(): expected = int(value, 16) if type(account.storage[int(index, 16)]) != int: actual = model.eval(account.storage[int(index, 16)]) actual = 1 if actual == True else 0 if actual == False else actual else: actual = account.storage[int(index, 16)] assert actual == expected
def __init__(self, code, creation_code="", name="Unknown", enable_online_lookup=True): # Workaround: We currently do not support compile-time linking. # Dynamic contract addresses of the format __[contract-name]_____________ are replaced with a generic address # Apply this for creation_code & code creation_code = re.sub(r'(_{2}.{38})', 'aa' * 20, creation_code) code = re.sub(r'(_{2}.{38})', 'aa' * 20, code) self.creation_code = creation_code self.name = name self.code = code self.disassembly = Disassembly( code, enable_online_lookup=enable_online_lookup) self.creation_disassembly = Disassembly( creation_code, enable_online_lookup=enable_online_lookup)
def get_state(): world_state = WorldState() account = world_state.create_account(balance=10, address=101) account.code = Disassembly("60606040") environment = Environment(account, None, None, None, None, None) state = GlobalState(world_state, environment, None, MachineState(gas_limit=8000000)) state.transaction_stack.append( (MessageCallTransaction(world_state=WorldState(), gas_limit=8000000), None)) return state
def end(self, global_state, return_data=None): if not all([isinstance(element, int) for element in return_data]): self.return_data = None raise TransactionEndSignal(global_state) contract_code = bytes.hex(array.array('B', return_data).tostring()) global_state.environment.active_account.code = Disassembly(contract_code) self.return_data = global_state.environment.active_account.address raise TransactionEndSignal(global_state)
def test_vmtest(test_name: str, pre_condition: dict, action: dict, post_condition: dict) -> None: # Arrange accounts = {} for address, details in pre_condition.items(): account = Account(address) account.code = Disassembly(details['code'][2:]) account.balance = int(details['balance'], 16) account.nonce = int(details['nonce'], 16) accounts[address] = account laser_evm = LaserEVM(accounts) # Act laser_evm.time = datetime.now() try: execute_message_call( laser_evm, callee_address=action['address'], caller_address=action['caller'], origin_address=action['origin'], code=action['code'][2:], gas=action['gas'], data=binascii.a2b_hex(action['data'][2:]), gas_price=int(action['gasPrice'], 16), value=int(action['value'], 16) ) except VmException as e: if post_condition == {}: return else: raise e # Assert if 'Suicide' not in test_name: assert len(laser_evm.open_states) == 1 else: assert len(laser_evm.open_states) == 0 return world_state = laser_evm.open_states[0] for address, details in post_condition.items(): account = world_state[address] assert account.nonce == int(details['nonce'], 16) assert account.code.bytecode == details['code'][2:] for index, value in details['storage'].items(): expected = int(value, 16) actual = get_concrete_int(account.storage[int(index, 16)]) assert actual == expected
def check_annotations(contracts, address, eth, dynld, max_depth=12): logging.debug("Executing annotations check") for contract in contracts: contr_to_const = deepcopy(contract) contr_to_const.disassembly = Disassembly(contr_to_const.creation_code) contr_to_const.code = contr_to_const.creation_code dynloader = DynLoader(eth) if dynld else None glbstate = get_constr_glbstate(contr_to_const, address) sym_constructor = SymExecWrapper(contr_to_const, address, dynloader, max_depth, glbstate) sym_contract = SymExecWrapper(contract, address, dynloader, max_depth=max_depth) constructor_trace = get_construction_traces( sym_constructor ) # Todo the traces here should not contain references to storages anymore for t in constructor_trace: t.pp_trace() traces = get_transaction_traces(sym_contract) print("Start") a = process_time() print("const: " + str(len(constructor_trace)) + " trans: " + str(len(traces))) trace_chains = [] for trace in constructor_trace: comp_trace_lvls = trace.apply_up_to_trace_levels(traces, 1) if not trace_chains: trace_chains = comp_trace_lvls else: for index in range(len(trace_chains)): trace_chains[index].extend(comp_trace_lvls[index]) print() for lvl in trace_chains: print("Traces: " + str(len(lvl))) #for tt in trace_chains[-1]: # tt.pp_trace() # for trace_lvl in comp_trace_lvls: # print(len(trace_lvl)) # print(sum(map(lambda t: len(t.tran_constraints), trace_lvl))/len(trace_lvl)) # for t in trace_lvl: # print("constraints: " + str(len(t.tran_constraints))) # t.pp_trace() print(process_time() - a)
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 end(self, global_state: GlobalState, return_data=None, revert=False): if (not all([isinstance(element, int) for element in return_data]) or len(return_data) == 0): self.return_data = None raise TransactionEndSignal(global_state) contract_code = bytes.hex(array.array("B", return_data).tostring()) global_state.environment.active_account.code = Disassembly( contract_code) self.return_data = global_state.environment.active_account.address assert global_state.environment.active_account.code.instruction_list != [] raise TransactionEndSignal(global_state, revert=revert)
def test_codecopy_concrete(): # Arrange active_account = Account("0x0", code=Disassembly("60606040")) environment = Environment(active_account, None, None, None, None, None) og_state = GlobalState(None, environment, None, MachineState(gas=10000000)) og_state.mstate.stack = [2, 2, 2] instruction = Instruction("codecopy", dynamic_loader=None) # Act new_state = instruction.evaluate(og_state)[0] # Assert assert new_state.mstate.memory[2] == 96 assert new_state.mstate.memory[3] == 64
def execute_message_call( laser_evm, callee_address, caller_address, origin_address, code, data, gas_limit, gas_price, value, track_gas=False, ) -> Union[None, List[GlobalState]]: """Execute a message call transaction from all open states. :param laser_evm: :param callee_address: :param caller_address: :param origin_address: :param code: :param data: :param gas_limit: :param gas_price: :param value: :param track_gas: :return: """ # 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: next_transaction_id = get_next_transaction_id() transaction = MessageCallTransaction( world_state=open_world_state, identifier=next_transaction_id, gas_price=gas_price, gas_limit=gas_limit, origin=origin_address, code=Disassembly(code), caller=caller_address, callee_account=open_world_state[callee_address], call_data=ConcreteCalldata(next_transaction_id, data), call_value=value, ) _setup_global_state_for_execution(laser_evm, transaction) return laser_evm.exec(track_gas=track_gas)
def get_xrefs(self): instruction_list = Disassembly(self.code).instruction_list xrefs = [] for instruction in instruction_list: if instruction['opcode'] == "PUSH20": if instruction['argument']: addr = instruction['argument'] if (re.match(r'^0x[a-zA-Z0-9]{40}$', addr) and addr != "0xffffffffffffffffffffffffffffffffffffffff"): if addr not in xrefs: xrefs.append(addr) return xrefs