From 2462468f8925ab7065fa50619faa1634468ae082 Mon Sep 17 00:00:00 2001 From: Disconnect3d Date: Mon, 16 Jan 2023 04:01:24 +0100 Subject: [PATCH] Improve hexdump collapse display (#1526) --- pwndbg/hexdump.py | 64 ++++++++++++++++++--------- tests/gdb-tests/tests/test_hexdump.py | 24 ++++++++++ 2 files changed, 66 insertions(+), 22 deletions(-) diff --git a/pwndbg/hexdump.py b/pwndbg/hexdump.py index cf990468c..7348b1b6b 100644 --- a/pwndbg/hexdump.py +++ b/pwndbg/hexdump.py @@ -94,23 +94,44 @@ def hexdump( return data = list(bytearray(data)) - last_line = None - skipping = False - for i, line in enumerate(groupby(width, data, fill=-1)): - if skip and line == last_line: - if skipping: - continue - skipping = True - yield "..." - else: - skipping = False - last_line = line - - hexline = [] - - hexline.append(H.offset("+%04x " % ((i + offset) * width))) - hexline.append(H.address("%#08x " % (address + (i * width)))) + # Hexdump lines to skip_lines values and yields: + # + # + # line AAAA => skip_lines = 0 => yield "AAAA" + # line AAAA => skip_lines = 1 => + # 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 + + hexline = [ + H.offset("+%04x " % ((i + offset) * width)), + H.address("%#08x " % (address + (i * width))), + ] for group in groupby(group_width, line): group = reversed(group) if flip_group_endianess else group @@ -119,19 +140,18 @@ def hexdump( hexline.append(H.highlight_group_lsb(color_scheme[char])) else: hexline.append(color_scheme[char]) - hexline.append(str(config_byte_separator)) + hexline.append(config_byte_separator_str) hexline.append(" ") - hexline.append(H.separator("%s" % config_separator)) + hexline.append(config_separator_str) for group in groupby(group_width, line): for char in group: hexline.append(printable[char]) - hexline.append(H.separator("%s" % config_separator)) + hexline.append(config_separator_str) yield "".join(hexline) else: - # Traditionally, windbg will display 16 bytes of data per line. values = [] @@ -155,8 +175,8 @@ def hexdump( print("Could not access the provided address") return - n_rows = int(math.ceil(count * size / float(16))) - row_sz = int(16 / size) + 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 = [] diff --git a/tests/gdb-tests/tests/test_hexdump.py b/tests/gdb-tests/tests/test_hexdump.py index 505e95be2..18f06c85a 100644 --- a/tests/gdb-tests/tests/test_hexdump.py +++ b/tests/gdb-tests/tests/test_hexdump.py @@ -56,3 +56,27 @@ def test_hexdump(start_binary): f"""+0000 0x{stack_addr:x} 616161 │aaa │ │\n""", ] run_tests(stack_addr, False, expected) + + +def test_hexdump_collapse_lines(start_binary): + start_binary(BINARY) + sp = pwndbg.gdblib.regs.rsp + + pwndbg.gdblib.memory.write(sp, b"abcdefgh\x01\x02\x03\x04\x05\x06\x07\x08" * 16) + + def hexdump_lines(lines): + offset = (lines - 1) * 0x10 # last line offset + skipped_lines = lines - 2 + + out = gdb.execute(f"hexdump $rsp {offset+16}", to_string=True) + + expected = ( + f"+0000 0x{sp:x} 61 62 63 64 65 66 67 68 01 02 03 04 05 06 07 08 │abcdefgh│........│\n" + f"... ↓ skipped {skipped_lines} identical lines ({skipped_lines*16} bytes)\n" + f"+{offset:04x} 0x{sp+offset:x} 61 62 63 64 65 66 67 68 01 02 03 04 05 06 07 08 │abcdefgh│........│\n" + ) + assert out == expected + + hexdump_lines(3) + hexdump_lines(4) + hexdump_lines(10)