summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--binaryninjaapi.h30
-rw-r--r--binaryninjacore.h33
-rw-r--r--interaction.cpp255
-rw-r--r--python/__init__.py227
4 files changed, 544 insertions, 1 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h
index 7e496591..a62b4239 100644
--- a/binaryninjaapi.h
+++ b/binaryninjaapi.h
@@ -276,6 +276,7 @@ namespace BinaryNinja
class MainThreadAction;
class MainThreadActionHandler;
class InteractionHandler;
+ struct FormInputField;
/*! Logs to the error console with the given BNLogLevel.
@@ -401,6 +402,7 @@ namespace BinaryNinja
bool GetSaveFileNameInput(std::string& result, const std::string& prompt, const std::string& ext = "",
const std::string& defaultName = "");
bool GetDirectoryNameInput(std::string& result, const std::string& prompt, const std::string& defaultName = "");
+ bool GetFormInput(std::vector<FormInputField>& fields, const std::string& title);
BNMessageBoxButtonResult ShowMessageBox(const std::string& title, const std::string& text,
BNMessageBoxButtonSet buttons = OKButtonSet, BNMessageBoxIcon icon = InformationIcon);
@@ -2311,6 +2313,33 @@ namespace BinaryNinja
static std::vector<Ref<BackgroundTask>> GetRunningTasks();
};
+ struct FormInputField
+ {
+ BNFormInputFieldType type;
+ std::string prompt;
+ Ref<BinaryView> view; // For AddressFormField
+ uint64_t currentAddress; // For AddressFormField
+ std::vector<std::string> choices; // For ChoiceFormField
+ std::string ext; // For OpenFileNameFormField, SaveFileNameFormField
+ std::string defaultName; // For SaveFileNameFormField
+ int64_t intResult;
+ uint64_t addressResult;
+ std::string stringResult;
+ size_t indexResult;
+
+ static FormInputField Label(const std::string& text);
+ static FormInputField Separator();
+ static FormInputField TextLine(const std::string& prompt);
+ static FormInputField MultilineText(const std::string& prompt);
+ static FormInputField Integer(const std::string& prompt);
+ static FormInputField Address(const std::string& prompt, BinaryView* view = nullptr, uint64_t currentAddress = 0);
+ static FormInputField Choice(const std::string& prompt, const std::vector<std::string>& choices);
+ static FormInputField OpenFileName(const std::string& prompt, const std::string& ext);
+ static FormInputField SaveFileName(const std::string& prompt, const std::string& ext,
+ const std::string& defaultName = "");
+ static FormInputField DirectoryName(const std::string& prompt, const std::string& defaultName = "");
+ };
+
class InteractionHandler
{
public:
@@ -2331,6 +2360,7 @@ namespace BinaryNinja
const std::string& ext = "", const std::string& defaultName = "");
virtual bool GetDirectoryNameInput(std::string& result, const std::string& prompt,
const std::string& defaultName = "");
+ virtual bool GetFormInput(std::vector<FormInputField>& fields, const std::string& title) = 0;
virtual BNMessageBoxButtonResult ShowMessageBox(const std::string& title, const std::string& text,
BNMessageBoxButtonSet buttons = OKButtonSet, BNMessageBoxIcon icon = InformationIcon) = 0;
diff --git a/binaryninjacore.h b/binaryninjacore.h
index 6339b1c3..b6d2f7ed 100644
--- a/binaryninjacore.h
+++ b/binaryninjacore.h
@@ -1073,6 +1073,36 @@ extern "C"
CancelButton = 3
};
+ enum BNFormInputFieldType
+ {
+ LabelFormField,
+ SeparatorFormField,
+ TextLineFormField,
+ MultilineTextFormField,
+ IntegerFormField,
+ AddressFormField,
+ ChoiceFormField,
+ OpenFileNameFormField,
+ SaveFileNameFormField,
+ DirectoryNameFormField
+ };
+
+ struct BNFormInputField
+ {
+ BNFormInputFieldType type;
+ const char* prompt;
+ BNBinaryView* view; // For AddressFormField
+ uint64_t currentAddress; // For AddressFormField
+ const char** choices; // For ChoiceFormField
+ size_t count; // For ChoiceFormField
+ const char* ext; // For OpenFileNameFormField, SaveFileNameFormField
+ const char* defaultName; // For SaveFileNameFormField
+ int64_t intResult;
+ uint64_t addressResult;
+ char* stringResult;
+ size_t indexResult;
+ };
+
struct BNInteractionHandlerCallbacks
{
void* context;
@@ -1091,6 +1121,7 @@ extern "C"
bool (*getSaveFileNameInput)(void* ctxt, char** result, const char* prompt, const char* ext,
const char* defaultName);
bool (*getDirectoryNameInput)(void* ctxt, char** result, const char* prompt, const char* defaultName);
+ bool (*getFormInput)(void* ctxt, BNFormInputField* fields, size_t count, const char* title);
BNMessageBoxButtonResult (*showMessageBox)(void* ctxt, const char* title, const char* text,
BNMessageBoxButtonSet buttons, BNMessageBoxIcon icon);
};
@@ -2107,6 +2138,8 @@ extern "C"
BINARYNINJACOREAPI bool BNGetSaveFileNameInput(char** result, const char* prompt, const char* ext,
const char* defaultName);
BINARYNINJACOREAPI bool BNGetDirectoryNameInput(char** result, const char* prompt, const char* defaultName);
+ BINARYNINJACOREAPI bool BNGetFormInput(BNFormInputField* fields, size_t count, const char* title);
+ BINARYNINJACOREAPI void BNFreeFormInputResults(BNFormInputField* fields, size_t count);
BINARYNINJACOREAPI BNMessageBoxButtonResult BNShowMessageBox(const char* title, const char* text,
BNMessageBoxButtonSet buttons, BNMessageBoxIcon icon);
diff --git a/interaction.cpp b/interaction.cpp
index 432859d9..fb4eb6d4 100644
--- a/interaction.cpp
+++ b/interaction.cpp
@@ -6,6 +6,102 @@ using namespace std;
using namespace BinaryNinja;
+FormInputField FormInputField::Label(const string& text)
+{
+ FormInputField result;
+ result.type = LabelFormField;
+ result.prompt = text;
+ return result;
+}
+
+
+FormInputField FormInputField::Separator()
+{
+ FormInputField result;
+ result.type = SeparatorFormField;
+ return result;
+}
+
+
+FormInputField FormInputField::TextLine(const string& prompt)
+{
+ FormInputField result;
+ result.type = TextLineFormField;
+ result.prompt = prompt;
+ return result;
+}
+
+
+FormInputField FormInputField::MultilineText(const string& prompt)
+{
+ FormInputField result;
+ result.type = MultilineTextFormField;
+ result.prompt = prompt;
+ return result;
+}
+
+
+FormInputField FormInputField::Integer(const string& prompt)
+{
+ FormInputField result;
+ result.type = IntegerFormField;
+ result.prompt = prompt;
+ return result;
+}
+
+
+FormInputField FormInputField::Address(const std::string& prompt, BinaryView* view, uint64_t currentAddress)
+{
+ FormInputField result;
+ result.type = AddressFormField;
+ result.prompt = prompt;
+ result.view = view;
+ result.currentAddress = currentAddress;
+ return result;
+}
+
+
+FormInputField FormInputField::Choice(const string& prompt, const vector<string>& choices)
+{
+ FormInputField result;
+ result.type = ChoiceFormField;
+ result.prompt = prompt;
+ result.choices = choices;
+ return result;
+}
+
+
+FormInputField FormInputField::OpenFileName(const string& prompt, const string& ext)
+{
+ FormInputField result;
+ result.type = OpenFileNameFormField;
+ result.prompt = prompt;
+ result.ext = ext;
+ return result;
+}
+
+
+FormInputField FormInputField::SaveFileName(const string& prompt, const string& ext, const string& defaultName)
+{
+ FormInputField result;
+ result.type = SaveFileNameFormField;
+ result.prompt = prompt;
+ result.ext = ext;
+ result.defaultName = defaultName;
+ return result;
+}
+
+
+FormInputField FormInputField::DirectoryName(const string& prompt, const string& defaultName)
+{
+ FormInputField result;
+ result.type = DirectoryNameFormField;
+ result.prompt = prompt;
+ result.defaultName = defaultName;
+ return result;
+}
+
+
void InteractionHandler::ShowMarkdownReport(Ref<BinaryView> view, const string& title, const string& contents,
const string& plainText)
{
@@ -172,6 +268,85 @@ static bool GetDirectoryNameInputCallback(void* ctxt, char** result, const char*
}
+static bool GetFormInputCallback(void* ctxt, BNFormInputField* fieldBuf, size_t count, const char* title)
+{
+ InteractionHandler* handler = (InteractionHandler*)ctxt;
+
+ // Convert list of fields from core structure to API structure
+ vector<FormInputField> fields;
+ for (size_t i = 0; i < count; i++)
+ {
+ vector<string> choices;
+ switch (fieldBuf[i].type)
+ {
+ case SeparatorFormField:
+ fields.push_back(FormInputField::Separator());
+ break;
+ case TextLineFormField:
+ fields.push_back(FormInputField::TextLine(fieldBuf[i].prompt));
+ break;
+ case MultilineTextFormField:
+ fields.push_back(FormInputField::MultilineText(fieldBuf[i].prompt));
+ break;
+ case IntegerFormField:
+ fields.push_back(FormInputField::Integer(fieldBuf[i].prompt));
+ break;
+ case AddressFormField:
+ fields.push_back(FormInputField::Address(fieldBuf[i].prompt, fieldBuf[i].view ?
+ new BinaryView(BNNewViewReference(fieldBuf[i].view)) : nullptr, fieldBuf[i].currentAddress));
+ break;
+ case ChoiceFormField:
+ for (size_t j = 0; j < fieldBuf[i].count; j++)
+ choices.push_back(fieldBuf[i].choices[j]);
+ fields.push_back(FormInputField::Choice(fieldBuf[i].prompt, choices));
+ break;
+ case OpenFileNameFormField:
+ fields.push_back(FormInputField::OpenFileName(fieldBuf[i].prompt, fieldBuf[i].ext));
+ break;
+ case SaveFileNameFormField:
+ fields.push_back(FormInputField::SaveFileName(fieldBuf[i].prompt, fieldBuf[i].ext, fieldBuf[i].defaultName));
+ break;
+ case DirectoryNameFormField:
+ fields.push_back(FormInputField::DirectoryName(fieldBuf[i].prompt, fieldBuf[i].defaultName));
+ break;
+ default:
+ fields.push_back(FormInputField::Label(fieldBuf[i].prompt));
+ break;
+ }
+ }
+
+ if (!handler->GetFormInput(fields, title))
+ return false;
+
+ // Place results into core structure
+ for (size_t i = 0; i < count; i++)
+ {
+ switch (fieldBuf[i].type)
+ {
+ case TextLineFormField:
+ case MultilineTextFormField:
+ case OpenFileNameFormField:
+ case SaveFileNameFormField:
+ case DirectoryNameFormField:
+ fieldBuf[i].stringResult = BNAllocString(fields[i].stringResult.c_str());
+ break;
+ case IntegerFormField:
+ fieldBuf[i].intResult = fields[i].intResult;
+ break;
+ case AddressFormField:
+ fieldBuf[i].addressResult = fields[i].addressResult;
+ break;
+ case ChoiceFormField:
+ fieldBuf[i].indexResult = fields[i].indexResult;
+ break;
+ default:
+ break;
+ }
+ }
+ return true;
+}
+
+
static BNMessageBoxButtonResult ShowMessageBoxCallback(void* ctxt, const char* title, const char* text,
BNMessageBoxButtonSet buttons, BNMessageBoxIcon icon)
{
@@ -194,6 +369,7 @@ void BinaryNinja::RegisterInteractionHandler(InteractionHandler* handler)
cb.getOpenFileNameInput = GetOpenFileNameInputCallback;
cb.getSaveFileNameInput = GetSaveFileNameInputCallback;
cb.getDirectoryNameInput = GetDirectoryNameInputCallback;
+ cb.getFormInput = GetFormInputCallback;
cb.showMessageBox = ShowMessageBoxCallback;
BNRegisterInteractionHandler(&cb);
}
@@ -295,6 +471,85 @@ bool BinaryNinja::GetDirectoryNameInput(string& result, const string& prompt, co
}
+bool BinaryNinja::GetFormInput(vector<FormInputField>& fields, const string& title)
+{
+ // Construct field list in core format
+ BNFormInputField* fieldBuf = new BNFormInputField[fields.size()];
+ for (size_t i = 0; i < fields.size(); i++)
+ {
+ fieldBuf[i].type = fields[i].type;
+ fieldBuf[i].prompt = fields[i].prompt.c_str();
+ switch (fields[i].type)
+ {
+ case AddressFormField:
+ fieldBuf[i].view = fields[i].view ? fields[i].view->GetObject() : nullptr;
+ fieldBuf[i].currentAddress = fields[i].currentAddress;
+ break;
+ case ChoiceFormField:
+ fieldBuf[i].choices = new const char*[fields[i].choices.size()];
+ fieldBuf[i].count = fields[i].choices.size();
+ for (size_t j = 0; j < fields[i].choices.size(); j++)
+ fieldBuf[i].choices[j] = fields[i].choices[j].c_str();
+ break;
+ case OpenFileNameFormField:
+ fieldBuf[i].ext = fields[i].ext.c_str();
+ break;
+ case SaveFileNameFormField:
+ fieldBuf[i].ext = fields[i].ext.c_str();
+ fieldBuf[i].defaultName = fields[i].defaultName.c_str();
+ break;
+ case DirectoryNameFormField:
+ fieldBuf[i].defaultName = fields[i].defaultName.c_str();
+ break;
+ default:
+ break;
+ }
+ }
+
+ bool ok = BNGetFormInput(fieldBuf, fields.size(), title.c_str());
+
+ // Free any memory used by field descriptions
+ for (size_t i = 0; i < fields.size(); i++)
+ {
+ if (fields[i].type == ChoiceFormField)
+ delete[] fieldBuf[i].choices;
+ }
+
+ // If user cancelled, there are no results
+ if (!ok)
+ return false;
+
+ // Copy results to API structures
+ for (size_t i = 0; i < fields.size(); i++)
+ {
+ switch (fields[i].type)
+ {
+ case TextLineFormField:
+ case MultilineTextFormField:
+ case OpenFileNameFormField:
+ case SaveFileNameFormField:
+ case DirectoryNameFormField:
+ fields[i].stringResult = fieldBuf[i].stringResult;
+ break;
+ case IntegerFormField:
+ fields[i].intResult = fieldBuf[i].intResult;
+ break;
+ case AddressFormField:
+ fields[i].addressResult = fieldBuf[i].addressResult;
+ break;
+ case ChoiceFormField:
+ fields[i].indexResult = fieldBuf[i].indexResult;
+ break;
+ default:
+ break;
+ }
+ }
+
+ BNFreeFormInputResults(fieldBuf, fields.size());
+ return true;
+}
+
+
BNMessageBoxButtonResult BinaryNinja::ShowMessageBox(const string& title, const string& text,
BNMessageBoxButtonSet buttons, BNMessageBoxIcon icon)
{
diff --git a/python/__init__.py b/python/__init__.py
index c8880648..847b2c06 100644
--- a/python/__init__.py
+++ b/python/__init__.py
@@ -10177,6 +10177,170 @@ class BackgroundTaskThread(BackgroundTask):
def join(self):
self.thread.join()
+class LabelField(object):
+ def __init__(self, text):
+ self.text = text
+
+ def _fill_core_struct(self, value):
+ value.type = core.LabelFormField
+ value.prompt = self.text
+
+ def _fill_core_result(self, value):
+ pass
+
+ def _get_result(self, value):
+ pass
+
+class SeparatorField(object):
+ def _fill_core_struct(self, value):
+ value.type = core.SeparatorFormField
+
+ def _fill_core_result(self, value):
+ pass
+
+ def _get_result(self, value):
+ pass
+
+class TextLineField(object):
+ def __init__(self, prompt):
+ self.prompt = prompt
+ self.result = None
+
+ def _fill_core_struct(self, value):
+ value.type = core.TextLineFormField
+ value.prompt = self.prompt
+
+ def _fill_core_result(self, value):
+ value.stringResult = core.BNAllocString(str(self.result))
+
+ def _get_result(self, value):
+ self.result = value.stringResult
+
+class MultilineTextField(object):
+ def __init__(self, prompt):
+ self.prompt = prompt
+ self.result = None
+
+ def _fill_core_struct(self, value):
+ value.type = core.MultilineTextFormField
+ value.prompt = self.prompt
+
+ def _fill_core_result(self, value):
+ value.stringResult = core.BNAllocString(str(self.result))
+
+ def _get_result(self, value):
+ self.result = value.stringResult
+
+class IntegerField(object):
+ def __init__(self, prompt):
+ self.prompt = prompt
+ self.result = None
+
+ def _fill_core_struct(self, value):
+ value.type = core.IntegerFormField
+ value.prompt = self.prompt
+
+ def _fill_core_result(self, value):
+ value.intResult = self.result
+
+ def _get_result(self, value):
+ self.result = value.intResult
+
+class AddressField(object):
+ def __init__(self, prompt, view = None, current_address = 0):
+ self.prompt = prompt
+ self.view = view
+ self.current_address = current_address
+ self.result = None
+
+ def _fill_core_struct(self, value):
+ value.type = core.AddressFormField
+ value.prompt = self.prompt
+ value.view = None
+ if self.view is not None:
+ value.view = self.view.handle
+ value.currentAddress = self.current_address
+
+ def _fill_core_result(self, value):
+ value.addressResult = self.result
+
+ def _get_result(self, value):
+ self.result = value.addressResult
+
+class ChoiceField(object):
+ def __init__(self, prompt, choices):
+ self.prompt = prompt
+ self.choices = choices
+ self.result = None
+
+ def _fill_core_struct(self, value):
+ value.type = core.ChoiceFormField
+ value.prompt = self.prompt
+ choice_buf = (ctypes.c_char_p * len(self.choices))()
+ for i in xrange(0, len(self.choices)):
+ choice_buf[i] = str(self.choices[i])
+ value.choices = choice_buf
+ value.count = len(self.choices)
+
+ def _fill_core_result(self, value):
+ value.indexResult = self.result
+
+ def _get_result(self, value):
+ self.result = value.indexResult
+
+class OpenFileNameField(object):
+ def __init__(self, prompt, ext = ""):
+ self.prompt = prompt
+ self.ext = ext
+ self.result = None
+
+ def _fill_core_struct(self, value):
+ value.type = core.OpenFileNameFormField
+ value.prompt = self.prompt
+ value.ext = self.ext
+
+ def _fill_core_result(self, value):
+ value.stringResult = core.BNAllocString(str(self.result))
+
+ def _get_result(self, value):
+ self.result = value.stringResult
+
+class SaveFileNameField(object):
+ def __init__(self, prompt, ext = "", default_name = ""):
+ self.prompt = prompt
+ self.ext = ext
+ self.default_name = default_name
+ self.result = None
+
+ def _fill_core_struct(self, value):
+ value.type = core.SaveFileNameFormField
+ value.prompt = self.prompt
+ value.ext = self.ext
+ value.defaultName = self.default_name
+
+ def _fill_core_result(self, value):
+ value.stringResult = core.BNAllocString(str(self.result))
+
+ def _get_result(self, value):
+ self.result = value.stringResult
+
+class DirectoryNameField(object):
+ def __init__(self, prompt, default_name = ""):
+ self.prompt = prompt
+ self.default_name = default_name
+ self.result = None
+
+ def _fill_core_struct(self, value):
+ value.type = core.DirectoryNameField
+ value.prompt = self.prompt
+ value.defaultName = self.default_name
+
+ def _fill_core_result(self, value):
+ value.stringResult = core.BNAllocString(str(self.result))
+
+ def _get_result(self, value):
+ self.result = value.stringResult
+
class InteractionHandler(object):
_interaction_handler = None
@@ -10193,6 +10357,7 @@ class InteractionHandler(object):
self._cb.getOpenFileNameInput = self._cb.getOpenFileNameInput.__class__(self._get_open_filename_input)
self._cb.getSaveFileNameInput = self._cb.getSaveFileNameInput.__class__(self._get_save_filename_input)
self._cb.getDirectoryNameInput = self._cb.getDirectoryNameInput.__class__(self._get_directory_name_input)
+ self._cb.getFormInput = self._cb.getFormInput.__class__(self._get_form_input)
self._cb.showMessageBox = self._cb.showMessageBox.__class__(self._show_message_box)
def register(self):
@@ -10306,6 +10471,46 @@ class InteractionHandler(object):
except:
log_error(traceback.format_exc())
+ def _get_form_input(self, ctxt, fields, count, title):
+ try:
+ field_objs = []
+ for i in xrange(0, count):
+ if fields[i].type == core.LabelFormField:
+ field_objs.append(LabelField(fields[i].prompt))
+ elif fields[i].type == core.SeparatorFormField:
+ field_objs.append(SeparatorField())
+ elif fields[i].type == core.TextLineFormField:
+ field_objs.append(TextLineField(fields[i].prompt))
+ elif fields[i].type == core.MultilineTextFormField:
+ field_objs.append(MultilineTextField(fields[i].prompt))
+ elif fields[i].type == core.IntegerFormField:
+ field_objs.append(IntegerField(fields[i].prompt))
+ elif fields[i].type == core.AddressFormField:
+ view = None
+ if fields[i].view:
+ view = BinaryView(None, handle = core.BNNewViewReference(fields[i].view))
+ field_objs.append(AddressField(fields[i].prompt, view, fields[i].currentAddress))
+ elif fields[i].type == core.ChoiceFormField:
+ choices = []
+ for i in xrange(0, fields[i].count):
+ choices.append(fields[i].choices[i])
+ field_objs.append(ChoiceField(fields[i].prompt, choices))
+ elif fields[i].type == core.OpenFileNameFormField:
+ field_objs.append(OpenFileNameField(fields[i].prompt, fields[i].ext))
+ elif fields[i].type == core.SaveFileNameFormField:
+ field_objs.append(SaveFileNameField(fields[i].prompt, fields[i].ext, fields[i].defaultName))
+ elif fields[i].type == core.DirectoryNameField:
+ field_objs.append(DirectoryNameField(fields[i].prompt, fields[i].defaultName))
+ else:
+ field_objs.append(LabelField(fields[i].prompt))
+ if not self.get_form_input(field_objs, title):
+ return False
+ for i in xrange(0, count):
+ field_objs[i]._fill_core_result(fields[i])
+ return True
+ except:
+ log_error(traceback.format_exc())
+
def _show_message_box(self, ctxt, title, text, buttons, icon):
try:
return self.show_message_box(title, text, buttons, icon)
@@ -10350,8 +10555,11 @@ class InteractionHandler(object):
def get_directory_name_input(self, prompt, default_name):
return get_text_line_input(title, "Select Directory")
+ def get_form_input(self, fields, title):
+ return False
+
def show_message_box(self, title, text, buttons, icon):
- return CancelButton
+ return core.CancelButton
class _DestructionCallbackHandler:
def __init__(self):
@@ -10753,6 +10961,23 @@ def get_directory_name_input(prompt, default_name = ""):
core.BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte)))
return result
+def get_form_input(fields, title):
+ value = (core.BNFormInputField * len(fields))()
+ for i in xrange(0, len(fields)):
+ if isinstance(fields[i], str):
+ LabelField(fields[i])._fill_core_struct(value[i])
+ elif fields[i] is None:
+ SeparatorField()._fill_core_struct(value[i])
+ else:
+ fields[i]._fill_core_struct(value[i])
+ if not core.BNGetFormInput(value, len(fields), title):
+ return False
+ for i in xrange(0, len(fields)):
+ if not (isinstance(fields[i], str) or (fields[i] is None)):
+ fields[i]._get_result(value[i])
+ core.BNFreeFormInputResults(value, len(fields))
+ return True
+
def show_message_box(title, text, buttons = core.OKButtonSet, icon = core.InformationIcon):
return core.BNShowMessageBox(title, text, buttons, icon)