Skip to content

Commit c254e4d

Browse files
committed
fix: function matching (batch) api update
1 parent bb8d841 commit c254e4d

3 files changed

Lines changed: 76 additions & 17 deletions

File tree

reai_toolkit/features/match_functions/match_functions.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,9 @@ def parse_confidence(item):
137137
"icon_text": "Failed",
138138
"function_address": function_by_distance.matched_functions[0].function_vaddr,
139139
"function_name": "N/A",
140+
"source_function_id": function_by_distance.function_id,
140141
"matched_function_name": function_by_distance.matched_functions[0].function_name,
142+
"matched_mangled_name": function_by_distance.matched_functions[0].mangled_name,
141143
"signature": "N/A",
142144
"matched_hash": function_by_distance.matched_functions[0].sha_256_hash,
143145
"matched_binary_name": function_by_distance.matched_functions[0].binary_name,
@@ -219,7 +221,7 @@ def parse_confidence(item):
219221
raise e
220222

221223

222-
def _process_rename_batch(self, chunk: List[Dict], bv: BinaryView, deci: DecompilerInterface = None) -> Tuple[int, int]:
224+
def _process_rename_batch(self, config, chunk: List[Dict], bv: BinaryView, deci: DecompilerInterface = None) -> Tuple[int, int]:
223225
try:
224226
log_info(f"RevEng.AI | Processing chunk of {len(chunk)} functions")
225227
renamed_count = 0
@@ -230,7 +232,7 @@ def _process_rename_batch(self, chunk: List[Dict], bv: BinaryView, deci: Decompi
230232
return 0, 0
231233

232234
addr = int(result['function_address'])
233-
if rename_function_util(bv, addr, result["matched_function_name"]):
235+
if rename_function_util(config, bv, addr, result["matched_function_name"], result["matched_mangled_name"], result["source_function_id"]):
234236
renamed_count += 1
235237

236238
if result.get('signature_data', None) is not None:
@@ -270,7 +272,7 @@ def rename_functions(self, bv: BinaryView, selected_results: List[Dict]) -> List
270272

271273
with ThreadPoolExecutor(max_workers=4) as executor:
272274
future_to_chunk = {
273-
executor.submit(self._process_rename_batch, chunk, bv, deci): i
275+
executor.submit(self._process_rename_batch, self.config, chunk, bv, deci): i
274276
for i, chunk in enumerate(chunks)
275277
}
276278

reai_toolkit/features/match_functions/match_functions_dialog.py

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -189,21 +189,22 @@ def init_ui(self):
189189

190190

191191
self.results_table = QTableWidget()
192-
self.results_table.setColumnCount(8)
192+
self.results_table.setColumnCount(9)
193193
self.results_table.setHorizontalHeaderLabels([
194-
"Virtual Address", "Function Name", "Matched Function", "Signature", "Similarity", "Confidence", "Matched Hash", "Matched Binary"
194+
"Select", "Virtual Address", "Function Name", "Matched Function", "Signature", "Similarity", "Confidence", "Matched Hash", "Matched Binary"
195195
])
196196
self.results_table.setSelectionMode(QAbstractItemView.NoSelection)
197197

198198
header = self.results_table.horizontalHeader()
199199
header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
200-
header.setSectionResizeMode(1, QHeaderView.Stretch)
200+
header.setSectionResizeMode(1, QHeaderView.ResizeToContents)
201201
header.setSectionResizeMode(2, QHeaderView.Stretch)
202-
header.setSectionResizeMode(3, QHeaderView.ResizeToContents)
202+
header.setSectionResizeMode(3, QHeaderView.Stretch)
203203
header.setSectionResizeMode(4, QHeaderView.ResizeToContents)
204204
header.setSectionResizeMode(5, QHeaderView.ResizeToContents)
205-
header.setSectionResizeMode(6, QHeaderView.Stretch)
205+
header.setSectionResizeMode(6, QHeaderView.ResizeToContents)
206206
header.setSectionResizeMode(7, QHeaderView.Stretch)
207+
header.setSectionResizeMode(8, QHeaderView.Stretch)
207208
self.results_table.setAlternatingRowColors(True)
208209
self.results_table.verticalHeader().setVisible(False)
209210

@@ -306,7 +307,8 @@ def on_matching_finished(self, success, data):
306307

307308

308309
def populate_results_table(self, results):
309-
self.selected_results.clear()
310+
# Store results first so _update_selected_results can access them
311+
self.all_results = results
310312

311313
self.results_table.setRowCount(0)
312314
self.results_table.setRowCount(len(results))
@@ -336,6 +338,21 @@ def populate_results_table(self, results):
336338
""")
337339

338340
for row, match in enumerate(results):
341+
# Add checkbox in first column
342+
checkbox = QCheckBox()
343+
if match.get("icon_text", "Failed") == "Success":
344+
checkbox.setChecked(True)
345+
else:
346+
checkbox.setChecked(False)
347+
checkbox.stateChanged.connect(lambda state, m=match: self._on_checkbox_changed(state, m))
348+
349+
checkbox_widget = QWidget()
350+
checkbox_layout = QHBoxLayout(checkbox_widget)
351+
checkbox_layout.addWidget(checkbox)
352+
checkbox_layout.setAlignment(Qt.AlignCenter)
353+
checkbox_layout.setContentsMargins(0, 0, 0, 0)
354+
self.results_table.setCellWidget(row, 0, checkbox_widget)
355+
339356
column_data = [
340357
"function_address",
341358
"function_name",
@@ -347,7 +364,7 @@ def populate_results_table(self, results):
347364
"matched_binary_name"
348365
]
349366

350-
for column, field in enumerate(column_data, start=0):
367+
for column, field in enumerate(column_data, start=1):
351368
value = match.get(field, "N/A")
352369
if field == "function_address":
353370
if isinstance(value, int):
@@ -373,17 +390,38 @@ def populate_results_table(self, results):
373390
icon_path = f"{os.path.dirname(__file__)}/../../images/success.png"
374391
item.setToolTip(value)
375392
item.setIcon(QIcon(icon_path))
376-
#item.setIconAlignment(Qt.AlignCenter)
377393
item.setText("")
378394
else:
379395
item.setToolTip(value)
380396
self.results_table.setItem(row, column, item)
381-
382-
if match.get("icon_text", "Failed") == "Success":
383-
self.selected_results.append(match)
397+
398+
# Initialize selected_results based on checkboxes
399+
self._update_selected_results()
400+
401+
def _on_checkbox_changed(self, state, match):
402+
self._update_selected_results()
403+
404+
def _update_selected_results(self):
405+
self.selected_results.clear()
406+
for row in range(self.results_table.rowCount()):
407+
checkbox_widget = self.results_table.cellWidget(row, 0)
408+
if checkbox_widget:
409+
checkbox = checkbox_widget.findChild(QCheckBox)
410+
if checkbox and checkbox.isChecked():
411+
# Get the match data from the row
412+
addr_item = self.results_table.item(row, 1)
413+
if addr_item:
414+
addr_text = addr_item.text()
415+
# Find the corresponding match in all_results
416+
if hasattr(self, 'all_results'):
417+
for match in self.all_results:
418+
match_addr = match.get("function_address", "")
419+
if isinstance(match_addr, int):
420+
match_addr = f"0x{match_addr:x}"
421+
if match_addr == addr_text:
422+
self.selected_results.append(match)
423+
break
384424
log_info(f"RevEng.AI | Selected results: {len(self.selected_results)}")
385-
for result in self.selected_results:
386-
log_info(f"RevEng.AI | Result: {result}")
387425

388426

389427
def start_fetching_data_types(self):

reai_toolkit/utils/core/binary_ninja.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,24 @@
55
from os.path import isfile
66
from os import access, R_OK
77

8-
def rename_function(bv: BinaryView, addr: int, new_name: str, data_type: dict = None) -> bool:
8+
9+
def _rename_in_portal(config: revengai.Configuration, function_id:int, new_name:str, new_mangled_name:str):
10+
try:
11+
with revengai.ApiClient(config.api_config) as api_client:
12+
api_instance = revengai.FunctionsRenamingHistoryApi(api_client)
13+
api_instance.rename_function_id(
14+
function_id=function_id,
15+
function_rename=revengai.FunctionRename(
16+
new_name=new_name,
17+
new_mangled_name=new_mangled_name
18+
)
19+
)
20+
log_info(f"RevEng.AI | Renamed function in portal at {function_id} to {new_name}")
21+
22+
except Exception as e:
23+
log_error(f"RevEng.AI | Error renaming function in portal at {function_id}: {str(e)}")
24+
25+
def rename_function(config, bv: BinaryView, addr: int, new_name: str, new_mangled_name: str, source_function_id: int, data_type: dict = None) -> bool:
926
try:
1027
func = bv.get_function_at(addr)
1128
if not func:
@@ -22,6 +39,8 @@ def rename_function(bv: BinaryView, addr: int, new_name: str, data_type: dict =
2239

2340
new_symbol = Symbol(SymbolType.FunctionSymbol, addr, new_name)
2441
bv.define_user_symbol(new_symbol)
42+
43+
_rename_in_portal(config, source_function_id, new_name, new_mangled_name)
2544

2645
log_info(f"RevEng.AI | Renamed function at {hex(addr)} to {new_name}")
2746
return True

0 commit comments

Comments
 (0)