Source code for angr.state_plugins.debug_variables

from typing import TYPE_CHECKING
import logging

from cle.backends.elf.variable import Variable
from cle.backends.elf.variable_type import VariableType, PointerType, ArrayType, StructType, TypedefType

from angr.sim_state import SimState
from angr.sim_type import ALL_TYPES, SimTypeReg

from .plugin import SimStatePlugin

l = logging.getLogger(name=__name__)

if TYPE_CHECKING:
    from angr.state_plugins.view import SimMemView
    from angr.sim_type import SimType


[docs]class SimDebugVariable: """ A SimDebugVariable will get dynamically created when queriyng for variable in a state with the SimDebugVariablePlugin. It features a link to the state, an address and a type. """
[docs] def __init__(self, state: SimState, addr, var_type: VariableType): self.state = state self.addr = addr self.type = var_type
[docs] @staticmethod def from_cle_variable(state: SimState, cle_variable: Variable, dwarf_cfa) -> "SimDebugVariable": addr = cle_variable.rebased_addr_from_cfa(dwarf_cfa) var_type = cle_variable.type return SimDebugVariable(state, addr, var_type)
@property def mem_untyped(self) -> "SimMemView": if self.addr is None: raise Exception("Cannot view a variable without an address") return self.state.mem[self.addr] @property def mem(self) -> "SimMemView": if isinstance(self.type, TypedefType): unpacked = SimDebugVariable(self.state, self.addr, self.type.type) return unpacked.mem if self.type is None or self.type.byte_size is None: return self.mem_untyped arch = self.state.arch size = self.type.byte_size * arch.byte_width name = self.type.name if name in ALL_TYPES: sim_type = ALL_TYPES[name].with_arch(arch) assert size == sim_type.size else: # FIXME A lot more types are supported by angr that are not in ALL_TYPES (structs, arrays, pointers) # Use a fallback type sim_type = SimTypeReg(size, label=name) return self.mem_untyped.with_type(sim_type) # methods and properties equivalent to SimMemView @property def string(self) -> "SimMemView": first_char = self.deref # first char should have some char type (could be checked here) return first_char.mem_untyped.string
[docs] def with_type(self, sim_type: "SimType") -> "SimMemView": return self.mem_untyped.with_type(sim_type)
@property def resolvable(self): return self.mem.resolvable @property def resolved(self): return self.mem.resolved @property def concrete(self): return self.mem.concrete
[docs] def store(self, value): return self.mem.store(value)
def __getitem__(self, i): if isinstance(i, int): return self.array(i) elif isinstance(i, str): return self.member(i) raise KeyError @property def deref(self) -> "SimDebugVariable": # dereferincing is equivalent to getting the first array element return self.array(0)
[docs] def array(self, i) -> "SimDebugVariable": if isinstance(self.type, TypedefType): unpacked = SimDebugVariable(self.state, self.addr, self.type.type) return unpacked.array(i) elif isinstance(self.type, ArrayType): # an array already addresses its first element addr = self.addr el_type = self.type.element_type elif isinstance(self.type, PointerType): if self.addr is None: addr = None else: addr = self.state.memory.load(self.addr, self.state.arch.bytes, endness=self.state.arch.memory_endness) el_type = self.type.referenced_type else: raise Exception(f"{self.type} object cannot be dereferenced") if i == 0: new_addr = addr elif addr is None or el_type is None or el_type.byte_size is None: new_addr = None else: new_addr = addr + i * el_type.byte_size return SimDebugVariable(self.state, new_addr, el_type)
[docs] def member(self, member_name: str) -> "SimDebugVariable": if isinstance(self.type, TypedefType): unpacked = SimDebugVariable(self.state, self.addr, self.type.type) return unpacked.member(member_name) elif isinstance(self.type, StructType): member = self.type[member_name] if self.addr is None: addr = None else: addr = self.addr + member.addr_offset return SimDebugVariable(self.state, addr, member.type) raise Exception(f"{self.type} object has no members")
[docs]class SimDebugVariablePlugin(SimStatePlugin): """ This is the plugin you'll use to interact with (global/local) program variables. These variables have a name and a visibility scope which depends on the pc address of the state. With this plugin, you can access/modify the value of such variable or find its memory address. For creating program varibles, or for importing them from cle, see the knowledge plugin debug_variables. Run ``p.kb.dvars.load_from_dwarf()`` before using this plugin. Example: >>> p = angr.Project("various_variables", load_debug_info=True) >>> p.kb.dvars.load_from_dwarf() >>> state = # navigate to the state you want >>> state.dvars.get_variable("pointer2").deref.mem <int (32 bits) <BV32 0x1> at 0x404020> """
[docs] def get_variable(self, var_name: str) -> SimDebugVariable: """ Returns the visible variable (if any) with name ``var_name`` based on the current ``state.ip``. """ kb = self.state.project.kb cle_var = kb.dvars[var_name][self.state.ip] if cle_var: return SimDebugVariable.from_cle_variable(self.state, cle_var, self.dwarf_cfa) return None
def __getitem__(self, var_name: str) -> SimDebugVariable: return self.get_variable(var_name) # DWARF cfa @property def dwarf_cfa(self): """ Returns the current cfa computation. Set this property to the correct value if needed. """ try: return self._dwarf_cfa except AttributeError: return self.dwarf_cfa_approx @dwarf_cfa.setter def dwarf_cfa(self, new_val): self._dwarf_cfa = new_val @property def dwarf_cfa_approx(self): # FIXME This is only an approximation! if self.state.arch.name == "AMD64": return self.state.regs.rbp + 16 elif self.state.arch.name == "X86": return self.state.regs.ebp + 8 return 0
SimState.register_default("dvars", SimDebugVariablePlugin)