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
178 changes: 82 additions & 96 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ and use it for Binary Code Similarity to help you Reverse Engineer stripped bina
## Table of contents

- [Key Features](#key-features)
- [Installation](#installation)
- [Setup](#setup)
- [Verifying the Installation](#verifying-the-installation)
- [Usage](#usage)
- [Configuration](#configuration)
- [Analysis](#analysis)
- [Auto Unstrip](#auto-unstrip)
- [Function Matching](#function-matching)
- [AI Decompilation](#ai-decompilation)
- [Contributing](#contributing)
- [Reporting Bugs](#reporting-bugs)
- [Known Issues](#known-issues)

### Key features

Expand All @@ -26,12 +38,21 @@ This plugin brings the power of RevEng.AI directly into Binary Ninja. Here are t
- **Auto-Unstrip**: Automatically restore stripped symbols in your binary using our AI engine.
- **Match Functions**: Compare and match functions from your current binary with those in your existing collections.
- **Match Unique Function**: Compare and match a single function from your current binary with those in your existing collections.
- **View Function in Portal**: Convenient shortcut to explore the function within the platform interface.
- **View Function in Portal**: Convenient shortcuts to explore the function within the platform interface.


## Installation

### Step 1: Locate Binary Ninja Plugins Folder
The builds for latest stable version of the RevEng.AI Binary Ninja plugin can be downloaded from the [Releases](https://github.com/revengai/plugin-binary-ninja/releases/latest) page.

### Supported Binary Ninja versions

We support Binary Ninja 3.0+ on Windows, macOS and Linux with Python version `3.10`.

### Setup

1. **Download** the plugin code for your platform from the [Releases](https://github.com/revengai/plugin-binary-ninja/releases/latest) page.
2. **Extract** the archive contents into your Binary Ninja plugins directory.

Locate your Binary Ninja user plugin directory:

Expand All @@ -45,143 +66,108 @@ Expected output locations:
- macOS: `~/Library/Application Support/Binary Ninja/plugins/`


### Step 2: Download & Install the Plugin

1. Visit the releases page: https://github.com/RevEngAI/reai-ida/releases
2. Download the latest release (look for the most recent version).
3. Extract the contents into the opened plugin folder.
4. Ensure the folder structure looks like this:

After extraction, the directory structure should look like this:
```
Example in Linux...
Example for Linux...
~/.binaryninja/plugins/
└── reai_toolkit/
└── [plugin files...]
```

### For Mac Users

After installation, run the following command:

```bash
xattr -dr com.apple.quarantine "$HOME/Library/Application Support/Binary Ninja/plugins/reai_toolkit"
```

It removes the macOS “quarantine” flag (added to files downloaded from the internet), so the system won’t block or warn when loading the plugin.

---

## Using the Plugin ⚙️

Once installed, you’ll find `RevEng.AI` listed in the Binary Ninja plugins toolbar menu.
3. **Restart** Binary Ninja if it was running.

<img src="images/plugintoolbar.png" >
### Verifying the Installation
Once Binary Ninja is open, confirm that the RevEng.AI menu appears in the plugins menu bar.
If no errors are displayed in the console and the menu loads, your installation is complete.

Make sure to restart Binary Ninja completely after installation.
Then, check the Plugins menu — the RevEng.AI plugin should be visible.
Finally, load a binary and explore the features described below.
[![Plugin Menu](images/installed.png)]()

### 1. Configure the Plugin
## Usage

Select `Configuration` from the menu to set up your API key and host.
In this section, we provide an example workflow for our plugin.

<img src="./images/config.png" >
### Configuration

Clicking "Continue" will validate your API key.
The first thing we need to do is configure the plugin with our API key and the host to use.

---
When you load the plugin for the first time, or by selecting `RevEng.AI -> Configure`, you will be guided through the configuration process.

### 2. Process a Binary
[![Plugin Menu](images/configure.png)]()

Upload the currently loaded binary to RevEng.AI:
Enter your API Key from the [RevEng.AI Portal](https://portal.reveng.ai/settings) into the API Key field
where it will be validated and saved for future use.

- Select `RevEng.AI > Process Binary`
### Analysis

<img src="./images/processbinary.png" >
Once configured, you can upload the current binary for analysis by selecting `RevEng.AI -> Analysis -> Create New`.
It's usually enough to keep the default options, but you can adjust the analysis settings as needed.

Before starting the process, you can add a PDB file and debug information, assign custom tags for better tracking, choose which AI model you want to use, and decide whether to keep the analysis private (default) or make it publicly available.
The plugin will handle the upload and initiate the analysis. Once completed, an internal analysis ID is assigned.
![Analysis Window](images/create-new.png)

---
If you have already processed your binary on the platform or if there are publicly available analyses, you can select
one as your working source without needing to upload again.

### 3. Choose Source Analysis
- Select `RevEng.AI -> Analysis -> Attach to existing`

If you have already processed your binary on the platform or if there are publicly available analyses, you can select one as your working source.
![Atach to existing](images/attach-to-existing.png)

- Select `RevEng.AI > Choose Source`
This is required before using features like function matching or auto unstrip.

<img src="./images/choosesource.png" >
### Auto Unstrip

This is required before using some features like function matching or auto unstrip.
The `Auto Unstrip` tool allows you to automatically recover function names based on our debug symbol database. It is
an automated process that will recover all function names from the currently attached binary.

---
You can access it by selecting `RevEng.AI -> Auto Unstrip` from the menu.

### 4. Auto Unstrip
### Function Matching

Bring back symbol names automatically:
The function matching tool allows you to rename functions in a binary based on similarity to functions in our database.
It is a manual process that can be done on a per-function basis, or in batch mode for the entire binary. It allows you
to have more control over which functions are renamed, and when as well as the ability to review the suggested names before
applying them.

- Select `RevEng.AI > Auto Unstrip`
To match with all functions in the binary, select `RevEng.AI -> Function Matching`.
![Function Matching Window](images/function-matching-full.png)

<img src="./images/autounstrip.png" >
Or to match a single function, select the function and then navigate to `RevEng.AI -> Functions -> Match Function`.

Functions will be renamed with the most likely matching names from your configured collections.
Adjust the filters as necessary and when ready click `Match Functions`.
For multiple functions at most 1 result will be returned. For individual functions, up to 10 functions will be returned.
You can then decide to rename a function to one of the suggested names by clicking `Rename Matched Functions`.

---
### AI Decompilation

### 5. Match Functions
The `AI Decompilation` tool allows you to get AI generated decompilation of selected functions. You can access it by
selecting a function in Binary Ninja's listing or decompiler view and selecting `RevEng.AI -> Functions -> AI Decompilation`.

Use function matching to identify similar functions in other binaries or collections:
![AI Decomp](images/ai-decompilation.png)

- Click `RevEng.AI > Match Functions`
The window will show you the AI generated decompilation of the selected function as well as a
natural language explanation of what the function does.

<img src="./images/matchedfunctions.png" >
The window can be pinned and will update as you select different functions.

Matched functions are displayed based on the given confidence value. You can navigate or rename based on the results.
## Contributing

---
We welcome pull requests from the community.

### 6. Match Current Function
The plugin is still undergoing active development currently, and we are looking for feedback on how to improve it.

Use function matching to identify similar functions in other binaries or collections:
### Reporting Bugs

- Click `RevEng.AI > Match Current Functions`
If you've found a bug in the plugin, please open an issue via [GitHub](https://github.com/RevEngAi/plugin-binary-ninja/issues/new/choose), or create a post on our [Discord](https://discord.com/invite/ZwQTvzfSbA).

<img src="./images/currentfunction.png" >
## Known Issues

Matched functions are displayed based on the given similarity value. You can navigate or rename based on the results.
- On macOS, you might need to remove the quarantine attribute from the plugin folder to allow Binary Ninja to load it properly.

---

### 7. View Current Function in Portal

Quick and easy way to open the current function in the RevEng.AI Portal.

- Click `RevEng.AI > View Function in Portal`

<img src="./images/output.gif" >

---


## Troubleshooting

- Only Binary Ninja 3.0+ is supported
- Python 3.9 or later is required
- Ensure your API key is valid and your analysis contains function-level information

## Software Requirements

This plugin relies on:

- [reait](https://github.com/RevEngAI/reait)
- requests
- PySide6
- libbs

## License
After installation, run the following command:

This plugin is released under the GPL-2.0 license.
```bash
xattr -dr com.apple.quarantine "$HOME/Library/Application Support/Binary Ninja/plugins/reai_toolkit"
```

## Disclaimer
It removes the macOS “quarantine” flag (added to files downloaded from the internet), so the system won’t block or warn when loading the plugin.

Binary Ninja is a trademark of Vector 35. This project isn't affiliated with or endorsed by Vector 35.
---
Binary file added images/ai-decompilation.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/attach-to-existing.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed images/autounstrip.png
Binary file not shown.
Binary file removed images/banner.png
Binary file not shown.
Binary file removed images/choosesource.png
Binary file not shown.
Binary file removed images/config.png
Binary file not shown.
Binary file added images/configure.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/create-new.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed images/currentfunction.png
Binary file not shown.
Binary file added images/function-matching-full.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/installed.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed images/matchedfunctions.png
Binary file not shown.
Binary file removed images/output.gif
Binary file not shown.
Binary file removed images/plugintoolbar.png
Binary file not shown.
Binary file removed images/processbinary.png
Binary file not shown.
8 changes: 4 additions & 4 deletions reai_toolkit/features/ai_decompiler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ def __init__(self, config=None):

def register(self):
PluginCommand.register_for_address(
"RevEng.AI\\\u200b\u200b\u200bFunctions\\AI Decompiler",
"Get the AI decompiler for the current function",
"RevEng.AI\\\u200b\u200b\u200bFunctions\\AI Decompilation",
"Get the AI decompilation for the current function",
self.show_ai_decompiler_dialog,
self.is_valid
)
Expand All @@ -44,9 +44,9 @@ def show_ai_decompiler_dialog(self, bv: BinaryView, func):
self.widget.pre_tab_setup(bv, func)
return

self.dock_widget = QDockWidget("RevEng.AI | AI Decompiler", main_win)
self.dock_widget = QDockWidget("RevEng.AI | AI Decompilation", main_win)
self.widget = AIDecompilerDialog(self.config, self.ai_decompiler, bv, func)
self.dock_widget.setObjectName("RevEng.AI | AI Decompiler")
self.dock_widget.setObjectName("RevEng.AI | AI Decompilation")
self.dock_widget.setWidget(self.widget)
main_win.addDockWidget(Qt.RightDockWidgetArea, self.dock_widget)
self.dock_widget.raise_()
Expand Down
8 changes: 4 additions & 4 deletions reai_toolkit/features/ai_decompiler/ai_decompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,10 @@ def start_ai_decompiler(self, bv: BinaryView, options: Dict) -> None:
callback = options.get("callback")
analysis_id = self.config.get_analysis_id(bv)
if not analysis_id:
raise Exception("Analysis not found. Please choose one using 'Choose Source' feature.")
raise Exception("Analysis not found. Please choose one using the 'Attach to existing' feature.")
function_id = get_function_id_by_addr_util(bv, function.start, self.config)

with revengai.ApiClient(self.config.api_config) as api_client:
with self.config.create_api_client() as api_client:
api_instance = revengai.FunctionsAIDecompilationApi(api_client)
api_response_status = api_instance.get_ai_decompilation_task_status(function_id)
log_info(f"RevEng.AI | AI Decompilation task created for function at 0x{function.start:x}")
Expand All @@ -106,7 +106,7 @@ def start_ai_decompiler(self, bv: BinaryView, options: Dict) -> None:

if poll_status.lower() == "uninitialised":
try:
with revengai.ApiClient(self.config.api_config) as api_client:
with self.config.create_api_client() as api_client:
api_instance = revengai.FunctionsAIDecompilationApi(api_client)
api_response_status = api_instance.create_ai_decompilation_task(function_id)
log_info(f"RevEng.AI | AI Decompilation task created for function at 0x{function.start:x}")
Expand All @@ -126,7 +126,7 @@ def start_ai_decompiler(self, bv: BinaryView, options: Dict) -> None:

if poll_status.lower() == "completed":
log_info(f"RevEng.AI | AI Decompilation for function at 0x{function.start:x} is completed")
with revengai.ApiClient(self.config.api_config) as api_client:
with self.config.create_api_client() as api_client:
api_instance = revengai.FunctionsAIDecompilationApi(api_client)
api_response_status = api_instance.get_ai_decompilation_task_result(function_id, summarise=True, generate_inline_comments=True)

Expand Down
6 changes: 3 additions & 3 deletions reai_toolkit/features/ai_decompiler/ai_decompiler_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def __init__(self, config, ai_decompiler, bv, func):
self.init_ui()

def init_ui(self):
self.setWindowTitle("RevEng.AI: AI Decompiler")
self.setWindowTitle("RevEng.AI: AI Decompilation")
layout = QVBoxLayout()
self.setLayout(layout)
layout.addWidget(self.tabs)
Expand Down Expand Up @@ -49,7 +49,7 @@ def clear_tabs(self):

def pre_tab_setup(self, bv, func):
try:
progress_dialog = create_progress_dialog(self, "RevEng.AI", "Setting up AI Decompiler...")
progress_dialog = create_progress_dialog(self, "RevEng.AI", "Setting up AI Decompilation...")
progress_dialog.show()
QCoreApplication.processEvents()

Expand All @@ -70,7 +70,7 @@ def pre_tab_setup(self, bv, func):
layout = QVBoxLayout()

editor = QPlainTextEdit()
editor.setPlainText("Starting AI Decompiler...")
editor.setPlainText("Starting AI Decompilation...")
editor.setReadOnly(True)

editor.show()
Expand Down
28 changes: 24 additions & 4 deletions reai_toolkit/features/auto_unstrip/auto_unstrip.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,14 @@ def rename_functions(self, bv: BinaryView, options: List[Dict]):
try:
renamed_count = 0
for option in options:
if rename_function_util(bv, option['virtual_address'], option['suggested_name']):
if rename_function_util(
self.config,
bv,
option['virtual_address'],
option['suggested_name'],
option['suggested_mangled_name'],
option['source_function_id'],
):
renamed_count += 1
return True, f"Successfully renamed {renamed_count} functions"
except Exception as e:
Expand All @@ -102,7 +109,7 @@ def auto_unstrip(self, bv: BinaryView):
if self.cancelled.is_set():
return False, "Operation cancelled"

with revengai.ApiClient(self.config.api_config) as api_client:
with self.config.create_api_client() as api_client:
api_instance = revengai.FunctionsCoreApi(api_client)
api_response = api_instance.auto_unstrip(analysis_id, auto_unstrip_request)

Expand All @@ -122,15 +129,28 @@ def auto_unstrip(self, bv: BinaryView):
else:
raise Exception(api_response.error)
for match in matches:
print(match, flush=True)
try:
if self.cancelled.is_set():
return False, "Operation cancelled"

function = get_function_by_addr_util(bv, match.function_vaddr)
results.append({"virtual_address": match.function_vaddr, "current_name": function.name, "suggested_name": match.suggested_demangled_name})
results.append({
"virtual_address": match.function_vaddr,
"current_name": function.name,
"suggested_name": match.suggested_demangled_name,
"suggested_mangled_name": match.suggested_name,
"source_function_id": match.function_id,
})
except Exception as e:
log_error(f"RevEng.AI | Error getting function by address {match.function_vaddr}: {str(e)}")
results.append({"virtual_address": match.function_vaddr, "current_name": "N/A", "suggested_name": match.suggested_demangled_name})
results.append({
"virtual_address": match.function_vaddr,
"current_name": "N/A",
"suggested_name": match.suggested_demangled_name,
"suggested_mangled_name": match.suggested_name,
"source_function_id": match.function_id,
})

if self.cancelled.is_set():
return False, "Operation cancelled"
Expand Down
2 changes: 1 addition & 1 deletion reai_toolkit/features/auto_unstrip/auto_unstrip_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ def populate_results_table(self, results):
select_item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsSelectable)
select_item.setCheckState(Qt.Checked)
self.results_table.setItem(i, 0, select_item)

columns = [
(1, "virtual_address", lambda x: f"0x{x:x}" if isinstance(x, int) else str(x)),
(2, "current_name", lambda x: str(x)),
Expand Down
Loading