Source code for angr.knowledge_plugins.key_definitions.atoms

from typing import Dict, Tuple, Union, Optional

import claripy

from ...calling_conventions import SimFunctionArgument, SimRegArg, SimStackArg
from ...engines.light import SpOffset
from .heap_address import HeapAddress
from ...storage.memory_mixins.paged_memory.pages.multi_values import MultiValues


[docs]class Atom: """ This class represents a data storage location manipulated by IR instructions. It could either be a Tmp (temporary variable), a Register, a MemoryLocation. """ __slots__ = ("_hash",) def __init__(self): self._hash = None def __repr__(self): raise NotImplementedError() @property def size(self) -> int: raise NotImplementedError()
[docs] @staticmethod def from_argument(argument: SimFunctionArgument, registers: Dict[str, Tuple[int, int]]): """ Instanciate an `Atom` from a given argument. :param argument: The argument to create a new atom from. :param registers: A mapping representing the registers of a given architecture. """ if isinstance(argument, SimRegArg): return Register(registers[argument.reg_name][0], argument.size) elif isinstance(argument, SimStackArg): return MemoryLocation(registers["sp"][0] + argument.stack_offset, argument.size) else: raise TypeError("Argument type %s is not yet supported." % type(argument))
def _core_hash(self): raise NotImplementedError() def __hash__(self): if self._hash is None: self._hash = self._core_hash() return self._hash
[docs]class GuardUse(Atom): """ Implements a guard use. """ __slots__ = ("target",) def __init__(self, target): super().__init__() self.target = target def __repr__(self): return "<Guard %#x>" % self.target @property def size(self) -> int: raise NotImplementedError() __hash__ = Atom.__hash__ def _core_hash(self): return hash((GuardUse, self.target))
[docs]class FunctionCall(Atom): """ Represents a function call. """ __slots__ = ("target", "callsite") def __init__(self, target, callsite): super().__init__() self.target = target self.callsite = callsite @property def single_target(self): if ( type(self.target) is MultiValues and len(self.target.values) == 1 and 0 in self.target.values and len(self.target.values[0]) == 1 and next(iter(self.target.values[0])).op == "BVV" ): return next(iter(self.target.values[0])).args[0] return None def __repr__(self): target = self.single_target target_txt = hex(target) if target is not None else "(indirect)" return "<Call %s>" % target_txt def __eq__(self, other): return type(other) is FunctionCall and self.callsite == other.callsite __hash__ = Atom.__hash__ def _core_hash(self): return hash(self.callsite) @property def size(self): raise NotImplementedError
[docs]class ConstantSrc(Atom): """ Represents a constant. """ __slots__ = ("const",) def __init__(self, const): super().__init__() self.const = const def __repr__(self): return repr(self.const) def __eq__(self, other): return type(other) is ConstantSrc and self.const == other.const __hash__ = Atom.__hash__ def _core_hash(self): return hash(self.const) @property def size(self): return self.const.size
[docs]class Tmp(Atom): """ Represents a variable used by the IR to store intermediate values. """ __slots__ = ( "tmp_idx", "_size", ) def __init__(self, tmp_idx: int, size: int): super().__init__() self.tmp_idx = tmp_idx self._size = size def __repr__(self): return "<Tmp %d>" % self.tmp_idx def __eq__(self, other): return type(other) is Tmp and self.tmp_idx == other.tmp_idx __hash__ = Atom.__hash__ def _core_hash(self): return hash(("tmp", self.tmp_idx)) @property def size(self) -> int: return self._size
[docs]class Register(Atom): """ Represents a given CPU register. As an IR abstracts the CPU design to target different architectures, registers are represented as a separated memory space. Thus a register is defined by its offset from the base of this memory and its size. :ivar int reg_offset: The offset from the base to define its place in the memory bloc. :ivar int size: The size, in number of bytes. """ __slots__ = ( "reg_offset", "_size", ) def __init__(self, reg_offset: int, size: int): super().__init__() self.reg_offset = reg_offset self._size = size def __repr__(self): return "<Reg %d<%d>>" % (self.reg_offset, self.size) def __eq__(self, other): return type(other) is Register and self.reg_offset == other.reg_offset and self.size == other.size __hash__ = Atom.__hash__ def _core_hash(self): return hash(("reg", self.reg_offset, self.size)) @property def bits(self) -> int: return self._size * 8 @property def size(self) -> int: return self._size
[docs]class MemoryLocation(Atom): """ Represents a memory slice. It is characterized by its address and its size. """ __slots__ = ( "addr", "_size", "endness", ) def __init__(self, addr: Union[SpOffset, HeapAddress, int], size: int, endness: Optional[str] = None): """ :param int addr: The address of the beginning memory location slice. :param int size: The size of the represented memory location, in bytes. """ super().__init__() self.addr: Union[SpOffset, int, claripy.ast.BV] = addr self._size: int = size self.endness = endness def __repr__(self): address_format = hex(self.addr) if type(self.addr) is int else self.addr stack_format = " (stack)" if self.is_on_stack else "" size = "%d" % self.size if isinstance(self.size, int) else self.size return f"<Mem {address_format}<{size}>{stack_format}>" @property def is_on_stack(self) -> bool: """ True if this memory location is located on the stack. """ return isinstance(self.addr, SpOffset) @property def bits(self) -> int: return self.size * 8 @property def size(self) -> int: return self._size @property def symbolic(self) -> bool: if isinstance(self.addr, int): return False elif isinstance(self.addr, SpOffset): return type(self.addr.offset) is not int return True def __eq__(self, other): # pylint:disable=isinstance-second-argument-not-valid-type return ( type(other) is MemoryLocation and ( self.addr is other.addr if (isinstance(self.addr, claripy.ast.BV) or isinstance(other.addr, claripy.ast.BV)) else self.addr == other.addr ) and self.size == other.size and self.endness == other.endness ) __hash__ = Atom.__hash__ def _core_hash(self): return hash(("mem", self.addr, self.size, self.endness))