# 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 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 tab
: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 title: title to display in the tab
: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 title: Title to display in the tab
: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 str title: Title to display in the tab
: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 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)