Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions .github/workflows/ci-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: CI Tests

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
ghidra-tests:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4

- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: "3.10"

- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y gdb

- name: Install decomp2dbg
run: |
python -m pip install --upgrade pip
pip install -e .
pip install pytest

- name: Install deps for GDB's system Python
run: |
# GDB links against the system Python, not setup-python's Python.
# Install decomp2dbg deps so GDB can import them at runtime.
/usr/bin/python3 -m pip install --break-system-packages -e .

- name: Set up Java 21
uses: actions/setup-java@v4
with:
distribution: "oracle"
java-version: "21"

- name: Install Ghidra
uses: antoniovazquezblanco/setup-ghidra@v2.0.12
with:
version: "12.0"
auth_token: ${{ secrets.GITHUB_TOKEN }}

- name: Run Ghidra integration tests
run: pytest tests.py::TestGDBIntegrationGhidra -sv

- name: Run Ghidra PIE integration tests
run: pytest tests.py::TestGDBIntegrationGhidraPIE -sv
75 changes: 0 additions & 75 deletions .github/workflows/ghidra-build.yml

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: '3.8'
python-version: '3.10'
- name: Install build
run: pip install build
- name: Build dists
Expand Down
53 changes: 53 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

decomp2dbg is a symbol syncing framework that bridges decompilers (IDA Pro, Binary Ninja, Ghidra, angr-management) with GDB debuggers. It enables reversers to use symbols and decompiled code from their decompiler directly within GDB via XML-RPC communication.

## Build and Installation

```bash
# Development install
pip install -e .

# Production build
python -m build

# Install decompiler plugins (interactive installer)
pip install decomp2dbg && decomp2dbg --install

# Manual GDB setup: copy d2d_client.py and add to .gdbinit
cp decomp2dbg/d2d_client.py ~/.d2d_client.py && echo "source ~/.d2d_client.py" >> ~/.gdbinit
```

**Key Components:**

- `decompilers/server_template.py` - Base XML-RPC interface all decompiler plugins implement
- `decomp2dbg/clients/client.py` - Base DecompilerClient with XML-RPC connection logic
- `decomp2dbg/clients/gdb/gdb_client.py` - Main GDB client, command interface, symbol syncing
- `decomp2dbg/clients/gdb/symbol_mapper.py` - Native GDB symbols via ELF manipulation (Linux only, requires gcc/objcopy)
- `decomp2dbg/clients/gdb/decompiler_pane.py` - Decompilation display in GEF/pwndbg context
- `d2d.py` - GDB entry point, auto-detects GDB variant (vanilla/GEF/pwndbg)

## GDB Commands

When connected via `decompiler connect [host] [port]`:
- `decompiler connect` - Connect to decompiler server
- `decompiler disconnect` - Disconnect from server
- `decompiler info` - Show connection status
- Symbol syncing and decompilation pane updates happen automatically on breakpoints

## Important Patterns

- **Decorators**: `@only_if_connected`, `@only_if_gdb_running` guard methods requiring active connections
- **PIE/ASLR rebasing**: GDB client rebases addresses for position-independent executables
- **Convenience variables**: Local/stack variables available as GDB vars (`$v4`, `$a1`, etc.)
- **Factory pattern**: `d2d.py` detects GDB variant and instantiates appropriate client class

## Dependencies

- Core: `sortedcontainers`, `pyelftools`, `libbs>=0.12.0`
- External tools (Linux native symbols): `gcc`, `objcopy`
- Environment: `NO_COLOR` env var disables terminal colors
4 changes: 0 additions & 4 deletions MANIFEST.in

This file was deleted.

25 changes: 15 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ the symbols and decompilation lines they recover in their decompiler.

![decomp2dbg](./assets/decomp2dbg.png)

Interested in seeing what decomp2dbg looks like in practice? Checkout the recorded [talk at CactusCon 2023](https://youtu.be/-J8fGMt6UmE?t=22442),
Interested in seeing what decomp2dbg looks like in practice? Check out the recorded [talk at CactusCon 2023](https://youtu.be/-J8fGMt6UmE?t=22442),
featuring debugging a remote arm32 binary from a x64 machine with Ghidra symbols.

For active help, join the BinSync Discord below, where we answer decomp2dbg questions:
Expand All @@ -19,13 +19,13 @@ For active help, join the BinSync Discord below, where we answer decomp2dbg ques

## Supported Platforms
### Decompilers
- IDA Pro (>= 7.0): [Demo w/ GEF](https://asciinema.org/a/442740)
- IDA Pro (>= 9.0): [Demo w/ GEF](https://asciinema.org/a/442740)
- Binary Ninja (>= 2.4): [Demo w/ GEF](https://t.co/M2IZd0fmi3)
- Ghidra (>= 11.3.1): [Demo w/ GEF](https://youtu.be/MK7N7uQTUNY)
- Ghidra (>= 12): [Demo w/ GEF](https://youtu.be/MK7N7uQTUNY)
- [angr-management](https://github.com/angr/angr-management) (>= 9.0)

### Debuggers
- gdb (works best with [GEF](https://github.com/hugsy/gef))
- gdb
- GEF
- pwndbg
- vanilla
Expand All @@ -36,9 +36,10 @@ Install through pip, then use the built-in installer for decompilers:
pip3 install decomp2dbg && decomp2dbg --install
```

This will open a prompt where you be asked to input the path to your decompiler and debugger of choice. For Ghidra installs,
you must follow the extra steps to enable extensions [here](https://github.com/mahaloz/decomp2dbg/tree/main/decompilers/d2d_ghidra/README.md).
This will open a prompt where you be asked to input the path to your decompiler and debugger of choice.
For Ghidra, you will need to launch in PyGhidra mode, and enable the plugin (d2d_client.py), as shown [here](https://docs.binsync.net/decompilers/ghidra).
If you installed the decompiler-side in the Binja Plugin Manager, you still need to install the debugger side with the above.
Note: you must install `decomp2dbg` to the same Python enviornment as your decompiler and debugger.

**Note**: You may need to allow inbound connections on port 3662, or the port you use, for decomp2dbg to connect
to the decompiler. If you are installing decomp2dbg with GEF or pwndbg it's important that in your `~/.gdbinit` the
Expand All @@ -57,15 +58,19 @@ First, clone the repo:
git clone https://github.com/mahaloz/decomp2dbg.git
```

Copy all the files in `./decompilers/d2d_ida/` into your ida `plugins` folder:
Install:
```bash
cp -r ./decompilers/d2d_ida/* /path/to/ida/plugins/
pip install .
```

Copy all the server file:
```bash
cp ./decomp2dbg/d2d_server.py /path/to/ida/plugins/
```

If you also need to install the gdb side of things, use the line below:
```bash
pip3 install . && \
cp d2d.py ~/.d2d.py && echo "source ~/.d2d.py" >> ~/.gdbinit
cp ./decomp2dbg/d2d_client.py ~/.d2d.py && echo "source ~/.d2d.py" >> ~/.gdbinit
```

## Usage
Expand Down
2 changes: 1 addition & 1 deletion decomp2dbg/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "3.14.1"
__version__ = "4.0.0"

try:
from .clients.client import DecompilerClient
Expand Down
68 changes: 65 additions & 3 deletions decomp2dbg/__main__.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,91 @@
import argparse
import tempfile

from .installer import D2dInstaller


SUPPORTED_DECOMPILERS = ("ghidra", "ida", "binja", "angr")


def _run_server(binary_path: str, decompiler: str, host: str, port: int):
"""Start a headless decompiler server for the given binary."""
from libbs.api import DecompilerInterface
from libbs.decompilers import GHIDRA_DECOMPILER, IDA_DECOMPILER, BINJA_DECOMPILER, ANGR_DECOMPILER

from decomp2dbg.server import LibBSDecompilerServer

decompiler_map = {
"ghidra": GHIDRA_DECOMPILER,
"ida": IDA_DECOMPILER,
"binja": BINJA_DECOMPILER,
"angr": ANGR_DECOMPILER,
}

force_decompiler = decompiler_map[decompiler]
tmpdir = tempfile.mkdtemp(prefix="d2d_server_")

print(f"[*] Starting headless {decompiler} server for {binary_path} on {host}:{port}...")
deci = DecompilerInterface.discover(
force_decompiler=force_decompiler,
headless=True,
binary_path=binary_path,
project_location=tmpdir,
project_name="d2d_server",
)

server = LibBSDecompilerServer(deci=deci, host=host, port=port)
try:
server.start_xmlrpc_server()
except KeyboardInterrupt:
print("\n[*] Shutting down server...")
finally:
deci.shutdown()


def main():
parser = argparse.ArgumentParser(
description="""
The decomp2dbg Command Line Util.
The decomp2dbg Command Line Util.
""",
epilog="""
Examples:
decomp2dbg --install
decomp2dbg --server ./path/to/binary -d ghidra --port 3662
"""
)
parser.add_argument(
"--install", action="store_true", help="""
Install the decomp2dbg core to supported decompilers as plugins. This option will start an interactive
prompt asking for install paths for all supported decompilers. Each install path is optional and
will be skipped if not path is provided during install.
prompt asking for install paths for all supported decompilers. Each install path is optional and
will be skipped if not path is provided during install.
"""
)
parser.add_argument(
"--server", metavar="BINARY", help="""
Start a headless decompiler XML-RPC server for the given binary.
The server will run in the foreground until interrupted (Ctrl+C).
"""
)
parser.add_argument(
"-d", "--decompiler", choices=SUPPORTED_DECOMPILERS, default="ghidra",
help="Decompiler backend to use with --server (default: ghidra)."
)
parser.add_argument(
"--host", default="localhost",
help="Host address for the server (default: localhost)."
)
parser.add_argument(
"--port", type=int, default=3662,
help="Port for the server (default: 3662)."
)
args = parser.parse_args()

if args.install:
D2dInstaller().install()
elif args.server:
_run_server(args.server, args.decompiler, args.host, args.port)
else:
parser.print_help()


if __name__ == "__main__":
Expand Down
File renamed without changes.
Loading