from typing import TYPE_CHECKING
import logging
import claripy
from archinfo.arch_soot import ArchSoot, SootAddressDescriptor
from archinfo.arch_arm import is_arm_arch
from .plugin import SimStatePlugin
if TYPE_CHECKING:
from angr.sim_type import SimType
l = logging.getLogger(name=__name__)
[docs]class SimRegNameView(SimStatePlugin):
def __getattr__(self, k) -> claripy.ast.base.Base:
"""
Get the value of a register.
:param str k: Name of the register. Prefix it with "_" prevents SimInspect being triggered and SimActions being
created.
:param v: Value to set to the register.
:return: Value of the register.
:rtype: claripy.ast.Base
"""
state: SimState = super().__getattribute__("state")
if isinstance(k, str) and k.startswith("_"):
k = k[1:]
inspect = False
disable_actions = True
events = False
else:
inspect = True
disable_actions = False
events = True
try:
return state.registers.load(k, inspect=inspect, disable_actions=disable_actions, events=events)
except KeyError:
return super().__getattribute__(k)
def __setattr__(self, k, v):
"""
Set value to a register.
:param str k: Name of the register. Prefix it with "_" prevents SimInspect being triggered and SimActions being
created.
:param v: Value to set to the register.
:return: None
"""
if k == "state" or k in dir(SimStatePlugin):
return object.__setattr__(self, k, v)
if isinstance(k, str) and k.startswith("_"):
k = k[1:]
inspect = False
disable_actions = True
else:
inspect = True
disable_actions = False
# When executing a Java JNI application, we need to update the state's
# instruction pointer flag every time the ip gets written.
# This flag will then toggle between the native and the java view of the
# state.
if (
self.state.project
and isinstance(self.state.project.arch, ArchSoot)
and k == "ip"
and self.state.project.simos.is_javavm_with_jni_support
):
self.state.ip_is_soot_addr = isinstance(v, SootAddressDescriptor)
try:
return self.state.registers.store(k, v, inspect=inspect, disable_actions=disable_actions)
except KeyError:
# What do we do in case we are dealing with soot? there are no register
if isinstance(self.state.arch, ArchSoot):
pass
else:
raise AttributeError(k)
def __dir__(self):
if self.state.arch.name in ("X86", "AMD64"):
return (
list(self.state.arch.registers.keys())
+ ["st%d" % n for n in range(8)]
+ ["tag%d" % n for n in range(8)]
+ ["flags", "eflags", "rflags"]
)
elif is_arm_arch(self.state.arch):
return list(self.state.arch.registers.keys()) + ["flags"]
return self.state.arch.registers.keys()
@SimStatePlugin.memo
def copy(self, memo): # pylint: disable=unused-argument
return SimRegNameView()
[docs] def merge(self, others, merge_conditions, common_ancestor=None): # pylint: disable=unused-argument
return False
[docs] def widen(self, others): # pylint: disable=unused-argument
return False
[docs] def get(self, reg_name):
return self.__getattr__(reg_name)
[docs]class SimMemView(SimStatePlugin):
"""
This is a convenient interface with which you can access a program's memory.
The interface works like this:
- You first use [array index notation] to specify the address you'd like to load from
- If at that address is a pointer, you may access the ``deref`` property to return a SimMemView at the
address present in memory.
- You then specify a type for the data by simply accesing a property of that name. For a list of supported
types, look at ``state.mem.types``.
- You can then *refine* the type. Any type may support any refinement it likes. Right now the only refinements
supported are that you may access any member of a struct by its member name, and you may index into a
string or array to access that element.
- If the address you specified initially points to an array of that type, you can say `.array(n)` to view the
data as an array of n elements.
- Finally, extract the structured data with ``.resolved`` or ``.concrete``. ``.resolved`` will return bitvector
values, while ``.concrete`` will return integer, string, array, etc values, whatever best represents the
data.
- Alternately, you may store a value to memory, by assigning to the chain of properties that you've
constructed. Note that because of the way python works, ``x = s.mem[...].prop; x = val`` will NOT work,
you must say ``s.mem[...].prop = val``.
For example:
>>> s.mem[0x601048].long
<long (64 bits) <BV64 0x4008d0> at 0x601048>
>>> s.mem[0x601048].long.resolved
<BV64 0x4008d0>
>>> s.mem[0x601048].deref
<<untyped> <unresolvable> at 0x4008d0>
>>> s.mem[0x601048].deref.string.concrete
'SOSNEAKY'
"""
[docs] def __init__(self, ty=None, addr=None, state=None):
super().__init__()
self._type = ty
self._addr = addr
if state is not None:
self.set_state(state)
[docs] def set_state(self, state):
super().set_state(state)
# Make sure self._addr is always an AST
if isinstance(self._addr, int):
self._addr = self.state.solver.BVV(self._addr, self.state.arch.bits)
def _deeper(self, **kwargs) -> "SimMemView":
if "ty" not in kwargs:
kwargs["ty"] = self._type
if "addr" not in kwargs:
kwargs["addr"] = self._addr
if "state" not in kwargs:
kwargs["state"] = self.state
return SimMemView(**kwargs)
def __getitem__(self, k) -> "SimMemView":
if isinstance(k, slice):
if k.step is not None:
raise ValueError("Slices with strides are not supported")
elif k.start is None:
raise ValueError("Must specify start index")
elif k.stop is not None:
raise ValueError("Slices with stop index are not supported")
else:
addr = k.start
elif self._type is not None and self._type._can_refine_int:
return self._type._refine(self, k)
else:
addr = k
return self._deeper(addr=addr)
def __setitem__(self, k, v):
self.__getitem__(k).store(v)
types = {}
state = None
def __repr__(self):
if self._addr is None:
return "<SimMemView>"
value = "<unresolvable>" if not self.resolvable else self.resolved
if isinstance(self._addr, claripy.ast.Base):
addr = self._addr.__repr__(inner=True)
elif hasattr(self._addr, "ast") and isinstance(self._addr.ast, claripy.ast.Base):
addr = self._addr.ast.__repr__(inner=True)
else:
addr = repr(self._addr)
type_name = repr(self._type) if self._type is not None else "<untyped>"
return f"<{type_name} {value} at {addr}>"
def __dir__(self):
return self._type._refine_dir() if self._type else [x for x in SimMemView.types if " " not in x] + ["struct"]
struct: "StructMode"
def __getattr__(self, k):
if k in (
"concrete",
"deref",
"resolvable",
"resolved",
"state",
"array",
"store",
"_addr",
"_type",
) or k in dir(SimStatePlugin):
return object.__getattribute__(self, k)
if self._type and k in self._type._refine_dir():
return self._type._refine(self, k)
if k == "struct":
return StructMode(self)
if k in SimMemView.types:
return self._deeper(ty=SimMemView.types[k].with_arch(self.state.arch))
raise AttributeError(k)
def __setattr__(self, k, v):
if k in ("state", "_addr", "_type") or k in dir(SimStatePlugin):
return object.__setattr__(self, k, v)
return self.__getattr__(k).store(v)
def __cmp__(self, other):
raise ValueError("Trying to compare SimMemView is not what you want to do")
[docs] def with_type(self, sim_type: "SimType") -> "SimMemView":
"""
Returns a copy of the SimMemView with a type.
:param sim_type: The new type.
:returns: The typed SimMemView copy.
"""
ty = sim_type.with_arch(self.state.arch)
return self._deeper(ty=ty)
@SimStatePlugin.memo
def copy(self, memo): # pylint: disable=unused-argument
return SimMemView()
[docs] def merge(self, others, merge_conditions, common_ancestor=None): # pylint: disable=unused-argument
return False
[docs] def widen(self, others): # pylint: disable=unused-argument
return False
@property
def resolvable(self):
return self._type is not None and self._addr is not None
@property
def resolved(self):
if not self.resolvable:
raise ValueError("Trying to resolve value without type and addr defined")
return self._type.extract(self.state, self._addr)
@property
def concrete(self):
if not self.resolvable:
raise ValueError("Trying to resolve value without type and addr defined")
return self._type.extract(self.state, self._addr, True)
@property
def deref(self) -> "SimMemView":
if self._addr is None:
raise ValueError("Trying to dereference pointer without addr defined")
ptr = self.state.memory.load(self._addr, self.state.arch.bytes, endness=self.state.arch.memory_endness)
if ptr.symbolic:
l.warning("Dereferencing symbolic pointer %s at %#x", repr(ptr), self.state.solver.eval(self._addr))
ptr = self.state.solver.eval(ptr)
return self._deeper(ty=self._type.pts_to if isinstance(self._type, SimTypePointer) else None, addr=ptr)
[docs] def array(self, n) -> "SimMemView":
if self._addr is None:
raise ValueError("Trying to produce array without specifying adddress")
if self._type is None:
raise ValueError("Trying to produce array without specifying type")
return self._deeper(ty=SimTypeFixedSizeArray(self._type, n))
[docs] def member(self, member_name: str) -> "SimMemView":
"""
If self is a struct and member_name is a member of the struct, return
that member element. Otherwise raise an exception.
"""
if self._type and member_name in self._type._refine_dir():
return self._type._refine(self, member_name)
raise ValueError("Trying to find a struct member that cannot be found")
[docs] def store(self, value):
if self._addr is None:
raise ValueError("Trying to store to location without specifying address")
if self._type is None:
raise ValueError("Trying to store to location without specifying type")
return self._type.store(self.state, self._addr, value)
[docs]class StructMode:
[docs] def __init__(self, view):
self._view = view
def __dir__(self):
return [x[7:] for x in SimMemView.types if x.startswith("struct ")]
def __getattr__(self, k):
assert k != "_view"
return self._view._deeper(ty=SimMemView.types["struct " + k].with_arch(self._view.state.arch))
def __setattr__(self, k, v):
if k == "_view":
object.__setattr__(self, k, v)
else:
self.__getattr__(k).store(v)
from ..sim_type import ALL_TYPES, SimTypeFixedSizeArray, SimTypePointer
SimMemView.types = ALL_TYPES # identity purposefully here
from angr.sim_state import SimState
SimState.register_default("mem", SimMemView)
SimState.register_default("regs", SimRegNameView)