diff --git a/pwndbg/aglib/memory.py b/pwndbg/aglib/memory.py index 4e6c88ebd..c2735bbe4 100644 --- a/pwndbg/aglib/memory.py +++ b/pwndbg/aglib/memory.py @@ -65,8 +65,8 @@ def write(addr: int, data: str | bytes | bytearray) -> None: pwndbg.dbg.selected_inferior().write_memory(address=addr, data=bytearray(data), partial=False) -def peek(address: int) -> str | None: - """peek(address) -> str +def peek(address: int) -> bytearray | None: + """peek(address) -> bytearray Read one byte from the specified address. @@ -74,11 +74,11 @@ def peek(address: int) -> str | None: address(int): Address to read Returns: - :class:`str`: A single byte of data, or ``None`` if the + :class:`bytearray`: A single byte of data, or ``None`` if the address cannot be read. """ try: - return chr(read(address, 1)[0]) + return read(address, 1) except Exception: pass return None diff --git a/pwndbg/gdblib/memory.py b/pwndbg/gdblib/memory.py index b9b23eaf3..5b04fe079 100644 --- a/pwndbg/gdblib/memory.py +++ b/pwndbg/gdblib/memory.py @@ -111,8 +111,8 @@ def write(addr: int, data: str | bytes | bytearray) -> None: gdb.selected_inferior().write_memory(addr, data) -def peek(address: int) -> str | None: - """peek(address) -> str +def peek(address: int) -> bytearray | None: + """peek(address) -> bytearray Read one byte from the specified address. @@ -120,11 +120,11 @@ def peek(address: int) -> str | None: address(int): Address to read Returns: - :class:`str`: A single byte of data, or ``None`` if the + :class:`bytearray`: A single byte of data, or ``None`` if the address cannot be read. """ try: - return chr(read(address, 1)[0]) + return read(address, 1) except Exception: pass return None diff --git a/tests/gdb-tests/tests/test_memory.py b/tests/gdb-tests/tests/test_memory.py index 9d0492035..77fd3eb63 100644 --- a/tests/gdb-tests/tests/test_memory.py +++ b/tests/gdb-tests/tests/test_memory.py @@ -35,6 +35,55 @@ def test_memory_read_write(start_binary): ) +def test_memory_peek_poke(start_binary): + """ + This tests ensures that doing a peek, poke, peek round-robin operations + ends up with the same value in memory. + It also sets a given byte in memory via memory.write + and tests all single-byte values. + """ + start_binary(REFERENCE_BINARY) + + # Address 0 is not mapped, so peek should return None + # and poke should fail + assert pwndbg.gdblib.memory.poke(0) is False + assert pwndbg.gdblib.memory.peek(0) is None + + stack_addr = pwndbg.gdblib.regs.rsp + + for v in range(256): + data = bytearray([v, 0, 0, 0]) + pwndbg.gdblib.memory.write(stack_addr, data) + + # peek should return the first byte + assert pwndbg.gdblib.memory.peek(stack_addr) == bytearray([v]) + + # Now poke it! + assert pwndbg.gdblib.memory.poke(stack_addr) is True + + # Now make sure poke did not change the underlying bytes + assert pwndbg.gdblib.memory.read(stack_addr, 4) == data + + # TODO/FIXME: Fix peek/poke exception handling and uncomment this! + """ + # Ensure that peek and poke correctly raises exceptions + # when incorrect argument type is passed + for not_parsable_as_int in (b"asdf", "asdf"): + with pytest.raises(ValueError): + pwndbg.gdblib.memory.peek(not_parsable_as_int) + + for not_parsable_as_int in (b"asdf", "asdf"): + with pytest.raises(ValueError): + pwndbg.gdblib.memory.poke(not_parsable_as_int) + """ + + # Acceptable inputs (not great; maybe we should ban them?) + # Note: they return 0 because the address 0 is not mapped + assert pwndbg.gdblib.memory.peek(0.0) is None + assert pwndbg.gdblib.memory.peek("0") is None + assert pwndbg.gdblib.memory.peek(b"0") is None + + def test_fetch_struct_as_dictionary(start_binary): """ Test pwndbg.gdblib.memory.fetch_struct_as_dictionary()