Repository: pwndbg/pwndbg

Issue:
`dX` commands truncate output longer than native word size
### Example
The screenshot below shows pwndbg commands issued when debugging an x86 program.
Note that some of the data printed by the `dd` command is omitted by the `dq` command:
![dX issue](https://user-images.githubusercontent.com/16000770/186494094-3c455a3d-7945-4aea-a919-b0a15105588e.png)

### Cause
This happens in the first line of `enhex()`, which is called by `dX()`:
https://github.com/pwndbg/pwndbg/blob/5d358585b1149aead6774f17c5721f10c4bed7be/pwndbg/commands/windbg.py#L137-L138

`value` is masked to the native word size, resulting in loss of information when `dX()` tries to print words longer than this, e.g. printing quadwords from an x86 process memory.

### Possible solution
Making the mask in `enhex()` fit the requested data width could fix this.
`pwndbg.arch.ptrmask` is calculated like so: https://github.com/pwndbg/pwndbg/blob/5d358585b1149aead6774f17c5721f10c4bed7be/pwndbg/arch.py#L53

So perhaps replacing the first line of `enhex()` with `value = value & (1 << 8*size) - 1` might work.


Discussion:

Resolving patch:
diff --git a/pwndbg/commands/windbg.py b/pwndbg/commands/windbg.py
--- a/pwndbg/commands/windbg.py
+++ b/pwndbg/commands/windbg.py
@@ -190,7 +190,7 @@ def dX(size, address, count, to_string=False, repeat=False):
 
 
 def enhex(size, value):
-    value = value & pwndbg.arch.ptrmask
+    value = value & ((1 << 8 * size) - 1)
     x = "%x" % abs(value)
     x = x.rjust(size * 2, "0")
     return x


Test patch:
diff --git a/tests/test_windbg.py b/tests/test_windbg.py
--- a/tests/test_windbg.py
+++ b/tests/test_windbg.py
@@ -4,6 +4,8 @@
 import tests
 
 MEMORY_BINARY = tests.binaries.get("memory.out")
+X86_BINARY = tests.binaries.get("gosample.x86")
+
 data_addr = "0x400081"
 
 
@@ -299,3 +301,67 @@ def test_windbg_eX_commands(start_binary):
 
     # Check if the write actually occurred
     assert pwndbg.memory.read(stack_last_qword_ea, 8) == b"\xef\xbe\xad\xde\xbe\xba\xfe\xca"
+
+
+def test_windbg_commands_x86(start_binary):
+    """
+    Tests windbg compatibility commands that dump memory
+    like dq, dw, db, ds etc.
+    """
+    start_binary(X86_BINARY)
+
+    # Prepare memory
+    pwndbg.memory.write(pwndbg.regs.esp, b"1234567890abcdef_")
+    pwndbg.memory.write(pwndbg.regs.esp + 16, b"\x00" * 16)
+    pwndbg.memory.write(pwndbg.regs.esp + 32, bytes(range(16)))
+    pwndbg.memory.write(pwndbg.regs.esp + 48, b"Z" * 16)
+
+    #################################################
+    #### dX command tests
+    #################################################
+    db = gdb.execute("db $esp", to_string=True).splitlines()
+    assert db == [
+        "%x     31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66" % pwndbg.regs.esp,
+        "%x     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" % (pwndbg.regs.esp + 16),
+        "%x     00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" % (pwndbg.regs.esp + 32),
+        "%x     5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a" % (pwndbg.regs.esp + 48),
+    ]
+
+    dw = gdb.execute("dw $esp", to_string=True).splitlines()
+    assert dw == [
+        "%x     3231 3433 3635 3837 3039 6261 6463 6665" % pwndbg.regs.esp,
+        "%x     0000 0000 0000 0000 0000 0000 0000 0000" % (pwndbg.regs.esp + 16),
+        "%x     0100 0302 0504 0706 0908 0b0a 0d0c 0f0e" % (pwndbg.regs.esp + 32),
+        "%x     5a5a 5a5a 5a5a 5a5a 5a5a 5a5a 5a5a 5a5a" % (pwndbg.regs.esp + 48),
+    ]
+
+    dd = gdb.execute("dd $esp", to_string=True).splitlines()
+    assert dd == [
+        "%x     34333231 38373635 62613039 66656463" % pwndbg.regs.esp,
+        "%x     00000000 00000000 00000000 00000000" % (pwndbg.regs.esp + 16),
+        "%x     03020100 07060504 0b0a0908 0f0e0d0c" % (pwndbg.regs.esp + 32),
+        "%x     5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a" % (pwndbg.regs.esp + 48),
+    ]
+
+    dq = gdb.execute("dq $esp", to_string=True).splitlines()
+    assert dq == [
+        "%x     3837363534333231 6665646362613039" % pwndbg.regs.esp,
+        "%x     0000000000000000 0000000000000000" % (pwndbg.regs.esp + 16),
+        "%x     0706050403020100 0f0e0d0c0b0a0908" % (pwndbg.regs.esp + 32),
+        "%x     5a5a5a5a5a5a5a5a 5a5a5a5a5a5a5a5a" % (pwndbg.regs.esp + 48),
+    ]
+
+    #################################################
+    #### eX command tests
+    #################################################
+    gdb.execute("eb $esp 00")
+    assert pwndbg.memory.read(pwndbg.regs.esp, 1) == b"\x00"
+
+    gdb.execute("ew $esp 4141")
+    assert pwndbg.memory.read(pwndbg.regs.esp, 2) == b"\x41\x41"
+
+    gdb.execute("ed $esp 5252525252")
+    assert pwndbg.memory.read(pwndbg.regs.esp, 4) == b"\x52" * 4
+
+    gdb.execute("eq $esp 1122334455667788")
+    assert pwndbg.memory.read(pwndbg.regs.esp, 8) == b"\x88\x77\x66\x55\x44\x33\x22\x11"