add patch command (#1150)

* add patch command

This commit adds the `patch`, `patch_list` and `patch_revert` commands
and adds the `pwntools==4.8.0` as Pwndbg dependency.

The current implementation could be further improved by:
- adding tests :)
- maybe moving `patch_list` and `patch_revert` to `patch --list` and
  `patch --revert` flags?
- better handling of incorrect args/pwnlib exceptions

* lint
This commit is contained in:
Disconnect3d 2022-09-19 15:30:45 -07:00 committed by GitHub
parent cc3f86d5bc
commit 446f5d5ae2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 86 additions and 1 deletions

View File

@ -70,4 +70,6 @@ if encoding != "UTF-8":
print("Make sure that en_US.UTF-8 is activated in /etc/locale.gen and you called locale-gen")
print("******")
environ["PWNLIB_NOTERM"] = "1"
import pwndbg # noqa: F401

View File

@ -31,6 +31,7 @@ import pwndbg.commands.misc
import pwndbg.commands.mprotect
import pwndbg.commands.next
import pwndbg.commands.p2p
import pwndbg.commands.patch
import pwndbg.commands.peda
import pwndbg.commands.pie
import pwndbg.commands.probeleak

81
pwndbg/commands/patch.py Normal file
View File

@ -0,0 +1,81 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import argparse
from pwnlib.asm import asm
from pwnlib.asm import disasm
import pwndbg.color.message as message
import pwndbg.commands
import pwndbg.gdblib.memory
import pwndbg.lib.memoize
# Keep old patches made so we can revert them
patches = {}
parser = argparse.ArgumentParser(description="Patches given instruction with given code or bytes")
parser.add_argument("address", type=int, help="The address to patch")
parser.add_argument("ins", type=str, help="instruction[s]")
@pwndbg.commands.ArgparsedCommand(parser)
@pwndbg.commands.OnlyWhenRunning
def patch(address, ins):
new_mem = asm(ins, arch=pwndbg.gdblib.arch.current)
old_mem = pwndbg.gdblib.memory.read(address, len(new_mem))
patches[address] = (old_mem, new_mem)
pwndbg.gdblib.memory.write(address, new_mem)
pwndbg.lib.memoize.reset()
parser2 = argparse.ArgumentParser(description="Revert patch at given address")
parser2.add_argument("address", type=int, help="Address to revert patch on")
@pwndbg.commands.ArgparsedCommand(parser2)
@pwndbg.commands.OnlyWhenRunning
def patch_revert(address):
if not patches:
print(message.notice("No patches to revert"))
return
if address == -1:
for addr, (old, _new) in patches.items():
pwndbg.gdblib.memory.write(addr, old)
print(message.notice("Reverted patch at %#x" % addr))
patches.clear()
else:
old, _new = patches[address]
pwndbg.gdblib.memory.write(address, old)
pwndbg.lib.memoize.reset()
parser3 = argparse.ArgumentParser(description="List all patches")
@pwndbg.commands.ArgparsedCommand(parser3)
@pwndbg.commands.OnlyWhenRunning
def patch_list():
if not patches:
print(message.hint("No patches to list"))
return
print(message.hint("Patches:"))
for addr, (old, new) in patches.items():
old_insns = disasm(old, arch=pwndbg.gdblib.arch.current)
new_insns = disasm(new, arch=pwndbg.gdblib.arch.current)
print(
message.hint("Patch at"),
message.warning("%#x:" % addr),
message.hint("from"),
message.warning(old_insns.replace("\n", "; ")),
message.hint("to"),
message.warning(new_insns.replace("\n", "; ")),
)

View File

@ -20,4 +20,5 @@ testresources==2.0.1
tomli==2.0.1; python_version >= '3.7'
tomli==1.2.3; python_version < '3.7'
unicorn==2.0.0; python_version >= '3.7'
unicorn==2.0.0rc7; python_version < '3.7'
unicorn==2.0.0rc7; python_version < '3.7'
pwntools==4.8.0