diff options
| author | kat <katherine@vector35.com> | 2022-06-16 13:57:26 -0400 |
|---|---|---|
| committer | kat <katherine@vector35.com> | 2022-06-16 13:59:40 -0400 |
| commit | 30d84ee263fea44a7fa686d86d1934e4782f8aaa (patch) | |
| tree | f3d0e71d6d1f311c90a061e813efbe45e10b4794 | |
| parent | b46050815d7b3a1a4777a87dbcb23b053da66577 (diff) | |
Add support for executing scripts from files
| -rw-r--r-- | binaryninjaapi.h | 3 | ||||
| -rw-r--r-- | binaryninjacore.h | 5 | ||||
| -rw-r--r-- | python/scriptingprovider.py | 66 | ||||
| -rw-r--r-- | scriptingprovider.cpp | 4 | ||||
| -rw-r--r-- | ui/scriptingconsole.h | 3 |
5 files changed, 69 insertions, 12 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h index fa076984..a50fcc62 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -6230,6 +6230,7 @@ namespace BinaryNinja { static void DestroyInstanceCallback(void* ctxt); static BNScriptingProviderExecuteResult ExecuteScriptInputCallback(void* ctxt, const char* input); + static BNScriptingProviderExecuteResult ExecuteScriptFromFilenameCallback(void *ctxt, const char* filename); static void CancelScriptInputCallback(void* ctxt); static void SetCurrentBinaryViewCallback(void* ctxt, BNBinaryView* view); static void SetCurrentFunctionCallback(void* ctxt, BNFunction* func); @@ -6243,6 +6244,7 @@ namespace BinaryNinja { public: virtual BNScriptingProviderExecuteResult ExecuteScriptInput(const std::string& input) = 0; + virtual BNScriptingProviderExecuteResult ExecuteScriptInputFromFilename(const std::string& filename) = 0; virtual void CancelScriptInput(); virtual void SetCurrentBinaryView(BinaryView* view); virtual void SetCurrentFunction(Function* func); @@ -6271,6 +6273,7 @@ namespace BinaryNinja { virtual ~CoreScriptingInstance() {}; virtual BNScriptingProviderExecuteResult ExecuteScriptInput(const std::string& input) override; + virtual BNScriptingProviderExecuteResult ExecuteScriptInputFromFilename(const std::string& filename) override; virtual void CancelScriptInput() override; virtual void SetCurrentBinaryView(BinaryView* view) override; virtual void SetCurrentFunction(Function* func) override; diff --git a/binaryninjacore.h b/binaryninjacore.h index 381bb948..58fb6bf2 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -2417,6 +2417,7 @@ extern "C" void (*externalRefTaken)(void* ctxt); void (*externalRefReleased)(void* ctxt); BNScriptingProviderExecuteResult (*executeScriptInput)(void* ctxt, const char* input); + BNScriptingProviderExecuteResult (*executeScriptInputFromFilename)(void *ctxt, const char* input); void (*cancelScriptInput)(void* ctxt); void (*setCurrentBinaryView)(void* ctxt, BNBinaryView* view); void (*setCurrentFunction)(void* ctxt, BNFunction* func); @@ -5710,7 +5711,9 @@ extern "C" BINARYNINJACOREAPI BNScriptingProviderInputReadyState BNGetScriptingInstanceInputReadyState( BNScriptingInstance* instance); BINARYNINJACOREAPI BNScriptingProviderExecuteResult BNExecuteScriptInput( - BNScriptingInstance* instance, const char* input); + BNScriptingInstance* instance, const char* input); + BINARYNINJACOREAPI BNScriptingProviderExecuteResult BNExecuteScriptInputFromFilename( + BNScriptingInstance* instance, const char* filename); BINARYNINJACOREAPI void BNCancelScriptInput(BNScriptingInstance* instance); BINARYNINJACOREAPI void BNSetScriptingInstanceCurrentBinaryView(BNScriptingInstance* instance, BNBinaryView* view); BINARYNINJACOREAPI void BNSetScriptingInstanceCurrentFunction(BNScriptingInstance* instance, BNFunction* func); diff --git a/python/scriptingprovider.py b/python/scriptingprovider.py index 83b7d7a4..60b7a16d 100644 --- a/python/scriptingprovider.py +++ b/python/scriptingprovider.py @@ -117,6 +117,7 @@ class ScriptingInstance: self._cb.externalRefTaken = self._cb.externalRefTaken.__class__(self._external_ref_taken) self._cb.externalRefReleased = self._cb.externalRefReleased.__class__(self._external_ref_released) self._cb.executeScriptInput = self._cb.executeScriptInput.__class__(self._execute_script_input) + self._cb.executeScriptInputFromFilename = self._cb.executeScriptInputFromFilename.__class__(self._execute_script_input_from_filename) self._cb.cancelScriptInput = self._cb.cancelScriptInput.__class__(self._cancel_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) @@ -155,6 +156,13 @@ class ScriptingInstance: log_error(traceback.format_exc()) return ScriptingProviderExecuteResult.InvalidScriptInput + def _execute_script_input_from_filename(self, ctxt, filename): + try: + return self.perform_execute_script_input_from_filename(filename) + except: + log_error(traceback.format_exc()) + return ScriptingProviderExecuteResult.InvalidScriptInput + def _cancel_script_input(self, ctxt): try: return self.perform_cancel_script_input() @@ -235,6 +243,10 @@ class ScriptingInstance: return ScriptingProviderExecuteResult.InvalidScriptInput @abc.abstractmethod + def perform_execute_script_input_from_filename(self, text): + return ScriptingProviderExecuteResult.InvalidScriptInput + + @abc.abstractmethod def perform_cancel_script_input(self): return ScriptingProviderExecuteResult.ScriptExecutionCancelled @@ -283,6 +295,9 @@ class ScriptingInstance: def execute_script_input(self, text): return core.BNExecuteScriptInput(self.handle, text) + def execute_script_input_from_filename(self, filename): + return core.BNExecuteScriptInputFromFilename(self.handle, filename) + def cancel_script_input(self, text): return core.BNCancelScriptInput(self.handle) @@ -632,8 +647,8 @@ from binaryninja import * with open(startup_file, 'r') as f: self.interpreter.runsource(f.read(), filename="startup.py", symbol="exec") - def execute(self, code): - self.code = code + def execute(self, _code): + self.code = _code self.event.set() def add_input(self, data): @@ -667,22 +682,28 @@ from binaryninja import * break if self.code is not None: self.instance.input_ready_state = ScriptingProviderInputReadyState.NotReadyForInput - code = self.code + _code = self.code self.code = None PythonScriptingInstance._interpreter.value = self try: self.update_locals() - # If a single-line command ends in ?, show docs as well - if code[-2:] == b'?\n' and len(code.split(b'\n')) < 3: - escaped_code = repr(code[:-2]) - self.interpreter.push(f'bninspect({escaped_code}, globals(), locals())\n') - # Strip ? from the evaluated input - code = code[:-2] + b'\n' + if isinstance(_code, (lambda: 0).__code__.__class__): + self.interpreter.runcode(_code) + self.locals['__name__'] = '__console__' + del self.locals['__file__'] + + else: + # If a single-line command ends in ?, show docs as well + if _code[-2:] == b'?\n' and len(_code.split(b'\n')) < 3: + escaped_code = repr(_code[:-2]) + self.interpreter.push(f'bninspect({escaped_code}, globals(), locals())\n') + # Strip ? from the evaluated input + _code = _code[:-2] + b'\n' - for line in code.split(b'\n'): - self.interpreter.push(line.decode("utf-8")) + for line in _code.split(b'\n'): + self.interpreter.push(line.decode("utf-8")) if self.active_view is not None: tryNavigate = True @@ -815,6 +836,29 @@ from binaryninja import * return ScriptingProviderExecuteResult.SuccessfulScriptExecution @abc.abstractmethod + def perform_execute_script_input_from_filename(self, filename): + if isinstance(filename, bytes): + filename = filename.decode("utf-8") + if not os.path.exists(filename) and os.path.isfile(filename): + return ScriptingProviderExecuteResult.InvalidScriptInput # TODO: maybe this isn't the best result to use? + try: + with open(filename, 'r') as fp: + file_contents = fp.read() + except IOError: + # File was not readable or something went horribly wrong + return ScriptingProviderExecuteResult.InvalidScriptInput + + if len(file_contents) == 0: + return ScriptingProviderExecuteResult.SuccessfulScriptExecution + + _code = code.compile_command(file_contents, filename, 'exec') + self.interpreter.locals['__file__'] = filename + self.interpreter.locals['__name__'] = '__main__' + self.interpreter.execute(_code) + + return ScriptingProviderExecuteResult.SuccessfulScriptExecution + + @abc.abstractmethod def perform_cancel_script_input(self): for tid, tobj in threading._active.items(): # type: ignore if tobj is self.interpreter: diff --git a/scriptingprovider.cpp b/scriptingprovider.cpp index 8b86bbc3..0a89c5a2 100644 --- a/scriptingprovider.cpp +++ b/scriptingprovider.cpp @@ -234,6 +234,10 @@ BNScriptingProviderExecuteResult CoreScriptingInstance::ExecuteScriptInput(const return BNExecuteScriptInput(m_object, input.c_str()); } +BNScriptingProviderExecuteResult CoreScriptingInstance::ExecuteScriptInputFromFilename(const string& filename) +{ + return BNExecuteScriptInputFromFilename(m_object, filename.c_str()); +} void CoreScriptingInstance::CancelScriptInput() { diff --git a/ui/scriptingconsole.h b/ui/scriptingconsole.h index 815c823c..0324e37b 100644 --- a/ui/scriptingconsole.h +++ b/ui/scriptingconsole.h @@ -144,6 +144,7 @@ class BINARYNINJAUIAPI ScriptingConsole : public GlobalAreaWidget, BinaryNinja:: QLabel* m_prompt; QPushButton* m_button; QTimer* m_runTimer; + BinaryNinja::Ref<BinaryNinja::Logger> m_logger; std::mutex m_mutex; std::vector<ScriptOutput> m_pendingOutput; @@ -177,6 +178,7 @@ class BINARYNINJAUIAPI ScriptingConsole : public GlobalAreaWidget, BinaryNinja:: QString getProviderName() const { return m_providerName; } QString getInstanceName() const { return m_instanceName; } ScriptingInstanceRef getInstance() { return m_instance; } + BNScriptingProviderInputReadyState getCurrentState() { return m_currentState; } void clearConsole(); void hideConsole(); @@ -195,4 +197,5 @@ class BINARYNINJAUIAPI ScriptingConsole : public GlobalAreaWidget, BinaryNinja:: std::vector<std::string> reverseSearch(const QString& text); void closing(); + void runScriptFromFile(std::string filename = std::string()); }; |
