Source code for binaryninja.interaction

# Copyright (c) 2015-2024 Vector 35 Inc
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.

import ctypes
import traceback
import webbrowser
from typing import Optional, Callable, List

# Binary Ninja components
from . import _binaryninjacore as core
from .enums import FormInputFieldType, MessageBoxIcon, MessageBoxButtonSet, MessageBoxButtonResult, ReportType
from . import binaryview
from .log import log_error
from . import flowgraph
from . import mainthread


[docs]class LabelField: """ ``LabelField`` adds a text label to the display. """ def __init__(self, text: str): self._text = text def _fill_core_struct(self, value): value.type = FormInputFieldType.LabelFormField value.hasDefault = False value.prompt = self._text def _fill_core_result(self, value): pass def _get_result(self, value): pass @property def text(self) -> str: return self._text @text.setter def text(self, value: str) -> None: self._text = value
[docs]class SeparatorField: """ ``SeparatorField`` adds vertical separation to the display. """ def _fill_core_struct(self, value): value.type = FormInputFieldType.SeparatorFormField value.hasDefault = False def _fill_core_result(self, value): pass def _get_result(self, value): pass
[docs]class TextLineField: """ ``TextLineField`` Adds prompt for text string input. Result is stored in self.result as a string on completion. """ def __init__(self, prompt: str, default: Optional[str] = None): self._prompt = prompt self._default = default self._result = None def _fill_core_struct(self, value): value.type = FormInputFieldType.TextLineFormField value.prompt = self._prompt value.hasDefault = self._default is not None if self._default is not None: value.stringDefault = self._default def _fill_core_result(self, value): value.stringResult = core.BNAllocString(str(self._result)) def _get_result(self, value): self._result = value.stringResult @property def prompt(self): return self._prompt @prompt.setter def prompt(self, value): self._prompt = value @property def result(self): return self._result @result.setter def result(self, value): self._result = value
[docs]class MultilineTextField: """ ``MultilineTextField`` add multi-line text string input field. Result is stored in self.result as a string. This option is not supported on the command-line. """ def __init__(self, prompt: str, default: Optional[str] = None): self._prompt = prompt self._default = default self._result = None def _fill_core_struct(self, value): value.type = FormInputFieldType.MultilineTextFormField value.prompt = self._prompt value.hasDefault = self._default is not None if self._default is not None: value.stringDefault = self._default def _fill_core_result(self, value): value.stringResult = core.BNAllocString(str(self._result)) def _get_result(self, value): self._result = value.stringResult @property def prompt(self): return self._prompt @prompt.setter def prompt(self, value): self._prompt = value @property def result(self): return self._result @result.setter def result(self, value): self._result = value
[docs]class IntegerField: """ ``IntegerField`` add prompt for integer. Result is stored in self.result as an int. """ def __init__(self, prompt: str, default: Optional[int] = None): self._prompt = prompt self._default = default self._result = None def _fill_core_struct(self, value): value.type = FormInputFieldType.IntegerFormField value.prompt = self._prompt value.hasDefault = self._default is not None if self._default is not None: value.intDefault = self._default def _fill_core_result(self, value): value.intResult = self._result def _get_result(self, value): self._result = value.intResult @property def prompt(self): return self._prompt @prompt.setter def prompt(self, value): self._prompt = value @property def result(self): return self._result @result.setter def result(self, value): self._result = value
[docs]class AddressField: """ ``AddressField`` prompts the user for an address. By passing the optional view and current_address parameters \ offsets can be used instead of just an address. The result is stored as in int in self.result. .. note:: This API currently functions differently on the command-line, as the view and current_address are \ disregarded. Additionally where as in the UI the result defaults to hexadecimal on the command-line 0x must be \ specified. """ def __init__(self, prompt: str, view: Optional['binaryview.BinaryView'] = None, current_address: int = 0, default: Optional[int] = None): self._prompt = prompt self._view = view self._current_address = current_address self._default = default self._result = None def _fill_core_struct(self, value): value.type = FormInputFieldType.AddressFormField value.prompt = self._prompt value.view = None if self._view is not None: value.view = self._view.handle value.currentAddress = self._current_address value.hasDefault = self._default is not None if self._default is not None: value.addressDefault = self._default def _fill_core_result(self, value): value.addressResult = self._result def _get_result(self, value): self._result = value.addressResult @property def prompt(self): """prompt to be presented to the user""" return self._prompt @prompt.setter def prompt(self, value): self._prompt = value @property def view(self): """BinaryView for the address""" return self._view @view.setter def view(self, value): self._view = value @property def current_address(self): """current address to use as a base for relative calculations""" return self._current_address @current_address.setter def current_address(self, value): self._current_address = value @property def result(self): return self._result @result.setter def result(self, value): self._result = value
[docs]class ChoiceField: """ ``ChoiceField`` prompts the user to choose from the list of strings provided in ``choices``. Result is stored \ in self.result as an index in to the choices array. :attr str prompt: prompt to be presented to the user :attr list(str) choices: list of choices to choose from """ def __init__(self, prompt: str, choices: List[str], default: Optional[str] = None): self._prompt = prompt self._choices = choices self._default = default self._result = None def _fill_core_struct(self, value): value.type = FormInputFieldType.ChoiceFormField value.prompt = self._prompt choice_buf = (ctypes.c_char_p * len(self._choices))() for i in range(0, len(self._choices)): choice_buf[i] = self._choices[i].encode('charmap') value.choices = choice_buf value.count = len(self._choices) value.hasDefault = self._default is not None if self._default is not None: value.indexDefault = self._default def _fill_core_result(self, value): value.indexResult = self._result def _get_result(self, value): self._result = value.indexResult @property def prompt(self): return self._prompt @prompt.setter def prompt(self, value): self._prompt = value @property def choices(self): return self._choices @choices.setter def choices(self, value): self._choices = value @property def result(self): return self._result @result.setter def result(self, value): self._result = value
[docs]class OpenFileNameField: """ ``OpenFileNameField`` prompts the user to specify a file name to open. Result is stored in self.result as a string. """ def __init__(self, prompt: str, ext: str = "", default: Optional[str] = None): self._prompt = prompt self._ext = ext self._default = default self._result = None def _fill_core_struct(self, value): value.type = FormInputFieldType.OpenFileNameFormField value.prompt = self._prompt value.ext = self._ext value.hasDefault = self._default is not None if self._default is not None: value.stringDefault = self._default def _fill_core_result(self, value): value.stringResult = core.BNAllocString(str(self.result)) def _get_result(self, value): self._result = value.stringResult @property def prompt(self): return self._prompt @prompt.setter def prompt(self, value): self._prompt = value @property def ext(self): return self._ext @ext.setter def ext(self, value): self._ext = value @property def result(self): return self._result @result.setter def result(self, value): self._result = value
[docs]class SaveFileNameField: """ ``SaveFileNameField`` prompts the user to specify a file name to save. Result is stored in self.result as a string. """ def __init__(self, prompt, ext="", default_name="", default=None): self._prompt = prompt self._ext = ext self._default_name = default_name self._default = default self._result = None def _fill_core_struct(self, value): value.type = FormInputFieldType.SaveFileNameFormField value.prompt = self._prompt value.ext = self._ext value.defaultName = self._default_name value.hasDefault = self._default is not None if self._default is not None: value.stringDefault = self._default def _fill_core_result(self, value): value.stringResult = core.BNAllocString(str(self._result)) def _get_result(self, value): self._result = value.stringResult @property def prompt(self): return self._prompt @prompt.setter def prompt(self, value): self._prompt = value @property def ext(self): return self._ext @ext.setter def ext(self, value): self._ext = value @property def default_name(self): return self._default_name @default_name.setter def default_name(self, value): self._default_name = value @property def result(self): return self._result @result.setter def result(self, value): self._result = value
[docs]class DirectoryNameField: """ ``DirectoryNameField`` prompts the user to specify a directory name to open. Result is stored in self.result as a string. """ def __init__(self, prompt, default_name="", default=None): self._prompt = prompt self._default_name = default_name self._default = default self._result = None def _fill_core_struct(self, value): value.type = FormInputFieldType.DirectoryNameFormField value.prompt = self._prompt value.defaultName = self._default_name value.hasDefault = self._default is not None if self._default is not None: value.stringDefault = self._default def _fill_core_result(self, value): value.stringResult = core.BNAllocString(str(self._result)) def _get_result(self, value): self._result = value.stringResult @property def prompt(self): return self._prompt @prompt.setter def prompt(self, value): self._prompt = value @property def default_name(self): return self._default_name @default_name.setter def default_name(self, value): self._default_name = value @property def result(self): return self._result @result.setter def result(self, value): self._result = value
[docs]class InteractionHandler: _interaction_handler = None def __init__(self): self._cb = core.BNInteractionHandlerCallbacks() self._cb.context = 0 self._cb.showPlainTextReport = self._cb.showPlainTextReport.__class__(self._show_plain_text_report) self._cb.showMarkdownReport = self._cb.showMarkdownReport.__class__(self._show_markdown_report) self._cb.showHTMLReport = self._cb.showHTMLReport.__class__(self._show_html_report) self._cb.showGraphReport = self._cb.showGraphReport.__class__(self._show_graph_report) self._cb.showReportCollection = self._cb.showReportCollection.__class__(self._show_report_collection) self._cb.getTextLineInput = self._cb.getTextLineInput.__class__(self._get_text_line_input) self._cb.getIntegerInput = self._cb.getIntegerInput.__class__(self._get_int_input) self._cb.getAddressInput = self._cb.getAddressInput.__class__(self._get_address_input) self._cb.getChoiceInput = self._cb.getChoiceInput.__class__(self._get_choice_input) self._cb.getLargeChoiceInput = self._cb.getLargeChoiceInput.__class__(self._get_large_choice_input) 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) self._cb.openUrl = self._cb.openUrl.__class__(self._open_url) self._cb.runProgressDialog = self._cb.runProgressDialog.__class__(self._run_progress_dialog)
[docs] def register(self): self.__class__._interaction_handler = self core.BNRegisterInteractionHandler(self._cb)
def _show_plain_text_report(self, ctxt, view, title, contents): try: if view: view = binaryview.BinaryView(handle=core.BNNewViewReference(view)) else: view = None self.show_plain_text_report(view, title, contents) except: log_error(traceback.format_exc()) def _show_markdown_report(self, ctxt, view, title, contents, plaintext): try: if view: view = binaryview.BinaryView(handle=core.BNNewViewReference(view)) else: view = None self.show_markdown_report(view, title, contents, plaintext) except: log_error(traceback.format_exc()) def _show_html_report(self, ctxt, view, title, contents, plaintext): try: if view: view = binaryview.BinaryView(handle=core.BNNewViewReference(view)) else: view = None self.show_html_report(view, title, contents, plaintext) except: log_error(traceback.format_exc()) def _show_graph_report(self, ctxt, view, title, graph): try: if view: view = binaryview.BinaryView(handle=core.BNNewViewReference(view)) else: view = None self.show_graph_report(view, title, flowgraph.CoreFlowGraph(core.BNNewFlowGraphReference(graph))) except: log_error(traceback.format_exc()) def _show_report_collection(self, ctxt, title, reports): try: self.show_report_collection(title, ReportCollection(core.BNNewReportCollectionReference(reports))) except: log_error(traceback.format_exc()) def _get_text_line_input(self, ctxt, result, prompt, title): try: value = self.get_text_line_input(prompt, title) if value is None: return False result[0] = core.BNAllocString(str(value)) return True except: log_error(traceback.format_exc()) def _get_int_input(self, ctxt, result, prompt, title): try: value = self.get_int_input(prompt, title) if value is None: return False result[0] = value return True except: log_error(traceback.format_exc()) def _get_address_input(self, ctxt, result, prompt, title, view, current_address): try: if view: view = binaryview.BinaryView(handle=core.BNNewViewReference(view)) else: view = None value = self.get_address_input(prompt, title, view, current_address) if value is None: return False result[0] = value return True except: log_error(traceback.format_exc()) def _get_choice_input(self, ctxt, result, prompt, title, choice_buf, count): try: choices = [] for i in range(0, count): choices.append(choice_buf[i]) value = self.get_choice_input(prompt, title, choices) if value is None: return False result[0] = value return True except: log_error(traceback.format_exc()) def _get_large_choice_input(self, ctxt, result, prompt, title, choice_buf, count): try: choices = [] for i in range(0, count): choices.append(choice_buf[i]) value = self.get_large_choice_input(prompt, title, choices) if value is None: return False result[0] = value return True except: log_error(traceback.format_exc()) def _get_open_filename_input(self, ctxt, result, prompt, ext): try: value = self.get_open_filename_input(prompt, ext) if value is None: return False result[0] = core.BNAllocString(str(value)) return True except: log_error(traceback.format_exc()) def _get_save_filename_input(self, ctxt, result, prompt, ext, default_name): try: value = self.get_save_filename_input(prompt, ext, default_name) if value is None: return False result[0] = core.BNAllocString(str(value)) return True except: log_error(traceback.format_exc()) def _get_directory_name_input(self, ctxt, result, prompt, default_name): try: value = self.get_directory_name_input(prompt, default_name) if value is None: return False result[0] = core.BNAllocString(str(value)) return True except: log_error(traceback.format_exc()) def _get_form_input(self, ctxt, fields, count, title): try: field_objs = [] for i in range(0, count): if fields[i].type == FormInputFieldType.LabelFormField: field_objs.append(LabelField(fields[i].prompt)) elif fields[i].type == FormInputFieldType.SeparatorFormField: field_objs.append(SeparatorField()) elif fields[i].type == FormInputFieldType.TextLineFormField: field_objs.append( TextLineField( fields[i].prompt, default=fields[i].stringDefault if fields[i].hasDefault else None ) ) elif fields[i].type == FormInputFieldType.MultilineTextFormField: field_objs.append( MultilineTextField( fields[i].prompt, default=fields[i].stringDefault if fields[i].hasDefault else None ) ) elif fields[i].type == FormInputFieldType.IntegerFormField: field_objs.append( IntegerField(fields[i].prompt, default=fields[i].intDefault if fields[i].hasDefault else None) ) elif fields[i].type == FormInputFieldType.AddressFormField: view = None if fields[i].view: view = binaryview.BinaryView(handle=core.BNNewViewReference(fields[i].view)) field_objs.append( AddressField( fields[i].prompt, view, fields[i].currentAddress, default=fields[i].addressDefault if fields[i].hasDefault else None ) ) elif fields[i].type == FormInputFieldType.ChoiceFormField: choices = [] for j in range(0, fields[i].count): choices.append(fields[i].choices[j]) field_objs.append( ChoiceField( fields[i].prompt, choices, default=fields[i].choiceDefault if fields[i].hasDefault else None ) ) elif fields[i].type == FormInputFieldType.OpenFileNameFormField: field_objs.append( OpenFileNameField( fields[i].prompt, fields[i].ext, default=fields[i].stringDefault if fields[i].hasDefault else None ) ) elif fields[i].type == FormInputFieldType.SaveFileNameFormField: field_objs.append( SaveFileNameField( fields[i].prompt, fields[i].ext, fields[i].defaultName, default=fields[i].stringDefault if fields[i].hasDefault else None ) ) elif fields[i].type == FormInputFieldType.DirectoryNameFormField: field_objs.append( DirectoryNameField( fields[i].prompt, fields[i].defaultName, default=fields[i].stringDefault if fields[i].hasDefault else None ) ) else: field_objs.append(LabelField(fields[i].prompt)) if not self.get_form_input(field_objs, title): return False for i in range(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) except: log_error(traceback.format_exc()) def _open_url(self, ctxt, url): try: return self.open_url(url) except: log_error(traceback.format_exc()) return False def _run_progress_dialog(self, title, can_cancel, task, task_ctxt): try: def py_task(progress: Callable[[int, int], bool]): progress_c = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_size_t, ctypes.c_size_t, lambda ctxt, cur, max: progress(cur, max)) task(task_ctxt, progress_c, None) return self.run_progress_dialog(title, can_cancel, py_task) except: log_error(traceback.format_exc()) return False
[docs] def show_plain_text_report(self, view, title, contents): pass
[docs] def show_markdown_report(self, view, title, contents, plaintext): self.show_html_report(view, title, markdown_to_html(contents), plaintext)
[docs] def show_html_report(self, view, title, contents, plaintext): if len(plaintext) != 0: self.show_plain_text_report(view, title, plaintext)
[docs] def show_graph_report(self, view, title, graph): pass
[docs] def show_report_collection(self, title, reports): pass
[docs] def get_text_line_input(self, prompt, title): return NotImplemented
[docs] def get_int_input(self, prompt, title): while True: text = self.get_text_line_input(prompt, title) if len(text) == 0: return False try: return int(text) except: continue
[docs] def get_address_input(self, prompt, title, view, current_address): return get_int_input(prompt, title)
[docs] def get_choice_input(self, prompt, title, choices): return NotImplemented
[docs] def get_large_choice_input(self, prompt, title, choices): return NotImplemented
[docs] def get_open_filename_input(self, prompt, ext): return get_text_line_input(prompt, "Open File")
[docs] def get_save_filename_input(self, prompt, ext, default_name): return get_text_line_input(prompt, "Save File")
[docs] def get_directory_name_input(self, prompt, default_name): return get_text_line_input(prompt, "Select Directory")
[docs] def get_form_input(self, fields, title): return False
[docs] def show_message_box(self, title, text, buttons, icon): return MessageBoxButtonResult.CancelButton
[docs] def open_url(self, url): webbrowser.open(url) return True
[docs] def run_progress_dialog(self, task: Callable[[Callable[[int, int], bool]], None]) -> bool: mainthread.execute_on_main_thread_and_wait(lambda: task(lambda cur, max: True)) return True
[docs]class PlainTextReport: def __init__(self, title, contents, view=None): self._view = view self._title = title self._contents = contents def __repr__(self): return "<plaintext report: %s>" % self._title def __str__(self): return self._contents @property def view(self): return self._view @view.setter def view(self, value): self._view = value @property def title(self): return self._title @title.setter def title(self, value): self._title = value @property def contents(self): return self._contents @contents.setter def contents(self, value): self._contents = value
[docs]class MarkdownReport: def __init__(self, title, contents, plaintext="", view=None): self._view = view self._title = title self._contents = contents self._plaintext = plaintext def __repr__(self): return "<markdown report: %s>" % self._title def __str__(self): return self._contents @property def view(self): return self._view @view.setter def view(self, value): self._view = value @property def title(self): return self._title @title.setter def title(self, value): self._title = value @property def contents(self): return self._contents @contents.setter def contents(self, value): self._contents = value @property def plaintext(self): return self._plaintext @plaintext.setter def plaintext(self, value): self._plaintext = value
[docs]class HTMLReport: def __init__(self, title, contents, plaintext="", view=None): self._view = view self._title = title self._contents = contents self._plaintext = plaintext def __repr__(self): return "<html report: %s>" % self._title def __str__(self): return self._contents @property def view(self): return self._view @view.setter def view(self, value): self._view = value @property def title(self): return self._title @title.setter def title(self, value): self._title = value @property def contents(self): return self._contents @contents.setter def contents(self, value): self._contents = value @property def plaintext(self): return self._plaintext @plaintext.setter def plaintext(self, value): self._plaintext = value
[docs]class FlowGraphReport: def __init__(self, title, graph, view=None): self._view = view self._title = title self._graph = graph def __repr__(self): return "<graph report: %s>" % self._title @property def view(self): return self._view @view.setter def view(self, value): self._view = value @property def title(self): return self._title @title.setter def title(self, value): self._title = value @property def graph(self): return self._graph @graph.setter def graph(self, value): self._graph = value
[docs]class ReportCollection: def __init__(self, handle=None): if handle is None: self.handle = core.BNCreateReportCollection() else: self.handle = handle def __len__(self): return core.BNGetReportCollectionCount(self.handle) def _report_from_index(self, i): report_type = core.BNGetReportType(self.handle, i) title = core.BNGetReportTitle(self.handle, i) view = core.BNGetReportView(self.handle, i) if view: view = binaryview.BinaryView(handle=view) else: view = None if report_type == ReportType.PlainTextReportType: contents = core.BNGetReportContents(self.handle, i) return PlainTextReport(title, contents, view) elif report_type == ReportType.MarkdownReportType: contents = core.BNGetReportContents(self.handle, i) plaintext = core.BNGetReportPlainText(self.handle, i) return MarkdownReport(title, contents, plaintext, view) elif report_type == ReportType.HTMLReportType: contents = core.BNGetReportContents(self.handle, i) plaintext = core.BNGetReportPlainText(self.handle, i) return HTMLReport(title, contents, plaintext, view) elif report_type == ReportType.FlowGraphReportType: graph = flowgraph.CoreFlowGraph(core.BNGetReportFlowGraph(self.handle, i)) return FlowGraphReport(title, graph, view) raise TypeError("invalid report type %s" % repr(report_type)) def __getitem__(self, i): if isinstance(i, slice) or isinstance(i, tuple): raise IndexError("expected integer report index") if (i < 0) or (i >= len(self)): raise IndexError("index out of range") return self._report_from_index(i) def __iter__(self): count = len(self) for i in range(0, count): yield self._report_from_index(i) def __repr__(self): return "<reports: %s>" % repr(list(self))
[docs] def append(self, report): if report.view is None: view = None else: view = report.view.handle if isinstance(report, PlainTextReport): core.BNAddPlainTextReportToCollection(self.handle, view, report.title, report.contents) elif isinstance(report, MarkdownReport): core.BNAddMarkdownReportToCollection(self.handle, view, report.title, report.contents, report.plaintext) elif isinstance(report, HTMLReport): core.BNAddHTMLReportToCollection(self.handle, view, report.title, report.contents, report.plaintext) elif isinstance(report, FlowGraphReport): core.BNAddGraphReportToCollection(self.handle, view, report.title, report.graph.handle) else: raise TypeError("expected report object")
[docs] def update(self, i, report): # if isinstance(report, PlainTextReport): # core.BNUpdatePlainTextReportToCollection(self.handle, i, report.contents) # elif isinstance(report, MarkdownReport): # core.BNUpdateMarkdownReportToCollection(self.handle, i, report.contents, report.plaintext) # elif isinstance(report, HTMLReport): # core.BNUpdateHTMLReportToCollection(self.handle, i, report.contents, report.plaintext) if isinstance(report, FlowGraphReport): core.BNUpdateReportFlowGraph(self.handle, i, report.graph.handle) else: raise TypeError("expected report object")
[docs]def markdown_to_html(contents): """ ``markdown_to_html`` converts the provided markdown to HTML :param str contents: Markdown contents to convert to HTML :rtype: str :Example: >>> markdown_to_html("##Yay") '<h2>Yay</h2>' """ return core.BNMarkdownToHTML(contents)
[docs]def show_plain_text_report(title, contents): """ ``show_plain_text_report`` displays contents to the user in the UI or on the command-line .. note:: This API functions differently on the command-line vs the UI. In the UI, a pop-up is used. On the command-line, \ a simple text prompt is used. :param str title: title to display in the UI pop-up :param str contents: plaintext contents to display :rtype: None :Example: >>> show_plain_text_report("title", "contents") contents """ core.BNShowPlainTextReport(None, title, contents)
[docs]def show_markdown_report(title, contents, plaintext=""): """ ``show_markdown_report`` displays the markdown contents in UI applications and plaintext in command-line \ applications. This API doesn't support hyperlinking into the BinaryView, use the \ :py:meth:`BinaryView.show_markdown_report` API if hyperlinking is needed. .. note:: This API function differently on the command-line vs the UI. In the UI a pop-up is used. On the command-line \ a simple text prompt is used. :param str contents: markdown contents to display :param str plaintext: Plain text version to display (used on the command-line) :rtype: None :Example: >>> show_markdown_report("title", "##Contents", "Plain text contents") Plain text contents """ core.BNShowMarkdownReport(None, title, contents, plaintext)
[docs]def show_html_report(title, contents, plaintext=""): """ ``show_html_report`` displays the HTML contents in UI applications and plaintext in command-line \ applications. This API doesn't support hyperlinking into the BinaryView, use the :py:meth:`BinaryView.show_html_report` \ API if hyperlinking is needed. :param str contents: HTML contents to display :param str plaintext: Plain text version to display (used on the command-line) :rtype: None :Example: >>> show_html_report("title", "<h1>Contents</h1>", "Plain text contents") Plain text contents """ core.BNShowHTMLReport(None, title, contents, plaintext)
[docs]def show_graph_report(title, graph): """ ``show_graph_report`` displays a flow graph in UI applications and nothing in command-line applications. \ This API doesn't support clickable references into an existing BinaryView. Use the :py:meth:`BinaryView.show_html_report` \ API if hyperlinking is needed. .. note:: This API function will have no effect outside the UI. :param FlowGraph graph: Flow graph to display :rtype: None """ func = graph.function if func is None: core.BNShowGraphReport(None, title, graph.handle) else: core.BNShowGraphReport(func.view.handle, title, graph.handle)
[docs]def show_report_collection(title, reports): """ ``show_report_collection`` displays multiple reports in UI applications .. note:: This API function will have no effect outside the UI. :param ReportCollection reports: Reports to display :rtype: None """ core.BNShowReportCollection(title, reports.handle)
[docs]def get_text_line_input(prompt, title): """ ``get_text_line_input`` prompts the user to input a string with the given prompt and title .. note:: This API function differently on the command-line vs the UI. In the UI a pop-up is used. On the command-line \ a simple text prompt is used. :param str prompt: String to prompt with. :param str title: Title of the window when executed in the UI. :rtype: str containing the input without trailing newline character. :Example: >>> get_text_line_input("PROMPT>", "getinfo") PROMPT> Input! 'Input!' """ value = ctypes.c_char_p() if not core.BNGetTextLineInput(value, prompt, title): return None result = value.value core.free_string(value) return result
[docs]def get_int_input(prompt, title): """ ``get_int_input`` prompts the user to input a integer with the given prompt and title .. note:: This API function differently on the command-line vs the UI. In the UI a pop-up is used. On the command-line \ a simple text prompt is used. :param str prompt: String to prompt with. :param str title: Title of the window when executed in the UI. :rtype: integer value input by the user. :Example: >>> get_int_input("PROMPT>", "getinfo") PROMPT> 10 10 """ value = ctypes.c_longlong() if not core.BNGetIntegerInput(value, prompt, title): return None return value.value
[docs]def get_address_input(prompt, title): """ ``get_address_input`` prompts the user for an address with the given prompt and title .. note:: This API function differently on the command-line vs the UI. In the UI a pop-up is used. On the command-line \ a simple text prompt is used. :param str prompt: String to prompt with. :param str title: Title of the window when executed in the UI. :rtype: integer value input by the user. :Example: >>> get_address_input("PROMPT>", "getinfo") PROMPT> 10 10L """ value = ctypes.c_ulonglong() if not core.BNGetAddressInput(value, prompt, title, None, 0): return None return value.value
[docs]def get_choice_input(prompt, title, choices): """ ``get_choice_input`` prompts the user to select the one of the provided choices .. note:: This API function differently on the command-line vs the UI. In the UI a pop-up is used. On the command-line \ a simple text prompt is used. The UI uses a combo box. :param str prompt: String to prompt with. :param str title: Title of the window when executed in the UI. :param choices: A list of strings for the user to choose from. :type choices: list(str) :rtype: integer array index of the selected option :Example: >>> get_choice_input("PROMPT>", "choices", ["Yes", "No", "Maybe"]) choices 1) Yes 2) No 3) Maybe PROMPT> 1 0L """ choice_buf = (ctypes.c_char_p * len(choices))() for i in range(0, len(choices)): choice_buf[i] = str(choices[i]).encode('charmap') value = ctypes.c_ulonglong() if not core.BNGetChoiceInput(value, prompt, title, choice_buf, len(choices)): return None return value.value
[docs]def get_large_choice_input(prompt, title, choices): """ ``get_large_choice_input`` prompts the user to select the one of the provided choices from a large pool .. note:: This API function differently on the command-line vs the UI. In the UI a pop-up is used. On the command-line \ a text prompt is used. The UI uses a filterable list of entries :param str prompt: Text for the button when executed in the UI. Prompt shown for selection headless. :param str title: Title of the window when executed in the UI. :param choices: A list of strings for the user to choose from. :type choices: list(str) :rtype: integer array index of the selected option :Example: >>> get_large_choice_input("Select Function", "Select a Function", [f.symbol.short_name for f in bv.functions]) """ choice_buf = (ctypes.c_char_p * len(choices))() for i in range(0, len(choices)): choice_buf[i] = str(choices[i]).encode('charmap') value = ctypes.c_ulonglong() if not core.BNGetLargeChoiceInput(value, prompt, title, choice_buf, len(choices)): return None return value.value
[docs]def get_open_filename_input(prompt: str, ext: str = "") -> Optional[str]: """ ``get_open_filename_input`` prompts the user for a file name to open .. note:: This API functions differently on the command-line vs the UI. In the UI a pop-up is used. On the command-line \ a simple text prompt is used. The UI uses the native window pop-up for file selection. Multiple file selection groups can be included if separated by two semicolons. Multiple file wildcards may be specified by using a space within the parenthesis. Also, a simple selector of `*.extension` by itself may also be used instead of specifying the description. :param str prompt: Prompt to display. :param str ext: Optional, file extension :Example: >>> get_open_filename_input("filename:", "Executables (*.exe *.com);;Python Files (*.py);;All Files (*)") 'foo.exe' >>> get_open_filename_input("filename:", "*.py") 'test.py' """ value = ctypes.c_char_p() if not core.BNGetOpenFileNameInput(value, prompt, ext): return None result = value.value assert result is not None core.free_string(value) return result.decode("utf-8")
[docs]def get_save_filename_input(prompt: str, ext: str = "", default_name: str = "") -> Optional[str]: """ ``get_save_filename_input`` prompts the user for a file name to save as, optionally providing a file extension and \ default_name .. note:: This API function differently on the command-line vs the UI. In the UI a pop-up is used. On the command-line \ a simple text prompt is used. The UI uses the native window pop-up for file selection. :param str prompt: Prompt to display. :param str ext: Optional, file extension :param str default_name: Optional, default file name. :Example: >>> get_save_filename_input("filename:", "exe", "foo.exe") filename: foo.exe 'foo.exe' """ value = ctypes.c_char_p() if not core.BNGetSaveFileNameInput(value, prompt, ext, default_name): return None result = value.value assert result is not None core.free_string(value) return result.decode("utf-8")
[docs]def get_directory_name_input(prompt: str, default_name: str = ""): """ ``get_directory_name_input`` prompts the user for a directory name to save as, optionally providing a default_name .. note:: This API function differently on the command-line vs the UI. In the UI a pop-up is used. On the command-line a simple text prompt is used. The UI uses the native window pop-up for file selection. :param str prompt: Prompt to display. :param str default_name: Optional, default directory name. :rtype: str :Example: >>> get_directory_name_input("prompt") prompt dirname 'dirname' """ value = ctypes.c_char_p() if not core.BNGetDirectoryNameInput(value, prompt, default_name): return None result = value.value assert result is not None core.free_string(value) return result.decode("utf-8")
[docs]def get_form_input(fields, title): """ ``get_from_input`` Prompts the user for a set of inputs specified in ``fields`` with given title. \ The fields parameter is a list which can contain the following types: ===================== =================================================== FieldType Description ===================== =================================================== str an alias for LabelField None an alias for SeparatorField LabelField Text output SeparatorField Vertical spacing TextLineField Prompt for a string value MultilineTextField Prompt for multi-line string value IntegerField Prompt for an integer AddressField Prompt for an address ChoiceField Prompt for a choice from provided options OpenFileNameField Prompt for file to open SaveFileNameField Prompt for file to save to DirectoryNameField Prompt for directory name ===================== =================================================== This API is flexible and works both in the UI via a pop-up dialog and on the command-line. .. note:: More complicated APIs should consider using the included pyside2 functionality in the `binaryninjaui` module. Returns true or false depending on whether the user submitted responses or cancelled the dialog. :param fields: A list containing these classes, strings or None :type fields: list(str) or list(None) or list(LabelField) or list(SeparatorField) or list(TextLineField) or list(MultilineTextField) or list(IntegerField) or list(AddressField) or list(ChoiceField) or list(OpenFileNameField) or list(SaveFileNameField) or list(DirectoryNameField) :param str title: The title of the pop-up dialog :rtype: bool :Example: >>> int_f = IntegerField("Specify Integer") >>> tex_f = TextLineField("Specify name") >>> choice_f = ChoiceField("Options", ["Yes", "No", "Maybe"]) >>> get_form_input(["Get Data", None, int_f, tex_f, choice_f], "The options") Get Data <empty> Specify Integer 1337 Specify name Peter The options 1) Yes 2) No 3) Maybe Options 1 >>> True >>> print(tex_f.result, int_f.result, choice_f.result) Peter 1337 0 """ value = (core.BNFormInputField * len(fields))() for i in range(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 range(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
[docs]def show_message_box(title, text, buttons=MessageBoxButtonSet.OKButtonSet, icon=MessageBoxIcon.InformationIcon): """ ``show_message_box`` Displays a configurable message box in the UI, or prompts on the console as appropriate :param str title: Text title for the message box. :param str text: Text for the main body of the message box. :param MessageBoxButtonSet buttons: One of :py:class:`MessageBoxButtonSet` :param MessageBoxIcon icon: One of :py:class:`MessageBoxIcon` :return: Which button was selected :rtype: MessageBoxButtonResult """ return core.BNShowMessageBox(title, text, buttons, icon)
[docs]def open_url(url): """ ``open_url`` Opens a given url in the user's web browser, if available. :param str url: Url to open :return: True if successful :rtype: bool """ return core.BNOpenUrl(url)
[docs]def run_progress_dialog(title: str, can_cancel: bool, task: Callable[[Callable[[int, int], bool]], None]) -> bool: """ ``run_progress_dialog`` runs a given task in a background thread, showing an updating progress bar which the user can cancel. :param title: Dialog title :param can_cancel: If the task can be cancelled :param task: Function to perform the task, taking as a parameter a function which should be called to report progress updates and check for cancellation. If the progress function returns false, the user has requested to cancel, and the task should handle this appropriately. :return: True if not cancelled """ progress_type = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_size_t, ctypes.c_size_t) task_type = ctypes.CFUNCTYPE(None, ctypes.c_void_p, progress_type, ctypes.c_void_p) def do_task(ctxt: ctypes.c_void_p, progress: progress_type, progress_ctxt: ctypes.c_void_p): def py_progress(cur: int, max: int) -> bool: return progress(progress_ctxt, cur, max) task(py_progress) return core.BNRunProgressDialog(title, can_cancel, task_type(do_task), None)