pwndbg/tests/test_windbg.py

370 lines
15 KiB
Python

import gdb
import pwndbg
import tests
MEMORY_BINARY = tests.binaries.get("memory.out")
X86_BINARY = tests.binaries.get("gosample.x86")
data_addr = "0x400081"
def test_windbg_dX_commands(start_binary):
"""
Tests windbg compatibility commands that dump memory
like dq, dw, db, ds etc.
"""
start_binary(MEMORY_BINARY)
# Try to fail commands in different way
for cmd_prefix in ("dq", "dd", "dw", "db"):
# With a non-existent symbol
cmd = cmd_prefix + " nonexistentsymbol"
assert gdb.execute(cmd, to_string=True) == (
"usage: XX [-h] address [count]\n"
"XX: error: argument address: invalid HexOrAddressExpr value: 'nonexistentsymbol'\n"
).replace("XX", cmd_prefix)
# With an invalid/unmapped address
cmd = cmd_prefix + " 0"
assert gdb.execute(cmd, to_string=True) == "Could not access the provided address\n"
#################################################
#### dq command tests
#################################################
# Try `dq` with symbol, &symbol, 0x<address> and <address> without 0x prefix (treated as hex!)
dq1 = gdb.execute("dq data", to_string=True)
dq2 = gdb.execute("dq &data", to_string=True)
dq3 = gdb.execute("dq %s" % data_addr, to_string=True)
dq4 = gdb.execute("dq %s" % data_addr.replace("0x", ""), to_string=True)
assert (
dq1
== dq2
== dq3
== dq4
== (
"0000000000400081 0000000000000000 0000000000000001\n"
"0000000000400091 0000000100000002 0001000200030004\n"
"00000000004000a1 0102030405060708 1122334455667788\n"
"00000000004000b1 0123456789abcdef 0000000000000000\n"
)
)
# Try `dq` with different counts
dq_count1 = gdb.execute("dq data 2", to_string=True)
dq_count2 = gdb.execute("dq &data 2", to_string=True)
dq_count3 = gdb.execute("dq %s 2" % data_addr, to_string=True)
assert (
dq_count1
== dq_count2
== dq_count3
== "0000000000400081 0000000000000000 0000000000000001\n"
)
assert gdb.execute("dq data 1", to_string=True) == "0000000000400081 0000000000000000\n"
assert gdb.execute("dq data 3", to_string=True) == (
"0000000000400081 0000000000000000 0000000000000001\n"
"0000000000400091 0000000100000002\n"
)
# Try 'dq' with count equal to a register, but lets set it before ;)
# also note that we use `data2` here
assert gdb.execute("set $eax=4", to_string=True) == "" # assert as a sanity check
assert gdb.execute("dq data2 $eax", to_string=True) == (
"00000000004000a9 1122334455667788 0123456789abcdef\n"
"00000000004000b9 0000000000000000 ffffffffffffffff\n"
)
# See if we can repeat dq command (use count for shorter data)
assert gdb.execute("dq data2 2", to_string=True) == (
"00000000004000a9 1122334455667788 0123456789abcdef\n"
)
# TODO/FIXME: Can we test command repeating here? Neither passing `from_tty=True`
# or setting `pwndbg.commands.windbg.dq.repeat = True` works here
# assert gdb.execute('dq data2 2', to_string=True, from_tty=True) == (
# '00000000004000b9 0000000000000000 ffffffffffffffff\n'
# )
#################################################
#### dd command tests
#################################################
dd1 = gdb.execute("dd data", to_string=True)
dd2 = gdb.execute("dd &data", to_string=True)
dd3 = gdb.execute("dd %s" % data_addr, to_string=True)
dd4 = gdb.execute("dd %s" % data_addr.replace("0x", ""), to_string=True)
assert (
dd1
== dd2
== dd3
== dd4
== (
"0000000000400081 00000000 00000000 00000001 00000000\n"
"0000000000400091 00000002 00000001 00030004 00010002\n"
"00000000004000a1 05060708 01020304 55667788 11223344\n"
"00000000004000b1 89abcdef 01234567 00000000 00000000\n"
)
)
# count tests
assert gdb.execute("dd data 4", to_string=True) == (
"0000000000400081 00000000 00000000 00000001 00000000\n"
)
assert gdb.execute("dd data 3", to_string=True) == (
"0000000000400081 00000000 00000000 00000001\n"
)
#################################################
#### dw command tests
#################################################
dw1 = gdb.execute("dw data", to_string=True)
dw2 = gdb.execute("dw &data", to_string=True)
dw3 = gdb.execute("dw %s" % data_addr, to_string=True)
dw4 = gdb.execute("dw %s" % data_addr.replace("0x", ""), to_string=True)
assert (
dw1
== dw2
== dw3
== dw4
== (
"0000000000400081 0000 0000 0000 0000 0001 0000 0000 0000\n"
"0000000000400091 0002 0000 0001 0000 0004 0003 0002 0001\n"
"00000000004000a1 0708 0506 0304 0102 7788 5566 3344 1122\n"
"00000000004000b1 cdef 89ab 4567 0123 0000 0000 0000 0000\n"
)
)
# count tests
assert gdb.execute("dw data 8", to_string=True) == (
"0000000000400081 0000 0000 0000 0000 0001 0000 0000 0000\n"
)
assert gdb.execute("dw data 8/2", to_string=True) == (
"0000000000400081 0000 0000 0000 0000\n"
)
assert gdb.execute("dw data $eax", to_string=True) == (
"0000000000400081 0000 0000 0000 0000\n"
)
#################################################
#### db command tests
#################################################
db1 = gdb.execute("db data", to_string=True)
db2 = gdb.execute("db &data", to_string=True)
db3 = gdb.execute("db %s" % data_addr, to_string=True)
db4 = gdb.execute("db %s" % data_addr.replace("0x", ""), to_string=True)
assert (
db1
== db2
== db3
== db4
== (
"0000000000400081 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00\n"
"0000000000400091 02 00 00 00 01 00 00 00 04 00 03 00 02 00 01 00\n"
"00000000004000a1 08 07 06 05 04 03 02 01 88 77 66 55 44 33 22 11\n"
"00000000004000b1 ef cd ab 89 67 45 23 01 00 00 00 00 00 00 00 00\n"
)
)
# count tests
assert gdb.execute("db data 31", to_string=True) == (
"0000000000400081 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00\n"
"0000000000400091 02 00 00 00 01 00 00 00 04 00 03 00 02 00 01\n"
)
assert gdb.execute("db data $ax", to_string=True) == ("0000000000400081 00 00 00 00\n")
#################################################
#### dc command tests
#################################################
dc1 = gdb.execute("dc data", to_string=True)
dc2 = gdb.execute("dc &data", to_string=True)
dc3 = gdb.execute("dc %s" % data_addr, to_string=True)
dc4 = gdb.execute("dc %s" % data_addr.replace("0x", ""), to_string=True)
assert (
dc1
== dc2
== dc3
== dc4
== (
"+0000 0x400081 00 00 00 00 00 00 00 00 "
"│....│....│ │ │\n"
)
)
assert gdb.execute("dc data 3", to_string=True) == (
"+0000 0x400081 00 00 00 │... "
"│ │ │ │\n"
)
#################################################
#### ds command tests
#################################################
ds1 = gdb.execute("ds short_str", to_string=True)
ds2 = gdb.execute("ds &short_str", to_string=True)
ds3 = gdb.execute("ds 0x4000d9", to_string=True)
ds4 = gdb.execute("ds 4000d9", to_string=True)
assert ds1 == ds2 == ds3 == ds4 == "4000d9 'some cstring here'\n"
# Check too low maxlen
assert gdb.execute("ds short_str 5", to_string=True) == (
"Max str len of 5 too low, changing to 256\n" "4000d9 'some cstring here'\n"
)
# Check output for a string longer than (the default) maxlen of 256
assert gdb.execute("ds long_str", to_string=True) == (
"4000eb 'long string: "
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...'\n"
)
# Check impossible address
assert gdb.execute("ds 0", to_string=True) == (
"Data at address can't be dereferenced or is not a printable null-terminated "
"string or is too short.\n"
"Perhaps try: db <address> <count> or hexdump <address>\n"
)
def test_windbg_eX_commands(start_binary):
"""
Tests windbg compatibility commands that write to memory
like eq, ed, ew, eb etc.
"""
start_binary(MEMORY_BINARY)
# Try to fail commands in different way
for cmd_prefix in ("eq", "ed", "ew", "eb"):
# With a non-existent symbol
cmd = cmd_prefix + " nonexistentsymbol"
# Seems there is some mismatch between Python 3.x argparse output
expected_in = (
# This version occurred locally when tested on Python 3.9.5
(
"usage: XX [-h] address [data ...]\n"
"XX: error: argument address: invalid HexOrAddressExpr value: 'nonexistentsymbol'\n"
).replace("XX", cmd_prefix),
# This version occurs on CI on Python 3.8.10
(
"usage: XX [-h] address [data [data ...]]\n"
"XX: error: argument address: invalid HexOrAddressExpr value: 'nonexistentsymbol'\n"
).replace("XX", cmd_prefix),
)
assert gdb.execute(cmd, to_string=True) in expected_in
assert gdb.execute(cmd, to_string=True) in expected_in
# With no data arguments provided
cmd = cmd_prefix + " 0"
assert gdb.execute(cmd, to_string=True) == "Cannot write empty data into memory.\n"
# With invalid/unmapped address 0
cmd = cmd_prefix + " 0 1122"
assert gdb.execute(cmd, to_string=True) == ("Cannot access memory at address 0x0\n")
# With invalid data which can't be parsed as hex
cmd = cmd_prefix + " 0 x"
assert gdb.execute(cmd, to_string=True) == (
"Incorrect data format: it must all be a hex value (0x1234 or 1234, both "
"interpreted as 0x1234)\n"
)
#########################################
### Test eq write
#########################################
assert gdb.execute("eq $sp 0xcafebabe", to_string=True) == ""
assert "0x00000000cafebabe" in gdb.execute("x/xg $sp", to_string=True)
assert gdb.execute("eq $sp 0xbabe 0xcafe", to_string=True) == ""
assert "0x000000000000babe\t0x000000000000cafe" in gdb.execute("x/2xg $sp", to_string=True)
assert gdb.execute("eq $sp cafe000000000000 babe000000000000", to_string=True) == ""
assert "0xcafe000000000000\t0xbabe000000000000" in gdb.execute("x/2xg $sp", to_string=True)
# TODO/FIXME: implement tests for others (ed, ew, eb etc)
#########################################
### Test write & output on partial write
#########################################
# e.g. when we make a write to the last stack address
stack_ea = pwndbg.gdblib.regs[pwndbg.gdblib.regs.stack]
stack_page = pwndbg.vmmap.find(stack_ea)
# Last possible address on stack where we can perform an 8-byte write
stack_last_qword_ea = stack_page.end - 8
gdb_result = gdb.execute(
"eq %#x 0xCAFEBABEdeadbeef 0xABCD" % stack_last_qword_ea, to_string=True
).split("\n")
assert "Cannot access memory at address" in gdb_result[0]
assert gdb_result[1] == "(Made 1 writes to memory; skipping further writes)"
# Check if the write actually occurred
assert pwndbg.gdblib.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.gdblib.memory.write(pwndbg.gdblib.regs.esp, b"1234567890abcdef_")
pwndbg.gdblib.memory.write(pwndbg.gdblib.regs.esp + 16, b"\x00" * 16)
pwndbg.gdblib.memory.write(pwndbg.gdblib.regs.esp + 32, bytes(range(16)))
pwndbg.gdblib.memory.write(pwndbg.gdblib.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.gdblib.regs.esp,
"%x 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" % (pwndbg.gdblib.regs.esp + 16),
"%x 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" % (pwndbg.gdblib.regs.esp + 32),
"%x 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a" % (pwndbg.gdblib.regs.esp + 48),
]
dw = gdb.execute("dw $esp", to_string=True).splitlines()
assert dw == [
"%x 3231 3433 3635 3837 3039 6261 6463 6665" % pwndbg.gdblib.regs.esp,
"%x 0000 0000 0000 0000 0000 0000 0000 0000" % (pwndbg.gdblib.regs.esp + 16),
"%x 0100 0302 0504 0706 0908 0b0a 0d0c 0f0e" % (pwndbg.gdblib.regs.esp + 32),
"%x 5a5a 5a5a 5a5a 5a5a 5a5a 5a5a 5a5a 5a5a" % (pwndbg.gdblib.regs.esp + 48),
]
dd = gdb.execute("dd $esp", to_string=True).splitlines()
assert dd == [
"%x 34333231 38373635 62613039 66656463" % pwndbg.gdblib.regs.esp,
"%x 00000000 00000000 00000000 00000000" % (pwndbg.gdblib.regs.esp + 16),
"%x 03020100 07060504 0b0a0908 0f0e0d0c" % (pwndbg.gdblib.regs.esp + 32),
"%x 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a" % (pwndbg.gdblib.regs.esp + 48),
]
dq = gdb.execute("dq $esp", to_string=True).splitlines()
assert dq == [
"%x 3837363534333231 6665646362613039" % pwndbg.gdblib.regs.esp,
"%x 0000000000000000 0000000000000000" % (pwndbg.gdblib.regs.esp + 16),
"%x 0706050403020100 0f0e0d0c0b0a0908" % (pwndbg.gdblib.regs.esp + 32),
"%x 5a5a5a5a5a5a5a5a 5a5a5a5a5a5a5a5a" % (pwndbg.gdblib.regs.esp + 48),
]
#################################################
#### eX command tests
#################################################
gdb.execute("eb $esp 00")
assert pwndbg.gdblib.memory.read(pwndbg.gdblib.regs.esp, 1) == b"\x00"
gdb.execute("ew $esp 4141")
assert pwndbg.gdblib.memory.read(pwndbg.gdblib.regs.esp, 2) == b"\x41\x41"
gdb.execute("ed $esp 5252525252")
assert pwndbg.gdblib.memory.read(pwndbg.gdblib.regs.esp, 4) == b"\x52" * 4
gdb.execute("eq $esp 1122334455667788")
assert (
pwndbg.gdblib.memory.read(pwndbg.gdblib.regs.esp, 8) == b"\x88\x77\x66\x55\x44\x33\x22\x11"
)