summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkat <katherine@vector35.com>2022-06-16 13:57:26 -0400
committerkat <katherine@vector35.com>2022-06-16 13:59:40 -0400
commit30d84ee263fea44a7fa686d86d1934e4782f8aaa (patch)
treef3d0e71d6d1f311c90a061e813efbe45e10b4794
parentb46050815d7b3a1a4777a87dbcb23b053da66577 (diff)
Add support for executing scripts from files
-rw-r--r--binaryninjaapi.h3
-rw-r--r--binaryninjacore.h5
-rw-r--r--python/scriptingprovider.py66
-rw-r--r--scriptingprovider.cpp4
-rw-r--r--ui/scriptingconsole.h3
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());
};