summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGordon Messmer <gordon.messmer@gmail.com>2024-04-27 13:09:15 -0700
committerGordon Messmer <gordon.messmer@gmail.com>2024-04-27 13:09:15 -0700
commit6726e366e50cc003d9411fc9052f1079fcbc6bf5 (patch)
tree9ee565f141ea1d2059c8f0244bb98b833f845d5e
downloadgdb-gef-main.tar.gz
gdb-gef-main.tar.xz
gdb-gef-main.zip
Package gef 2024.01HEADmain
-rw-r--r--gdb-gef3
-rw-r--r--gdb-gef.spec86
-rw-r--r--gef-got-audit.patch207
3 files changed, 296 insertions, 0 deletions
diff --git a/gdb-gef b/gdb-gef
new file mode 100644
index 0000000..89a1286
--- /dev/null
+++ b/gdb-gef
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+exec gdb -ex "source @libdir@/gdb/gef.py" "$@"
diff --git a/gdb-gef.spec b/gdb-gef.spec
new file mode 100644
index 0000000..6ba1358
--- /dev/null
+++ b/gdb-gef.spec
@@ -0,0 +1,86 @@
+%global forgeurl https://github.com/hugsy/gef
+
+Name: gdb-gef
+Version: 2024.01
+
+%forgemeta
+
+Release: 1%{?dist}
+Summary: GEF (GDB Enhanced Features)
+
+License: MIT
+URL: %{forgeurl}
+Source0: %{forgesource}
+Source1: gdb-gef
+# https://github.com/hugsy/gef/pull/1094
+Patch0: gef-got-audit.patch
+
+Requires: gdb
+Requires: file
+Requires: binutils
+Requires: procps-ng
+Requires: python3
+
+BuildRequires: gdb
+BuildRequires: gdb-gdbserver
+BuildRequires: file
+BuildRequires: binutils
+BuildRequires: procps-ng
+BuildRequires: python3
+BuildRequires: python3-pylint
+BuildRequires: python3-pytest
+BuildRequires: python3-pytest-benchmark
+BuildRequires: python3-pytest-cov
+BuildRequires: python3-pytest-xdist
+BuildRequires: python3-coverage
+BuildRequires: python3-rpyc
+BuildRequires: python3-requests
+BuildRequires: gcc
+BuildRequires: gcc-c++
+BuildRequires: make
+BuildRequires: sed
+BuildRequires: qemu-user
+BuildRequires: git
+
+
+%description
+GEF (pronounced ʤɛf - "Jeff") is a set of commands for x86/64, ARM,
+MIPS, PowerPC and SPARC to assist exploit developers and
+reverse-engineers when using old school GDB. It provides additional
+features to GDB using the Python API to assist during the process of
+dynamic analysis and exploit development. Application developers will
+also benefit from it, as GEF lifts a great part of regular GDB
+obscurity, avoiding repeating traditional commands, or bringing out
+the relevant information from the debugging runtime.
+
+
+%prep
+%forgesetup
+%patch 0 -p1
+
+
+%install
+mkdir -p %{buildroot}/%{_libdir}/gdb
+cp gef.py %{buildroot}/%{_libdir}/gdb/gef.py
+
+sed -e "s:@libdir@:%{_libdir}:g" < %{SOURCE1} > %{SOURCE1}.sh
+mkdir -p %{buildroot}/%{_bindir}
+install -m 0755 %{SOURCE1}.sh %{buildroot}/%{_bindir}/gdb-gef
+
+
+%check
+make -C tests/binaries
+python3 -m pytest -v -m "not benchmark" -m "not online" tests/
+
+
+%files
+%license LICENSE
+%doc docs README.md
+%dir %{_libdir}/gdb
+%{_libdir}/gdb/gef.py
+%{_bindir}/gdb-gef
+
+
+%changelog
+* Wed Apr 24 2024 Gordon Messmer <gordon.messmer@gmail.com>
+- Package gef 2024.01
diff --git a/gef-got-audit.patch b/gef-got-audit.patch
new file mode 100644
index 0000000..cc412df
--- /dev/null
+++ b/gef-got-audit.patch
@@ -0,0 +1,207 @@
+diff --git a/docs/commands/got-audit.md b/docs/commands/got-audit.md
+new file mode 100644
+index 0000000..e14a834
+--- /dev/null
++++ b/docs/commands/got-audit.md
+@@ -0,0 +1,36 @@
++## Command `got-audit`
++
++Display the current state of GOT table of the running process.
++
++The `got-audit` command optionally takes function names and filters the output displaying only the
++matching functions.
++
++The command output will list symbols in the GOT along with the file providing the mapped memory
++where the symbol's value points.
++
++If the file providing the mapped memory doesn't export the symbol, `got-audit` will print an
++error. If multiple files export the named symbol, `got-audit` will print an error.
++
++```text
++gef➤ got-audit
++```
++
++![gef-got-audit](https://i.imgur.com/KWStygQ.png)
++
++The applied filter partially matches the name of the functions, so you can do something like this.
++
++```text
++gef➤ got-audit str
++gef➤ got-audit print
++gef➤ got-audit read
++```
++
++![gef-got-audit-one-filter](https://i.imgur.com/YucJboD.png)
++
++Example of multiple partial filters:
++
++```text
++gef➤ got-audit str get
++```
++
++![gef-got-audit-multi-filter](https://i.imgur.com/VhMvXYZ.png)
+diff --git a/docs/install.md b/docs/install.md
+index b2b5f6f..3a29fc3 100644
+--- a/docs/install.md
++++ b/docs/install.md
+@@ -7,6 +7,7 @@ Therefore it requires the following binaries to be present:
+
+ * `file`
+ * `readelf`
++* `nm`
+ * `ps`
+ * `python3`
+
+diff --git a/gef.py b/gef.py
+index f9c6f7e..f808e5d 100644
+--- a/gef.py
++++ b/gef.py
+@@ -9196,6 +9196,11 @@ class GotCommand(GenericCommand):
+ "Line color of the got command output for unresolved function")
+ return
+
++ def build_line(self, name: str, color: str, address_val: int, got_address: int):
++ line = f"[{hex(address_val)}] "
++ line += Color.colorify(f"{name} {RIGHT_ARROW} {hex(got_address)}", color)
++ return line
++
+ @only_if_gdb_running
+ def do_invoke(self, argv: List[str]) -> None:
+ readelf = gef.session.constants["readelf"]
+@@ -9222,7 +9227,7 @@ class GotCommand(GenericCommand):
+ relro_status = "No RelRO"
+
+ # retrieve jump slots using readelf
+- lines = gef_execute_external([readelf, "--relocs", elf_file], as_list=True)
++ lines = gef_execute_external([readelf, "--wide", "--relocs", elf_file], as_list=True)
+ jmpslots = [line for line in lines if "JUMP" in line]
+
+ gef_print(f"\nGOT protection: {relro_status} | GOT functions: {len(jmpslots)}\n ")
+@@ -9250,12 +9255,68 @@ class GotCommand(GenericCommand):
+ else:
+ color = self["function_resolved"]
+
+- line = f"[{hex(address_val)}] "
+- line += Color.colorify(f"{name} {RIGHT_ARROW} {hex(got_address)}", color)
++ line = self.build_line(name, color, address_val, got_address)
+ gef_print(line)
+ return
+
+
++@register
++class GotAuditCommand(GotCommand, GenericCommand):
++ """Display current status of the got inside the process with paths providing functions."""
++
++ _cmdline_ = "got-audit"
++ _syntax_ = f"{_cmdline_} [FUNCTION_NAME ...] "
++ _example_ = "got-audit read printf exit"
++ _symbols_: Dict[str, List[str]] = collections.defaultdict(list)
++ _paths_: Dict[str, List[str]] = collections.defaultdict(list)
++ _expected_dups_ = ['__cxa_finalize']
++
++ def get_symbols_from_path(self, elf_file):
++ nm = gef.session.constants["nm"]
++ # retrieve symbols using nm
++ lines = gef_execute_external([nm, "-D", elf_file], as_list=True)
++ for line in lines:
++ words = line.split()
++ # Record the symbol if it is in the text section or
++ # an indirect function or weak symbol
++ if len(words) == 3 and words[-2] in ('T', 'i', 'I', 'v', 'V', 'w', 'W'):
++ sym = words[-1].split('@')[0]
++ if elf_file not in self._symbols_[sym]:
++ self._symbols_[sym].append(elf_file)
++ self._paths_[elf_file].append(sym)
++
++ @only_if_gdb_running
++ def do_invoke(self, argv: List[str]) -> None:
++ # Build a list of the symbols provided by each path, and
++ # a list of paths that provide each symbol.
++ for section in gef.memory.maps:
++ if (section.path not in self._paths_
++ and pathlib.Path(section.path).is_file()
++ and section.permission & Permission.EXECUTE):
++ self.get_symbols_from_path(section.path)
++ return super().do_invoke(argv)
++
++ def build_line(self, name: str, color: str, address_val: int, got_address: int):
++ line = Color.colorify(f"{name}", color)
++ found = 0
++ for section in gef.memory.maps:
++ if not got_address in range(section.page_start, section.page_end):
++ continue
++ line += f" : {section.path}"
++ found = 1
++ short_name = name.split('@')[0]
++ if (len(self._symbols_[short_name]) > 1
++ and short_name not in self._expected_dups_):
++ line += f" :: ERROR {short_name} found in multiple paths ({str(self._symbols_[short_name])})"
++ if (section.path != "[vdso]"
++ and short_name not in self._paths_[section.path]):
++ line += f" :: ERROR {short_name} not exported by {section.path}"
++ break
++ if not found:
++ line += " : no mapping found"
++ return line
++
++
+ @register
+ class HighlightCommand(GenericCommand):
+ """Highlight user-defined text matches in GEF output universally."""
+@@ -10979,7 +11040,7 @@ class GefSessionManager(GefManager):
+ self.aliases: List[GefAlias] = []
+ self.modules: List[FileFormat] = []
+ self.constants = {} # a dict for runtime constants (like 3rd party file paths)
+- for constant in ("python3", "readelf", "file", "ps"):
++ for constant in ("python3", "readelf", "nm", "file", "ps"):
+ self.constants[constant] = which(constant)
+ return
+
+diff --git a/tests/commands/got_audit.py b/tests/commands/got_audit.py
+new file mode 100644
+index 0000000..ae2470b
+--- /dev/null
++++ b/tests/commands/got_audit.py
+@@ -0,0 +1,42 @@
++"""
++`got-audit` command test module
++"""
++
++import pytest
++
++from tests.base import RemoteGefUnitTestGeneric
++
++from tests.utils import (
++ ARCH,
++ ERROR_INACTIVE_SESSION_MESSAGE,
++ debug_target,
++)
++
++
++@pytest.mark.skipif(ARCH in ("ppc64le",), reason=f"Skipped for {ARCH}")
++class GotAuditCommand(RemoteGefUnitTestGeneric):
++ """`got-audit` command test module"""
++
++ def setUp(self) -> None:
++ self._target = debug_target("format-string-helper")
++ return super().setUp()
++
++
++ def test_cmd_got_audit(self):
++ gdb = self._gdb
++
++ self.assertEqual(ERROR_INACTIVE_SESSION_MESSAGE,gdb.execute("got-audit", to_string=True))
++
++ # Advance the program until after GOT symbols have been resolved
++ gdb.execute("start")
++ gdb.execute("break greetz")
++ gdb.execute("run beep")
++ gdb.execute("step 4")
++ res = gdb.execute("got-audit", to_string=True)
++ self.assertIn("printf", res)
++ self.assertIn("strcpy", res)
++ self.assertIn("/libc.so.6", res)
++
++ res = gdb.execute("got-audit printf", to_string=True)
++ self.assertIn("printf", res)
++ self.assertNotIn("strcpy", res)