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.
193 lines
6.0 KiB
Python
193 lines
6.0 KiB
Python
"""
|
|
Given an address in memory which does not contain a pointer elsewhere
|
|
into memory, attempt to describe the data as best as possible.
|
|
|
|
Currently prints out code, integers, or strings, in a best-effort manner
|
|
dependent on page permissions, the contents of the data, and any
|
|
supplemental information sources (e.g. active IDA Pro connection).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import string
|
|
from typing import Tuple
|
|
|
|
import pwndbg
|
|
import pwndbg.color.enhance as E
|
|
import pwndbg.color.memory
|
|
import pwndbg.gdblib.arch
|
|
import pwndbg.gdblib.disasm
|
|
import pwndbg.gdblib.memory
|
|
import pwndbg.gdblib.strings
|
|
import pwndbg.gdblib.typeinfo
|
|
import pwndbg.lib.cache
|
|
from pwndbg import color
|
|
from pwndbg.color.syntax_highlight import syntax_highlight
|
|
|
|
bad_instrs = [".byte", ".long", "rex.R", "rex.XB", ".inst", "(bad)"]
|
|
|
|
|
|
def good_instr(i) -> bool:
|
|
return not any(bad in i for bad in bad_instrs)
|
|
|
|
|
|
def format_small_int(value: int) -> str:
|
|
if value < 10:
|
|
return str(value)
|
|
else:
|
|
return hex(value & pwndbg.gdblib.arch.ptrmask)
|
|
|
|
|
|
def format_small_int_pair(first: int, second: int) -> Tuple[str, str]:
|
|
if first < 10 and second < 10:
|
|
return (str(first), str(second))
|
|
else:
|
|
return (
|
|
hex(first & pwndbg.gdblib.arch.ptrmask),
|
|
hex(second & pwndbg.gdblib.arch.ptrmask),
|
|
)
|
|
|
|
|
|
def int_str(value: int) -> str:
|
|
retval = format_small_int(value)
|
|
|
|
# Try to unpack the value as a string
|
|
packed = pwndbg.gdblib.arch.pack(int(value))
|
|
if all(c in string.printable.encode("utf-8") for c in packed):
|
|
if len(retval) > 4:
|
|
retval = "{} ({!r})".format(retval, str(packed.decode("ascii", "ignore")))
|
|
|
|
return retval
|
|
|
|
|
|
# @pwndbg.lib.cache.cache_until("stop")
|
|
def enhance(
|
|
value: int,
|
|
code: bool = True,
|
|
safe_linking: bool = False,
|
|
attempt_dereference=True,
|
|
enhance_string_len: int = None,
|
|
) -> str:
|
|
"""
|
|
Given the last pointer in a chain, attempt to characterize
|
|
|
|
Note that 'the last pointer in a chain' may not at all actually be a pointer.
|
|
|
|
Additionally, optimizations are made based on various sources of data for
|
|
'value'. For example, if it is set to RWX, we try to get information on whether
|
|
it resides on the stack, or in a RW section that *happens* to be RWX, to
|
|
determine which order to print the fields.
|
|
|
|
Arguments:
|
|
value(obj): Value to enhance
|
|
code(bool): Hint that indicates the value may be an instruction
|
|
safe_linking(bool): Whether this chain use safe-linking
|
|
enhance_string_len(int): The length of string to display for enhancement of the last pointer
|
|
"""
|
|
value = int(value)
|
|
|
|
page = pwndbg.gdblib.vmmap.find(value)
|
|
|
|
# If it's not in a page we know about, try to dereference
|
|
# it anyway just to test.
|
|
can_read = True
|
|
if not attempt_dereference or not page or None is pwndbg.gdblib.memory.peek(value):
|
|
can_read = False
|
|
|
|
# If it's a pointer that we told we cannot deference, then color it accordingly and add symbol if can
|
|
if page and not attempt_dereference:
|
|
return pwndbg.color.memory.get_address_and_symbol(value)
|
|
|
|
if not can_read:
|
|
return E.integer(int_str(value))
|
|
|
|
# It's mapped memory, or we can at least read it.
|
|
# Try to find out if it's a string.
|
|
instr: str | None = None
|
|
exe = page and page.execute
|
|
rwx = page and page.rwx
|
|
|
|
# For the purpose of following pointers, don't display
|
|
# anything on the stack or heap as 'code'
|
|
if "[stack" in page.objfile or "[heap" in page.objfile:
|
|
rwx = exe = False
|
|
|
|
# If IDA doesn't think it's in a function, don't display it as code.
|
|
if pwndbg.ida.available() and not pwndbg.ida.GetFunctionName(value):
|
|
rwx = exe = False
|
|
|
|
if exe:
|
|
pwndbg_instr = pwndbg.gdblib.disasm.one(value, enhance=False)
|
|
if pwndbg_instr:
|
|
instr = f"{pwndbg_instr.mnemonic} {pwndbg_instr.op_str}"
|
|
if pwndbg.config.syntax_highlight:
|
|
instr = syntax_highlight(instr)
|
|
|
|
szval = pwndbg.gdblib.strings.get(value, maxlen=enhance_string_len) or None
|
|
szval0 = szval
|
|
if szval:
|
|
szval = E.string(repr(szval))
|
|
|
|
# Fix for case when we can't read the end address anyway (#946)
|
|
if value + pwndbg.gdblib.arch.ptrsize > page.end:
|
|
return E.integer(int_str(value))
|
|
|
|
intval = int(pwndbg.gdblib.memory.get_typed_pointer_value(pwndbg.gdblib.typeinfo.pvoid, value))
|
|
if safe_linking:
|
|
intval ^= value >> 12
|
|
intval0 = intval
|
|
if 0 <= intval < 10:
|
|
intval = E.integer(str(intval))
|
|
else:
|
|
intval = E.integer("%#x" % int(intval & pwndbg.gdblib.arch.ptrmask))
|
|
|
|
retval = []
|
|
|
|
# print([instr,intval0,szval])
|
|
if not code:
|
|
instr = None
|
|
|
|
# If it's on the stack, don't display it as code in a chain.
|
|
if instr and "[stack" in page.objfile:
|
|
retval = [intval, szval]
|
|
|
|
# If it's RWX but a small value, don't display it as code in a chain.
|
|
elif instr and rwx and intval0 < 0x1000:
|
|
retval = [intval, szval]
|
|
|
|
# If it's an instruction and *not* RWX, display it unconditionally
|
|
elif instr and exe:
|
|
if not rwx:
|
|
if szval:
|
|
retval = [instr, szval]
|
|
else:
|
|
retval = [instr]
|
|
else:
|
|
retval = [instr, intval, szval]
|
|
|
|
# Otherwise strings have preference
|
|
elif szval:
|
|
if len(szval0) < pwndbg.gdblib.arch.ptrsize:
|
|
retval = [intval, szval]
|
|
else:
|
|
retval = [szval]
|
|
|
|
# And then integer
|
|
else:
|
|
# It might be a pointer or just a plain integer
|
|
new_page = pwndbg.gdblib.vmmap.find(intval0)
|
|
if new_page:
|
|
return pwndbg.color.memory.get_address_and_symbol(intval0)
|
|
else:
|
|
return E.integer(int_str(intval0))
|
|
|
|
retval = tuple(filter(lambda x: x is not None, retval))
|
|
|
|
if len(retval) == 0:
|
|
return E.unknown("???")
|
|
|
|
if len(retval) == 1:
|
|
return retval[0] # type: ignore[return-value]
|
|
|
|
return retval[0] + E.comment(color.strip(f" /* {'; '.join(retval[1:])} */")) # type: ignore[arg-type]
|