mirror of https://github.com/pwndbg/pwndbg.git
Express context saved to the stack + Arm Cortex M Exception return address resolution (#2807)
* Add generic structure to express saved context on the stack. Add handler for Arm Cortex M exception return * Rename to SavedRegisterFrame, recreate command * add description * lint * Generate docs * Update pwndbg/aglib/saved_register_frames.py Co-authored-by: Disconnect3d <dominik.b.czarnota@gmail.com> * Clean up --------- Co-authored-by: Disconnect3d <dominik.b.czarnota@gmail.com>pull/2915/head
parent
357738c53c
commit
491800e5a3
@ -0,0 +1,34 @@
|
||||
<!-- THIS PART OF THIS FILE IS AUTOGENERATED. DO NOT MODIFY IT. See scripts/generate_docs.sh -->
|
||||
|
||||
|
||||
|
||||
|
||||
# dump-register-frame
|
||||
|
||||
## Description
|
||||
|
||||
|
||||
Display the registers saved to memory for a certain frame type
|
||||
## Usage:
|
||||
|
||||
|
||||
```bash
|
||||
usage: dump-register-frame [-h] [-p] {armcm-exception} [address]
|
||||
|
||||
```
|
||||
## Positional Arguments
|
||||
|
||||
|Positional Argument|Help|
|
||||
| :--- | :--- |
|
||||
|`frame_type`|The type of frame to print|
|
||||
|`address`|The address to read the frame from|
|
||||
|
||||
## Optional Arguments
|
||||
|
||||
|Short|Long|Default|Help|
|
||||
| :--- | :--- | :--- | :--- |
|
||||
|`-h`|`--help`||show this help message and exit|
|
||||
|`-p`|`--print`||Show addresses of frame values (default: %(default)s)|
|
||||
|
||||
<!-- END OF AUTOGENERATED PART. Do not modify this line or the line below, they mark the end of the auto-generated part of the file. If you want to extend the documentation in a way which cannot easily be done by adding to the command help description, write below the following line. -->
|
||||
<!-- ------------\>8---- ----\>8---- ----\>8------------ -->
|
||||
@ -0,0 +1,50 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
import pwndbg.aglib.memory
|
||||
|
||||
|
||||
@dataclass
|
||||
class SavedRegisterFrame:
|
||||
"""
|
||||
A list of registers that have been saved to process memory for later restoration.
|
||||
|
||||
For example, on syscall entry, the process registers are saved to the kernel stack.
|
||||
"""
|
||||
|
||||
# List of (offset, register name), sorted from smallest to largest offset
|
||||
frame_layout: list[tuple[int, str]]
|
||||
offsets: dict[str, int]
|
||||
|
||||
def __init__(self, register_offsets: dict[str, int]):
|
||||
self.offsets = register_offsets
|
||||
|
||||
self.frame_layout = sorted(((y, x) for (x, y) in register_offsets.items()))
|
||||
|
||||
def read_saved_register(self, reg: str, sp: int = None) -> int | None:
|
||||
if sp is None:
|
||||
sp = pwndbg.aglib.regs.sp
|
||||
|
||||
try:
|
||||
mem = pwndbg.aglib.memory.read(sp + self.offsets[reg], pwndbg.aglib.arch.ptrsize)
|
||||
except pwndbg.dbg_mod.Error:
|
||||
return None
|
||||
|
||||
return pwndbg.aglib.arch.unpack(mem)
|
||||
|
||||
|
||||
# Basic exception stack frame defined here - https://developer.arm.com/documentation/107706/0100/Exceptions-and-interrupts-overview/Stack-frames
|
||||
ARM_CORTEX_M_EXCEPTION_STACK_FRAME_OFFSETS = {
|
||||
"r0": 0x0,
|
||||
"r1": 0x4,
|
||||
"r2": 0x8,
|
||||
"r3": 0xC,
|
||||
"r12": 0x10,
|
||||
"lr": 0x14,
|
||||
"pc": 0x18,
|
||||
"xpsr": 0x1C,
|
||||
}
|
||||
|
||||
|
||||
ARM_CORTEX_M_EXCEPTION_STACK = SavedRegisterFrame(ARM_CORTEX_M_EXCEPTION_STACK_FRAME_OFFSETS)
|
||||
@ -0,0 +1,85 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
|
||||
import pwndbg
|
||||
import pwndbg.aglib.memory
|
||||
import pwndbg.chain
|
||||
import pwndbg.color.context as C
|
||||
import pwndbg.commands
|
||||
from pwndbg.aglib.saved_register_frames import ARM_CORTEX_M_EXCEPTION_STACK
|
||||
from pwndbg.aglib.saved_register_frames import SavedRegisterFrame
|
||||
from pwndbg.commands import CommandCategory
|
||||
from pwndbg.commands.sigreturn import print_value
|
||||
|
||||
|
||||
def print_saved_register_frame(
|
||||
context: SavedRegisterFrame, address: int = None, print_address=False
|
||||
):
|
||||
address = pwndbg.aglib.regs.sp if address is None else address
|
||||
|
||||
ptr_size = pwndbg.aglib.arch.ptrsize
|
||||
|
||||
frame_layout = context.frame_layout
|
||||
|
||||
# Offset to the stack pointer where the frame values really begins. Start reading memory there.
|
||||
# Can be negative, 0, or positive
|
||||
frame_start_offset = frame_layout[0][0]
|
||||
|
||||
read_size = frame_layout[-1][0] - frame_start_offset + ptr_size
|
||||
|
||||
mem = pwndbg.aglib.memory.read(address + frame_start_offset, read_size)
|
||||
|
||||
for stack_offset, reg in frame_layout:
|
||||
# Subtract the offset of start of frame, to get the correct offset into "mem"
|
||||
mem_offset = stack_offset - frame_start_offset
|
||||
|
||||
regname = C.register(reg.ljust(4).upper())
|
||||
value = pwndbg.aglib.arch.unpack(mem[mem_offset : mem_offset + ptr_size])
|
||||
|
||||
if reg in pwndbg.aglib.regs.flags: # eflags or cpsr
|
||||
reg_flags = pwndbg.aglib.regs.flags[reg]
|
||||
desc = C.format_flags(value, reg_flags)
|
||||
else:
|
||||
desc = pwndbg.chain.format(value)
|
||||
|
||||
print_value(f"{regname} {desc}", address + stack_offset, print_address)
|
||||
|
||||
|
||||
VALID_FRAME_TYPES = {
|
||||
"armcm-exception": ARM_CORTEX_M_EXCEPTION_STACK,
|
||||
"armcm-exception2": ARM_CORTEX_M_EXCEPTION_STACK,
|
||||
}
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Display the registers saved to memory for a certain frame type"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"frame_type", choices=tuple(VALID_FRAME_TYPES), type=str, help="The type of frame to print"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"address", nargs="?", default=None, type=int, help="The address to read the frame from"
|
||||
)
|
||||
|
||||
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--print",
|
||||
dest="print_address",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Show addresses of frame values",
|
||||
)
|
||||
|
||||
|
||||
@pwndbg.commands.ArgparsedCommand(parser, category=CommandCategory.MEMORY)
|
||||
@pwndbg.commands.OnlyWhenRunning
|
||||
def dump_register_frame(frame_type: str, address: int = None, print_address=False) -> None:
|
||||
register_frame = VALID_FRAME_TYPES.get(frame_type)
|
||||
if register_frame is None:
|
||||
print(f"Invalid frame type: {frame_type} (valid: {','.join(VALID_FRAME_TYPES.keys())})")
|
||||
return
|
||||
|
||||
print_saved_register_frame(register_frame, address, print_address)
|
||||
Loading…
Reference in new issue