Skip to content

Commit c182e9b

Browse files
committed
implementing choose source and loading functions
1 parent f10129d commit c182e9b

15 files changed

Lines changed: 391 additions & 57 deletions

features/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from .configuration import ConfigurationFeature
22
from .upload import UploadFeature
3-
from .autounstrip import AutoUnstripFeature
3+
from .auto_unstrip import AutoUnstripFeature
4+
from .choose_source import ChooseSourceFeature
45

5-
__all__ = ['ConfigurationFeature', 'UploadFeature', 'AutoUnstripFeature']
6+
__all__ = ['ConfigurationFeature', 'UploadFeature', 'AutoUnstripFeature', 'ChooseSourceFeature']
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
from binaryninja import PluginCommand, log_info, BinaryView
2-
from .autounstrip import AutoUnstrip
3-
from .autounstrip_dialog import AutoUnstripDialog
2+
from .auto_unstrip import AutoUnstrip
3+
from .auto_unstrip_dialog import AutoUnstripDialog
44
from revengai_bn.utils import BaseAuthFeature
55

66
class AutoUnstripFeature(BaseAuthFeature):
77
def __init__(self, config=None):
88
super().__init__(config)
9-
self.autounstrip = AutoUnstrip(config)
9+
self.auto_unstrip = AutoUnstrip(config)
1010
log_info("RevEng.AI | AutoUnstrip Feature initialized")
1111

1212
def register(self):
1313
PluginCommand.register(
1414
"RevEng.AI\\AutoUnstrip",
1515
"Attempt to recover stripped function names",
16-
self.show_autounstrip_dialog,
16+
self.show_auto_unstrip_dialog,
1717
self.is_valid
1818
)
1919
log_info("RevEng.AI | AutoUnstrip Feature registered")
2020

21-
def show_autounstrip_dialog(self, bv: BinaryView):
21+
def show_auto_unstrip_dialog(self, bv: BinaryView):
2222
log_info("RevEng.AI | Opening AutoUnstrip dialog")
23-
dialog = AutoUnstripDialog(self.config, self.autounstrip, bv)
23+
dialog = AutoUnstripDialog(self.config, self.auto_unstrip, bv)
2424
dialog.exec_()
2525

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,7 @@ def __init__(self, config):
1010
self.auto_unstrip_distance = 0.09999999999999998
1111
self.base_addr = None
1212
self.path = None
13-
self._analysed_functions = {}
14-
self._functions = []
15-
self.function_ids = []
16-
self.max_workers = 4 # Number of parallel threads
17-
18-
def _get_all_functions(self):
19-
return list(self.bv.functions)
20-
21-
def _get_analysed_functions(self):
22-
return self._analysed_functions
23-
24-
def _get_sync_analysed_ids_local(self):
25-
return self.function_ids
13+
self.max_workers = 4
2614

2715
def _rename_function(self, bv: BinaryView, addr: int, new_name: str, new_name_mangled: str) -> bool:
2816
try:
@@ -38,7 +26,7 @@ def _rename_function(self, bv: BinaryView, addr: int, new_name: str, new_name_ma
3826
new_symbol = Symbol(SymbolType.FunctionSymbol, addr, new_name_mangled)
3927
bv.define_user_symbol(new_symbol)
4028

41-
log_info(f"RevEng.AI | Renamed function at {hex(addr)} to {new_name}")
29+
log_info(f"RevEng.AI | Renamed function at {hex(addr)} to {new_name_mangled}")
4230
return True
4331

4432
except Exception as e:
@@ -90,7 +78,6 @@ def auto_unstrip(self, bv: BinaryView):
9078
self.path = bv.file.filename
9179
log_info(f"RevEng.AI | Path: {self.path}")
9280
log_info(f"RevEng.AI | Binary ID: {self.config.binary_id}")
93-
self._analysed_functions = {}
9481

9582
results = RE_search(fpath=self.path).json()["query_results"]
9683
log_info(f"RevEng.AI | Search Results: {results}")
@@ -106,23 +93,19 @@ def auto_unstrip(self, bv: BinaryView):
10693
for func in analyzed_functions
10794
}
10895

109-
# Use fixed chunk size of 50
11096
chunk_size = 50
11197
chunks = [function_ids[i:i + chunk_size] for i in range(0, len(function_ids), chunk_size)]
11298

11399
log_info(f"RevEng.AI | Processing {len(function_ids)} functions in {len(chunks)} chunks of size {chunk_size}")
114100

115-
# Process chunks in parallel
116101
total_renamed = 0
117102
all_errors = []
118103
with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
119-
# Submit all tasks
120104
future_to_chunk = {
121105
executor.submit(self._process_batch, chunk, id_to_addr, bv): i
122106
for i, chunk in enumerate(chunks)
123107
}
124108

125-
# Wait for all tasks to complete and collect results
126109
for future in as_completed(future_to_chunk):
127110
chunk_index = future_to_chunk[future]
128111
try:
@@ -146,12 +129,4 @@ def auto_unstrip(self, bv: BinaryView):
146129

147130
except Exception as e:
148131
log_error(f"RevEng.AI | Error: {str(e)}")
149-
return False, str(e)
150-
"""
151-
self._functions = self._get_all_functions()
152-
self._get_analysed_functions()
153-
self.function_ids = self._get_sync_analysed_ids_local()
154-
155-
log_info(f"RevEng.AI | {bv.file.original_filename}")
156-
"""
157-
132+
return False, str(e)

features/autounstrip/autounstrip_dialog.py renamed to features/auto_unstrip/auto_unstrip_dialog.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@
88
from PySide6.QtCore import QCoreApplication
99
from PySide6.QtWidgets import QMessageBox
1010
from revengai_bn.utils import create_progress_dialog
11-
from .autounstrip_thread import AutoUnstripThread
11+
from .auto_unstrip_thread import AutoUnstripThread
1212
import os
1313

1414
class AutoUnstripDialog(QDialog):
15-
def __init__(self, config, autounstrip, bv):
15+
def __init__(self, config, auto_unstrip, bv):
1616
super().__init__()
1717
self.config = config
18-
self.autounstrip = autounstrip
18+
self.auto_unstrip = auto_unstrip
1919
self.bv = bv
2020
self.init_ui()
2121

@@ -52,7 +52,7 @@ def init_ui(self):
5252
background-color: #4400ff;
5353
}
5454
""")
55-
self.save_button.clicked.connect(self.auto_unstrip)
55+
self.save_button.clicked.connect(self._auto_unstrip)
5656
self.cancel_button = QPushButton("Cancel")
5757
self.cancel_button.setStyleSheet("""
5858
QPushButton {
@@ -68,13 +68,13 @@ def init_ui(self):
6868

6969
self.setLayout(layout)
7070

71-
def auto_unstrip(self):
71+
def _auto_unstrip(self):
7272
log_info("RevEng.AI | Auto Unstripping binary")
7373
# Create and show progress dialog using utility function
7474
self.progress = create_progress_dialog(self, "RevEng.AI Auto Unstrip", "Auto Unstripping binary...")
7575

7676
# Create and start upload thread
77-
self.auto_unstrip_thread = AutoUnstripThread(self.autounstrip, self.bv)
77+
self.auto_unstrip_thread = AutoUnstripThread(self.auto_unstrip, self.bv)
7878
self.auto_unstrip_thread.finished.connect(self._on_auto_unstrip_finished)
7979
self.auto_unstrip_thread.start()
8080

features/autounstrip/autounstrip_thread.py renamed to features/auto_unstrip/auto_unstrip_thread.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
class AutoUnstripThread(QThread):
44
finished = Signal(bool, str) # Signal for success/failure and error message
55

6-
def __init__(self, autounstrip, bv):
6+
def __init__(self, auto_unstrip, bv):
77
super().__init__()
8-
self.autounstrip = autounstrip
8+
self.auto_unstrip = auto_unstrip
99
self.bv = bv
1010

1111
def run(self):
1212
try:
13-
success, message = self.autounstrip.auto_unstrip(self.bv)
13+
success, message = self.auto_unstrip.auto_unstrip(self.bv)
1414
if success:
1515
self.finished.emit(True, message)
1616
else:

features/choose_source/__init__.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from binaryninja import PluginCommand, log_info, BinaryView
2+
from .choose_source import ChooseSource
3+
from .choose_source_dialog import ChooseSourceDialog
4+
from revengai_bn.utils import BaseAuthFeature
5+
6+
class ChooseSourceFeature(BaseAuthFeature):
7+
def __init__(self, config=None):
8+
super().__init__(config)
9+
self.choose_source = ChooseSource(config)
10+
log_info("RevEng.AI | Choose Source Feature initialized")
11+
12+
def register(self):
13+
PluginCommand.register(
14+
"RevEng.AI\\Choose Source",
15+
"Choose a source for the binary analysis",
16+
self.show_choose_source_dialog,
17+
self.is_valid
18+
)
19+
log_info("RevEng.AI | Choose Source Feature registered")
20+
21+
def show_choose_source_dialog(self, bv: BinaryView):
22+
log_info("RevEng.AI | Opening Choose Source dialog")
23+
dialog = ChooseSourceDialog(self.config, self.choose_source, bv)
24+
dialog.exec_()
25+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from PySide6.QtCore import QThread, Signal
2+
from binaryninja import log_error
3+
4+
class AnalysisLoadThread(QThread):
5+
finished = Signal(list)
6+
error = Signal(str)
7+
8+
def __init__(self, choose_source, bv):
9+
super().__init__()
10+
self.choose_source = choose_source
11+
self.bv = bv
12+
13+
def run(self):
14+
try:
15+
analysis = self.choose_source.get_analysis(self.bv)
16+
if not len(analysis):
17+
raise Exception("No analysis found, try processing the binary again.")
18+
self.finished.emit(analysis)
19+
except Exception as e:
20+
log_error(f"RevEng.AI | Failed to load analysis: {str(e)}")
21+
self.error.emit(str(e))
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from binaryninja import BinaryView, log_info, log_error, Symbol, SymbolType
2+
from reait.api import RE_authentication, RE_search, RE_nearest_symbols_batch, RE_analyze_functions
3+
from concurrent.futures import ThreadPoolExecutor, as_completed
4+
from typing import List, Dict, Tuple
5+
import math
6+
7+
class ChooseSource:
8+
def __init__(self, config):
9+
self.config = config
10+
11+
def choose_source(self, bv: BinaryView, chose: str):
12+
try:
13+
log_info(f"RevEng.AI | Chose: {chose}")
14+
binary_id = chose.split("ID: ")[1].split(" -")[0]
15+
self.config.binary_id = binary_id
16+
log_info(f"RevEng.AI | Binary ID: {self.config.binary_id}")
17+
# TODO: implement array like to store binary id and binaryhash and filesize
18+
return True
19+
except Exception as e:
20+
log_error(f"RevEng.AI | Failed to choose source: {str(e)}")
21+
return False
22+
23+
def get_analysis(self, bv: BinaryView):
24+
try:
25+
self.base_addr = bv.image_base
26+
self.path = bv.file.filename
27+
log_info(f"RevEng.AI | Path: {self.path}")
28+
log_info(f"RevEng.AI | Binary ID: {self.config.binary_id}")
29+
30+
results = RE_search(fpath=self.path).json()["query_results"]
31+
32+
if not len(results):
33+
raise Exception("Binary not found in RevEng.AI, try processing the binary again.")
34+
35+
options = []
36+
for result in results:
37+
option = f"Name: {result['binary_name'][:10]}{'...' if len(result['binary_name']) > 10 else ''} - ID: {result['binary_id']} - Model: {result['model_name']} - Created at: {result['creation'].split('T')[0]} {result['creation'].split('T')[1].split('.')[0]}"
38+
options.append(option)
39+
log_info(f"RevEng.AI | Analysis: {option}")
40+
# TODO: put the current binary id first in the list
41+
42+
return options
43+
except Exception as e:
44+
log_error(f"RevEng.AI | Failed to get analysis: {str(e)}")
45+
return []

0 commit comments

Comments
 (0)