diff options
| author | Theofilos Augoustis <theofilos.augoustis@gmail.com> | 2024-07-12 11:52:00 +0200 |
|---|---|---|
| committer | Theofilos Augoustis <theofilos.augoustis@gmail.com> | 2024-07-12 11:52:00 +0200 |
| commit | 243aaa08afd66f1b409774693b716e30fa9ffacc (patch) | |
| tree | b81dc00bf3b2dd10ba8a239672cf30a9d3360507 | |
| parent | ef31d11c7bb0ec6505622ea61f963c56ddf79672 (diff) | |
| download | focaccia-243aaa08afd66f1b409774693b716e30fa9ffacc.tar.gz focaccia-243aaa08afd66f1b409774693b716e30fa9ffacc.zip | |
Add support for aarch64
- Implement an architecture description for aarch64 - Add endianness information to the `Arch` class. - Move conversion from flags register to logical flag values from the calling code to the concrete targets (LLDB and GDB), which is the only point where we (have to) deal in flags registers. - Handle assembly/disassembly errors in serialization of SymbolicTransform - Move ProgramState's `arch` attribute into ReadableProgramState. Co-authored-by: Theofilos Augoustis <theofilos.augoustis@gmail.com> Co-authored-by: Nicola Crivellin <nicola.crivellin98@gmail.com>
| -rw-r--r-- | README.md | 21 | ||||
| -rw-r--r-- | focaccia/arch/__init__.py | 11 | ||||
| -rw-r--r-- | focaccia/arch/aarch64.py | 55 | ||||
| -rw-r--r-- | focaccia/arch/arch.py | 8 | ||||
| -rw-r--r-- | focaccia/lldb_target.py | 85 | ||||
| -rw-r--r-- | focaccia/miasm_util.py | 55 | ||||
| -rw-r--r-- | focaccia/snapshot.py | 6 | ||||
| -rw-r--r-- | focaccia/symbolic.py | 47 | ||||
| -rw-r--r-- | nix.shell | 12 | ||||
| -rw-r--r-- | tools/_qemu_tool.py | 81 |
10 files changed, 293 insertions, 88 deletions
diff --git a/README.md b/README.md index 5c14d91..67db62c 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ The `tools/` directory contains additional utility scripts to work with focaccia ## Project Overview (for developers) -### Snapshot-comparison framework +### Snapshots and comparison The following files belong to a rough framework for the snapshot comparison engine: @@ -43,8 +43,7 @@ representation of program snapshots. - `focaccia/compare.py`: The central algorithms that work on snapshots. - - `focaccia/arch/`: Abstractions over different processor architectures. Will be used to integrate support for more -architectures later. Currently, we only have X86. + - `focaccia/arch/`: Abstractions over different processor architectures. Currently we have x86 and aarch64. ### Concolic execution @@ -67,4 +66,18 @@ our own log format. - `focaccia/match.py`: Algorithms for trace matching. - - `miasm_test.py`: A test script that traces a program concolically. +### Supporting new architectures + +To add support for an architecture <arch>, do the following: + + - Add a file `focaccia/arch/<arch>.py`. This module declares the architecture's description, such as register names and +an architecture class. The convention is to declare state flags (e.g. flags in RFLAGS for x86) as separate registers. + + - Add the class to the `supported_architectures` dict in `focaccia/arch/__init__.py`. + + - Depending on Miasm's support for <arch>, add register name aliases to the `MiasmSymbolResolver.miasm_flag_aliases` +dict in `focaccia/miasm_util.py`. + + - Depending on the existence of a flags register in <arch>, implement conversion from the flags register's value to +values of single logical flags (e.g. implement the operation `RFLAGS['OF']`) in the respective concrete targets (LLDB, +GDB, ...). diff --git a/focaccia/arch/__init__.py b/focaccia/arch/__init__.py index 2926d20..1797176 100644 --- a/focaccia/arch/__init__.py +++ b/focaccia/arch/__init__.py @@ -1,11 +1,14 @@ from .arch import Arch -from . import x86 +from . import x86, aarch64 +supported_architectures: dict[str, Arch] = { + 'x86_64': x86.ArchX86(), + 'aarch64': aarch64.ArchAArch64('little'), + 'aarch64l': aarch64.ArchAArch64('little'), + 'aarch64b': aarch64.ArchAArch64('big'), +} """A dictionary containing all supported architectures at their names. The arch names (keys) should be compatible with the string returned from `platform.machine()`. """ -supported_architectures: dict[str, Arch] = { - "x86_64": x86.ArchX86(), -} diff --git a/focaccia/arch/aarch64.py b/focaccia/arch/aarch64.py new file mode 100644 index 0000000..c0efc1a --- /dev/null +++ b/focaccia/arch/aarch64.py @@ -0,0 +1,55 @@ +"""Description of 64-bit ARM.""" +from typing import Literal + +from .arch import Arch + +archname = 'aarch64' + +regnames = [ + 'PC', 'SP', 'LR', + 'CPSR', + + 'X0', 'X1', 'X2', 'X3', 'X4', 'X5', 'X6', 'X7', 'X8', 'X9', + 'X10', 'X11', 'X12', 'X13', 'X14', 'X15', 'X16', 'X17', 'X18', 'X19', + 'X20', 'X21', 'X22', 'X23', 'X24', 'X25', 'X26', 'X27', 'X28', 'X29', + 'X30', + + 'Q0', 'Q1', 'Q2', 'Q3', 'Q4', 'Q5', 'Q6', 'Q7', 'Q8', 'Q9', + 'Q10', 'Q11', 'Q12', 'Q13', 'Q14', 'Q15', + + 'V0', 'V1', 'V2', 'V3', 'V4', 'V5', 'V6', 'V7', 'V8', 'V9', + 'V10', 'V11', 'V12', 'V13', 'V14', 'V15', 'V16', 'V17', 'V18', 'V19', + 'V20', 'V21', 'V22', 'V23', 'V24', 'V25', 'V26', 'V27', 'V28', 'V29', + 'V30', 'V31', + + 'N', 'Z', 'C', 'V', 'Q', + 'SSBS', 'PAN', 'DIT', 'GE', + 'E', 'A', 'I', 'F', 'M', +] + +def decompose_cpsr(cpsr: int) -> dict[str, int]: + """Extract individual flag values from the CPSR register.""" + return { + 'N': (cpsr & (1 << 31)) != 0, + 'Z': (cpsr & (1 << 30)) != 0, + 'C': (cpsr & (1 << 29)) != 0, + 'V': (cpsr & (1 << 28)) != 0, + 'Q': (cpsr & (1 << 27)) != 0, + # Reserved: [26:24] + 'SSBS': (cpsr & (1 << 23)) != 0, + 'PAN': (cpsr & (1 << 22)) != 0, + 'DIT': (cpsr & (1 << 21)) != 0, + # Reserved: [20] + 'GE': (cpsr & (0b1111 << 16)) != 0, + # Reserved: [15:10] + 'E': (cpsr & (1 << 9)) != 0, + 'A': (cpsr & (1 << 8)) != 0, + 'I': (cpsr & (1 << 7)) != 0, + 'F': (cpsr & (1 << 6)) != 0, + # Reserved: [5:4] + 'M': (cpsr & 0b1111) != 0, + } + +class ArchAArch64(Arch): + def __init__(self, endianness: Arch.Endianness): + super().__init__(archname, regnames, 64, endianness) diff --git a/focaccia/arch/arch.py b/focaccia/arch/arch.py index e64f1fa..dee7288 100644 --- a/focaccia/arch/arch.py +++ b/focaccia/arch/arch.py @@ -1,13 +1,17 @@ -from typing import Iterable +from typing import Iterable, Literal class Arch(): + Endianness = Literal['little', 'big'] + def __init__(self, archname: str, regnames: Iterable[str], - ptr_size: int): + ptr_size: int, + endianness: Endianness = 'little'): self.archname = archname self.regnames = set(name.upper() for name in regnames) self.ptr_size = ptr_size + self.endianness: Literal['little', 'big'] = endianness def to_regname(self, name: str) -> str | None: """Transform a string into a standard register name. diff --git a/focaccia/lldb_target.py b/focaccia/lldb_target.py index a4e96a8..2ed0757 100644 --- a/focaccia/lldb_target.py +++ b/focaccia/lldb_target.py @@ -2,7 +2,7 @@ import os import lldb -from .arch import supported_architectures, x86 +from .arch import supported_architectures from .snapshot import ProgramState class MemoryMap: @@ -32,6 +32,18 @@ class ConcreteSectionError(Exception): pass class LLDBConcreteTarget: + from focaccia.arch import aarch64, x86 + + flag_register_names = { + aarch64.archname: 'cpsr', + x86.archname: 'rflags', + } + + flag_register_decompose = { + aarch64.archname: aarch64.decompose_cpsr, + x86.archname: x86.decompose_rflags, + } + def __init__(self, executable: str, argv: list[str] = [], @@ -67,7 +79,14 @@ class LLDBConcreteTarget: raise RuntimeError(f'[In LLDBConcreteTarget.__init__]: Failed to' f' launch process.') + # Determine current arch self.archname = self.target.GetPlatform().GetTriple().split('-')[0] + if self.archname not in supported_architectures: + err = f'LLDBConcreteTarget: Architecture {self.archname} is not' \ + f' supported by Focaccia.' + print(f'[ERROR] {err}') + raise NotImplementedError(err) + self.arch = supported_architectures[self.archname] def is_exited(self): """Signals whether the concrete process has exited. @@ -99,28 +118,17 @@ class LLDBConcreteTarget: def record_snapshot(self) -> ProgramState: """Record the concrete target's state in a ProgramState object.""" - # Determine current arch - if self.archname not in supported_architectures: - print(f'[ERROR] LLDBConcreteTarget: Recording snapshots is not' - f' supported for architecture {self.archname}!') - raise NotImplementedError() - arch = supported_architectures[self.archname] - - state = ProgramState(arch) + state = ProgramState(self.arch) # Query and store register state - for regname in arch.regnames: + for regname in self.arch.regnames: try: conc_val = self.read_register(regname) state.set_register(regname, conc_val) except KeyError: pass except ConcreteRegisterError: - # Special rule for flags on X86 - if arch.archname == x86.archname: - rflags = x86.decompose_rflags(self.read_register('rflags')) - if regname in rflags: - state.set_register(regname, rflags[regname]) + pass # Query and store memory state for mapping in self.get_mappings(): @@ -142,12 +150,35 @@ class LLDBConcreteTarget: """ frame = self.process.GetThreadAtIndex(0).GetFrameAtIndex(0) reg = frame.FindRegister(regname) - if reg is None: + if not reg.IsValid(): raise ConcreteRegisterError( f'[In LLDBConcreteTarget._get_register]: Register {regname}' f' not found.') return reg + def read_flags(self) -> dict[str, int | bool]: + """Read the current state flags. + + If the concrete target's architecture has state flags, read and return + their current values. + + This handles the conversion from implementation details like flags + registers to the logical flag values. For example: On X86, this reads + the RFLAGS register and extracts the flag bits from its value. + + :return: Dictionary mapping flag names to values. The values may be + booleans in the case of true binary flags or integers in the + case of multi-byte flags. Is empty if the current architecture + does not have state flags of the access is not implemented for + it. + """ + if self.archname not in self.flag_register_names: + return {} + + flags_reg = self.flag_register_names[self.archname] + flags_val = self._get_register(flags_reg).GetValueAsUnsigned() + return self.flag_register_decompose[self.archname](flags_val) + def read_register(self, regname: str) -> int: """Read the value of a register. @@ -155,14 +186,17 @@ class LLDBConcreteTarget: or the target is otherwise unable to read the register's value. """ - reg = self._get_register(regname) - val = reg.GetValue() - if val is None: + try: + reg = self._get_register(regname) + assert(reg.IsValid()) + return reg.GetValueAsUnsigned() + except ConcreteRegisterError as err: + flags = self.read_flags() + if regname in flags: + return flags[regname] raise ConcreteRegisterError( - f'[In LLDBConcreteTarget.read_register]: Register has an' - f' invalid value of {val}.') - - return int(val, 16) + f'[In LLDBConcreteTarget.read_register]: Unable to read' + f' register {regname}: {err}') def write_register(self, regname: str, value: int): """Read the value of a register. @@ -189,7 +223,10 @@ class LLDBConcreteTarget: if not err.success: raise ConcreteMemoryError(f'Error when reading {size} bytes at' f' address {hex(addr)}: {err}') - return bytes(reversed(content)) # Convert to big endian + if self.arch.endianness == 'little': + return content + else: + return bytes(reversed(content)) def write_memory(self, addr, value: bytes): """Write bytes to memory. diff --git a/focaccia/miasm_util.py b/focaccia/miasm_util.py index 83a8778..a2cd025 100644 --- a/focaccia/miasm_util.py +++ b/focaccia/miasm_util.py @@ -1,14 +1,26 @@ from typing import Callable +from miasm.analysis.machine import Machine from miasm.core.locationdb import LocationDB, LocKey from miasm.expression.expression import Expr, ExprOp, ExprId, ExprLoc, \ ExprInt, ExprMem, ExprCompose, \ ExprSlice, ExprCond from miasm.expression.simplifications import expr_simp_explicit +from . import arch +from .arch import Arch from .snapshot import ReadableProgramState, \ RegisterAccessError, MemoryAccessError +def make_machine(_arch: Arch) -> Machine: + """Create a Miasm `Machine` object corresponding to an `Arch`.""" + machines = { + arch.x86.archname: lambda _: Machine('x86_64'), + # Miasm only has ARM machine names with the l/b suffix: + arch.aarch64.archname: lambda a: Machine(f'aarch64{a.endianness[0]}'), + } + return machines[_arch.archname](_arch) + def simp_segm(expr_simp, expr: ExprOp): """Simplify a segmentation expression to an addition of the segment register's base value and the address argument. @@ -60,27 +72,50 @@ class MiasmSymbolResolver: """Resolves atomic symbols to some state.""" miasm_flag_aliases = { - 'NF': 'SF', - 'I_F': 'IF', - 'IOPL_F': 'IOPL', - 'I_D': 'ID', + arch.x86.archname: { + 'NF': 'SF', + 'I_F': 'IF', + 'IOPL_F': 'IOPL', + 'I_D': 'ID', + }, + arch.aarch64.archname: { + 'NF': 'N', + 'SF': 'N', + 'ZF': 'Z', + 'CF': 'C', + 'VF': 'V', + 'OF': 'V', + 'QF': 'Q', + + 'AF': 'A', + 'EF': 'E', + 'IF': 'I', + 'FF': 'F', + } } - def __init__(self, state: ReadableProgramState, loc_db: LocationDB): + def __init__(self, + state: ReadableProgramState, + loc_db: LocationDB): self._state = state self._loc_db = loc_db + self._arch = state.arch + self.endianness: Arch.Endianness = self._arch.endianness - @staticmethod - def _miasm_to_regname(regname: str) -> str: + def _miasm_to_regname(self, regname: str) -> str: """Convert a register name as used by Miasm to one that follows Focaccia's naming conventions.""" regname = regname.upper() - return MiasmSymbolResolver.miasm_flag_aliases.get(regname, regname) + if self._arch.archname in self.miasm_flag_aliases: + aliases = self.miasm_flag_aliases[self._arch.archname] + return aliases.get(regname, regname) + return regname def resolve_register(self, regname: str) -> int | None: try: return self._state.read_register(self._miasm_to_regname(regname)) - except RegisterAccessError: + except RegisterAccessError as err: + print(f'Not a register: {regname} ({err})') return None def resolve_memory(self, addr: int, size: int) -> bytes | None: @@ -164,7 +199,7 @@ def _eval_exprmem(expr: ExprMem, state: MiasmSymbolResolver): return expr assert(len(mem) * 8 == expr.size) - return ExprInt(int.from_bytes(mem, byteorder='big'), expr.size) + return ExprInt(int.from_bytes(mem, byteorder=state.endianness), expr.size) def _eval_exprcond(expr, state: MiasmSymbolResolver): """Evaluate an ExprCond using the current state""" diff --git a/focaccia/snapshot.py b/focaccia/snapshot.py index 104ca0a..506cf27 100644 --- a/focaccia/snapshot.py +++ b/focaccia/snapshot.py @@ -80,7 +80,9 @@ class SparseMemory: assert(len(data) == offset) # Exactly all data was written class ReadableProgramState: - """Interface for read-only program states. Used for typing purposes.""" + """Interface for read-only program states.""" + def __init__(self, arch: Arch): + self.arch = arch def read_register(self, reg: str) -> int: """Read a register's value. @@ -106,7 +108,7 @@ class ReadableProgramState: class ProgramState(ReadableProgramState): """A snapshot of the program's state.""" def __init__(self, arch: Arch): - self.arch = arch + super().__init__(arch=arch) self.regs: dict[str, int | None] = {reg: None for reg in arch.regnames} self.mem = SparseMemory() diff --git a/focaccia/symbolic.py b/focaccia/symbolic.py index dfc2966..d5475a6 100644 --- a/focaccia/symbolic.py +++ b/focaccia/symbolic.py @@ -17,7 +17,7 @@ from .arch import Arch, supported_architectures from .lldb_target import LLDBConcreteTarget, \ ConcreteRegisterError, \ ConcreteMemoryError -from .miasm_util import MiasmSymbolResolver, eval_expr +from .miasm_util import MiasmSymbolResolver, eval_expr, make_machine from .snapshot import ProgramState, ReadableProgramState, \ RegisterAccessError, MemoryAccessError from .trace import Trace, TraceEnvironment @@ -80,7 +80,7 @@ class Instruction: @staticmethod def from_bytecode(asm: bytes, arch: Arch) -> Instruction: """Disassemble an instruction.""" - machine = Machine(arch.archname) + machine = make_machine(arch) assert(machine.mn is not None) _instr = machine.mn.dis(asm, arch.ptr_size) return Instruction(_instr, machine, arch, None) @@ -244,14 +244,15 @@ class SymbolicTransform: accessed_regs = set[str]() class RegisterCollector(MiasmSymbolResolver): - def __init__(self): pass + def __init__(self, arch: Arch): + self._arch = arch # MiasmSymbolResolver needs this def resolve_register(self, regname: str) -> int | None: accessed_regs.add(self._miasm_to_regname(regname)) return None def resolve_memory(self, addr: int, size: int): pass def resolve_location(self, loc): assert(False) - resolver = RegisterCollector() + resolver = RegisterCollector(self.arch) for expr in self.changed_regs.values(): eval_expr(expr, resolver) for addr_expr, mem_expr in self.changed_mem.items(): @@ -340,7 +341,8 @@ class SymbolicTransform: for addr, expr in self.changed_mem.items(): addr = eval_symbol(addr, conc_state) length = int(expr.size / 8) - res[addr] = eval_symbol(expr, conc_state).to_bytes(length, byteorder='big') + res[addr] = eval_symbol(expr, conc_state) \ + .to_bytes(length, byteorder=self.arch.endianness) return res @classmethod @@ -352,8 +354,13 @@ class SymbolicTransform: from miasm.expression.parser import str_to_expr as parse def decode_inst(obj: Iterable[int], arch: Arch): - b = b''.join(i.to_bytes(1, byteorder='big') for i in obj) - return Instruction.from_bytecode(b, arch) + b = b''.join(i.to_bytes(1) for i in obj) + try: + return Instruction.from_bytecode(b, arch) + except Exception as err: + warn(f'[In SymbolicTransform.from_json] Unable to disassemble' + f' bytes {obj}: {err}.') + return None arch = supported_architectures[data['arch']] start_addr = int(data['from_addr']) @@ -362,7 +369,8 @@ class SymbolicTransform: t = SymbolicTransform({}, [], arch, start_addr, end_addr) t.changed_regs = { name: parse(val) for name, val in data['regs'].items() } t.changed_mem = { parse(addr): parse(val) for addr, val in data['mem'].items() } - t.instructions = [decode_inst(b, arch) for b in data['instructions']] + instrs = [decode_inst(b, arch) for b in data['instructions']] + t.instructions = [inst for inst in instrs if inst is not None] # Recover the instructions' address information addr = t.addr @@ -375,13 +383,21 @@ class SymbolicTransform: def to_json(self) -> dict: """Serialize a symbolic transformation as a JSON object.""" def encode_inst(inst: Instruction): - return [int(b) for b in inst.to_bytecode()] + try: + return [int(b) for b in inst.to_bytecode()] + except Exception as err: + warn(f'[In SymbolicTransform.to_json] Unable to assemble' + f' "{inst}" to bytecode: {err}. This instruction will not' + f' be serialized.') + return None + instrs = [encode_inst(inst) for inst in self.instructions] + instrs = [inst for inst in instrs if inst is not None] return { 'arch': self.arch.archname, 'from_addr': self.range[0], 'to_addr': self.range[1], - 'instructions': [encode_inst(inst) for inst in self.instructions], + 'instructions': instrs, 'regs': { name: repr(expr) for name, expr in self.changed_regs.items() }, 'mem': { repr(addr): repr(val) for addr, val in self.changed_mem.items() }, } @@ -554,24 +570,17 @@ class _LLDBConcreteState(ReadableProgramState): allows us instead to read values from LLDB on demand. """ def __init__(self, target: LLDBConcreteTarget, arch: Arch): + super().__init__(arch) self._target = target - self._arch = arch def read_register(self, reg: str) -> int: - from focaccia.arch import x86 - - regname = self._arch.to_regname(reg) + regname = self.arch.to_regname(reg) if regname is None: raise RegisterAccessError(reg, f'Not a register name: {reg}') try: return self._target.read_register(regname) except ConcreteRegisterError: - # Special case for X86 - if self._arch.archname == x86.archname: - rflags = x86.decompose_rflags(self._target.read_register('rflags')) - if regname in rflags: - return rflags[regname] raise RegisterAccessError(regname, '') def read_memory(self, addr: int, size: int) -> bytes: diff --git a/nix.shell b/nix.shell new file mode 100644 index 0000000..00fef51 --- /dev/null +++ b/nix.shell @@ -0,0 +1,12 @@ +{ pkgs ? import <nixpkgs> {} }: +pkgs.mkShell { + nativeBuildInputs = with pkgs; [ + python311 + python311Packages.pip + virtualenv + + gcc gnumake binutils cmake ninja pkg-config + musl qemu swig4 + gdb + ]; +} diff --git a/tools/_qemu_tool.py b/tools/_qemu_tool.py index 38715dc..e3341ad 100644 --- a/tools/_qemu_tool.py +++ b/tools/_qemu_tool.py @@ -10,51 +10,86 @@ import gdb from typing import Iterable import focaccia.parser as parser -from focaccia.arch import supported_architectures +from focaccia.arch import supported_architectures, Arch from focaccia.compare import compare_symbolic from focaccia.snapshot import ProgramState, ReadableProgramState, \ RegisterAccessError, MemoryAccessError from focaccia.symbolic import SymbolicTransform, eval_symbol, ExprMem from focaccia.trace import Trace, TraceEnvironment -from focaccia.utils import get_envp, print_result +from focaccia.utils import print_result from verify_qemu import make_argparser, verbosity class GDBProgramState(ReadableProgramState): - def __init__(self, process: gdb.Inferior, frame: gdb.Frame): + from focaccia.arch import aarch64, x86 + + flag_register_names = { + aarch64.archname: 'cpsr', + x86.archname: 'eflags', + } + + flag_register_decompose = { + aarch64.archname: aarch64.decompose_cpsr, + x86.archname: x86.decompose_rflags, + } + + def __init__(self, process: gdb.Inferior, frame: gdb.Frame, arch: Arch): + super().__init__(arch) self._proc = process self._frame = frame + @staticmethod + def _read_vector_reg_aarch64(val, size) -> int: + return int(str(val['u']), 10) + + @staticmethod + def _read_vector_reg_x86(val, size) -> int: + num_longs = size // 64 + vals = val[f'v{num_longs}_int64'] + res = 0 + for i in range(num_longs): + val = int(vals[i].cast(gdb.lookup_type('unsigned long'))) + res += val << i * 64 + return res + + read_vector_reg = { + aarch64.archname: _read_vector_reg_aarch64, + x86.archname: _read_vector_reg_x86, + } + def read_register(self, reg: str) -> int: try: val = self._frame.read_register(reg.lower()) size = val.type.sizeof * 8 - # For vector registers, we have to assemble Python's - # arbitrary-length integers from GDB's fixed-size integers - # ourselves: + # For vector registers, we need to apply architecture-specific + # logic because GDB's interface is not consistent. if size > 64: # Value is a vector - num_longs = size // 64 - vals = val[f'v{num_longs}_int64'] - res = 0 - for i in range(num_longs): - val = int(vals[i].cast(gdb.lookup_type('unsigned long'))) - res += val << i * 64 - return res + if self.arch.archname not in self.read_vector_reg: + raise NotImplementedError( + f'Reading vector registers is not implemented for' + f' architecture {self.arch.archname}.') + return self.read_vector_reg[self.arch.archname](val, size) # For non-vector values, just return the 64-bit value return int(val.cast(gdb.lookup_type('unsigned long'))) except ValueError as err: - from focaccia.arch import x86 - rflags = int(self._frame.read_register('eflags')) - rflags = x86.decompose_rflags(rflags) - if reg in rflags: - return rflags[reg] - raise RegisterAccessError(reg, str(err)) + # Try to access the flags register with `reg` as a logical flag name + if self.arch.archname in self.flag_register_names: + flags_reg = self.flag_register_names[self.arch.archname] + flags = int(self._frame.read_register(flags_reg)) + flags = self.flag_register_decompose[self.arch.archname](flags) + if reg in flags: + return flags[reg] + raise RegisterAccessError(reg, + f'[GDB] Unable to access {reg}: {err}') def read_memory(self, addr: int, size: int) -> bytes: try: mem = self._proc.read_memory(addr, size).tobytes() - return bytes(reversed(mem)) # Convert to big endian + if self.arch.endianness == 'little': + return mem + else: + return bytes(reversed(mem)) # Convert to big endian except gdb.MemoryError as err: raise MemoryAccessError(addr, size, str(err)) @@ -87,7 +122,7 @@ class GDBServerStateIterator: # i.e. before stepping the first time if self._first_next: self._first_next = False - return GDBProgramState(self._process, gdb.selected_frame()) + return GDBProgramState(self._process, gdb.selected_frame(), self.arch) # Step pc = gdb.selected_frame().read_register('pc') @@ -98,7 +133,7 @@ class GDBServerStateIterator: raise StopIteration new_pc = gdb.selected_frame().read_register('pc') - return GDBProgramState(self._process, gdb.selected_frame()) + return GDBProgramState(self._process, gdb.selected_frame(), self.arch) def record_minimal_snapshot(prev_state: ReadableProgramState, cur_state: ReadableProgramState, @@ -140,8 +175,8 @@ def record_minimal_snapshot(prev_state: ReadableProgramState, resolved relative to this state. """ for regname in regs: - regval = cur_state.read_register(regname) try: + regval = cur_state.read_register(regname) out_state.set_register(regname, regval) except RegisterAccessError: pass |