summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorGlenn Smith <glenn@vector35.com>2024-06-05 13:08:09 -0400
committerGlenn Smith <glenn@vector35.com>2024-06-06 12:11:05 -0400
commit9319302615964227234021606063023761363473 (patch)
tree90bd7fb609b2feb6958f286bf7f0c84cdd3b5907 /python
parent4ef7a93c06417147b9a05b19fdb700739d2f7e5f (diff)
Add api/modular system to python magic variables
Diffstat (limited to 'python')
-rw-r--r--python/scriptingprovider.py953
1 files changed, 661 insertions, 292 deletions
diff --git a/python/scriptingprovider.py b/python/scriptingprovider.py
index 8eb6e7b8..d659637b 100644
--- a/python/scriptingprovider.py
+++ b/python/scriptingprovider.py
@@ -21,6 +21,7 @@
import abc
import code
import ctypes
+import dataclasses
import importlib
import os
import re
@@ -29,9 +30,10 @@ import sys
import threading
import traceback
+from collections.abc import Callable
from ctypes.util import find_library
from pathlib import Path
-from typing import Generator, Optional, List, Tuple, Dict
+from typing import Generator, Optional, List, Tuple, Dict, Any
from typing import Type as TypeHintType
# Just windows things...
@@ -398,7 +400,7 @@ class ScriptingProvider(metaclass=_ScriptingProviderMetaclass):
_registered_providers = []
name = ''
apiName = ''
- instance_class: Optional['ScriptingInstance'] = None
+ instance_class: Optional[TypeHintType[ScriptingInstance]] = None
def __init__(self, handle=None):
if handle is not None:
@@ -579,8 +581,8 @@ class BlacklistedDict(dict):
def __setitem__(self, k, v):
if self.blacklist_enabled and k in self.__blacklist:
- log_error(
- 'Setting variable "{}" will have no affect as it is automatically controlled by the ScriptingProvider.'.
+ sys.stderr.write(
+ 'Setting variable "{}" will have no affect as it is automatically controlled by the ScriptingProvider.\n'.
format(k)
)
super(BlacklistedDict, self).__setitem__(k, v)
@@ -588,6 +590,15 @@ class BlacklistedDict(dict):
def enable_blacklist(self, enabled):
self.__enable_blacklist = enabled
+ def add_blacklist_item(self, item):
+ self.__blacklist.add(item)
+
+ def remove_blacklist_item(self, item):
+ self.__blacklist.remove(item)
+
+ def is_blacklisted_item(self, item):
+ return item in self.__blacklist
+
@property
def blacklist_enabled(self):
return self._blacklist_enabled
@@ -655,40 +666,9 @@ class PythonScriptingInstance(ScriptingInstance):
def __init__(self, instance):
super(PythonScriptingInstance.InterpreterThread, self).__init__()
self.instance = instance
- # Note: "current_address", "here", "current_comment", "current_selection", and "current_raw_offset" are
- # interactive auto-variables (i.e. can be set by user and programmatically)
blacklisted_vars = {
- "current_thread",
- "current_view",
- "current_project",
- "bv",
- "current_function",
- "current_basic_block",
- "current_llil",
- "current_mlil",
- "current_hlil",
- "dbg",
- "current_data_var",
- "current_symbol",
- "current_symbols",
- "current_segment",
- "current_sections",
- "current_comment"
- "current_ui_context",
- "current_ui_view_frame",
- "current_ui_view",
- "current_ui_action_handler",
- "current_ui_view_location",
- "current_ui_action_context",
- "current_token",
- "current_variable",
"get_selected_data",
"write_at_cursor",
- "current_il_index",
- "current_il_function",
- "current_il_instruction",
- "current_il_instructions",
- "current_il_basic_block"
}
self.locals = BlacklistedDict(
blacklisted_vars, {"__name__": "__console__", "__doc__": None, "binaryninja": sys.modules[__name__]}
@@ -709,6 +689,7 @@ class PythonScriptingInstance(ScriptingInstance):
self.current_project = None
# Selections that were current as of last issued command
+ self.active_project = None
self.active_view = None
self.active_func = None
self.active_block = None
@@ -721,6 +702,8 @@ class PythonScriptingInstance(ScriptingInstance):
self.selection_start_il_index = 0
self.active_il_function = None
+ self.update_magic_variables()
+
self.locals.blacklist_enabled = False
self.locals["get_selected_data"] = self.get_selected_data
self.locals["write_at_cursor"] = self.write_at_cursor
@@ -744,6 +727,13 @@ from binaryninja import *
with open(startup_file, 'r') as f:
self.interpreter.runsource(f.read(), filename="startup.py", symbol="exec")
+ def update_magic_variables(self):
+ for (name, var) in PythonScriptingProvider.magic_variables.items():
+ if var.set_value is None:
+ self.locals.add_blacklist_item(name)
+ elif self.locals.is_blacklisted_item(name):
+ self.locals.remove_blacklist_item(name)
+
def execute(self, _code):
self.code = _code
self.event.set()
@@ -824,279 +814,61 @@ from binaryninja import *
self.active_selection_end = self.current_selection_end
self.active_dbg = self.current_dbg
self.active_project = self.current_project
- if self.active_view is not None:
- self.active_file_offset = self.active_view.get_data_offset_for_address(self.active_addr)
- else:
- self.active_file_offset = None
self.locals.blacklist_enabled = False
- self.locals["current_thread"] = self.interpreter
- self.locals["current_view"] = self.active_view
- self.locals["current_project"] = self.active_project
- self.locals["bv"] = self.active_view
- self.locals["current_function"] = self.active_func
- self.locals["current_basic_block"] = self.active_block
- self.locals["current_address"] = self.active_addr
- self.locals["here"] = self.active_addr
- self.locals["current_selection"] = (self.active_selection_begin, self.active_selection_end)
- self.locals["current_raw_offset"] = self.active_file_offset
- self.locals["dbg"] = self.active_dbg
- if self.active_func is None:
- self.locals["current_llil"] = None
- self.locals["current_mlil"] = None
- self.locals["current_hlil"] = None
- self.locals["current_llil_ssa"] = None
- self.locals["current_mlil_ssa"] = None
- self.locals["current_hlil_ssa"] = None
- else:
- current_llil = self.active_func.llil_if_available
- current_mlil = self.active_func.mlil_if_available
- current_hlil = self.active_func.hlil_if_available
- self.locals["current_llil"] = current_llil
- self.locals["current_mlil"] = current_mlil
- self.locals["current_hlil"] = current_hlil
- if current_llil is not None:
- self.locals["current_llil_ssa"] = current_llil.ssa_form
- if current_mlil is not None:
- self.locals["current_mlil_ssa"] = current_mlil.ssa_form
- if current_hlil is not None:
- self.locals["current_hlil_ssa"] = current_hlil.ssa_form
-
- if self.active_view is not None:
- self.locals["current_data_var"] = self.active_view.get_data_var_at(self.active_addr)
- self.locals["current_symbol"] = self.active_view.get_symbol_at(self.active_addr)
- self.locals["current_symbols"] = self.active_view.get_symbols(self.active_addr, 1)
- self.locals["current_segment"] = self.active_view.get_segment_at(self.active_addr)
- self.locals["current_sections"] = self.active_view.get_sections_at(self.active_addr)
+ # Clear old values of magic variables first, so we don't update with stale data
+ for name in PythonScriptingProvider.magic_variables.keys():
+ if name in self.locals:
+ del self.locals[name]
- if self.active_func is None:
- self.locals["current_comment"] = self.active_view.get_comment_at(self.active_addr)
- else:
- if self.active_func.get_comment_at(self.active_addr) != '':
- self.locals["current_comment"] = self.active_func.get_comment_at(self.active_addr)
- else:
- self.locals["current_comment"] = self.active_view.get_comment_at(self.active_addr)
+ # Apply registered magic variables
+ vars = list(PythonScriptingProvider.magic_variables.items())
+ used_vars = set()
+ while len(vars) > 0:
+ (name, var) = vars.pop(0)
- else:
- self.locals["current_data_var"] = None
- self.locals["current_symbol"] = None
- self.locals["current_symbols"] = []
- self.locals["current_segment"] = None
- self.locals["current_sections"] = None
- self.locals["current_comment"] = None
+ # Vars depending on others should make sure their deps have loaded first
+ # TODO: Is this O(n^2)? Probably, but shouldn't be that big of a deal
+ needs_deps = False
+ for dep in var.depends_on:
+ if dep in PythonScriptingProvider.magic_variables.keys() and dep not in used_vars:
+ needs_deps = True
+ break
+ if needs_deps:
+ # Add to the end and we'll get to it later
+ vars.append((name, var))
+ continue
- # Keep a copy of the original version of this, so we can check if it has changed
- self.cached_locals["current_comment"] = self.locals["current_comment"]
+ used_vars.add(name)
- ui_locals_valid = False
- if binaryninja.core_ui_enabled():
try:
- from binaryninjaui import UIContext
- from binaryninja import Variable, FunctionGraphType
-
- context = UIContext.activeContext()
- action_handler = None
- view_frame = None
- view = None
- project = None
- if context is not None:
- action_handler = context.getCurrentActionHandler()
- view_frame = context.getCurrentViewFrame()
- view = context.getCurrentView()
- project = context.getProject()
-
- view_location = view_frame.getViewLocation() if view_frame is not None else None
- action_context = None
- if view is not None:
- action_context = view.actionContext()
- elif action_handler is not None:
- action_context = action_handler.actionContext()
-
- if view is not None:
- self.selection_start_il_index = view.getSelectionStartILInstructionIndex()
-
- token_state = None
- token = None
- var = None
- if action_context is not None:
- token_state = action_context.token
- token = token_state.token if token_state.valid else None
- var = token_state.localVar if token_state.localVarValid else None
- if var and self.active_func:
- var = Variable.from_core_variable(self.active_func, var)
-
- if view_location is not None and view_location.isValid():
- self.active_il_index = view_location.getInstrIndex()
- ilType = view_location.getILViewType()
- if ilType == FunctionGraphType.LowLevelILFunctionGraph:
- self.active_il_function = self.locals["current_llil"]
- elif ilType == FunctionGraphType.LowLevelILSSAFormFunctionGraph:
- self.active_il_function = self.locals["current_llil_ssa"]
- elif ilType == FunctionGraphType.MediumLevelILFunctionGraph:
- self.active_il_function = self.locals["current_mlil"]
- elif ilType == FunctionGraphType.MediumLevelILSSAFormFunctionGraph:
- self.active_il_function = self.locals["current_mlil_ssa"]
- elif ilType == FunctionGraphType.HighLevelILFunctionGraph:
- self.active_il_function = self.locals["current_hlil"]
- elif ilType == FunctionGraphType.HighLevelILSSAFormFunctionGraph:
- self.active_il_function = self.locals["current_hlil_ssa"]
- else:
- self.active_il_function = None
-
- self.locals["current_il_index"] = self.active_il_index
- self.locals["current_il_function"] = self.active_il_function
- if self.active_il_function:
- try:
- self.locals["current_il_instruction"] = self.active_il_function[self.active_il_index]
- except:
- self.locals["current_il_instruction"] = None
-
- invalid_il_index = 0xffffffffffffffff
- if invalid_il_index not in (self.active_il_index, self.selection_start_il_index):
- il_start = min(self.active_il_index, self.selection_start_il_index)
- il_end = max(self.active_il_index, self.selection_start_il_index)
- self.locals["current_il_instructions"] = (self.active_il_function[i] for i in \
- range(il_start, il_end + 1))
- else:
- self.locals["current_il_instructions"] = None
-
- if self.locals["current_il_instruction"]:
- self.locals["current_il_basic_block"] = self.locals["current_il_instruction"].il_basic_block
- else:
- self.locals["current_il_instruction"] = None
- self.locals["current_il_instructions"] = None
- self.locals["current_il_basic_block"] = None
- else:
- self.locals["current_il_index"] = None
- self.locals["current_il_function"] = None
- self.locals["current_il_instruction"] = None
- self.locals["current_il_instructions"] = None
- self.locals["current_il_basic_block"] = None
- self.active_il_function = None
-
- self.locals["current_ui_context"] = context
- self.locals["current_project"] = project
- self.locals["current_ui_view_frame"] = view_frame
- self.locals["current_ui_view"] = view
- self.locals["current_ui_action_handler"] = action_handler
- self.locals["current_ui_view_location"] = view_location
- self.locals["current_ui_action_context"] = action_context
- self.locals["current_token"] = token
- self.locals["current_variable"] = var
- ui_locals_valid = True
- except ImportError:
- pass
-
- if not ui_locals_valid:
- self.locals["current_ui_context"] = None
- self.locals["current_project"] = None
- self.locals["current_ui_view_frame"] = None
- self.locals["current_ui_view"] = None
- self.locals["current_ui_action_handler"] = None
- self.locals["current_ui_view_location"] = None
- self.locals["current_ui_action_context"] = None
- self.locals["current_token"] = None
- self.locals["current_variable"] = None
- self.locals["current_il_index"] = None
- self.locals["current_il_function"] = None
- self.locals["current_il_instruction"] = None
- self.locals["current_il_instructions"] = None
- self.locals["current_il_basic_block"] = None
+ value = var.get_value(self.instance)
+ except:
+ value = None
+ self.locals[name] = value
+ self.cached_locals[name] = value
self.locals.blacklist_enabled = True
- def update_here(self):
- tryNavigate = True
- if isinstance(self.locals["here"], str) or isinstance(self.locals["current_address"], str):
- try:
- self.locals["here"] = self.active_view.parse_expression(self.locals["here"], self.active_addr)
- except ValueError as e:
- sys.stderr.write(str(e) + '\n')
- tryNavigate = False
- if tryNavigate:
- if self.locals["here"] != self.active_addr:
- if not self.active_view.file.navigate(self.active_view.file.view, self.locals["here"]):
- binaryninja.mainthread.execute_on_main_thread(
- lambda: self.locals["current_ui_context"].navigateForBinaryView(
- self.active_view, self.locals["here"]))
-
- elif self.locals["current_address"] != self.active_addr:
- if not self.active_view.file.navigate(self.active_view.file.view, self.locals["current_address"]):
- binaryninja.mainthread.execute_on_main_thread(
- lambda: self.locals["current_ui_context"].navigateForBinaryView(
- self.active_view, self.locals["current_address"]))
-
- def update_current_raw_offset(self):
- tryNavigate = True
- if isinstance(self.locals["current_raw_offset"], str):
- try:
- self.locals["current_raw_offset"] =\
- self.active_view.parse_expression(self.locals["current_raw_offset"], self.active_addr)
- except ValueError as e:
- sys.stderr.write(str(e) + '\n')
- tryNavigate = False
- if tryNavigate:
- if self.locals["current_raw_offset"] != self.active_file_offset:
- addr = self.active_view.get_address_for_data_offset(self.locals["current_raw_offset"])
- if addr is not None:
- if not self.active_view.file.navigate(self.active_view.file.view, addr):
- binaryninja.mainthread.execute_on_main_thread(
- lambda: self.locals["current_ui_context"].navigateForBinaryView(self.active_view, addr))
-
- def update_current_comment(self):
- if self.active_view is None:
- return
-
- if self.active_func is None:
- if self.cached_locals["current_comment"] != self.locals["current_comment"]:
- self.active_view.set_comment_at(self.active_addr, self.locals["current_comment"])
- else:
- if self.cached_locals["current_comment"] != '':
- # Prefer editing active view comment if one exists
- if self.cached_locals["current_comment"] != self.locals["current_comment"]:
- self.active_view.set_comment_at(self.active_addr, self.locals["current_comment"])
- else:
- if self.cached_locals["current_comment"] != self.locals["current_comment"]:
- self.active_func.set_comment_at(self.active_addr, self.locals["current_comment"])
-
- def update_current_selection(self):
- if not self.locals["current_ui_view"]:
- return
+ def apply_locals(self):
+ for (name, var) in PythonScriptingProvider.magic_variables.items():
+ if var.set_value is None:
+ continue
- if (not isinstance(self.locals["current_selection"], list)) and \
- (not isinstance(self.locals["current_selection"], tuple)):
- return
+ old_value = self.cached_locals[name]
+ new_value = self.locals[name]
- tryNavigate = True
- if isinstance(self.locals["current_selection"][0], str):
- try:
- self.locals["current_selection"][0] = self.active_view.parse_expression(
- self.locals["current_selection"][0], self.active_addr)
- except ValueError as e:
- sys.stderr.write(str(e) + '\n')
- tryNavigate = False
+ if old_value == new_value:
+ continue
- if tryNavigate and isinstance(self.locals["current_selection"][1], str):
try:
- self.locals["current_selection"][1] = self.active_view.parse_expression(
- self.locals["current_selection"][1], self.active_addr)
- except ValueError as e:
- sys.stderr.write(str(e) + '\n')
- tryNavigate = False
+ var.set_value(self.instance, old_value, new_value)
+ except:
+ sys.stderr.write(f"Exception thrown trying to update variable:\n")
+ traceback.print_exc(file=sys.stderr)
- if tryNavigate:
- if self.locals["current_selection"][0] != self.active_selection_begin or \
- self.locals["current_selection"][1] != self.active_selection_end:
- new_selection = (self.locals["current_selection"][0], self.locals["current_selection"][1])
- binaryninja.mainthread.execute_on_main_thread(
- lambda: self.locals["current_ui_view"].setSelectionOffsets(new_selection))
-
- def apply_locals(self):
- self.update_here()
- self.update_current_raw_offset()
- self.update_current_comment()
- self.update_current_selection()
+ self.cached_locals.clear()
def get_selected_data(self):
if self.active_view is None:
@@ -1259,6 +1031,32 @@ class PythonScriptingProvider(ScriptingProvider):
name = "Python"
apiName = f"python{sys.version_info.major}" # Used for plugin compatibility testing
instance_class: TypeHintType[PythonScriptingInstance] = PythonScriptingInstance
+ magic_variables: Dict[str, 'MagicVariable'] = {}
+
+ @dataclasses.dataclass
+ class MagicVariable:
+ """
+ Represents an automatically-populated (magic) variable in the python scripting console
+ """
+
+ get_value: Callable[[PythonScriptingInstance], Any]
+ """
+ Function to call, before every time a script is evaluated,
+ to get the value of the variable
+ """
+
+ set_value: Optional[Callable[[PythonScriptingInstance, Any, Any], None]]
+ """
+ (Optional) function to call after a script is evaluated, if the value of the
+ variable has changed during the course of the script. If None, a warning will be
+ printed stating that the variable is read-only.
+ Signature: (instance: PythonScriptingInstance, old_value: any, new_value: any) -> None
+ """
+
+ depends_on: List[str]
+ """
+ List of other variables whose values on which this variable's value depends
+ """
@property
def _python_bin(self) -> Optional[str]:
@@ -1471,6 +1269,48 @@ class PythonScriptingProvider(ScriptingProvider):
return False
return re.split('>|=|,', module.strip(), 1)[0] in self._satisfied_dependencies(self._python_bin)
+ @classmethod
+ def register_magic_variable(
+ cls,
+ name: str,
+ get_value: Callable[[PythonScriptingInstance], Any],
+ set_value: Optional[Callable[[PythonScriptingInstance, Any, Any], None]] = None,
+ depends_on: Optional[List[str]] = None
+ ):
+ """
+ Add a magic variable to all scripting instances created by the scripting provider
+ :param name: Variable name identifier to be used in the interpreter
+ :param get_value: Function to call, before every time a script is evaluated,
+ to get the value of the variable
+ :param set_value: (Optional) Function to call after a script is evaluated, if the
+ value of the variable has changed during the course of the script.
+ If None, a warning will be printed stating that the variable is read-only.
+ Signature:
+ (instance: PythonScriptingInstance, old_value: any, new_value: any) -> None
+ :param depends_on: List of other variables whose values on which this variable's value depends
+ """
+ if depends_on is None:
+ depends_on = []
+ cls.magic_variables[name] = PythonScriptingProvider.MagicVariable(
+ get_value=get_value,
+ set_value=set_value,
+ depends_on=depends_on
+ )
+
+ for inst in PythonScriptingInstance._registered_instances:
+ inst.update_magic_variables()
+
+ @classmethod
+ def unregister_magic_variable(cls, name: str):
+ """
+ Remove a magic variable by name
+ :param name: Variable name
+ """
+ del cls.magic_variables[name]
+
+ for inst in PythonScriptingInstance._registered_instances:
+ inst.update_magic_variables()
+
PythonScriptingProvider().register()
# Wrap stdin/stdout/stderr for Python scripting provider implementation
@@ -1484,3 +1324,532 @@ def redirect_stdio():
sys.stdout = _PythonScriptingInstanceOutput(sys.stdout, False)
sys.stderr = _PythonScriptingInstanceOutput(sys.stderr, True)
sys.excepthook = sys.__excepthook__
+
+
+def _get_here(instance: PythonScriptingInstance):
+ return instance.interpreter.current_addr
+
+
+def _set_here(instance: PythonScriptingInstance, old_value: Any, new_value: Any):
+ if instance.interpreter.active_view is None:
+ return
+
+ if isinstance(new_value, str):
+ new_value = instance.interpreter.active_view.parse_expression(
+ new_value,
+ instance.interpreter.active_addr
+ )
+ if type(new_value) is not int:
+ raise ValueError("Can only replace this variable with an integer")
+
+ if not instance.interpreter.active_view.file.navigate(
+ instance.interpreter.active_view.file.view,
+ new_value
+ ):
+ binaryninja.mainthread.execute_on_main_thread(
+ lambda: instance.interpreter.locals["current_ui_context"].navigateForBinaryView(
+ instance.interpreter.active_view,
+ new_value
+ )
+ )
+
+
+PythonScriptingProvider.register_magic_variable(
+ "here",
+ _get_here,
+ _set_here,
+ ["current_ui_context"]
+)
+PythonScriptingProvider.register_magic_variable(
+ "current_address",
+ _get_here,
+ _set_here,
+ ["current_ui_context"]
+)
+
+
+def _get_current_comment(instance: PythonScriptingInstance):
+ if instance.interpreter.active_view is None:
+ return None
+
+ if instance.interpreter.active_func is None:
+ return instance.interpreter.active_view.get_comment_at(instance.interpreter.active_addr)
+ elif instance.interpreter.active_func.get_comment_at(instance.interpreter.active_addr) != '':
+ return instance.interpreter.active_func.get_comment_at(instance.interpreter.active_addr)
+ else:
+ return instance.interpreter.active_view.get_comment_at(instance.interpreter.active_addr)
+
+
+def _set_current_comment(instance: PythonScriptingInstance, old_value: Any, new_value: Any):
+ if instance.interpreter.active_view is None:
+ return
+
+ if instance.interpreter.active_func is None:
+ instance.interpreter.active_view.set_comment_at(instance.interpreter.active_addr, new_value)
+ else:
+ if instance.interpreter.active_view.get_comment_at(instance.interpreter.active_addr) != '':
+ # Prefer editing active view comment if one exists
+ instance.interpreter.active_view.set_comment_at(instance.interpreter.active_addr, new_value)
+ else:
+ instance.interpreter.active_func.set_comment_at(instance.interpreter.active_addr, new_value)
+
+
+PythonScriptingProvider.register_magic_variable(
+ "current_comment",
+ _get_current_comment,
+ _set_current_comment
+)
+
+
+def _get_current_raw_offset(instance: PythonScriptingInstance):
+ if instance.interpreter.active_view is not None:
+ return instance.interpreter.active_view.get_data_offset_for_address(instance.interpreter.active_addr)
+ else:
+ return None
+
+
+def _set_current_raw_offset(instance: PythonScriptingInstance, old_value: Any, new_value: Any):
+ if instance.interpreter.active_view is None:
+ return
+
+ if isinstance(new_value, str):
+ new_value = instance.interpreter.active_view.parse_expression(
+ new_value,
+ instance.interpreter.active_addr
+ )
+ if type(new_value) is not int:
+ raise ValueError("Can only replace this variable with an integer")
+
+ addr = instance.interpreter.active_view.get_address_for_data_offset(new_value)
+ if addr is not None:
+ if not instance.interpreter.active_view.file.navigate(
+ instance.interpreter.active_view.file.view, addr
+ ):
+ binaryninja.mainthread.execute_on_main_thread(
+ lambda: instance.interpreter.locals["current_ui_context"].navigateForBinaryView(
+ instance.interpreter.active_view,
+ new_value
+ )
+ )
+
+
+PythonScriptingProvider.register_magic_variable(
+ "current_raw_offset",
+ _get_current_raw_offset,
+ _set_current_raw_offset,
+ ["current_ui_context"]
+)
+
+
+def _get_current_selection(instance: PythonScriptingInstance):
+ return instance.interpreter.active_selection_begin, instance.interpreter.active_selection_end
+
+
+def _set_current_selection(instance: PythonScriptingInstance, old_value: Any, new_value: Any):
+ if not instance.interpreter.locals["current_ui_view"]:
+ return
+
+ if (not isinstance(new_value, list)) and \
+ (not isinstance(new_value, tuple)):
+ return
+ if len(new_value) != 2:
+ raise ValueError("Current selection needs to be a list or tuple of two items")
+
+ new_value = [new_value[0], new_value[1]]
+
+ if isinstance(new_value[0], str):
+ new_value[0] = instance.interpreter.active_view.parse_expression(
+ new_value[0],
+ instance.interpreter.active_addr
+ )
+
+ if isinstance(new_value[1], str):
+ new_value[1] = instance.interpreter.active_view.parse_expression(
+ new_value[1],
+ instance.interpreter.active_addr
+ )
+
+ if new_value[0] != instance.interpreter.active_selection_begin or \
+ new_value[1] != instance.interpreter.active_selection_end:
+ new_selection = (new_value[0], new_value[1])
+ binaryninja.mainthread.execute_on_main_thread(
+ lambda: instance.interpreter.locals["current_ui_view"].setSelectionOffsets(new_selection)
+ )
+
+
+PythonScriptingProvider.register_magic_variable(
+ "current_selection",
+ _get_current_selection,
+ _set_current_selection,
+ ["current_ui_view"]
+)
+
+
+PythonScriptingProvider.register_magic_variable(
+ "current_thread",
+ lambda instance: instance.interpreter.interpreter
+)
+PythonScriptingProvider.register_magic_variable(
+ "current_project",
+ lambda instance: instance.interpreter.active_project
+)
+PythonScriptingProvider.register_magic_variable(
+ "current_view",
+ lambda instance: instance.interpreter.active_view
+)
+PythonScriptingProvider.register_magic_variable(
+ "bv",
+ lambda instance: instance.interpreter.active_view
+)
+PythonScriptingProvider.register_magic_variable(
+ "current_function",
+ lambda instance: instance.interpreter.active_func
+)
+PythonScriptingProvider.register_magic_variable(
+ "current_basic_block",
+ lambda instance: instance.interpreter.active_block
+)
+# todo: this is the debugger's responsibility
+PythonScriptingProvider.register_magic_variable(
+ "dbg",
+ lambda instance: instance.interpreter.active_dbg
+)
+
+
+def _get_current_llil(instance: PythonScriptingInstance):
+ if instance.interpreter.active_func is None:
+ return None
+ return instance.interpreter.active_func.llil_if_available
+
+
+PythonScriptingProvider.register_magic_variable("current_llil", _get_current_llil)
+
+
+def _get_current_llil_ssa(instance: PythonScriptingInstance):
+ if instance.interpreter.locals["current_llil"] is None:
+ return None
+ return instance.interpreter.locals["current_llil"].ssa_form
+
+
+PythonScriptingProvider.register_magic_variable(
+ "current_llil_ssa",
+ _get_current_llil_ssa,
+ depends_on=["current_llil"]
+)
+
+
+def _get_current_mlil(instance: PythonScriptingInstance):
+ if instance.interpreter.active_func is None:
+ return None
+ return instance.interpreter.active_func.mlil_if_available
+
+
+PythonScriptingProvider.register_magic_variable("current_mlil", _get_current_mlil)
+
+
+def _get_current_mlil_ssa(instance: PythonScriptingInstance):
+ if instance.interpreter.locals["current_mlil"] is None:
+ return None
+ return instance.interpreter.locals["current_mlil"].ssa_form
+
+
+PythonScriptingProvider.register_magic_variable(
+ "current_mlil_ssa",
+ _get_current_mlil_ssa,
+ depends_on=["current_mlil"]
+)
+
+
+def _get_current_hlil(instance: PythonScriptingInstance):
+ if instance.interpreter.active_func is None:
+ return None
+ return instance.interpreter.active_func.hlil_if_available
+
+
+PythonScriptingProvider.register_magic_variable("current_hlil", _get_current_hlil)
+
+
+def _get_current_hlil_ssa(instance: PythonScriptingInstance):
+ if instance.interpreter.locals["current_hlil"] is None:
+ return None
+ return instance.interpreter.locals["current_hlil"].ssa_form
+
+
+PythonScriptingProvider.register_magic_variable(
+ "current_hlil_ssa",
+ _get_current_hlil_ssa,
+ depends_on=["current_hlil"]
+)
+
+
+def _get_current_data_var(instance: PythonScriptingInstance):
+ if instance.interpreter.active_view is None:
+ return None
+ return instance.interpreter.active_view.get_data_var_at(instance.interpreter.active_addr)
+
+
+PythonScriptingProvider.register_magic_variable("current_data_var", _get_current_data_var)
+
+
+def _get_current_symbol(instance: PythonScriptingInstance):
+ if instance.interpreter.active_view is None:
+ return None
+ return instance.interpreter.active_view.get_symbol_at(instance.interpreter.active_addr)
+
+
+PythonScriptingProvider.register_magic_variable("current_symbol", _get_current_symbol)
+
+
+def _get_current_symbols(instance: PythonScriptingInstance):
+ if instance.interpreter.active_view is None:
+ return []
+ return instance.interpreter.active_view.get_symbols(instance.interpreter.active_addr, 1)
+
+
+PythonScriptingProvider.register_magic_variable("current_symbols", _get_current_symbols)
+
+
+def _get_current_segment(instance: PythonScriptingInstance):
+ if instance.interpreter.active_view is None:
+ return None
+ return instance.interpreter.active_view.get_segment_at(instance.interpreter.active_addr)
+
+
+PythonScriptingProvider.register_magic_variable("current_segment", _get_current_segment)
+
+
+def _get_current_sections(instance: PythonScriptingInstance):
+ if instance.interpreter.active_view is None:
+ return []
+ return instance.interpreter.active_view.get_sections_at(instance.interpreter.active_addr)
+
+
+PythonScriptingProvider.register_magic_variable("current_sections", _get_current_sections)
+
+
+def _get_current_ui_context(instance: PythonScriptingInstance):
+ if binaryninja.core_ui_enabled():
+ try:
+ from binaryninjaui import UIContext
+ return UIContext.activeContext()
+ except ImportError:
+ pass
+ return None
+
+
+PythonScriptingProvider.register_magic_variable("current_ui_context", _get_current_ui_context)
+
+
+def _get_current_ui_action_handler(instance: PythonScriptingInstance):
+ if instance.interpreter.locals["current_ui_context"] is not None:
+ return instance.interpreter.locals["current_ui_context"].getCurrentActionHandler()
+ return None
+
+
+PythonScriptingProvider.register_magic_variable(
+ "current_ui_action_handler",
+ _get_current_ui_action_handler,
+ depends_on=["current_ui_context"]
+)
+
+
+def _get_current_ui_view_frame(instance: PythonScriptingInstance):
+ if instance.interpreter.locals["current_ui_context"] is not None:
+ return instance.interpreter.locals["current_ui_context"].getCurrentViewFrame()
+ return None
+
+
+PythonScriptingProvider.register_magic_variable(
+ "current_ui_view_frame",
+ _get_current_ui_view_frame,
+ depends_on=["current_ui_context"]
+)
+
+
+def _get_current_ui_view(instance: PythonScriptingInstance):
+ if instance.interpreter.locals["current_ui_context"] is not None:
+ return instance.interpreter.locals["current_ui_context"].getCurrentView()
+ return None
+
+
+PythonScriptingProvider.register_magic_variable(
+ "current_ui_view",
+ _get_current_ui_view,
+ depends_on=["current_ui_context"]
+)
+
+
+def _get_current_ui_view_location(instance: PythonScriptingInstance):
+ if instance.interpreter.locals["current_ui_view_frame"] is not None:
+ return instance.interpreter.locals["current_ui_view_frame"].getViewLocation()
+ return None
+
+
+PythonScriptingProvider.register_magic_variable(
+ "current_ui_view_location",
+ _get_current_ui_view_location,
+ depends_on=["current_ui_view_frame"]
+)
+
+
+def _get_current_ui_action_context(instance: PythonScriptingInstance):
+ if instance.interpreter.locals["current_ui_view"] is not None:
+ return instance.interpreter.locals["current_ui_view"].actionContext()
+ if instance.interpreter.locals["current_ui_action_handler"] is not None:
+ return instance.interpreter.locals["current_ui_action_handler"].actionContext()
+ return None
+
+
+PythonScriptingProvider.register_magic_variable(
+ "current_ui_action_context",
+ _get_current_ui_action_context,
+ depends_on=["current_ui_view", "current_ui_action_handler"]
+)
+
+
+def _get_current_ui_token_state(instance: PythonScriptingInstance):
+ if instance.interpreter.locals["current_ui_action_context"] is not None:
+ return instance.interpreter.locals["current_ui_action_context"].token
+ return None
+
+
+PythonScriptingProvider.register_magic_variable(
+ "current_ui_token_state",
+ _get_current_ui_token_state,
+ depends_on=["current_ui_action_context"]
+)
+
+
+def _get_current_token(instance: PythonScriptingInstance):
+ if instance.interpreter.locals["current_ui_token_state"] is not None:
+ if instance.interpreter.locals["current_ui_token_state"].valid:
+ return instance.interpreter.locals["current_ui_token_state"].token
+ return None
+
+
+PythonScriptingProvider.register_magic_variable(
+ "current_token",
+ _get_current_token,
+ depends_on=["current_ui_token_state"]
+)
+
+
+def _get_current_variable(instance: PythonScriptingInstance):
+ from binaryninja import Variable
+ if instance.interpreter.locals["current_ui_token_state"] is not None:
+ if instance.interpreter.locals["current_ui_token_state"].localVarValid:
+ var = instance.interpreter.locals["current_ui_token_state"].localVar
+ if var is not None and instance.interpreter.active_func:
+ return Variable.from_core_variable(instance.interpreter.active_func, var)
+ return None
+
+
+PythonScriptingProvider.register_magic_variable(
+ "current_variable",
+ _get_current_variable,
+ depends_on=["current_ui_token_state"]
+)
+
+
+def _get_current_il_index(instance: PythonScriptingInstance):
+ if instance.interpreter.locals["current_ui_view_location"] is not None:
+ return instance.interpreter.locals["current_ui_view_location"].getInstrIndex()
+ return None
+
+
+PythonScriptingProvider.register_magic_variable(
+ "current_il_index",
+ _get_current_il_index,
+ depends_on=["current_ui_view_location"]
+)
+
+
+def _get_current_il_function(instance: PythonScriptingInstance):
+ from binaryninja import FunctionGraphType
+ if instance.interpreter.locals["current_ui_view_location"] is not None:
+ ilType = instance.interpreter.locals["current_ui_view_location"].getILViewType()
+ if ilType == FunctionGraphType.LowLevelILFunctionGraph:
+ return instance.interpreter.locals["current_llil"]
+ elif ilType == FunctionGraphType.LowLevelILSSAFormFunctionGraph:
+ return instance.interpreter.locals["current_llil_ssa"]
+ elif ilType == FunctionGraphType.MediumLevelILFunctionGraph:
+ return instance.interpreter.locals["current_mlil"]
+ elif ilType == FunctionGraphType.MediumLevelILSSAFormFunctionGraph:
+ return instance.interpreter.locals["current_mlil_ssa"]
+ elif ilType == FunctionGraphType.HighLevelILFunctionGraph:
+ return instance.interpreter.locals["current_hlil"]
+ elif ilType == FunctionGraphType.HighLevelILSSAFormFunctionGraph:
+ return instance.interpreter.locals["current_hlil_ssa"]
+ return None
+
+
+PythonScriptingProvider.register_magic_variable(
+ "current_il_function",
+ _get_current_il_function,
+ depends_on=[
+ "current_ui_view_location",
+ "current_llil",
+ "current_llil_ssa",
+ "current_mlil",
+ "current_mlil_ssa",
+ "current_hlil",
+ "current_hlil_ssa"
+ ]
+)
+
+
+def _get_current_il_instruction(instance: PythonScriptingInstance):
+ if instance.interpreter.locals["current_il_function"] is not None \
+ and instance.interpreter.locals["current_il_index"] is not None:
+ return instance.interpreter.locals["current_il_function"][
+ instance.interpreter.locals["current_il_index"]
+ ]
+ return None
+
+
+PythonScriptingProvider.register_magic_variable(
+ "current_il_instruction",
+ _get_current_il_instruction,
+ depends_on=[
+ "current_il_index",
+ "current_il_function"
+ ]
+)
+
+
+def _get_current_il_basic_block(instance: PythonScriptingInstance):
+ if instance.interpreter.locals["current_il_instruction"] is not None:
+ return instance.interpreter.locals["current_il_instruction"].il_basic_block
+ return None
+
+
+PythonScriptingProvider.register_magic_variable(
+ "current_il_basic_block",
+ _get_current_il_basic_block,
+ depends_on=["current_il_instruction"]
+)
+
+
+def _get_current_il_instructions(instance: PythonScriptingInstance):
+ if instance.interpreter.locals["current_il_index"] is not None \
+ and instance.interpreter.locals["current_il_function"] is not None \
+ and instance.interpreter.locals["current_ui_view"] is not None:
+
+ start_index = instance.interpreter.locals["current_ui_view"].getSelectionStartILInstructionIndex()
+ current_index = instance.interpreter.locals["current_il_index"]
+ il_function = instance.interpreter.locals["current_il_function"]
+
+ invalid_il_index = 0xffffffffffffffff
+ if invalid_il_index not in (current_index, start_index):
+ il_start = min(current_index, start_index)
+ il_end = max(current_index, start_index)
+ return (il_function[i] for i in range(il_start, il_end + 1))
+
+ return None
+
+
+PythonScriptingProvider.register_magic_variable(
+ "current_il_instructions",
+ _get_current_il_instructions,
+ depends_on=["current_il_index", "current_il_function", "current_ui_view"]
+)