diff --git a/docs/commands/memory/hexdump.md b/docs/commands/memory/hexdump.md index 9b6c3ffc9..112962b38 100644 --- a/docs/commands/memory/hexdump.md +++ b/docs/commands/memory/hexdump.md @@ -2,7 +2,7 @@ # hexdump ```text -usage: hexdump [-h] [address] [count] +usage: hexdump [-h] [-C [{py,c}]] [address] [count] ``` @@ -19,6 +19,7 @@ Hexdumps data at the specified address or module name. |Short|Long|Help| | :--- | :--- | :--- | |-h|--help|show this help message and exit| +|-C|--code|Output as Python or C code data definition (default: py)| diff --git a/pwndbg/commands/hexdump.py b/pwndbg/commands/hexdump.py index ca73c6db1..246ac2640 100644 --- a/pwndbg/commands/hexdump.py +++ b/pwndbg/commands/hexdump.py @@ -53,6 +53,24 @@ def address_or_module_name(s) -> int: raise argparse.ArgumentTypeError("Unknown hexdump argument type.") +def format_c(data: bytes) -> str: + toks = [f"{b:#02x}" for b in data] + lines = [] + for i in range(0, len(toks), 16): + elem = ", ".join(toks[i : i + 16]) + lines.append(f" {elem},") + body = "\n".join(lines) + return f"static const unsigned char data[] = {{\n{body}\n}};\n" + + +def format_py(data: bytes) -> str: + lines = [] + for i in range(0, len(data), 16): + seg = "".join(f"\\x{b:02x}" for b in data[i : i + 16]) + lines.append(f' b"{seg}"') + return "data = (\n{}\n)\n".format("\n".join(lines)) + + parser = argparse.ArgumentParser( description="Hexdumps data at the specified address or module name." ) @@ -66,11 +84,20 @@ parser.add_argument( parser.add_argument( "count", nargs="?", default=pwndbg.config.hexdump_bytes, help="Number of bytes to dump" ) +parser.add_argument( + "-C", + "--code", + type=str, + nargs="?", + const="py", + choices=("py", "c"), + help="Output as Python or C code data definition (default: py)", +) @pwndbg.commands.Command(parser, category=CommandCategory.MEMORY) @pwndbg.commands.OnlyWhenRunning -def hexdump(address, count=pwndbg.config.hexdump_bytes) -> None: +def hexdump(address, count=pwndbg.config.hexdump_bytes, code: str | None = None) -> None: if hexdump.repeat: address = hexdump.last_address else: @@ -129,19 +156,23 @@ def hexdump(address, count=pwndbg.config.hexdump_bytes) -> None: print(e) return - result = pwndbg.hexdump.hexdump( - data, - address=address, - width=width, - group_width=group_width, - flip_group_endianness=flip_group_endianness, - offset=hexdump.offset, - ) - - for line in result: - print(line) + if code: + source = format_py(data) if code == "py" else format_c(data) + print(source) + hexdump.offset += len(data) + else: + result = pwndbg.hexdump.hexdump( + data, + address=address, + width=width, + group_width=group_width, + flip_group_endianness=flip_group_endianness, + offset=hexdump.offset, + ) + for line in result: + print(line) - hexdump.offset += count + hexdump.offset += len(data) hexdump.last_address = 0 diff --git a/tests/library/gdb/tests/test_hexdump.py b/tests/library/gdb/tests/test_hexdump.py index 4328bbf2c..bb0c4d3a5 100644 --- a/tests/library/gdb/tests/test_hexdump.py +++ b/tests/library/gdb/tests/test_hexdump.py @@ -158,3 +158,39 @@ def test_hexdump_limit_check(start_binary): # Reset to default for subsequent tests if any gdb.execute(f"set hexdump-limit-mb {default_limit_mb}") + + +def test_hexdump_code_py_format(start_binary): + start_binary(BINARY) + sp = pwndbg.aglib.regs.rsp + + pwndbg.aglib.memory.write(sp, b"abcdefgh\x01\x02\x03\x04\x05\x06\x07\x08" * 16) + + SIZE = 21 + out = gdb.execute(f"hexdump -C py $rsp {SIZE}", to_string=True) + + expected = ( + "data = (\n" + ' b"\\x61\\x62\\x63\\x64\\x65\\x66\\x67\\x68\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08"\n' + ' b"\\x61\\x62\\x63\\x64\\x65"\n' + ")\n" + ) + assert out.rstrip("\n") == expected.rstrip("\n") + + +def test_hexdump_code_c_format(start_binary): + start_binary(BINARY) + sp = pwndbg.aglib.regs.rsp + + pwndbg.aglib.memory.write(sp, b"abcdefgh\x01\x02\x03\x04\x05\x06\x07\x08" * 16) + + SIZE = 21 + out = gdb.execute(f"hexdump -C c $rsp {SIZE}", to_string=True) + + expected = ( + "static const unsigned char data[] = {\n" + " 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,\n" + " 0x61, 0x62, 0x63, 0x64, 0x65,\n" + "};\n" + ) + assert out.rstrip("\n") == expected.rstrip("\n")