Skip to content

Commit

Permalink
Merge pull request #779 from hugsy/gdb_8_py36_code_refactor
Browse files Browse the repository at this point in the history
GEF code quality improvement, with support with GDB 8+ and Python3.6+
  • Loading branch information
Grazfather committed Jan 16, 2022
2 parents 2975d5f + e4a77aa commit 5f31516
Show file tree
Hide file tree
Showing 13 changed files with 27,274 additions and 3,457 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/run-tests.yml
Expand Up @@ -80,4 +80,4 @@ jobs:
- name: Run linter
run: |
make lint
6 changes: 4 additions & 2 deletions README.md
Expand Up @@ -7,7 +7,7 @@

## Instant Setup ##

Simply make sure you have [GDB 7.7 or higher](https://www.gnu.org/s/gdb) compiled with Python3 bindings, then:
Simply make sure you have [GDB 8.0 or higher](https://www.gnu.org/s/gdb) compiled with Python3.6+ bindings, then:


```bash
Expand Down Expand Up @@ -85,7 +85,9 @@ Unlike other GDB plugins, GEF has an extensive and up-to-date [documentation](ht
To get involved, refer to the [Contribution documentation](https://gef.readthedocs.io/en/master/#contribution) and the [guidelines](https://github.com/hugsy/gef/blob/dev/.github/CONTRIBUTING.md) to start.
## Sponsors ##
Another way to contribute to keeping the project alive is by sponsoring it! Check out [the sponsoring documentation](https://gef.readthedocs.io/en/master/#sponsors) for details so you can be part of the list of those [awesome sponsors](https://github.com/sponsors/hugsy).
### Happy Hacking ###
## Happy Hacking ##
281 changes: 30 additions & 251 deletions docs/api.md
@@ -1,4 +1,4 @@
# Extending GEF #
# Extending GEF

`GEF` intends to provide a battery-included, quickly installable and crazy fast
debugging environment sitting on top of GDB.
Expand All @@ -12,30 +12,27 @@ A [dedicated repository](https://github.com/hugsy/gef-extras) was born to host
repo is open to all for contributions, no restrictions and the most valuable
ones will be integrated into `gef.py`.

## Quick start ##
## Quick start

Here is the most basic skeleton for creating a new `GEF` command named `newcmd`:

```python
class NewCommand(GenericCommand):
"""Dummy new command."""
_cmdline_ = "newcmd"
_syntax_ = "{:s}".format(_cmdline_)
_syntax_ = f"{_cmdline_}"

@only_if_gdb_running # not required, ensures that the debug session is started
def do_invoke(self, argv):
# do anything allowed by gef, for example show the current running
# architecture as Python object:
print(" = {}".format(current_arch) )
# let's say we want to print some info about the architecture of the current binary
print(f"gef.arch={gef.arch}")
# or showing the current $pc
print("pc = {:#x}".format(current_arch.pc))
print(f"gef.arch.pc={gef.arch.pc:#x}")
return

register_external_command(NewCommand())
```

Yes, that's it!

Loading it in `GEF` is as easy as
```
gef➤ source /path/to/newcmd.py
Expand All @@ -44,10 +41,15 @@ gef➤ source /path/to/newcmd.py

We can call it:

![](https://camo.githubusercontent.com/d41c1c0c0267916f4749800906d201fe5d328db5/687474703a2f2f692e696d6775722e636f6d2f306734416543622e706e67)
```
gef➤ newcmd
gef.arch=<__main__.X86_64 object at 0x7fd5583571c0>
gef.arch.pc=0x55555555a7d0
```

Yes, that's it! Check out [the complete API](api/gef.md) to see what else GEF offers.

## Detailed explanation ##
## Detailed explanation

Our new command must be a class that inherits from GEF's `GenericCommand`. The
*only* requirements are:
Expand All @@ -70,7 +72,8 @@ or add to your `~/.gdbinit`:
$ echo source /path/to/newcmd.py >> ~/.gdbinit
```

## Custom context panes ##

## Customizing context panes

Sometimes you want something similar to a command to run on each break-like
event and display itself as a part of the GEF context. Here is a simple example
Expand All @@ -97,7 +100,7 @@ It can even be included in the same file as a Command.
Now on each break you will notice a new pane near the bottom of the context.
The order can be modified in the `GEF` context config.

### Context Pane API ###
### Context Pane API

The API demonstrated above requires very specific argument types:
`register_external_context_pane(pane_name, display_pane_function, pane_title_function)`
Expand All @@ -107,7 +110,7 @@ The API demonstrated above requires very specific argument types:
in the pane
-`pane_title_function`: a function that returns the title string or None to hide the title

## API ##
## API

Some of the most important parts of the API for creating new commands are
mentioned (but not limited to) below. To see the full help of a function, open
Expand All @@ -124,111 +127,19 @@ or even from outside GDB:
$ gdb -q -ex 'pi help(hexdump)' -ex quit
```

The GEF API aims to provide a simpler and more Pythonic approach to GDB's.

### Reference

#### Global

```python
register_external_command()
```
Procedure to add the new GEF command

---

```python
parse_address()
```
Parse an expression into a integer from the current debugging context.


```python
gef ➤ pi hex(parse_address("main+0x4"))
'0x55555555a7d4'
```

---

```python
current_arch
```
Global variable associated with the architecture of the currently debugged process. The variable is an instance of the `Architecture` class (see below).

---

```python
current_elf
```
Global variable associated to the currently debugging ELF file.


#### Logging

```python
ok(msg)
info(msg)
warn(msg)
err(msg)
```


#### CPU

Some basic examples:
- read the memory
```python
get_register(register_name)
```

Returns the value of given register. The function will fail outside a running debugging context.




#### Memory


```python
read_memory(addr, length=0x10)
```
Returns a `length` long byte array with a copy of the process memory read from `addr`.

Ex:
```python
0:000 ➤ pi print(hexdump( read_memory(parse_address("$pc"), length=0x20 )))
gef ➤ pi print(hexdump( gef.memory.read(parse_address("$pc"), length=0x20 )))
0x0000000000000000 f3 0f 1e fa 31 ed 49 89 d1 5e 48 89 e2 48 83 e4 ....1.I..^H..H..
0x0000000000000010 f0 50 54 4c 8d 05 66 0d 01 00 48 8d 0d ef 0c 01 .PTL..f...H.....
```

---

```python
write_memory(addr, buffer, length=0x10)
```
Writes `buffer` to memory at address `addr`.

---

```python
read_int_from_memory(addr)
```

Reads the size of an integer from `addr`, and unpacks it correctly (based on the current arch's endianness)

---

```python
read_cstring_from_memory(addr)
```
Return a NULL-terminated array of bytes, from `addr`.

---

```python
get_process_maps()
- get access to the memory layout
```
Returns an iterable of Section objects (see below) corresponding to the current memory layout of the process.

```python
0:000 ➤ pi print('\n'.join([ f"{x.page_start:#x} -> {x.page_end:#x}" for x in get_process_maps()]))
gef ➤ pi print('\n'.join([ f"{x.page_start:#x} -> {x.page_end:#x}" for x in gef.memory.maps]))
0x555555554000 -> 0x555555558000
0x555555558000 -> 0x55555556c000
0x55555556c000 -> 0x555555575000
Expand All @@ -242,96 +153,19 @@ Returns an iterable of Section objects (see below) corresponding to the current
[...]
```

#### Code


```python
gef_disassemble(addr, nb_insn, from_top=False)
```
Disassemble `nb_insn` instructions after `addr`. If `from_top` is False (default), it will also disassemble the `nb_insn` instructions before `addr`. Return an iterator of Instruction objects (see below).



#### Runtime hooks

---

```python
gef_on_continue_hook
gef_on_continue_unhook
```
Takes a callback function FUNC as parameter: add/remove a call to `FUNC` when GDB continues execution.

---

```python
gef_on_stop_hook
gef_on_stop_unhook
```

Takes a callback function FUNC as parameter: add/remove a call to `FUNC` when GDB stops execution (breakpoints, watchpoints, interrupt, signal, etc.).

---

```python
gef_on_new_hook
gef_on_new_unhook
```

Takes a callback function FUNC as parameter: add/remove a call to `FUNC` when GDB loads a new binary.
The API also offers a number of decorators to simplify the creation of new/existing commands, such as:
- `@only_if_gdb_running` to execute only if a GDB session is running.
- `@only_if_gdb_target_local` to check if the target is local i.e. not debugging using GDB `remote`.
- and many more...

---

```python
gef_on_exit_hook
gef_on_exit_unhook
```

Takes a callback function FUNC as parameter: add/remove a call to `FUNC` when GDB exits an inferior.


### `do_invoke` decorators ###

```python
@only_if_gdb_running
```

Modifies a function to only execute if a GDB session is running. A GDB session is running if:
* a PID exists for the targeted binary
* GDB is running on a coredump of a binary

---

```python
@only_if_gdb_target_local
```
Checks if the current GDB session is local i.e. not debugging using GDB `remote`.

---

```python
@only_if_gdb_version_higher_than( (MAJOR, MINOR) )
```

Checks if the GDB version is higher or equal to the MAJOR and MINOR providedas arguments (both as Integers). This is required since some commands/API ofGDB are only present in the very latest version of GDB.

---

```python
@obsolete_command
```

Decorator to add a warning when a command is obsolete and may be removed without warning in future releases.

---
### Reference

```python
@experimental_feature
```
Decorator to add a warning when a feature is experimental, and its API/behavior may change in future releases.
For a complete reference of the API offered by GEF, visit [`docs/api/gef.md`](api/gef.md).


---
### Parsing command arguments

```python
@parse_arguments( {"required_argument_1": DefaultValue1, ...}, {"--optional-argument-1": DefaultValue1, ...} )
Expand Down Expand Up @@ -379,58 +213,3 @@ args.foo --> [3, 14, 159, 2653] # a List(int) from user input
args.bleh --> "" # the default value
args.blah --> True # set to True because user input declared the option (would have been False otherwise)
```

---

```python
@only_if_current_arch_in(valid_architectures)
```
Decorator to allow commands for only a subset of the architectured supported by GEF. This decorator is to use lightly, as it goes against the purpose of GEF to support all architectures GDB does. However in some cases, it is necessary.

```python
@only_if_current_arch_in(["X86", "RISCV"])
def do_invoke(self, argv):
[...]
```


### Classes ###

For exhaustive documentation, run
```bash
$ gdb -q -ex 'pi help(<ClassName>)' -ex quit
```

#### Generic ####

New GEF commands **must** inherit `GenericCommand`, have `_cmdline_` and
`_syntax_` attributes, and have a instance method `do_invoke(args)` defined.

Other than that, new commands can enjoy all the GEF abstract layer
representation classes, such as:

* `Instruction` : GEF representation of instruction as pure Python objects.
* `Address`: GEF representation of memory addresses.
* `Section`: GEF representation of process memory sections.
* `Permission`: Page permission object.
* `Elf`: [ELF](http://www.skyfree.org/linux/references/ELF_Format.pdf) parsing
object.

#### Architectures ####

* `Architecture` : Generic metaclass for the architectures supported by GEF.
* `ARM`
* `AARCH64`
* `X86`
* `X86_64`
* `PowerPC`
* `PowerPC64`
* `SPARC`
* `SPARC64`
* `MIPS`


#### Heap ####

* `GlibcArena` : Glibc arena class
* `GlibcChunk` : Glibc chunk class.

0 comments on commit 5f31516

Please sign in to comment.