mirror of https://github.com/pwndbg/pwndbg.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
213 lines
6.3 KiB
Python
213 lines
6.3 KiB
Python
from __future__ import annotations
|
|
|
|
import argparse
|
|
import bz2
|
|
import datetime
|
|
import os
|
|
|
|
import gdb
|
|
|
|
import pwndbg
|
|
import pwndbg.aglib.regs
|
|
import pwndbg.commands
|
|
import pwndbg.commands.context
|
|
import pwndbg.dbg
|
|
import pwndbg.integration.ida
|
|
from pwndbg.commands import CommandCategory
|
|
from pwndbg.dbg import EventType
|
|
from pwndbg.gdblib.functions import GdbFunction
|
|
|
|
|
|
@pwndbg.commands.Command(
|
|
"Synchronize IDA's cursor with GDB.", category=CommandCategory.INTEGRATIONS
|
|
)
|
|
@pwndbg.commands.OnlyWhenRunning
|
|
@pwndbg.dbg.event_handler(EventType.STOP)
|
|
@pwndbg.integration.ida.enabledIDA
|
|
def j(*args) -> None:
|
|
"""
|
|
Synchronize IDA's cursor with GDB
|
|
"""
|
|
try:
|
|
pc = int(pwndbg.dbg.selected_frame().pc())
|
|
pwndbg.integration.ida.Jump(pc)
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
parser = argparse.ArgumentParser(description="Select and print stack frame that called this one.")
|
|
parser.add_argument(
|
|
"n", nargs="?", default=1, type=int, help="The number of stack frames to go up."
|
|
)
|
|
|
|
|
|
@pwndbg.commands.Command(parser, category=CommandCategory.MISC)
|
|
@pwndbg.commands.OnlyWhenRunning
|
|
def up(n=1) -> None:
|
|
"""
|
|
Select and print stack frame that called this one.
|
|
"""
|
|
f = gdb.selected_frame()
|
|
|
|
for i in range(int(n)):
|
|
if f.older():
|
|
f = f.older()
|
|
f.select()
|
|
|
|
# workaround for #632
|
|
gdb.execute("frame", to_string=True)
|
|
|
|
bt = pwndbg.commands.context.context_backtrace(with_banner=False)
|
|
print("\n".join(bt))
|
|
|
|
j()
|
|
|
|
|
|
parser = argparse.ArgumentParser(description="Select and print stack frame called by this one.")
|
|
parser.add_argument(
|
|
"n", nargs="?", default=1, type=int, help="The number of stack frames to go down."
|
|
)
|
|
|
|
|
|
# Since we are redefining a gdb command, we also redefine the original aliases.
|
|
# These aliases ("do", "dow") are necessary to ensure consistency in the help system
|
|
# and to pass the test_consistent_help test, which verifies that all commands and their
|
|
# aliases are documented correctly. See issue #2961 for more details.
|
|
@pwndbg.commands.Command(parser, category=CommandCategory.MISC, aliases=["do", "dow"])
|
|
@pwndbg.commands.OnlyWhenRunning
|
|
def down(n=1) -> None:
|
|
"""
|
|
Select and print stack frame called by this one.
|
|
"""
|
|
f = gdb.selected_frame()
|
|
|
|
for i in range(int(n)):
|
|
if f.newer():
|
|
f = f.newer()
|
|
f.select()
|
|
|
|
# workaround for #632
|
|
gdb.execute("frame", to_string=True)
|
|
|
|
bt = pwndbg.commands.context.context_backtrace(with_banner=False)
|
|
print("\n".join(bt))
|
|
|
|
j()
|
|
|
|
|
|
@pwndbg.commands.Command("Save the ida database.", category=CommandCategory.INTEGRATIONS)
|
|
@pwndbg.integration.ida.withIDA
|
|
def save_ida() -> None:
|
|
"""Save the IDA database"""
|
|
path = pwndbg.integration.ida.GetIdbPath()
|
|
|
|
# Need to handle emulated paths for Wine
|
|
if path.startswith("Z:"):
|
|
path = path[2:].replace("\\", "/")
|
|
pwndbg.integration.ida.SaveBase(path)
|
|
|
|
basename = os.path.basename(path)
|
|
dirname = os.path.dirname(path)
|
|
backups = os.path.join(dirname, "ida-backup")
|
|
|
|
if not os.path.isdir(backups):
|
|
os.mkdir(backups)
|
|
|
|
basename, ext = os.path.splitext(basename)
|
|
basename += "-%s" % datetime.datetime.now().isoformat()
|
|
basename += ext
|
|
|
|
# Windows doesn't like colons in paths
|
|
basename = basename.replace(":", "_")
|
|
|
|
full_path = os.path.join(backups, basename)
|
|
|
|
pwndbg.integration.ida.SaveBase(full_path)
|
|
|
|
with open(full_path, "rb") as f:
|
|
data = f.read()
|
|
|
|
# Compress!
|
|
full_path_compressed = full_path + ".bz2"
|
|
bz2.BZ2File(full_path_compressed, "w").write(data)
|
|
|
|
# Remove old version
|
|
os.unlink(full_path)
|
|
|
|
|
|
def _ida_local(name: str) -> int | None:
|
|
if not pwndbg.aglib.proc.alive:
|
|
return None
|
|
|
|
pc = int(pwndbg.dbg.selected_frame().pc())
|
|
frame_id = pwndbg.integration.ida.GetFuncAttr(pc, pwndbg.integration.ida.idc.FUNCATTR_FRAME) # type: ignore[attr-defined]
|
|
if frame_id == -1:
|
|
return None
|
|
|
|
stack_size = pwndbg.integration.ida.GetStrucSize(frame_id)
|
|
|
|
# workaround for bug in IDA 9 when looking up the " s" member offset raises
|
|
# AttributeError: module 'ida_typeinf' has no attribute 'FRAME_UDM_NAME_S'
|
|
saved_baseptr = pwndbg.integration.ida.GetMemberOffset(frame_id, "__saved_registers")
|
|
if saved_baseptr == -1:
|
|
saved_baseptr = pwndbg.integration.ida.GetMemberOffset(frame_id, " s")
|
|
|
|
for i in range(stack_size):
|
|
local_name = pwndbg.integration.ida.GetMemberName(frame_id, i)
|
|
if local_name != name:
|
|
continue
|
|
|
|
# Heuristic: Offset is relative to the base pointer or stack pointer
|
|
# depending on if IDA is detecting a saved frame pointer or not.
|
|
offset = pwndbg.integration.ida.GetMemberOffset(frame_id, local_name)
|
|
if offset == -1:
|
|
raise ValueError("ida.GetMemberOffset(%r) == -1" % local_name)
|
|
if saved_baseptr != -1 and pwndbg.aglib.regs.frame is not None:
|
|
return pwndbg.aglib.regs.read_reg(pwndbg.aglib.regs.frame) + offset - saved_baseptr
|
|
return pwndbg.aglib.regs.read_reg(pwndbg.aglib.regs.stack) + offset
|
|
return None
|
|
|
|
|
|
@GdbFunction()
|
|
def ida(name: gdb.Value) -> int:
|
|
"""
|
|
Lookup a symbol's address by name from IDA.
|
|
Evaluate ida.LocByName() on the supplied value.
|
|
|
|
This functions doesn't see stack local variables.
|
|
|
|
Example:
|
|
```
|
|
pwndbg> set integration-provider ida
|
|
Pwndbg successfully connected to Ida Pro xmlrpc: http://127.0.0.1:43718
|
|
Set which provider to use for integration features to 'ida'.
|
|
pwndbg> p main
|
|
No symbol "main" in current context.
|
|
pwndbg> p/x $ida("main")
|
|
$1 = 0x555555555645
|
|
pwndbg> b *$ida("main")
|
|
Breakpoint 2 at 0x555555555645
|
|
```
|
|
"""
|
|
# GDB convenience functions are not allowed to return None, so we cannot
|
|
# decorate with @withIda().
|
|
if not pwndbg.integration.ida.establish_connection():
|
|
return 0
|
|
|
|
name = name.string()
|
|
|
|
# Lookup local variables first
|
|
result = _ida_local(name)
|
|
if result is not None:
|
|
return result
|
|
|
|
result = pwndbg.integration.ida.LocByName(name)
|
|
if result is None:
|
|
raise ValueError("ida.LocByName(%r) == None" % name)
|
|
|
|
result_r = pwndbg.integration.ida.l2r(result)
|
|
if 0xFFFFE000 <= result_r <= 0xFFFFFFFF or 0xFFFFFFFFFFFFE000 <= result_r <= 0xFFFFFFFFFFFFFFFF:
|
|
raise ValueError("ida.LocByName(%r) == BADADDR" % name)
|
|
|
|
return result
|