diff options
| author | Rusty Wagner <rusty@vector35.com> | 2016-08-18 02:29:53 -0400 |
|---|---|---|
| committer | Rusty Wagner <rusty@vector35.com> | 2016-08-18 02:29:53 -0400 |
| commit | 228ee0e979268afab31b66efb357f2236238751d (patch) | |
| tree | 409bd231174c4724f05fa1592909beff3464f4e0 | |
| parent | 5f444e5444cec4ff3f270e1ced16b60f00f57f9e (diff) | |
Add Python script provider for UI Python console
| -rw-r--r-- | binaryninjaapi.h | 91 | ||||
| -rw-r--r-- | binaryninjacore.h | 78 | ||||
| -rw-r--r-- | python/__init__.py | 513 | ||||
| -rw-r--r-- | scriptingprovider.cpp | 289 |
4 files changed, 970 insertions, 1 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 2b2ccc48..c8d54c18 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -2062,4 +2062,95 @@ namespace BinaryNinja Ref<Platform> GetRelatedPlatform(Architecture* arch); void AddRelatedPlatform(Architecture* arch, Platform* platform); }; + + class ScriptingOutputListener + { + BNScriptingOutputListener m_callbacks; + + static void OutputCallback(void* ctxt, const char* text); + static void ErrorCallback(void* ctxt, const char* text); + static void InputReadyStateChangedCallback(void* ctxt, BNScriptingProviderInputReadyState state); + + public: + ScriptingOutputListener(); + BNScriptingOutputListener& GetCallbacks() { return m_callbacks; } + + virtual void NotifyOutput(const std::string& text); + virtual void NotifyError(const std::string& text); + virtual void NotifyInputReadyStateChanged(BNScriptingProviderInputReadyState state); + }; + + class ScriptingProvider; + + class ScriptingInstance: public CoreRefCountObject<BNScriptingInstance, + BNNewScriptingInstanceReference, BNFreeScriptingInstance> + { + protected: + ScriptingInstance(ScriptingProvider* provider); + ScriptingInstance(BNScriptingInstance* instance); + + static void DestroyInstanceCallback(void* ctxt); + static BNScriptingProviderExecuteResult ExecuteScriptInputCallback(void* ctxt, const char* input); + static void SetCurrentBinaryViewCallback(void* ctxt, BNBinaryView* view); + static void SetCurrentFunctionCallback(void* ctxt, BNFunction* func); + static void SetCurrentBasicBlockCallback(void* ctxt, BNBasicBlock* block); + static void SetCurrentAddressCallback(void* ctxt, uint64_t addr); + static void SetCurrentSelectionCallback(void* ctxt, uint64_t begin, uint64_t end); + + virtual void DestroyInstance(); + + public: + virtual BNScriptingProviderExecuteResult ExecuteScriptInput(const std::string& input) = 0; + virtual void SetCurrentBinaryView(BinaryView* view); + virtual void SetCurrentFunction(Function* func); + virtual void SetCurrentBasicBlock(BasicBlock* block); + virtual void SetCurrentAddress(uint64_t addr); + virtual void SetCurrentSelection(uint64_t begin, uint64_t end); + + void Output(const std::string& text); + void Error(const std::string& text); + void InputReadyStateChanged(BNScriptingProviderInputReadyState state); + BNScriptingProviderInputReadyState GetInputReadyState(); + + void RegisterOutputListener(ScriptingOutputListener* listener); + void UnregisterOutputListener(ScriptingOutputListener* listener); + }; + + class CoreScriptingInstance: public ScriptingInstance + { + public: + CoreScriptingInstance(BNScriptingInstance* instance); + + virtual BNScriptingProviderExecuteResult ExecuteScriptInput(const std::string& input) override; + virtual void SetCurrentBinaryView(BinaryView* view) override; + virtual void SetCurrentFunction(Function* func) override; + virtual void SetCurrentBasicBlock(BasicBlock* block) override; + virtual void SetCurrentAddress(uint64_t addr) override; + virtual void SetCurrentSelection(uint64_t begin, uint64_t end) override; + }; + + class ScriptingProvider: public StaticCoreRefCountObject<BNScriptingProvider> + { + std::string m_nameForRegister; + + protected: + ScriptingProvider(const std::string& name); + ScriptingProvider(BNScriptingProvider* provider); + + static BNScriptingInstance* CreateInstanceCallback(void* ctxt); + + public: + virtual Ref<ScriptingInstance> CreateNewInstance() = 0; + + static std::vector<Ref<ScriptingProvider>> GetList(); + static Ref<ScriptingProvider> GetByName(const std::string& name); + static void Register(ScriptingProvider* provider); + }; + + class CoreScriptingProvider: public ScriptingProvider + { + public: + CoreScriptingProvider(BNScriptingProvider* provider); + virtual Ref<ScriptingInstance> CreateNewInstance() override; + }; } diff --git a/binaryninjacore.h b/binaryninjacore.h index 13f54943..61ecb235 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -99,6 +99,8 @@ extern "C" struct BNPlatform; struct BNAnalysisCompletionEvent; struct BNDisassemblySettings; + struct BNScriptingProvider; + struct BNScriptingInstance; //! Console log levels enum BNLogLevel @@ -963,6 +965,47 @@ extern "C" FindCaseInsensitive = 1 }; + enum BNScriptingProviderInputReadyState + { + NotReadyForInput, + ReadyForScriptExecution, + ReadyForScriptProgramInput + }; + + enum BNScriptingProviderExecuteResult + { + InvalidScriptInput, + IncompleteScriptInput, + SuccessfulScriptExecution + }; + + struct BNScriptingInstanceCallbacks + { + void* context; + void (*destroyInstance)(void* ctxt); + BNScriptingProviderExecuteResult (*executeScriptInput)(void* ctxt, const char* input); + void (*setCurrentBinaryView)(void* ctxt, BNBinaryView* view); + void (*setCurrentFunction)(void* ctxt, BNFunction* func); + void (*setCurrentBasicBlock)(void* ctxt, BNBasicBlock* block); + void (*setCurrentAddress)(void* ctxt, uint64_t addr); + void (*setCurrentSelection)(void* ctxt, uint64_t begin, uint64_t end); + }; + + struct BNScriptingProviderCallbacks + { + void* context; + BNScriptingInstance* (*createInstance)(void* ctxt); + + }; + + struct BNScriptingOutputListener + { + void* context; + void (*output)(void* ctxt, const char* text); + void (*error)(void* ctxt, const char* text); + void (*inputReadyStateChanged)(void* ctxt, BNScriptingProviderInputReadyState state); + }; + BINARYNINJACOREAPI char* BNAllocString(const char* contents); BINARYNINJACOREAPI void BNFreeString(char* str); @@ -1844,6 +1887,41 @@ extern "C" BNType** outType, char*** outVarName, size_t* outVarNameElements); + + // Scripting providers + BINARYNINJACOREAPI BNScriptingProvider* BNRegisterScriptingProvider(const char* name, + BNScriptingProviderCallbacks* callbacks); + BINARYNINJACOREAPI BNScriptingProvider** BNGetScriptingProviderList(size_t* count); + BINARYNINJACOREAPI void BNFreeScriptingProviderList(BNScriptingProvider** providers); + BINARYNINJACOREAPI BNScriptingProvider* BNGetScriptingProviderByName(const char* name); + + BINARYNINJACOREAPI char* BNGetScriptingProviderName(BNScriptingProvider* provider); + BINARYNINJACOREAPI BNScriptingInstance* BNCreateScriptingProviderInstance(BNScriptingProvider* provider); + + BINARYNINJACOREAPI BNScriptingInstance* BNInitScriptingInstance(BNScriptingProvider* provider, + BNScriptingInstanceCallbacks* callbacks); + BINARYNINJACOREAPI BNScriptingInstance* BNNewScriptingInstanceReference(BNScriptingInstance* instance); + BINARYNINJACOREAPI void BNFreeScriptingInstance(BNScriptingInstance* instance); + BINARYNINJACOREAPI void BNNotifyOutputForScriptingInstance(BNScriptingInstance* instance, const char* text); + BINARYNINJACOREAPI void BNNotifyErrorForScriptingInstance(BNScriptingInstance* instance, const char* text); + BINARYNINJACOREAPI void BNNotifyInputReadyStateForScriptingInstance(BNScriptingInstance* instance, + BNScriptingProviderInputReadyState state); + + BINARYNINJACOREAPI void BNRegisterScriptingInstanceOutputListener(BNScriptingInstance* instance, + BNScriptingOutputListener* callbacks); + BINARYNINJACOREAPI void BNUnregisterScriptingInstanceOutputListener(BNScriptingInstance* instance, + BNScriptingOutputListener* callbacks); + + BINARYNINJACOREAPI BNScriptingProviderInputReadyState BNGetScriptingInstanceInputReadyState( + BNScriptingInstance* instance); + BINARYNINJACOREAPI BNScriptingProviderExecuteResult BNExecuteScriptInput(BNScriptingInstance* instance, + const char* input); + BINARYNINJACOREAPI void BNSetScriptingInstanceCurrentBinaryView(BNScriptingInstance* instance, BNBinaryView* view); + BINARYNINJACOREAPI void BNSetScriptingInstanceCurrentFunction(BNScriptingInstance* instance, BNFunction* func); + BINARYNINJACOREAPI void BNSetScriptingInstanceCurrentBasicBlock(BNScriptingInstance* instance, BNBasicBlock* block); + BINARYNINJACOREAPI void BNSetScriptingInstanceCurrentAddress(BNScriptingInstance* instance, uint64_t addr); + BINARYNINJACOREAPI void BNSetScriptingInstanceCurrentSelection(BNScriptingInstance* instance, + uint64_t begin, uint64_t end); #ifdef __cplusplus } #endif diff --git a/python/__init__.py b/python/__init__.py index cfb849ab..3b0ce739 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -19,7 +19,7 @@ # IN THE SOFTWARE. import _binaryninjacore as core -import ctypes, traceback, json, struct, threading +import ctypes, traceback, json, struct, threading, code, sys _plugin_init = False def _init_plugins(): @@ -9059,6 +9059,505 @@ class Platform(object): """ core.BNRegisterPlatformCallingConvention(self.handle, cc.handle) +class ScriptingOutputListener(object): + def _register(self, handle): + self._cb = core.BNScriptingOutputListener() + self._cb.context = 0 + self._cb.output = self._cb.output.__class__(self._output) + self._cb.error = self._cb.error.__class__(self._error) + self._cb.inputReadyStateChanged = self._cb.inputReadyStateChanged.__class__(self._input_ready_state_changed) + core.BNRegisterScriptingInstanceOutputListener(handle, self._cb) + + def _unregister(self, handle): + core.BNUnregisterScriptingInstanceOutputListener(handle, self._cb) + + def _output(self, ctxt, text): + try: + self.notify_output(text) + except: + log_error(traceback.format_exc()) + + def _error(self, ctxt, text): + try: + self.notify_error(text) + except: + log_error(traceback.format_exc()) + + def _input_ready_state_changed(self, ctxt, state): + try: + self.notify_input_ready_state_changed(state) + except: + log_error(traceback.format_exc()) + + def notify_output(self, text): + pass + + def notify_error(self, text): + pass + + def notify_input_ready_state_changed(self, state): + pass + +class ScriptingInstance(object): + def __init__(self, provider, handle = None): + if handle is None: + self._cb = core.BNScriptingInstanceCallbacks() + self._cb.context = 0 + self._cb.destroyInstance = self._cb.destroyInstance.__class__(self._destroy_instance) + self._cb.executeScriptInput = self._cb.executeScriptInput.__class__(self._execute_script_input) + self._cb.setCurrentBinaryView = self._cb.setCurrentBinaryView.__class__(self._set_current_binary_view) + self._cb.setCurrentFunction = self._cb.setCurrentFunction.__class__(self._set_current_function) + self._cb.setCurrentBasicBlock = self._cb.setCurrentBasicBlock.__class__(self._set_current_basic_block) + self._cb.setCurrentAddress = self._cb.setCurrentAddress.__class__(self._set_current_address) + self._cb.setCurrentSelection = self._cb.setCurrentSelection.__class__(self._set_current_selection) + self.handle = core.BNInitScriptingInstance(provider.handle, self._cb) + else: + self.handle = core.handle_of_type(handle, core.BNScriptingInstance) + self.listeners = [] + + def __del__(self): + core.BNFreeScriptingInstance(self.handle) + + def _destroy_instance(self, ctxt): + try: + self.perform_destroy_instance() + except: + log_error(traceback.format_exc()) + + def _execute_script_input(self, ctxt, text): + try: + return self.perform_execute_script_input(text) + except: + log_error(traceback.format_exc()) + return core.InvalidScriptInput + + def _set_current_binary_view(self, ctxt, view): + try: + if view: + view = BinaryView(None, handle = core.BNNewViewReference(view)) + else: + view = None + self.perform_set_current_binary_view(view) + except: + log_error(traceback.format_exc()) + + def _set_current_function(self, ctxt, func): + try: + if func: + func = Function(BinaryView(None, handle = core.BNGetFunctionData(func)), core.BNNewFunctionReference(func)) + else: + func = None + self.perform_set_current_function(func) + except: + log_error(traceback.format_exc()) + + def _set_current_basic_block(self, ctxt, block): + try: + if block: + func = core.BNGetBasicBlockFunction(block) + if func is None: + block = None + else: + block = BasicBlock(BinaryView(None, handle = core.BNGetFunctionData(func)), core.BNNewBasicBlockReference(block)) + core.BNFreeFunction(func) + else: + block = None + self.perform_set_current_basic_block(block) + except: + log_error(traceback.format_exc()) + + def _set_current_address(self, ctxt, addr): + try: + self.perform_set_current_address(addr) + except: + log_error(traceback.format_exc()) + + def _set_current_selection(self, ctxt, begin, end): + try: + self.perform_set_current_selection(begin, end) + except: + log_error(traceback.format_exc()) + + def perform_destroy_instance(self): + pass + + def perform_execute_script_input(self, text): + return core.InvalidScriptInput + + def perform_set_current_binary_view(self, view): + pass + + def perform_set_current_function(self, func): + pass + + def perform_set_current_basic_block(self, block): + pass + + def perform_set_current_address(self, addr): + pass + + def perform_set_current_selection(self, begin, end): + pass + + @property + def input_ready_state(self): + return core.BNGetScriptingInstanceInputReadyState(self.handle) + + @input_ready_state.setter + def input_ready_state(self, value): + core.BNNotifyInputReadyStateForScriptingInstance(self.handle, value) + + def output(self, text): + core.BNNotifyOutputForScriptingInstance(self.handle, text) + + def error(self, text): + core.BNNotifyErrorForScriptingInstance(self.handle, text) + + def execute_script_input(self, text): + return core.BNExecuteScriptInput(self.handle, text) + + def set_current_binary_view(self, view): + if view is not None: + view = view.handle + core.BNSetScriptingInstanceCurrentBinaryView(self.handle, view) + + def set_current_function(self, func): + if func is not None: + func = func.handle + core.BNSetScriptingInstanceCurrentFunction(self.handle, func) + + def set_current_basic_block(self, block): + if block is not None: + block = block.handle + core.BNSetScriptingInstanceCurrentBasicBlock(self.handle, block) + + def set_current_address(self, addr): + core.BNSetScriptingInstanceCurrentAddress(self.handle, addr) + + def set_current_selection(self, begin, end): + core.BNSetScriptingInstanceCurrentSelection(self.handle, begin, end) + + def register_output_listener(self, listener): + listener._register(self.handle) + self.listeners.append(listener) + + def unregister_output_listener(self, listener): + if listener in self.listeners: + listener._unregister(self.handle) + self.listeners.remove(listener) + +class _ScriptingProviderMetaclass(type): + @property + def list(self): + """List all ScriptingProvider types (read-only)""" + _init_plugins() + count = ctypes.c_ulonglong() + types = core.BNGetScriptingProviderList(count) + result = [] + for i in xrange(0, count.value): + result.append(ScriptingProvider(types[i])) + core.BNFreeScriptingProviderList(types) + return result + + def __iter__(self): + _init_plugins() + count = ctypes.c_ulonglong() + types = core.BNGetScriptingProviderList(count) + try: + for i in xrange(0, count.value): + yield ScriptingProvider(types[i]) + finally: + core.BNFreeScriptingProviderList(types) + + def __getitem__(self, value): + _init_plugins() + provider = core.BNGetScriptingProviderByName(str(value)) + if provider is None: + raise KeyError, "'%s' is not a valid scripting provider" % str(value) + return ScriptingProvider(provider) + + def __setattr__(self, name, value): + try: + type.__setattr__(self,name,value) + except AttributeError: + raise AttributeError, "attribute '%s' is read only" % name + +class ScriptingProvider(object): + __metaclass__ = _ScriptingProviderMetaclass + + name = None + instance_class = None + _registered_providers = [] + + def __init__(self, handle = None): + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNScriptingProvider) + self.__dict__["name"] = core.BNGetScriptingProviderName(handle) + + def register(self): + self._cb = core.BNScriptingProviderCallbacks() + self._cb.context = 0 + self._cb.createInstance = self._cb.createInstance.__class__(self._create_instance) + self.handle = core.BNRegisterScriptingProvider(self.__class__.name, self._cb) + self.__class__._registered_providers.append(self) + + def _create_instance(self, ctxt): + try: + result = self.__class__.instance_class(self) + if result is None: + return None + return ctypes.cast(core.BNNewScriptingInstanceReference(result.handle), ctypes.c_void_p).value + except: + log_error(traceback.format_exc()) + return None + + def create_instance(self): + result = core.BNCreateScriptingProviderInstance(self.handle) + if result is None: + return None + return ScriptingInstance(self, handle = result) + +class _PythonScriptingInstanceOutput(object): + def __init__(self, orig, is_error): + self.orig = orig + self.is_error = is_error + self.buffer = "" + + def write(self, data): + global _output_to_log + + interpreter = None + if "value" in dir(PythonScriptingInstance._interpreter): + interpreter = PythonScriptingInstance._interpreter.value + + if interpreter is None: + if _output_to_log: + self.buffer += data + while True: + i = self.buffer.find('\n') + if i == -1: + break + line = self.buffer[:i] + self.buffer = self.buffer[i + 1:] + + if self.is_error: + log_error(line) + else: + log_info(line) + else: + self.orig.write(data) + else: + PythonScriptingInstance._interpreter.value = None + try: + if self.is_error: + interpreter.instance.error(data) + else: + interpreter.instance.output(data) + finally: + PythonScriptingInstance._interpreter.value = interpreter + +class _PythonScriptingInstanceInput(object): + def __init__(self, orig): + self.orig = orig + + def read(self, size): + interpreter = None + if "value" in dir(PythonScriptingInstance._interpreter): + interpreter = PythonScriptingInstance._interpreter.value + + if interpreter is None: + return self.orig.read(size) + else: + PythonScriptingInstance._interpreter.value = None + try: + result = interpreter.read(size) + finally: + PythonScriptingInstance._interpreter.value = interpreter + return result + + def readline(self): + interpreter = None + if "value" in dir(PythonScriptingInstance._interpreter): + interpreter = PythonScriptingInstance._interpreter.value + + if interpreter is None: + return self.orig.readline() + else: + result = "" + while True: + data = interpreter.read(1) + result += data + if (len(data) == 0) or (data == "\n"): + break + return result + +class PythonScriptingInstance(ScriptingInstance): + _interpreter = threading.local() + + class InterpreterThread(threading.Thread): + def __init__(self, instance): + super(PythonScriptingInstance.InterpreterThread, self).__init__() + self.instance = instance + self.locals = {"__name__": "__console__", "__doc__": None, "binaryninja": sys.modules[__name__]} + self.interpreter = code.InteractiveInterpreter(self.locals) + self.event = threading.Event() + self.daemon = True + + # Latest selections from UI + self.current_view = None + self.current_func = None + self.current_block = None + self.current_addr = 0 + self.current_selection_begin = 0 + self.current_selection_end = 0 + + # Selections that were current as of last issued command + self.active_view = None + self.active_func = None + self.active_block = None + self.active_addr = 0 + self.active_selection_begin = 0 + self.active_selection_end = 0 + + self.locals["get_selected_data"] = self.get_selected_data + self.locals["write_at_cursor"] = self.write_at_cursor + + self.exit = False + self.code = None + self.input = "" + + self.interpreter.runsource("from binaryninja import *\n") + + def execute(self, code): + self.code = code + self.event.set() + + def add_input(self, data): + self.input += data + self.event.set() + + def end(self): + self.exit = True + self.event.set() + + def read(self, size): + while not self.exit: + if len(self.input) > size: + result = self.input[:size] + self.input = self.input[size:] + return result + elif len(self.input) > 0: + result = self.input + self.input = "" + return result + self.instance.input_ready_state = core.ReadyForScriptProgramInput + self.event.wait() + self.event.clear() + return "" + + def run(self): + while not self.exit: + self.event.wait() + self.event.clear() + if self.exit: + break + if self.code is not None: + self.instance.input_ready_state = core.NotReadyForInput + code = self.code + self.code = None + + PythonScriptingInstance._interpreter.value = self + try: + self.active_view = self.current_view + self.active_func = self.current_func + self.active_block = self.current_block + self.active_addr = self.current_addr + self.active_selection_begin = self.current_selection_begin + self.active_selection_end = self.current_selection_end + + self.locals["current_view"] = self.active_view + 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["current_selection"] = (self.active_selection_begin, self.active_selection_end) + + self.interpreter.runsource(code) + finally: + PythonScriptingInstance._interpreter.value = None + self.instance.input_ready_state = core.ReadyForScriptExecution + + def get_selected_data(self): + if self.active_view is None: + return None + length = self.active_selection_end - self.active_selection_begin + return self.active_view.read(self.active_selection_begin, length) + + def write_at_cursor(self, data): + if self.active_view is None: + return 0 + selected_length = self.active_selection_end - self.active_selection_begin + data = str(data) + if (len(data) == selected_length) or (selected_length == 0): + return self.active_view.write(self.active_selection_begin, data) + else: + self.active_view.remove(self.active_selection_begin, selected_length) + return self.active_view.insert(self.active_selection_begin, data) + + def __init__(self, provider): + super(PythonScriptingInstance, self).__init__(provider) + self.interpreter = PythonScriptingInstance.InterpreterThread(self) + self.interpreter.start() + self.queued_input = "" + self.input_ready_state = core.ReadyForScriptExecution + + def perform_destroy_instance(self): + self.interpreter.end() + + def perform_execute_script_input(self, text): + if self.input_ready_state == core.NotReadyForInput: + return core.InvalidScriptInput + + if self.input_ready_state == core.ReadyForScriptProgramInput: + if len(text) == 0: + return core.SuccessfulScriptExecution + self.input_ready_state = core.NotReadyForInput + self.interpreter.add_input(text) + return core.SuccessfulScriptExecution + + try: + result = code.compile_command(text) + except: + result = False + + if result is None: + # Command is not complete, ask for more input + return core.IncompleteScriptInput + + self.input_ready_state = core.NotReadyForInput + self.interpreter.execute(text) + return core.SuccessfulScriptExecution + + def perform_set_current_binary_view(self, view): + self.interpreter.current_view = view + + def perform_set_current_function(self, func): + self.interpreter.current_func = func + + def perform_set_current_basic_block(self, block): + self.interpreter.current_block = block + + def perform_set_current_address(self, addr): + self.interpreter.current_addr = addr + + def perform_set_current_selection(self, begin, end): + self.interpreter.current_selection_begin = begin + self.interpreter.current_selection_end = end + +class PythonScriptingProvider(ScriptingProvider): + name = "Python" + instance_class = PythonScriptingInstance + def LLIL_TEMP(n): return n | 0x80000000 @@ -9316,6 +9815,11 @@ def demangle_ms(arch, mangled_name): return (Type(handle), names) return (None, mangledName) +_output_to_log = False +def redirect_output_to_log(): + global _output_to_log + _output_to_log = True + bundled_plugin_path = core.BNGetBundledPluginDirectory() user_plugin_path = core.BNGetUserPluginDirectory() @@ -9325,3 +9829,10 @@ core_build_id = core.BNGetBuildId() # Ensure all enumeration constants from the core are exposed by this module for name in core.all_enum_values: globals()[name] = core.all_enum_values[name] + +PythonScriptingProvider().register() + +# Wrap stdin/stdout/stderr for Python scripting provider implementation +sys.stdin = _PythonScriptingInstanceInput(sys.stdin) +sys.stdout = _PythonScriptingInstanceOutput(sys.stdout, False) +sys.stderr = _PythonScriptingInstanceOutput(sys.stderr, True) diff --git a/scriptingprovider.cpp b/scriptingprovider.cpp new file mode 100644 index 00000000..6a52e095 --- /dev/null +++ b/scriptingprovider.cpp @@ -0,0 +1,289 @@ +#include "binaryninjaapi.h" + +using namespace BinaryNinja; +using namespace std; + + +ScriptingOutputListener::ScriptingOutputListener() +{ + m_callbacks.context = this; + m_callbacks.output = OutputCallback; + m_callbacks.error = ErrorCallback; + m_callbacks.inputReadyStateChanged = InputReadyStateChangedCallback; +} + + +void ScriptingOutputListener::OutputCallback(void* ctxt, const char* text) +{ + ScriptingOutputListener* listener = (ScriptingOutputListener*)ctxt; + listener->NotifyOutput(text); +} + + +void ScriptingOutputListener::ErrorCallback(void* ctxt, const char* text) +{ + ScriptingOutputListener* listener = (ScriptingOutputListener*)ctxt; + listener->NotifyError(text); +} + + +void ScriptingOutputListener::InputReadyStateChangedCallback(void* ctxt, BNScriptingProviderInputReadyState state) +{ + ScriptingOutputListener* listener = (ScriptingOutputListener*)ctxt; + listener->NotifyInputReadyStateChanged(state); +} + + +void ScriptingOutputListener::NotifyOutput(const string&) +{ +} + + +void ScriptingOutputListener::NotifyError(const string&) +{ +} + + +void ScriptingOutputListener::NotifyInputReadyStateChanged(BNScriptingProviderInputReadyState) +{ +} + + +ScriptingInstance::ScriptingInstance(ScriptingProvider* provider) +{ + BNScriptingInstanceCallbacks cb; + cb.context = this; + cb.destroyInstance = DestroyInstanceCallback; + cb.executeScriptInput = ExecuteScriptInputCallback; + cb.setCurrentBinaryView = SetCurrentBinaryViewCallback; + cb.setCurrentFunction = SetCurrentFunctionCallback; + cb.setCurrentBasicBlock = SetCurrentBasicBlockCallback; + cb.setCurrentAddress = SetCurrentAddressCallback; + cb.setCurrentSelection = SetCurrentSelectionCallback; + m_object = BNInitScriptingInstance(provider->GetObject(), &cb); +} + + +ScriptingInstance::ScriptingInstance(BNScriptingInstance* instance) +{ + m_object = instance; +} + + +void ScriptingInstance::DestroyInstanceCallback(void* ctxt) +{ + ScriptingInstance* instance = (ScriptingInstance*)ctxt; + instance->DestroyInstance(); +} + + +BNScriptingProviderExecuteResult ScriptingInstance::ExecuteScriptInputCallback(void* ctxt, const char* input) +{ + ScriptingInstance* instance = (ScriptingInstance*)ctxt; + return instance->ExecuteScriptInput(input); +} + + +void ScriptingInstance::SetCurrentBinaryViewCallback(void* ctxt, BNBinaryView* view) +{ + ScriptingInstance* instance = (ScriptingInstance*)ctxt; + instance->SetCurrentBinaryView(view ? new BinaryView(BNNewViewReference(view)) : nullptr); +} + + +void ScriptingInstance::SetCurrentFunctionCallback(void* ctxt, BNFunction* func) +{ + ScriptingInstance* instance = (ScriptingInstance*)ctxt; + instance->SetCurrentFunction(func ? new Function(BNNewFunctionReference(func)) : nullptr); +} + + +void ScriptingInstance::SetCurrentBasicBlockCallback(void* ctxt, BNBasicBlock* block) +{ + ScriptingInstance* instance = (ScriptingInstance*)ctxt; + instance->SetCurrentBasicBlock(block ? new BasicBlock(BNNewBasicBlockReference(block)) : nullptr); +} + + +void ScriptingInstance::SetCurrentAddressCallback(void* ctxt, uint64_t addr) +{ + ScriptingInstance* instance = (ScriptingInstance*)ctxt; + instance->SetCurrentAddress(addr); +} + + +void ScriptingInstance::SetCurrentSelectionCallback(void* ctxt, uint64_t begin, uint64_t end) +{ + ScriptingInstance* instance = (ScriptingInstance*)ctxt; + instance->SetCurrentSelection(begin, end); +} + + +void ScriptingInstance::DestroyInstance() +{ +} + + +void ScriptingInstance::SetCurrentBinaryView(BinaryView*) +{ +} + + +void ScriptingInstance::SetCurrentFunction(Function*) +{ +} + + +void ScriptingInstance::SetCurrentBasicBlock(BasicBlock*) +{ +} + + +void ScriptingInstance::SetCurrentAddress(uint64_t) +{ +} + + +void ScriptingInstance::SetCurrentSelection(uint64_t, uint64_t) +{ +} + + +void ScriptingInstance::Output(const string& text) +{ + BNNotifyOutputForScriptingInstance(m_object, text.c_str()); +} + + +void ScriptingInstance::Error(const string& text) +{ + BNNotifyErrorForScriptingInstance(m_object, text.c_str()); +} + + +void ScriptingInstance::InputReadyStateChanged(BNScriptingProviderInputReadyState state) +{ + BNNotifyInputReadyStateForScriptingInstance(m_object, state); +} + + +BNScriptingProviderInputReadyState ScriptingInstance::GetInputReadyState() +{ + return BNGetScriptingInstanceInputReadyState(m_object); +} + + +void ScriptingInstance::RegisterOutputListener(ScriptingOutputListener* listener) +{ + BNRegisterScriptingInstanceOutputListener(m_object, &listener->GetCallbacks()); +} + + +void ScriptingInstance::UnregisterOutputListener(ScriptingOutputListener* listener) +{ + BNUnregisterScriptingInstanceOutputListener(m_object, &listener->GetCallbacks()); +} + + +CoreScriptingInstance::CoreScriptingInstance(BNScriptingInstance* instance): ScriptingInstance(instance) +{ +} + + +BNScriptingProviderExecuteResult CoreScriptingInstance::ExecuteScriptInput(const string& input) +{ + return BNExecuteScriptInput(m_object, input.c_str()); +} + + +void CoreScriptingInstance::SetCurrentBinaryView(BinaryView* view) +{ + BNSetScriptingInstanceCurrentBinaryView(m_object, view ? view->GetObject() : nullptr); +} + + +void CoreScriptingInstance::SetCurrentFunction(Function* func) +{ + BNSetScriptingInstanceCurrentFunction(m_object, func ? func->GetObject() : nullptr); +} + + +void CoreScriptingInstance::SetCurrentBasicBlock(BasicBlock* block) +{ + BNSetScriptingInstanceCurrentBasicBlock(m_object, block ? block->GetObject() : nullptr); +} + + +void CoreScriptingInstance::SetCurrentAddress(uint64_t addr) +{ + BNSetScriptingInstanceCurrentAddress(m_object, addr); +} + + +void CoreScriptingInstance::SetCurrentSelection(uint64_t begin, uint64_t end) +{ + BNSetScriptingInstanceCurrentSelection(m_object, begin, end); +} + + +ScriptingProvider::ScriptingProvider(const string& name): m_nameForRegister(name) +{ +} + + +ScriptingProvider::ScriptingProvider(BNScriptingProvider* provider) +{ + m_object = provider; +} + + +BNScriptingInstance* ScriptingProvider::CreateInstanceCallback(void* ctxt) +{ + ScriptingProvider* provider = (ScriptingProvider*)ctxt; + Ref<ScriptingInstance> instance = provider->CreateNewInstance(); + return instance ? BNNewScriptingInstanceReference(instance->GetObject()) : nullptr; +} + + +vector<Ref<ScriptingProvider>> ScriptingProvider::GetList() +{ + size_t count; + BNScriptingProvider** list = BNGetScriptingProviderList(&count); + vector<Ref<ScriptingProvider>> result; + for (size_t i = 0; i < count; i++) + result.push_back(new CoreScriptingProvider(list[i])); + BNFreeScriptingProviderList(list); + return result; +} + + +Ref<ScriptingProvider> ScriptingProvider::GetByName(const string& name) +{ + BNScriptingProvider* result = BNGetScriptingProviderByName(name.c_str()); + if (!result) + return nullptr; + return new CoreScriptingProvider(result); +} + + +void ScriptingProvider::Register(ScriptingProvider* provider) +{ + BNScriptingProviderCallbacks cb; + cb.context = provider; + cb.createInstance = CreateInstanceCallback; + provider->m_object = BNRegisterScriptingProvider(provider->m_nameForRegister.c_str(), &cb); +} + + +CoreScriptingProvider::CoreScriptingProvider(BNScriptingProvider* provider): ScriptingProvider(provider) +{ +} + + +Ref<ScriptingInstance> CoreScriptingProvider::CreateNewInstance() +{ + BNScriptingInstance* result = BNCreateScriptingProviderInstance(m_object); + if (!result) + return nullptr; + return new CoreScriptingInstance(result); +} |
