Port to aglib: dt (#2568)

* dt: move dir

* dt: port to aglib
pull/2508/head^2
patryk4815 1 year ago committed by GitHub
parent 8e93085bdb
commit 0076f108ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,138 @@
"""
Prints structures in a manner similar to Windbg's "dt" command.
"""
from __future__ import annotations
from typing import List
import pwndbg
import pwndbg.aglib.memory
import pwndbg.aglib.typeinfo
import pwndbg.dbg
def _field_to_human(
f: pwndbg.dbg_mod.TypeField | pwndbg.dbg_mod.Value | pwndbg.dbg_mod.Type,
) -> str:
if isinstance(f, pwndbg.dbg_mod.TypeField):
t = f.type
elif isinstance(f, pwndbg.dbg_mod.Type):
t = f
elif isinstance(f, pwndbg.dbg_mod.Value):
t = f.type
else:
raise NotImplementedError("unknown type")
return t.name
def dt(
name: str = "",
addr: int | pwndbg.dbg_mod.Value | None = None,
obj: pwndbg.dbg_mod.Value | None = None,
) -> str:
"""
Dump out a structure type Windbg style.
"""
# Return value is a list of strings.of
# We concatenate at the end.
rv: List[str] = []
if obj and not name:
t = obj.type
while t.code == pwndbg.dbg_mod.TypeCode.POINTER:
t = t.target()
obj = obj.dereference()
name = str(t)
# Lookup the type name specified by the user
else:
t = pwndbg.aglib.typeinfo.load(name)
if not t:
return ""
# If it's not a struct (e.g. int or char*), bail
if t.code not in (
pwndbg.dbg_mod.TypeCode.STRUCT,
pwndbg.dbg_mod.TypeCode.TYPEDEF,
pwndbg.dbg_mod.TypeCode.UNION,
):
return f"Not a structure: {t}"
# If an address was specified, create a Value of the
# specified type at that address.
if addr is not None:
obj = pwndbg.aglib.memory.get_typed_pointer_value(t, addr)
# Header, optionally include the name
header = name
if obj:
header = f"{header} @ {hex(int(obj.address))}"
rv.append(header)
if t.strip_typedefs().code == pwndbg.dbg_mod.TypeCode.ARRAY:
return "Arrays not supported yet"
if t.strip_typedefs().code not in (
pwndbg.dbg_mod.TypeCode.STRUCT,
pwndbg.dbg_mod.TypeCode.UNION,
):
newobj = obj
if not newobj:
newobj = pwndbg.dbg.selected_inferior().create_value(0, t)
iter_fields = [(field.name, field) for field in newobj.type.fields()]
else:
iter_fields = [(field.name, field) for field in t.fields()]
for field_name, field in iter_fields:
# Offset into the parent structure
offset = field.bitpos // 8
bitpos = field.bitpos % 8
ftype = field.type.strip_typedefs()
extra = _field_to_human(field)
if obj and obj.type.strip_typedefs().code in (
pwndbg.dbg_mod.TypeCode.STRUCT,
pwndbg.dbg_mod.TypeCode.UNION,
):
obj_value = obj[field_name]
if ftype.code == pwndbg.dbg_mod.TypeCode.INT:
extra = hex(int(obj_value))
elif (
ftype.code in (pwndbg.dbg_mod.TypeCode.POINTER, pwndbg.dbg_mod.TypeCode.ARRAY)
and ftype.target() == pwndbg.aglib.typeinfo.uchar
):
data = pwndbg.aglib.memory.read(int(obj_value.address), ftype.sizeof)
extra = " ".join("%02x" % b for b in data)
else:
extra = obj_value.value_to_human_readable()
# Adjust trailing lines in 'extra' to line up
# This is necessary when there are nested structures.
# Ideally we'd expand recursively if the type is complex.
extra_lines: List[str] = []
for i, line in enumerate(str(extra).splitlines()):
if i == 0:
extra_lines.append(line)
else:
extra_lines.append(35 * " " + line)
extra = "\n".join(extra_lines)
bitpos_str = "" if not bitpos else (".%i" % bitpos)
if obj:
line = " 0x%016x +0x%04x%s %-20s : %s" % (
int(obj.address) + offset,
offset,
bitpos_str,
field_name,
extra,
)
else:
line = " +0x%04x%s %-20s : %s" % (offset, bitpos_str, field_name, extra)
rv.append(line)
return "\n".join(rv)

@ -233,7 +233,7 @@ def fix(
# no debugger-agnostic architecture functions. Those will come later.
#
# TODO: Port architecutre functions and `pwndbg.gdblib.regs.fix` to debugger-agnostic API and remove this.
arg = pwndbg.gdblib.regs.fix(arg)
arg = pwndbg.aglib.regs.fix(arg)
return target.evaluate_expression(arg)
except Exception as e:
ex = e
@ -692,7 +692,6 @@ def load_commands() -> None:
import pwndbg.commands.binja
import pwndbg.commands.branch
import pwndbg.commands.cymbol
import pwndbg.commands.dt
import pwndbg.commands.godbg
import pwndbg.commands.got
import pwndbg.commands.got_tracking
@ -732,6 +731,7 @@ def load_commands() -> None:
import pwndbg.commands.cyclic
import pwndbg.commands.dev
import pwndbg.commands.distance
import pwndbg.commands.dt
import pwndbg.commands.dumpargs
import pwndbg.commands.elf
import pwndbg.commands.flags

@ -2,12 +2,11 @@ from __future__ import annotations
import argparse
import gdb
import pwndbg
import pwndbg.aglib.dt
import pwndbg.aglib.vmmap
import pwndbg.color
import pwndbg.commands
import pwndbg.gdblib.dt
import pwndbg.gdblib.vmmap
parser = argparse.ArgumentParser(
formatter_class=argparse.RawTextHelpFormatter,
@ -24,7 +23,7 @@ parser.add_argument(
@pwndbg.commands.ArgparsedCommand(parser)
def dt(typename: str, address: int | gdb.Value | None = None) -> None:
def dt(typename: str, address: int | pwndbg.dbg_mod.Value | None = None) -> None:
"""
Dump out information on a type (e.g. ucontext_t).
@ -32,4 +31,5 @@ def dt(typename: str, address: int | gdb.Value | None = None) -> None:
"""
if address is not None:
address = pwndbg.commands.fix(str(address))
print(pwndbg.gdblib.dt.dt(typename, addr=address))
print(pwndbg.aglib.dt.dt(typename, addr=address))

@ -583,6 +583,16 @@ class Type:
Class representing a type in the context of an inferior process.
"""
@property
def name(self) -> str:
"""
Returns the name of this type, eg:
- char [16]
- int
- char *
- void *
"""
@property
def sizeof(self) -> int:
"""

@ -914,6 +914,11 @@ class GDBType(pwndbg.dbg_mod.Type):
return self.inner == other.inner
@property
@override
def name(self) -> str:
return str(self.inner)
@property
@override
def sizeof(self) -> int:

@ -327,6 +327,10 @@ class LLDBType(pwndbg.dbg_mod.Type):
return self.inner == other.inner
@property
def name(self) -> str:
return self.inner.name
@property
@override
def sizeof(self) -> int:

@ -1,164 +0,0 @@
"""
Prints structures in a manner similar to Windbg's "dt" command.
"""
from __future__ import annotations
import re
from typing import List
import gdb
import pwndbg.gdblib.memory
import pwndbg.gdblib.typeinfo
def get_type(v: gdb.Value) -> str:
t = v.type
while not t.name:
if t.code == gdb.TYPE_CODE_PTR:
t = t.target()
return t.name
def get_typename(t: gdb.Type) -> str:
return str(t)
def get_arrsize(f: gdb.Value) -> int:
t = f.type
if t.code != gdb.TYPE_CODE_ARRAY:
return 0
t2 = t.target()
return int(t.sizeof / t2.sizeof)
def get_field_by_name(obj: gdb.Value, field: str) -> gdb.Value:
# Dereference once
if obj.type.code == gdb.TYPE_CODE_PTR:
obj = obj.dereference()
for f in re.split(r"(->|\.|\[\d+\])", field):
if not f:
continue
if f == "->":
obj = obj.dereference()
elif f == ".":
pass
elif f.startswith("["):
n = int(f.strip("[]"))
obj = obj.cast(obj.dereference().type.pointer())
obj += n
obj = obj.dereference()
else:
obj = obj[f]
return obj
def happy(typename: str) -> str:
prefix = ""
if "unsigned" in typename:
prefix = "u"
typename = typename.replace("unsigned ", "")
return (
prefix
+ {
"char": "char",
"short int": "short",
"long int": "long",
"int": "int",
"long long": "longlong",
"float": "float",
"double": "double",
}[typename]
)
def dt(name: str = "", addr: int | gdb.Value | None = None, obj: gdb.Value | None = None) -> str:
"""
Dump out a structure type Windbg style.
"""
# Return value is a list of strings.of
# We concatenate at the end.
rv: List[str] = []
if obj and not name:
t = obj.type
while t.code == (gdb.TYPE_CODE_PTR):
t = t.target()
obj = obj.dereference()
name = str(t)
# Lookup the type name specified by the user
else:
t = pwndbg.gdblib.typeinfo.load(name)
if not t:
return ""
# If it's not a struct (e.g. int or char*), bail
if t.code not in (gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_TYPEDEF, gdb.TYPE_CODE_UNION):
raise Exception(f"Not a structure: {t}")
# If an address was specified, create a Value of the
# specified type at that address.
if addr is not None:
obj = pwndbg.gdblib.memory.get_typed_pointer_value(t, addr)
# Header, optionally include the name
header = name
if obj:
header = f"{header} @ {hex(int(obj.address))}"
rv.append(header)
if t.strip_typedefs().code == gdb.TYPE_CODE_ARRAY:
return "Arrays not supported yet"
if t.strip_typedefs().code not in (gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION):
t = {name: obj or gdb.Value(0).cast(t)}
for name, field in t.items():
# Offset into the parent structure
o = getattr(field, "bitpos", 0) // 8
b = getattr(field, "bitpos", 0) % 8
extra = str(field.type)
ftype = field.type.strip_typedefs()
if obj and obj.type.strip_typedefs().code in (gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION):
v = obj[name]
if ftype.code == gdb.TYPE_CODE_INT:
v = hex(int(v))
if (
ftype.code in (gdb.TYPE_CODE_PTR, gdb.TYPE_CODE_ARRAY)
and ftype.target() == pwndbg.gdblib.typeinfo.uchar
):
data = pwndbg.gdblib.memory.read(int(v.address), ftype.sizeof)
v = " ".join("%02x" % b for b in data)
extra = v
# Adjust trailing lines in 'extra' to line up
# This is necessary when there are nested structures.
# Ideally we'd expand recursively if the type is complex.
extra_lines: List[str] = []
for i, line in enumerate(str(extra).splitlines()):
if i == 0:
extra_lines.append(line)
else:
extra_lines.append(35 * " " + line)
extra = "\n".join(extra_lines)
bitpos = "" if not b else (".%i" % b)
if obj:
line = " 0x%016x +0x%04x%s %-20s : %s" % (
int(obj.address) + o,
o,
bitpos,
name,
extra,
)
else:
line = " +0x%04x%s %-20s : %s" % (o, bitpos, name, extra)
rv.append(line)
return "\n".join(rv)

@ -90,7 +90,6 @@ module = [
"pwndbg.commands.reload",
"pwndbg.commands.version",
"pwndbg.exception",
"pwndbg.gdblib.dt",
"pwndbg.aglib.dynamic",
"pwndbg.gdblib.events",
"pwndbg.gdblib.got",

@ -22,8 +22,8 @@ def test_command_dt_works_with_address(start_binary):
exp_regex = (
"struct tcache_perthread_struct @ 0x[0-9a-f]+\n"
" 0x[0-9a-f]+ \\+0x0000 counts : uint16_t \\[64\\]\n"
" 0x[0-9a-f]+ \\+0x[0-9a-f]{4} entries : tcache_entry \\*\\[64\\]\n"
" 0x[0-9a-f]+ \\+0x0000 counts : \\{[0-9]+, [0-9]+ <repeats 63 times>\\}\n"
" 0x[0-9a-f]+ \\+0x[0-9a-f]{4} entries : \\{0x[0-9a-f]+, 0x[0-9a-f]+ <repeats 63 times>\\}\n"
)
assert re.match(exp_regex, out)

@ -2,11 +2,11 @@ from __future__ import annotations
import os
import pwndbg.aglib.dt
import pwndbg.dbg
if pwndbg.dbg.is_gdblib_available():
import pwndbg.commands.cymbol
import pwndbg.gdblib.dt
import tests
@ -25,7 +25,7 @@ def create_symbol_file(symbol, source):
def check_symbol_existance(symbol_type):
try:
pwndbg.gdblib.dt.dt(symbol_type)
pwndbg.aglib.dt.dt(symbol_type)
except Exception as exception:
# In case it is an AttributeError symbol_type doesn't exists.
assert isinstance(exception, AttributeError)
@ -62,7 +62,7 @@ def test_cymbol(start_binary):
" +0x0004 b : char [16]\n"
" +0x0018 c : char *\n"
" +0x0020 d : void *"
) == pwndbg.gdblib.dt.dt("example_t").strip()
) == pwndbg.aglib.dt.dt("example_t").strip()
# Test whether unload_loaded_symbol() works properly.
pwndbg.commands.cymbol.unload_loaded_symbol("example")

Loading…
Cancel
Save