Skip to content

Commit

Permalink
Merge branch 'dev' of github.com:hugsy/gef into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
hugsy committed Mar 4, 2022
2 parents 7817bea + 6e3cd5c commit 93f3010
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 25 deletions.
40 changes: 35 additions & 5 deletions docs/commands/gef.md
Expand Up @@ -5,11 +5,11 @@
Displays a list of GEF commands and their descriptions.

```
gef➤ gef
gef➤ gef
─────────────────────────────────── GEF - GDB Enhanced Features ───────────────────────────────────
$ -- SmartEval: Smart eval (vague approach to mimic WinDBG `?`).
aslr -- View/modify the ASLR setting of GDB. By default, GDB will disable ASLR when it starts the process. (i.e. not
attached). This command allows to change that setting.
attached). This command allows to change that setting.
assemble -- Inline code assemble. Architecture can be set in GEF runtime config (default x86-32). (alias: asm)
bincompare -- BincompareCommand: compare an binary file with the memory position looking for badchars.
bytearray -- BytearrayCommand: Generate a bytearray to be compared with possible badchars.
Expand Down Expand Up @@ -57,7 +57,7 @@ context.libc_args (bool) = False
```

To filter the config settings you can use `gef config [setting]`.
To filter the config settings you can use `gef config [setting]`.

```
gef➤ gef config theme
Expand All @@ -81,7 +81,7 @@ gef➤ gef config theme.address_stack blue
### GEF Save Command

The `gef save` command saves the current settings (set with `gef config`) to
the user's `~/.gef.rc` file (making the changes persistent).
the user's `~/.gef.rc` file (making the changes persistent).

```
gef➤ gef save
Expand All @@ -92,7 +92,7 @@ gef➤ gef save

Using `gef restore` loads and applies settings from the `~/.gef.rc` file to the
current session. This is useful if you are modifying your GEF configuration
file and want to see the changes without completely reloading GEF.
file and want to see the changes without completely reloading GEF.

```
gef➤ gef restore
Expand All @@ -119,3 +119,33 @@ gef➤ gef run ./binary
```


### GEF Install Command

`gef install` allows to install one (or more) specific script(s) from `gef-extras`. The new scripts will be downloaded and sourced to be used immediately after by GEF. The syntax is straight forward:

```
gef➤ gef install SCRIPTNAME1 [SCRIPTNAME2...]
```

Where `SCRIPTNAME1` ... are the names of script from the [`gef-extras` repository](https://github.com/hugsy/gef-extras/tree/master/scripts/).


```
gef➤ gef install remote windbg stack
[+] Searching for 'remote.py' in `gef-extras@master`...
[+] Installed file '/tmp/gef/remote.py', new command(s) available: `rpyc-remote`
[+] Searching for 'windbg.py' in `gef-extras@master`...
[+] Installed file '/tmp/gef/windbg.py', new command(s) available: `pt`, `hh`, `tt`, `ptc`, `sxe`, `u`, `xs`, `tc`, `pc`, `g`, `r`
[+] Searching for 'stack.py' in `gef-extras@master`...
[+] Installed file '/tmp/gef/stack.py', new command(s) available: `current-stack-frame`
gef➤
```

This makes it easier to deploy new functionalities in limited environment. By default, the command looks up for script names in the `master` branch of `gef-extras`. However you can change specify a different branch through the `gef.default_branch` configuration setting:

```
gef➤ gef config gef.default_branch dev
```

The files will be dowloaded in the path configured in the `gef.extra_plugins_dir` setting, allowing to reload it easily without having to re-download.

59 changes: 59 additions & 0 deletions gef.py
Expand Up @@ -10263,6 +10263,7 @@ def setup(self) -> None:
GefMissingCommand()
GefSetCommand()
GefRunCommand()
GefInstallExtraScriptCommand()

# load the saved settings
gdb.execute("gef restore")
Expand Down Expand Up @@ -10901,6 +10902,64 @@ def screen_setup(self) -> None:
return


class GefInstallExtraScriptCommand(gdb.Command):
"""`gef install` command: installs one or more scripts from the `gef-extras` script repo. Note that the command
doesn't check for external dependencies the script(s) might require."""
_cmdline_ = "gef install"
_syntax_ = f"{_cmdline_} SCRIPTNAME [SCRIPTNAME [SCRIPTNAME...]]"

def __init__(self) -> None:
super().__init__(self._cmdline_, gdb.COMMAND_SUPPORT, gdb.COMPLETE_NONE, False)
self.branch = gef.config.get("gef.extras_default_branch", "master")
return

def invoke(self, args: str, from_tty: bool) -> None:
self.dont_repeat()
if not args:
err("No script name provided")
return

args = args.split()

if "--list" in args or "-l" in args:
subprocess.run(["xdg-open", f"https://github.com/hugsy/gef-extras/{self.branch}/"])
return

dir_setting = gef.config["gef.extra_plugins_dir"] or GEF_TEMP_DIR
self.dirpath = pathlib.Path(dir_setting).expanduser().absolute()
if not self.dirpath.is_dir():
err("'gef.extra_plugins_dir' is not a valid directory")
return

for script in args:
script = script.lower()
if not self.__install_extras_script(script):
warn(f"Failed to install '{script}', skipping...")
return


def __install_extras_script(self, script: str) -> bool:
fpath = self.dirpath / f"{script}.py"
if not fpath.exists():
url = f"https://raw.githubusercontent.com/hugsy/gef-extras/{self.branch}/scripts/{script}.py"
info(f"Searching for '{script}.py' in `gef-extras@{self.branch}`...")
data = http_get(url)
if not data:
warn("Not found")
return False

with fpath.open("wb") as fd:
fd.write(data)
fd.flush()

old_command_set = set(gef.gdb.commands)
gdb.execute(f"source {fpath}")
new_command_set = set(gef.gdb.commands)
new_commands = [f"`{c[0]}`" for c in (new_command_set - old_command_set)]
ok(f"Installed file '{fpath}', new command(s) available: {', '.join(new_commands)}")
return True


#
# GEF internal classes
#
Expand Down
109 changes: 89 additions & 20 deletions tests/commands/gef.py
Expand Up @@ -2,8 +2,15 @@
`gef` command test module
"""

import pytest
import pathlib

from tests.utils import gdb_run_cmd, GefUnitTestGeneric
from tests.utils import (
gdb_run_cmd,
GefUnitTestGeneric,
gdb_start_silent_cmd_last_line,
removeuntil,
)


class GefCommand(GefUnitTestGeneric):
Expand All @@ -17,30 +24,61 @@ def test_cmd_gef(self):


def test_cmd_gef_config(self):
pass


def test_cmd_gef_help(self):
pass


def test_cmd_gef_missing(self):
pass


def test_cmd_gef_restore(self):
pass

res = gdb_run_cmd("gef config")
self.assertNoException(res)
self.assertIn("GEF configuration settings", res)

known_patterns = (
"gef.autosave_breakpoints_file (str)",
"gef.debug (bool)",
"gef.disable_color (bool)",
"gef.extra_plugins_dir (str)",
"gef.follow_child (bool)",
"gef.readline_compat (bool)",
"gef.show_deprecation_warnings (bool)",
"gef.tempdir (str)",
"got.function_not_resolved (str)",
"got.function_resolved (str)",
)
for pattern in known_patterns:
self.assertIn(pattern, res)


def test_cmd_gef_config_get(self):
res = gdb_run_cmd("gef config gef.debug")
self.assertNoException(res)
self.assertIn("GEF configuration setting: gef.debug", res)
# the `True` is automatically set by `gdb_run_cmd` so we know it's there
self.assertIn("""gef.debug (bool) = True\n\nDescription:\n\tEnable debug mode for gef""",
res)

def test_cmd_gef_run(self):
pass

def test_cmd_gef_config_set(self):
res = gdb_start_silent_cmd_last_line("gef config gef.debug 0",
after=("pi print(is_debug())", ))
self.assertNoException(res)
self.assertEqual("False", res)

def test_cmd_gef_save(self):
pass

def test_cmd_gef_help(self):
res = gdb_run_cmd("help gef")
self.assertNoException(res)

def test_cmd_gef_set(self):
known_patterns = (
"gef config",
"gef help",
"gef install",
"gef missing",
"gef restore",
"gef run",
"gef save",
"gef set",
)
for pattern in known_patterns:
self.assertIn(pattern, res)


def test_cmd_gef_run_and_run(self):
res = gdb_run_cmd("gef set args $_gef0",
before=("pattern create -n 4", ),
after=("show args"))
Expand All @@ -51,3 +89,34 @@ def test_cmd_gef_set(self):
before=("pattern create -n 4", ),
after=("show args"))
self.assertException(res)


def test_cmd_gef_save(self):
# check
res = gdb_run_cmd("gef save")
self.assertNoException(res)
self.assertIn("Configuration saved to '", res)

gefrc_file = removeuntil("Configuration saved to '", res.rstrip("'"))

# set & check
for name in ("AAAABBBBCCCCDDDD", "gef"):
res = gdb_run_cmd("gef save", before=(f"gef config gef.tempdir /tmp/{name}", ))
self.assertNoException(res)
with pathlib.Path(gefrc_file).open() as f:
config = f.read()
self.assertIn(f'tempdir = /tmp/{name}\n', config)


@pytest.mark.online
def test_cmd_gef_install(self):
res = gdb_run_cmd("gef install skel windbg stack")
self.assertNoException(res)
# we install 3 plugins, the pattern must be found 3 times
pattern = "Installed file"
for _ in range(3):
idx = res.find(pattern)
self.assertNotEqual(-1, idx)
self.assertIn("new command(s) available", res)
res = res[idx:]

0 comments on commit 93f3010

Please sign in to comment.