From d56e87c52b1de38194361bed82deac4ebb59bdf5 Mon Sep 17 00:00:00 2001 From: Dreg Date: Mon, 17 Jul 2023 21:05:43 +0200 Subject: [PATCH 1/2] add skipi command to skip N instructions --- docs/commands/skipi.md | 18 ++++++++++++ gef.py | 34 ++++++++++++++++++++++ tests/commands/skipi.py | 63 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 docs/commands/skipi.md create mode 100644 tests/commands/skipi.py diff --git a/docs/commands/skipi.md b/docs/commands/skipi.md new file mode 100644 index 000000000..68ca46243 --- /dev/null +++ b/docs/commands/skipi.md @@ -0,0 +1,18 @@ +## Command `skipi` + +The `skipi` command allows you to easily skip instructions execution. + +``` +skipi [LOCATION] [--n NUM_INSTRUCTIONS] +``` + +`LOCATION` address/symbol from where to skip (default is `$pc`) + +`--n NUM_INSTRUCTIONS` Skip the specified number of instructions instead of the default 1. + +```bash +gef➤ skipi +gef➤ skipi --n 3 +gef➤ skipi 0x69696969 +gef➤ skipi 0x69696969 --n 6 +``` \ No newline at end of file diff --git a/gef.py b/gef.py index 8875fc48f..e66ec0677 100644 --- a/gef.py +++ b/gef.py @@ -5985,6 +5985,40 @@ def do_invoke(self, _: List[str], **kwargs: Any) -> None: return +@register +class SkipiCommand(GenericCommand): + """Skip N instruction(s) execution""" + + _cmdline_ = "skipi" + _syntax_ = ("{_cmdline_} [LOCATION] [--n NUM_INSTRUCTIONS]" + "\n\tLOCATION\taddress/symbol from where to skip" + "\t--n NUM_INSTRUCTIONS\tSkip the specified number of instructions instead of the default 1.") + + _example_ = [f"{_cmdline_}", + f"{_cmdline_} --n 3", + f"{_cmdline_} 0x69696969", + f"{_cmdline_} 0x69696969 --n 6",] + + def __init__(self) -> None: + super().__init__(complete=gdb.COMPLETE_LOCATION) + return + + @only_if_gdb_running + @parse_arguments({"address": "$pc"}, {"--n": 1}) + def do_invoke(self, _: List[str], **kwargs: Any) -> None: + args : argparse.Namespace = kwargs["arguments"] + address = parse_address(args.address) + num_instructions = args.n + + last_addr = gdb_get_nth_next_instruction_address(address, num_instructions) + total_bytes = (last_addr - address) + gef_get_instruction_at(last_addr).size() + target_addr = address + total_bytes + + info(f"skipping {num_instructions} instructions ({total_bytes} bytes) from {address:#x} to {target_addr:#x}") + gdb.execute(f"set $pc = {target_addr:#x}") + return + + @register class NopCommand(GenericCommand): """Patch the instruction(s) pointed by parameters with NOP. Note: this command is architecture diff --git a/tests/commands/skipi.py b/tests/commands/skipi.py new file mode 100644 index 000000000..268b2aed8 --- /dev/null +++ b/tests/commands/skipi.py @@ -0,0 +1,63 @@ +""" +`skipi` command test module +""" + +import pytest + +from tests.utils import (ARCH, GefUnitTestGeneric, _target, findlines, + gdb_run_cmd, gdb_run_silent_cmd, gdb_start_silent_cmd) + + +class SkipiCommand(GefUnitTestGeneric): + """`skipi` command test module""" + + + cmd = "skipi" + + + def test_cmd_nop_inactive(self): + res = gdb_run_cmd(f"{self.cmd}") + self.assertFailIfInactiveSession(res) + + + @pytest.mark.skipif(ARCH not in ("i686", "x86_64"), reason=f"Skipped for {ARCH}") + def test_cmd_skipi_no_arg(self): + + res = gdb_start_silent_cmd( + "pi gef.memory.write(gef.arch.pc, p32(0x9090feeb))", # 1 short jumps to pc + 2 nops + after=( + self.cmd, + "pi print(gef.memory.read(gef.arch.pc, 2))", # read 2 bytes + ) + ) + self.assertNoException(res) + self.assertIn(r"\x90\x90", res) # 2 nops + + + @pytest.mark.skipif(ARCH not in ("i686", "x86_64"), reason=f"Skipped for {ARCH}") + def test_cmd_skipi_skip_two_instructions(self): + + res = gdb_start_silent_cmd( + "pi gef.memory.write(gef.arch.pc, p64(0x90909090feebfeeb))", # 2 short jumps to pc + 4 nops + after=( + f"{self.cmd} --n 2", + "pi print(gef.memory.read(gef.arch.pc, 4))", # read 4 bytes + ) + ) + self.assertNoException(res) + self.assertIn(r"\x90\x90\x90\x90", res) # 4 nops + + + @pytest.mark.skipif(ARCH not in ("i686", "x86_64"), reason=f"Skipped for {ARCH}") + def test_cmd_skipi_two_instructions_from_location(self): + + res = gdb_start_silent_cmd( + "pi gef.memory.write(gef.arch.pc, p64(0x9090feebfeebfeeb))", # 2 short jumps to pc + 2 nops + after=( + f"{self.cmd} $pc+2 --n 2", # from the second short jump + "pi print(gef.memory.read(gef.arch.pc, 2))", # read 2 bytes + ) + ) + self.assertNoException(res) + self.assertIn(r"\x90\x90", res) # 2 nops + From 6ab2d299e126592ffd91f2cd1f3f20692b9935a7 Mon Sep 17 00:00:00 2001 From: Dreg Date: Wed, 19 Jul 2023 22:08:40 +0200 Subject: [PATCH 2/2] fix bug in NopCommand with example args --- gef.py | 1 - 1 file changed, 1 deletion(-) diff --git a/gef.py b/gef.py index e66ec0677..a97f3b6ef 100644 --- a/gef.py +++ b/gef.py @@ -6029,7 +6029,6 @@ class NopCommand(GenericCommand): "\n\tLOCATION\taddress/symbol to patch" "\t--n NUM_ITEMS\tInstead of writing one instruction/nop, patch the specified number of instructions/nops (full instruction size by default)" "\t--b\tInstead of writing full instruction size, patch the specified number of nops") - _example_ = f"{_cmdline_} $pc" _example_ = [f"{_cmdline_}", f"{_cmdline_} $pc+3",