Skip to content

Commit

Permalink
print-format and patch byte commands can work together (#843)
Browse files Browse the repository at this point in the history
  • Loading branch information
therealdreg committed Jun 24, 2022
1 parent f890579 commit 1e8f55f
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 10 deletions.
9 changes: 9 additions & 0 deletions docs/commands/patch.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,12 @@ gef➤ patch byte $eip 0x90
gef➤ patch string $eip "cool!"
```

These commands copy the first 10 bytes of $rsp+8 to $rip:

```
gef➤ print-format --lang bytearray -l 10 $rsp+8
Saved data b'\xcb\xe3\xff\xff\xff\x7f\x00\x00\x00\x00'... in '$_gef0'
gef➤ patch byte $rip $_gef0
```

Very handy to copy-paste-execute shellcodes/data from different memory regions.
13 changes: 13 additions & 0 deletions docs/commands/print-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ The command `print-format` (alias `pf`) will dump an arbitrary location as an ar
- Assembly (`asm`)
- Javascript (`js`)
- Hex string (`hex`)
- For patch byte command or GDB $_gef[N] byte access (`bytearray`)


```
Expand All @@ -26,3 +27,15 @@ gef➤ print-format --lang py --bitlen 8 -l 10 --clip $rsp
[+] Copied to clipboard
buf = [0x87, 0xfa, 0xa3, 0xf7, 0xff, 0x7f, 0x0, 0x0, 0x30, 0xe6]
```

These commands copy the first 10 bytes of $rsp+8 to $rip:

```
gef➤ print-format --lang bytearray -l 10 $rsp+8
Saved data b'\xcb\xe3\xff\xff\xff\x7f\x00\x00\x00\x00'... in '$_gef0'
gef➤ display/x $_gef0[5]
4: /x $_gef0[5] = 0x7f
gef➤ patch byte $rip $_gef0
```

Very handy to copy-paste-execute shellcodes/data from different memory regions.
41 changes: 31 additions & 10 deletions gef.py
Original file line number Diff line number Diff line change
Expand Up @@ -3766,12 +3766,18 @@ def dereference(addr: int) -> Optional["gdb.Value"]:
return None


def gef_convenience(value: str) -> str:
def gef_convenience(value: Union[str, bytes]) -> str:
"""Defines a new convenience value."""
global gef
var_name = f"$_gef{gef.session.convenience_vars_index:d}"
gef.session.convenience_vars_index += 1
gdb.execute(f"""set {var_name} = "{value}" """)
if isinstance(value, str):
gdb.execute(f"""set {var_name} = "{value}" """)
elif isinstance(value, bytes):
value_as_array = "{" + ", ".join(["%#.02x" % x for x in value]) + "}"
gdb.execute(f"""set {var_name} = {value_as_array} """)
else:
raise TypeError
return var_name


Expand Down Expand Up @@ -4541,7 +4547,7 @@ def do_invoke(self, argv: List[str]) -> None:
class PrintFormatCommand(GenericCommand):
"""Print bytes format in commonly used formats, such as literals in high level languages."""

valid_formats = ("py", "c", "js", "asm", "hex")
valid_formats = ("py", "c", "js", "asm", "hex", "bytearray")
valid_bitness = (8, 16, 32, 64)

_cmdline_ = "print-format"
Expand Down Expand Up @@ -4590,12 +4596,17 @@ def do_invoke(self, _: List[str], **kwargs: Any) -> None:
fmt = self.format_matrix[args.bitlen][0]
data = []

for addr in range(start_addr, end_addr, size):
value = struct.unpack(fmt, gef.memory.read(addr, size))[0]
data += [value]
sdata = ", ".join(map(hex, data))

if args.lang == "py":
if args.lang != "bytearray":
for addr in range(start_addr, end_addr, size):
value = struct.unpack(fmt, gef.memory.read(addr, size))[0]
data += [value]
sdata = ", ".join(map(hex, data))

if args.lang == "bytearray":
data = gef.memory.read(start_addr, args.length)
preview = str(data[0:10])
out = f"Saved data {preview}... in '{gef_convenience(data)}'"
elif args.lang == "py":
out = f"buf = [{sdata}]"
elif args.lang == "c":
c_type = self.format_matrix[args.bitlen][1]
Expand Down Expand Up @@ -8107,9 +8118,19 @@ def do_invoke(self, _: List[str], **kwargs: Any) -> None:

addr = align_address(parse_address(args.location))
size, fcode = self.SUPPORTED_SIZES[self.format]
values = args.values

if size == 1:
if values[0].startswith("$_gef"):
var_name = values[0]
try:
values = str(gdb.parse_and_eval(var_name)).lstrip("{").rstrip("}").replace(",","").split(" ")
except:
gef_print(f"Bad variable specified, check value with command: p {var_name}")
return

d = str(gef.arch.endianness)
for value in args.values:
for value in values:
value = parse_address(value) & ((1 << size * 8) - 1)
vstr = struct.pack(d + fcode, value)
gef.memory.write(addr, vstr, length=size)
Expand Down
4 changes: 4 additions & 0 deletions tests/commands/patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ def test_cmd_patch_byte(self):
self.assertNoException(res)
self.assertRegex(res, r"0xcc\s*0x[^c]{2}")

def test_cmd_patch_byte_bytearray(self):
res = gdb_start_silent_cmd_last_line("set $_gef69 = { 0xcc, 0xcc }", after=["patch byte $pc $_gef69", "display/8bx $pc",])
self.assertNoException(res)
self.assertRegex(res, r"(0xcc\s*)(\1)0x[^c]{2}")

def test_cmd_patch_word(self):
res = gdb_start_silent_cmd_last_line("patch word $pc 0xcccc", after=["display/8bx $pc",])
Expand Down
15 changes: 15 additions & 0 deletions tests/commands/print_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,18 @@ def test_cmd_print_format(self):
res = gdb_start_silent_cmd("print-format --lang iDontExist $sp")
self.assertNoException(res)
self.assertTrue("Language must be in:" in res)


def test_cmd_print_format_bytearray(self):
res = gdb_start_silent_cmd("set *((int*)$sp) = 0x41414141",
after=["print-format --lang bytearray -l 4 $sp"])
self.assertNoException(res)
try:
gef_var = res.split('$_gef')[1].split("'")[0]
except:
self.assertTrue(False)
self.assertTrue("\x41\x41\x41\x41" in res)
res = gdb_start_silent_cmd("set *((int*)$sp) = 0x41414141",
after=["print-format --lang bytearray -l 4 $sp", "p $_gef" + gef_var])
self.assertNoException(res)
self.assertTrue("0x41, 0x41, 0x41, 0x41" in res)

0 comments on commit 1e8f55f

Please sign in to comment.