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.
197 lines
6.4 KiB
Python
197 lines
6.4 KiB
Python
"""
|
|
Hexdump implementation, ~= stolen from pwntools.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import math
|
|
import string
|
|
|
|
import pwnlib.util.lists
|
|
|
|
import pwndbg
|
|
import pwndbg.aglib.arch
|
|
import pwndbg.aglib.memory
|
|
import pwndbg.aglib.typeinfo
|
|
import pwndbg.color.hexdump as H
|
|
from pwndbg.color import theme
|
|
from pwndbg.commands.windbg import enhex
|
|
|
|
color_scheme = None
|
|
printable = None
|
|
|
|
|
|
def groupby(width: int, array, fill=None):
|
|
return pwnlib.util.lists.group(width, array, underfull_action="fill", fill_value=fill)
|
|
|
|
|
|
config_colorize_ascii = theme.add_param(
|
|
"hexdump-colorize-ascii", True, "whether to colorize the hexdump command ascii section"
|
|
)
|
|
config_separator = theme.add_param(
|
|
"hexdump-ascii-block-separator", "│", "block separator char of the hexdump command"
|
|
)
|
|
config_byte_separator = theme.add_param(
|
|
"hexdump-byte-separator",
|
|
" ",
|
|
"separator of single bytes in hexdump (does NOT affect group separator)",
|
|
)
|
|
|
|
|
|
@pwndbg.config.trigger(
|
|
H.config_normal, H.config_zero, H.config_special, H.config_printable, config_colorize_ascii
|
|
)
|
|
def load_color_scheme() -> None:
|
|
global color_scheme, printable
|
|
#
|
|
# We want to colorize the hex characters and only print out
|
|
# printable values on the right hand side.
|
|
#
|
|
color_scheme = {i: H.normal("%02x" % i) for i in range(256)}
|
|
printable = {i: H.normal(".") for i in range(256)}
|
|
|
|
for c in bytearray(
|
|
(string.ascii_letters + string.digits + string.punctuation).encode("utf-8", "ignore")
|
|
):
|
|
color_scheme[c] = H.printable("%02x" % c)
|
|
printable[c] = (
|
|
H.printable(f"{chr(c)}") if pwndbg.config.hexdump_colorize_ascii else f"{chr(c)}"
|
|
)
|
|
|
|
for c in bytearray(b"\x00"):
|
|
color_scheme[c] = H.zero("%02x" % c)
|
|
printable[c] = H.zero(".") if pwndbg.config.hexdump_colorize_ascii else "."
|
|
|
|
for c in bytearray(b"\xff\x7f\x80"):
|
|
color_scheme[c] = H.special("%02x" % c)
|
|
printable[c] = H.special(".") if pwndbg.config.hexdump_colorize_ascii else "."
|
|
|
|
color_scheme[-1] = " "
|
|
printable[-1] = " "
|
|
|
|
|
|
def hexdump(
|
|
data: bytes,
|
|
address: int = 0,
|
|
width: int = 16,
|
|
group_width: int = 4,
|
|
flip_group_endianness: bool = False,
|
|
skip: bool = True,
|
|
offset: int = 0,
|
|
size: int = 0,
|
|
count: int = 0,
|
|
repeat: bool = False,
|
|
dX_call: bool = False,
|
|
):
|
|
if not dX_call:
|
|
if not color_scheme or not printable:
|
|
load_color_scheme()
|
|
|
|
# If there's nothing to print, just print the offset and address and return
|
|
if not data:
|
|
yield H.offset(f"+{offset:04x} ") + H.address(f"{address:#08x} ")
|
|
|
|
# Don't allow iterating over this generator again
|
|
return
|
|
|
|
data = list(bytearray(data))
|
|
|
|
# Hexdump lines to skip_lines values and yields:
|
|
#
|
|
# <init: skip_lines = -1>
|
|
# line AAAA => skip_lines = 0 => yield "AAAA"
|
|
# line AAAA => skip_lines = 1 => <continue>
|
|
# line AAAA => skip_lines = -1 => yield "skipped ..." + "AAAA"
|
|
# line BBBB => skip_lines = -1 => yield "BBBB"
|
|
skip_lines = -1
|
|
|
|
config_separator_str = H.separator(str(config_separator))
|
|
config_byte_separator_str = str(config_byte_separator)
|
|
|
|
groupped = groupby(width, data, fill=-1)
|
|
before_last_idx = len(groupped) - 2
|
|
|
|
for i, line in enumerate(groupped):
|
|
# Handle skipping of identical lines (see skip_lines comment above)
|
|
if skip:
|
|
# Count lines to be skipped by checking next/future line
|
|
if i <= before_last_idx and line == groupped[i + 1]:
|
|
skip_lines += 1
|
|
|
|
# Since we count from -1 then 0 means we are on first line
|
|
# We want to yield that line, so we do not continue on that counter
|
|
if skip_lines != 0:
|
|
continue
|
|
|
|
elif skip_lines > 0:
|
|
out = f"... ↓ skipped {skip_lines} identical lines ({skip_lines * width} bytes)"
|
|
skip_lines = -1
|
|
yield out
|
|
# Fallthrough (do not continue) so we yield the current line too
|
|
|
|
increment = i * width
|
|
hexline = [
|
|
H.offset(f"+{offset + increment:04x} "),
|
|
H.address(f"{address + increment:#08x} "),
|
|
]
|
|
|
|
for group in groupby(group_width, line):
|
|
group = reversed(group) if flip_group_endianness else group
|
|
for idx, char in enumerate(group):
|
|
if flip_group_endianness and idx == group_width - 1:
|
|
hexline.append(H.highlight_group_lsb(color_scheme[char]))
|
|
else:
|
|
hexline.append(color_scheme[char])
|
|
hexline.append(config_byte_separator_str)
|
|
hexline.append(" ")
|
|
|
|
hexline.append(config_separator_str)
|
|
for group in groupby(group_width, line):
|
|
for char in group:
|
|
hexline.append(printable[char])
|
|
hexline.append(config_separator_str)
|
|
|
|
yield "".join(hexline)
|
|
|
|
else:
|
|
# Traditionally, windbg will display 16 bytes of data per line.
|
|
values = []
|
|
|
|
if repeat:
|
|
count = hexdump.last_count
|
|
address = hexdump.last_address
|
|
else:
|
|
address = int(address) & pwndbg.aglib.arch.ptrmask
|
|
count = int(count)
|
|
|
|
size_type = pwndbg.aglib.typeinfo.get_type(size)
|
|
|
|
for i in range(count):
|
|
try:
|
|
gval = pwndbg.aglib.memory.get_typed_pointer_value(size_type, address + i * size)
|
|
values.append(int(gval))
|
|
except pwndbg.dbg_mod.Error:
|
|
break
|
|
|
|
if not values:
|
|
print("Could not access the provided address")
|
|
return
|
|
|
|
n_rows = int(math.ceil(count * size / 16.0))
|
|
row_sz = 16 // size
|
|
rows = [values[i * row_sz : (i + 1) * row_sz] for i in range(n_rows)]
|
|
lines = []
|
|
|
|
for i, row in enumerate(rows):
|
|
if not row:
|
|
continue
|
|
line = [enhex(pwndbg.aglib.arch.ptrsize, address + (i * 16)), " "]
|
|
for value in row:
|
|
line.append(enhex(size, value))
|
|
lines.append(" ".join(line))
|
|
|
|
hexdump.last_count = count
|
|
hexdump.last_address = address + len(rows) * 16
|
|
|
|
yield lines
|