Skip to content

Commit 462670f

Browse files
authored
dev: change focus added
1 parent 01062f8 commit 462670f

2 files changed

Lines changed: 415 additions & 85 deletions

File tree

revengai/features/ai_decompiler/ai_decompiler.py

Lines changed: 272 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from binaryninja.interaction import InteractionHandler
33
from reait.api import RE_authentication, RE_search, RE_nearest_symbols_batch, RE_analyze_functions, RE_name_score, RE_functions_data_types, RE_functions_data_types_poll, RE_get_analysis_id_from_binary_id, RE_get_functions_from_analysis, RE_poll_ai_decompilation, RE_begin_ai_decompilation
44
from concurrent.futures import ThreadPoolExecutor, as_completed
5-
from typing import List, Dict, Tuple
5+
from typing import List, Dict, Tuple, Optional, Callable
66
import math
77
from revengai.utils.datatypes import apply_data_types as apply_data_types_util
88
import time
@@ -21,14 +21,198 @@
2121
from revengai.utils.periodic_check import PeriodicChecker
2222
from PySide6.QtWidgets import QPlainTextEdit
2323
from binaryninja import BinaryView
24-
from binaryninjaui import UIContext
24+
from binaryninjaui import UIContext, UIContextNotification
2525
from PySide6.QtCore import QTimer
2626

27+
class AddressChangeMonitor(UIContextNotification):
28+
"""
29+
Monitors address changes in Binary Ninja's UI and calls a callback function
30+
when the user navigates to a new address in the decompiler view.
31+
"""
32+
33+
def __init__(self, callback: Optional[Callable] = None):
34+
"""
35+
Initialize the address change monitor.
36+
37+
Args:
38+
callback: Optional callback function to call when address changes.
39+
Should accept parameters: (context, view, address)
40+
"""
41+
super().__init__()
42+
self.callback = callback
43+
self._registered = False
44+
self._last_address = None
45+
46+
# Register for notifications
47+
self.register()
48+
49+
def register(self):
50+
"""Register this notification with the UI context"""
51+
if not self._registered:
52+
UIContext.registerNotification(self)
53+
self._registered = True
54+
log_info("RevEng.AI | AddressChangeMonitor registered for notifications")
55+
56+
def unregister(self):
57+
"""Unregister this notification from the UI context"""
58+
if self._registered:
59+
UIContext.unregisterNotification(self)
60+
self._registered = False
61+
log_info("RevEng.AI | AddressChangeMonitor unregistered from notifications")
62+
63+
def set_callback(self, callback: Callable):
64+
"""Set or update the callback function"""
65+
self.callback = callback
66+
log_info("RevEng.AI | AddressChangeMonitor callback updated")
67+
68+
def OnViewChange(self, context, view, frame):
69+
"""Called when the view changes"""
70+
log_info(f"RevEng.AI | OnViewChange called: {view}")
71+
if self.callback:
72+
try:
73+
self.callback(context, view, None, "view_changed")
74+
except Exception as e:
75+
log_error(f"RevEng.AI | Error in view change callback: {str(e)}")
76+
77+
def OnAddressChange(self, context, view, frame, addr):
78+
"""Called when the user navigates to a new address"""
79+
# addr is a ViewLocation object, not a simple integer
80+
try:
81+
# Extract the address from the ViewLocation object
82+
if hasattr(addr, 'addr'):
83+
current_addr = addr.addr
84+
elif hasattr(addr, 'address'):
85+
current_addr = addr.address
86+
else:
87+
current_addr = addr
88+
89+
log_info(f"RevEng.AI | OnAddressChange called: {addr} (address: 0x{current_addr:x})")
90+
91+
# Avoid duplicate notifications for the same address
92+
if current_addr == self._last_address:
93+
return
94+
95+
self._last_address = current_addr
96+
97+
if self.callback:
98+
try:
99+
self.callback(context, view, current_addr, "address_changed")
100+
except Exception as e:
101+
log_error(f"RevEng.AI | Error in address change callback: {str(e)}")
102+
except Exception as e:
103+
log_error(f"RevEng.AI | Error processing address change: {str(e)}")
104+
log_info(f"RevEng.AI | OnAddressChange called with addr type: {type(addr)}, value: {addr}")
105+
106+
def OnFunctionChange(self, context, view, frame, func):
107+
"""Called when the current function changes"""
108+
if func:
109+
log_info(f"RevEng.AI | OnFunctionChange called: {func.name} at 0x{func.start:x}")
110+
if self.callback:
111+
try:
112+
self.callback(context, view, func.start, "function_changed")
113+
except Exception as e:
114+
log_error(f"RevEng.AI | Error in function change callback: {str(e)}")
115+
116+
class TimerBasedAddressMonitor:
117+
"""
118+
Alternative address monitor that uses a timer to periodically check
119+
the current address in the active context.
120+
"""
121+
122+
def __init__(self, callback: Optional[Callable] = None, interval: int = 100):
123+
"""
124+
Initialize the timer-based address monitor.
125+
126+
Args:
127+
callback: Optional callback function to call when address changes.
128+
interval: Polling interval in milliseconds (default: 100ms)
129+
"""
130+
self.callback = callback
131+
self._last_address = None
132+
self._timer = QTimer()
133+
self._timer.timeout.connect(self._check_address)
134+
self._interval = interval
135+
self._active = False
136+
137+
def start(self):
138+
"""Start monitoring address changes"""
139+
if not self._active:
140+
self._timer.start(self._interval)
141+
self._active = True
142+
log_info(f"RevEng.AI | TimerBasedAddressMonitor started (interval: {self._interval}ms)")
143+
144+
def stop(self):
145+
"""Stop monitoring address changes"""
146+
if self._active:
147+
self._timer.stop()
148+
self._active = False
149+
self._last_address = None
150+
log_info("RevEng.AI | TimerBasedAddressMonitor stopped")
151+
152+
def set_callback(self, callback: Callable):
153+
"""Set or update the callback function"""
154+
self.callback = callback
155+
log_info("RevEng.AI | TimerBasedAddressMonitor callback updated")
156+
157+
def _check_address(self):
158+
"""Check the current address and call callback if it changed"""
159+
try:
160+
ctx = UIContext.activeContext()
161+
if not ctx:
162+
return
163+
164+
# Try to get the current address from the context
165+
current_addr = None
166+
167+
# Method 1: Try to get from the current view
168+
try:
169+
view = ctx.getCurrentView()
170+
if view:
171+
current_addr = view.getCurrentAddress()
172+
except:
173+
pass
174+
175+
# Method 2: Try to get from the current function
176+
if current_addr is None:
177+
try:
178+
func = ctx.getCurrentFunction()
179+
if func:
180+
current_addr = func.start
181+
except:
182+
pass
183+
184+
# Method 3: Try to get from the current binary view
185+
if current_addr is None:
186+
try:
187+
bv = ctx.getCurrentBinaryView()
188+
if bv:
189+
# Get the current function's address
190+
funcs = list(bv.functions)
191+
if funcs:
192+
current_addr = funcs[0].start
193+
except:
194+
pass
195+
196+
if current_addr is not None and current_addr != self._last_address:
197+
self._last_address = current_addr
198+
log_info(f"RevEng.AI | Timer detected address change to: 0x{current_addr:x}")
199+
200+
if self.callback:
201+
try:
202+
self.callback(ctx, None, current_addr, "address_changed")
203+
except Exception as e:
204+
log_error(f"RevEng.AI | Error in timer-based address change callback: {str(e)}")
205+
206+
except Exception as e:
207+
log_error(f"RevEng.AI | Error in timer-based address check: {str(e)}")
208+
27209
class AIDecompiler:
28210
def __init__(self, config):
29211
self.config = config
30212
self._current_checker = None
31-
self._track_timer = None # Add timer instance variable
213+
self._track_timer = None
214+
self._address_monitor = None # Store the address monitor instance
215+
self._timer_monitor = None # Store the timer-based monitor instance
32216

33217
def stop_ai_decompiler(self):
34218
"""Stop the current AI decompiler checking"""
@@ -50,11 +234,90 @@ def stop_tracking(self):
50234
except Exception as e:
51235
log_error(f"RevEng.AI | Error stopping active line tracking: {str(e)}")
52236

237+
def start_address_tracking(self, callback: Optional[Callable] = None, use_timer: bool = True):
238+
"""
239+
Start tracking address changes in the Binary Ninja UI.
240+
241+
Args:
242+
callback: Optional callback function to call when address changes.
243+
Should accept parameters: (context, view, address, change_type)
244+
where change_type can be "address_changed", "function_changed", or "view_changed"
245+
use_timer: Whether to also use timer-based monitoring as a fallback
246+
"""
247+
try:
248+
# Stop any existing monitors
249+
self.stop_address_tracking()
250+
251+
# Create notification-based monitor
252+
self._address_monitor = AddressChangeMonitor(callback)
253+
log_info("RevEng.AI | Started notification-based address tracking")
254+
255+
# Create timer-based monitor as fallback if requested
256+
if use_timer:
257+
self._timer_monitor = TimerBasedAddressMonitor(callback)
258+
self._timer_monitor.start()
259+
log_info("RevEng.AI | Started timer-based address tracking")
260+
261+
except Exception as e:
262+
log_error(f"RevEng.AI | Error starting address tracking: {str(e)}")
263+
264+
def stop_address_tracking(self):
265+
"""Stop tracking address changes"""
266+
try:
267+
if self._address_monitor:
268+
self._address_monitor.unregister()
269+
self._address_monitor = None
270+
log_info("RevEng.AI | Stopped notification-based address tracking")
271+
272+
if self._timer_monitor:
273+
self._timer_monitor.stop()
274+
self._timer_monitor = None
275+
log_info("RevEng.AI | Stopped timer-based address tracking")
276+
277+
except Exception as e:
278+
log_error(f"RevEng.AI | Error stopping address tracking: {str(e)}")
279+
280+
def set_address_tracking_callback(self, callback: Callable):
281+
"""
282+
Set or update the callback for address tracking.
283+
284+
Args:
285+
callback: Function to call when address changes.
286+
Should accept parameters: (context, view, address, change_type)
287+
"""
288+
try:
289+
if self._address_monitor:
290+
self._address_monitor.set_callback(callback)
291+
if self._timer_monitor:
292+
self._timer_monitor.set_callback(callback)
293+
else:
294+
# If no monitor exists, create one
295+
self.start_address_tracking(callback)
296+
except Exception as e:
297+
log_error(f"RevEng.AI | Error setting address tracking callback: {str(e)}")
53298

54299
def start_ai_decompiler(self, bv: BinaryView, options: Dict) -> None:
55300
"""Match functions from the binary against RevEng.AI database"""
56301
try:
57-
ClickMonitor()
302+
# Example of how to use address tracking with AI decompiler
303+
def address_change_callback(context, view, addr, change_type):
304+
"""Example callback for address changes"""
305+
if change_type == "address_changed" and addr is not None:
306+
log_info(f"RevEng.AI | Address changed to 0x{addr:x} - could trigger AI decompilation here")
307+
# You can add your custom logic here, such as:
308+
# - Automatically starting AI decompilation for the new function
309+
# - Updating UI elements
310+
# - Triggering other analysis
311+
312+
# Example: Get function at the new address
313+
if bv:
314+
functions = bv.get_functions_containing(addr)
315+
if functions:
316+
function = functions[0]
317+
log_info(f"RevEng.AI | Function at new address: {function.name} at 0x{function.start:x}")
318+
319+
# Start address tracking with both notification and timer-based monitoring
320+
self.start_address_tracking(address_change_callback, use_timer=True)
58321

59322
log_info("RevEng.AI | Starting function searching in portal")
60323
editor = options.get("editor")
@@ -107,13 +370,15 @@ def start_ai_decompiler(self, bv: BinaryView, options: Dict) -> None:
107370
log_info(f"RevEng.AI | AI Decompilation for function at 0x{function.start:x} is completed")
108371
callback(editor, res.get("data").get("decompilation"))
109372

373+
if poll_status == "error":
374+
log_info(f"RevEng.AI | AI Decompilation for function at 0x{function.start:x} failed")
375+
callback(editor, "AI Decompilation failed.")
376+
110377
except Exception as e:
111378
log_error(f"RevEng.AI | Error in AI decompiler: {str(e)}")
112379
return False, str(e)
113-
114-
from binaryninja import PluginCommand, BinaryView
115-
from binaryninjaui import UIContext, UIContextNotification
116380

381+
# Legacy ClickMonitor class for backward compatibility
117382
class ClickMonitor(UIContextNotification):
118383
def __init__(self):
119384
log_info("RevEng.AI | ClickMonitor initialized")

0 commit comments

Comments
 (0)