def test_bitvecfunc_ext_unequal_nested_comparison_f(): # Arrange s = Solver() input1 = symbol_factory.BitVecSym("input1", 256) input2 = symbol_factory.BitVecSym("input2", 256) input3 = symbol_factory.BitVecSym("input3", 256) input4 = symbol_factory.BitVecSym("input4", 256) bvf1 = symbol_factory.BitVecFuncSym("bvf1", "sha3", 256, input_=input1) bvf2 = symbol_factory.BitVecFuncSym("bvf2", "sha3", 256, input_=bvf1 + input3) bvf3 = symbol_factory.BitVecFuncSym("bvf3", "sha3", 256, input_=input2) bvf4 = symbol_factory.BitVecFuncSym("bvf4", "sha3", 256, input_=bvf3 + input4) # Act s.add(input1 != input2) s.add(input3 == input4) s.add(bvf2 == bvf4) # Assert assert s.check() == z3.unsat
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 test_bitvecfunc_bitvecfunc_comparison(operation, expected): # Arrange s = Solver() input1 = symbol_factory.BitVecSym("input1", 256) input2 = symbol_factory.BitVecSym("input2", 256) bvf1 = symbol_factory.BitVecFuncSym("bvf1", "sha3", 256, input_=input1) bvf2 = symbol_factory.BitVecFuncSym("bvf2", "sha3", 256, input_=input2) # Act s.add(operation(bvf1, bvf2)) s.add(input1 == input2) # Assert assert s.check() == expected
def __getitem__(self, item: Union[str, int]) -> Any: try: return self._storage[item] except KeyError: if (self.address and self.address.value != 0 and (self.dynld and self.dynld.storage_loading)): try: self._storage[item] = symbol_factory.BitVecVal( int( self.dynld.read_storage( contract_address=hex(self.address.value), index=int(item), ), 16, ), 256, ) return self._storage[item] except ValueError: pass if self.concrete: return symbol_factory.BitVecVal(0, 256) self._storage[item] = symbol_factory.BitVecSym( "storage_{}_{}".format(str(item), str(self.address)), 256) return self._storage[item]
def test_symbolic_calldata_equal_indices(): calldata = SymbolicCalldata(0) index_a = symbol_factory.BitVecSym("index_a", 256) index_b = symbol_factory.BitVecSym("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 __init__( self, address: str, code=None, contract_name="unknown", balance=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.balance = (balance if balance else symbol_factory.BitVecSym( "{}_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_bitvecfunc_arithmetic(operation, expected): # Arrange s = Solver() input_ = symbol_factory.BitVecVal(1, 8) bvf = symbol_factory.BitVecFuncSym("bvf", "sha3", 256, input_=input_) x = symbol_factory.BitVecSym("x", 256) y = symbol_factory.BitVecSym("y", 256) # Act s.add(x != y) s.add(operation(bvf, x) == operation(y, bvf)) # Assert assert s.check() == expected
def test_bitvecfunc_find_input(): # Arrange s = Solver() input1 = symbol_factory.BitVecSym("input1", 256) input2 = symbol_factory.BitVecSym("input2", 256) bvf1 = symbol_factory.BitVecFuncSym("bvf1", "sha3", 256, input_=input1) bvf2 = symbol_factory.BitVecFuncSym("bvf3", "sha3", 256, input_=input2) # Act s.add(input1 == symbol_factory.BitVecVal(1, 256)) s.add(bvf1 == bvf2) # Assert assert s.check() == z3.sat assert s.model()[input2.raw] == 1
def __init__(self, tx_id: str) -> None: """Initializes the SymbolicCalldata object. :param tx_id: Id of the transaction that the calldata is for. """ self._size = symbol_factory.BitVecSym(str(tx_id) + "_calldatasize", 256) self._calldata = Array("{}_calldata".format(tx_id), 256, 8) super().__init__(tx_id)
def __init__(self, tx_id: str) -> None: """Initializes the SymbolicCalldata object. :param tx_id: Id of the transaction that the calldata is for. """ self._reads = [] # type: List[Tuple[Union[int, BitVec], BitVec]] self._size = symbol_factory.BitVecSym(str(tx_id) + "_calldatasize", 256) super().__init__(tx_id)
def test_Independence_solver_unsat(): # Arrange x = symbol_factory.BitVecSym("x", 256) y = symbol_factory.BitVecSym("y", 256) z = symbol_factory.BitVecSym("z", 256) a = symbol_factory.BitVecSym("a", 256) b = symbol_factory.BitVecSym("b", 256) conditions = [x > y, y == z, y != z, a == b] solver = IndependenceSolver() # Act solver.add(*conditions) result = solver.check() # Assert assert z3.unsat == result
def execute_contract_creation( laser_evm, contract_initialization_code, contract_name=None, world_state=None ) -> Account: """Executes a contract creation transaction from all open states. :param laser_evm: :param contract_initialization_code: :param contract_name: :return: """ # TODO: Resolve circular import between .transaction and ..svm to import LaserEVM here del laser_evm.open_states[:] world_state = world_state or WorldState() open_states = [world_state] new_account = None for open_world_state in open_states: next_transaction_id = get_next_transaction_id() # call_data "should" be '[]', but it is easier to model the calldata symbolically # and add logic in codecopy/codesize/calldatacopy/calldatasize than to model code "correctly" transaction = ContractCreationTransaction( world_state=open_world_state, identifier=next_transaction_id, gas_price=symbol_factory.BitVecSym( "gas_price{}".format(next_transaction_id), 256 ), gas_limit=8000000, # block gas limit origin=symbol_factory.BitVecSym( "origin{}".format(next_transaction_id), 256 ), code=Disassembly(contract_initialization_code), caller=symbol_factory.BitVecVal(CREATOR_ADDRESS, 256), contract_name=contract_name, call_data=None, call_value=symbol_factory.BitVecSym( "call_value{}".format(next_transaction_id), 256 ), ) _setup_global_state_for_execution(laser_evm, transaction) new_account = new_account or transaction.callee_account laser_evm.exec(True) return new_account
def new_bitvec(self, name: str, size=256) -> BitVec: """ :param name: :param size: :return: """ transaction_id = self.current_transaction.id return symbol_factory.BitVecSym("{}_{}".format(transaction_id, name), size)
def test_bitvecfunc_nested_comparison(): # arrange s = Solver() input1 = symbol_factory.BitVecSym("input1", 256) input2 = symbol_factory.BitVecSym("input2", 256) bvf1 = symbol_factory.BitVecFuncSym("bvf1", "sha3", 256, input_=input1) bvf2 = symbol_factory.BitVecFuncSym("bvf2", "sha3", 256, input_=bvf1) bvf3 = symbol_factory.BitVecFuncSym("bvf3", "sha3", 256, input_=input2) bvf4 = symbol_factory.BitVecFuncSym("bvf4", "sha3", 256, input_=bvf3) # Act s.add(input1 == input2) s.add(bvf2 == bvf4) # Assert assert s.check() == z3.sat
def execute_contract_creation(laser_evm, contract_initialization_code, contract_name=None) -> Account: """Executes a contract creation transaction from all open states. :param laser_evm: :param contract_initialization_code: :param contract_name: :return: """ # TODO: Resolve circular import between .transaction and ..svm to import LaserEVM here 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, creator=CREATOR_ADDRESS) 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=symbol_factory.BitVecSym( "gas_price{}".format(next_transaction_id), 256), gas_limit=8000000, # block gas limit origin=symbol_factory.BitVecSym( "origin{}".format(next_transaction_id), 256), code=Disassembly(contract_initialization_code), caller=symbol_factory.BitVecVal(CREATOR_ADDRESS, 256), callee_account=new_account, call_data=[], call_value=symbol_factory.BitVecSym( "call_value{}".format(next_transaction_id), 256), ) _setup_global_state_for_execution(laser_evm, transaction) laser_evm.exec(True) return new_account
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 test_memory_write(): # Arrange mem = Memory() mem.extend(200 + 32) a = symbol_factory.BitVecSym("a", 256) b = symbol_factory.BitVecSym("b", 8) # Act mem[11] = 10 mem[12] = b mem.write_word_at(200, 0x12345) mem.write_word_at(100, a) # Assert assert mem[0] == 0 assert mem[11] == 10 assert mem[200 + 31] == 0x45 assert mem.get_word_at(200) == 0x12345 assert simplify(a == mem.get_word_at(100)) assert simplify(b == mem[12])
def test_decls(): # Arrange solver = Solver() x = symbol_factory.BitVecSym("x", 256) expression = x == symbol_factory.BitVecVal(2, 256) # Act solver.add(expression) result = solver.check() model = solver.model() decls = model.decls() # Assert assert z3.sat == result assert x.raw.decl() in decls
def test_as_long(): # Arrange solver = Solver() x = symbol_factory.BitVecSym("x", 256) expression = x == symbol_factory.BitVecVal(2, 256) # Act solver.add(expression) result = solver.check() model = solver.model() x_concrete = model.eval(x.raw).as_long() # Assert assert z3.sat == result assert 2 == x_concrete
def test_get_item(): # Arrange solver = Solver() x = symbol_factory.BitVecSym("x", 256) expression = x == symbol_factory.BitVecVal(2, 256) # Act solver.add(expression) result = solver.check() model = solver.model() x_concrete = model[x.raw.decl()] # Assert assert z3.sat == result assert 2 == x_concrete
def _load(self, item: Union[int, BitVec], clean=False) -> Any: expr_item = (symbol_factory.BitVecVal(item, 256) if isinstance( item, int) else item) # type: BitVec symbolic_base_value = If( expr_item >= self._size, symbol_factory.BitVecVal(0, 8), BitVec( symbol_factory.BitVecSym( "{}_calldata_{}".format(self.tx_id, str(item)), 8)), ) return_value = symbolic_base_value for r_index, r_value in self._reads: return_value = If(r_index == expr_item, r_value, return_value) if not clean: self._reads.append((expr_item, symbolic_base_value)) return simplify(return_value)
def test_staticness_call_symbolic(f1): # Arrange state = get_global_state() state.environment.static = True state.mstate.stack = [] call_value = symbol_factory.BitVecSym("x", 256) code = Disassembly(code="616263") f1.return_value = ("0", Account(code=code, address="0x19"), 0, call_value, 0, 0, 0) instruction = Instruction("call", dynamic_loader=None) # Act and Assert with pytest.raises(TransactionStartSignal) as ts: instruction.evaluate(state) assert ts.value.transaction.static assert ts.value.global_state.mstate.constraints[-1] == (call_value == 0)
def test_bitvecfunc_bitvecfuncval_comparison(): # Arrange s = Solver() input1 = symbol_factory.BitVecSym("input1", 256) input2 = symbol_factory.BitVecVal(1337, 256) bvf1 = symbol_factory.BitVecFuncSym("bvf1", "sha3", 256, input_=input1) bvf2 = symbol_factory.BitVecFuncVal(12345678910, "sha3", 256, input_=input2) # Act s.add(bvf1 == bvf2) # Assert assert s.check() == z3.sat assert s.model().eval(input2.raw) == 1337
def __init__( self, active_account: Account, sender: ExprRef, calldata: BaseCalldata, gasprice: ExprRef, callvalue: ExprRef, origin: ExprRef, code=None, static=False, ) -> None: """ :param active_account: :param sender: :param calldata: :param gasprice: :param callvalue: :param origin: :param code: :param static: Makes the environment static. """ # Metadata self.active_account = active_account self.active_function_name = "" self.address = active_account.address self.block_number = symbol_factory.BitVecSym("block_number", 256) # Ib self.code = active_account.code if code is None else code self.sender = sender self.calldata = calldata self.gasprice = gasprice self.origin = origin self.callvalue = callvalue self.static = static
def test_extcodecopy_fail(): # Arrange new_world_state = WorldState() new_account = new_world_state.create_account(balance=10, address=101) new_account.code = Disassembly("60616240") new_environment = Environment(new_account, None, None, None, None, None) state = GlobalState( new_world_state, new_environment, None, MachineState(gas_limit=8000000) ) state.transaction_stack.append( (MessageCallTransaction(world_state=WorldState(), gas_limit=8000000), None) ) state.mstate.stack = [2, 2, 2, symbol_factory.BitVecSym("FAIL", 256)] instruction = Instruction("extcodecopy", dynamic_loader=None) # Act new_state = instruction.evaluate(state)[0] # Assert assert new_state.mstate.stack == [] assert new_state.mstate.memory._memory == state.mstate.memory._memory