Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save bb33bb/3b6e6878d670f3d87c394d03c4d6e5a1 to your computer and use it in GitHub Desktop.

Select an option

Save bb33bb/3b6e6878d670f3d87c394d03c4d6e5a1 to your computer and use it in GitHub Desktop.

Revisions

  1. @psifertex psifertex revised this gist Jul 25, 2024. 1 changed file with 27 additions and 0 deletions.
    27 changes: 27 additions & 0 deletions copy_types_with_offsets.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,27 @@
    # Copy types with offsets included
    #
    # Requested by a customer who wanted to be able to copy structures simila to how they are shown in the UI with hex offsets

    import binaryninjaui
    import PySide6

    typeName = interaction.get_text_line_input("Type", "Type to copy:").decode('utf8')

    typeVar = bv.get_type_by_name(typeName)

    if not typeVar:
    # check libraries
    for lib in bv.type_libraries:
    typeVar = lib.get_named_type(typeName)
    if typeVar:
    break

    if typeVar:
    lines = ""
    for line in typeVar.get_lines(bv, typeName):
    lines += f"{hex(line.offset)} {str(line)}\n"
    clip = PySide6.QtGui.QGuiApplication.clipboard()
    clip.setText(lines)

    else:
    log_info(f"Could not find a type with the name {typeName}")
  2. @psifertex psifertex revised this gist Feb 19, 2024. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions export_settings.py
    Original file line number Diff line number Diff line change
    @@ -12,15 +12,15 @@
    excludeEnum = [ "analysis.unicode.blocks", "python.interpreter", "ui.theme"]
    allscope = set(["SettingsProjectScope", "SettingsUserScope", "SettingsResourceScope"])

    for category in settings:
    for setting in settings[category]['settings']:
    for category in sorted(settings):
    for setting in sorted(settings[category]['settings']):
    title = settings[category]['settings'][setting]['title']
    description = settings[category]['settings'][setting]['description']
    typ = settings[category]['settings'][setting]['type']
    key = settings[category]['settings'][setting]['key']
    default = settings[category]['settings'][setting]['default']
    if isinstance(default, list):
    default = "[" + ', '.join(["`%s`" % x for x in default]) + "]"
    default = "[" + ', '.join(["`%s`" % x for x in sorted(default)]) + "]"
    else:
    default = f"`{str(default)}`"
    if default == "``":
    @@ -30,7 +30,7 @@
    scope = allscope - set(settings[category]['settings'][setting]['ignore'])
    else:
    scope = allscope
    scope = "[" + ', '.join(["`%s`" % x for x in scope]) + "]"
    scope = "[" + ', '.join(["`%s`" % x for x in sorted(scope)]) + "]"
    table += f"|{category}|{title}|{description}|`{typ}`|{default}|{scope}|<a id='{key}'>{key}</a>|\n"
    if settings[category]['settings'][setting].get("enum") and key not in excludeEnum:
    for idx, enum in enumerate(settings[category]['settings'][setting]["enum"]):
  3. @psifertex psifertex revised this gist Feb 12, 2024. 1 changed file with 38 additions and 0 deletions.
    38 changes: 38 additions & 0 deletions struct_typer.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,38 @@
    # apply function types to structure parameters
    #
    # Lets you select a struct to automatically search type libraries for function signatures to apply type information

    # Thanks @d0mnik! Original code: https://gist.github.com/d0mnik/1ee595605fc7c9a39364fdbfb660f268

    from binaryninja import *

    api_call_protos = {}

    type_to_parse = get_text_line_input("Enter structure name to parse:", "Struct Parser")

    if type_to_parse is None:
    exit()

    struct_name = type_to_parse.decode()
    if bv.get_type_by_name(struct_name) is None:
    show_message_box(f"Struct Parser", "Struct with specified name not found!")
    exit()

    for typelib in bv.type_libraries:
    for name, obj in typelib.named_objects.items():
    if not isinstance(obj, FunctionType): # filter for function calls
    continue
    api_call_protos[name.name[0]] = obj

    mut: StructureBuilder = bv.get_type_by_name(struct_name).mutable_copy()

    for member_idx, member in enumerate(mut.members):
    for name, proto in api_call_protos.items():
    if name.lower() not in member.name.lower():
    continue
    # replace the prototype
    proto_pointer = PointerType.create(bv.arch, proto)
    mut.replace(member_idx, proto_pointer, member.name)
    break

    bv.define_user_type(struct_name, mut)
  4. @psifertex psifertex revised this gist Sep 20, 2023. 1 changed file with 1 addition and 2 deletions.
    3 changes: 1 addition & 2 deletions pseudo_c.py
    Original file line number Diff line number Diff line change
    @@ -19,5 +19,4 @@ def c_source(bv, func):
    lines += str(line) + '\n'
    return lines

    for line in c_source(bv, current_function):
    print(line)
    print(c_source(bv, current_function))
  5. @psifertex psifertex revised this gist Sep 20, 2023. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions list_plugins.py
    Original file line number Diff line number Diff line change
    @@ -16,7 +16,7 @@
    for repo in repos:
    html += f'''$(document).ready( function () {{
    $('#{repo}').DataTable({{
    "paging": false,
    "paging": false,
    "info": false,
    "searching": false,
    "order": [[2, "desc"], [0, "asc"]]
    @@ -43,7 +43,7 @@
    '''

    for repo in ["community", "official"]:
    html += f'''<h3>{repo.capitalize()} Plugins</h3>
    html += f'''<h3>{repo.capitalize()} Plugins</h3>
    <table id="{repo}" class="sortable" cellspacing="0" width="100%">
    <thead>
    <tr>
    @@ -68,7 +68,7 @@
    html += f'''</tbody>
    </table>
    <hr />
    '''
    '''

    html += '''
    </div>
  6. @psifertex psifertex revised this gist Sep 20, 2023. 1 changed file with 2 additions and 3 deletions.
    5 changes: 2 additions & 3 deletions highlight_dangerous_calls.py
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,3 @@
    # highlight dangerous calls
    #
    # Highlight with theme-aware colors any "dangerous" functions:

    # Note that prefixed versions like "_system" will not be highlighted using this sample code.
    @@ -10,9 +8,10 @@
    for fnname in dangerous + sus:
    if fnname in dangerous:
    color = HighlightStandardColor.RedHighlightColor
    if fname in sus:
    if fnname in sus:
    color = HighlightStandardColor.OrangeHighlightColor
    for sym in bv.get_symbols_by_name(fnname):
    for ref in bv.get_code_refs(sym.address):
    log_info(f"Highlighting dangerous call at {hex(ref.address)}")
    ref.function.set_user_instr_highlight(ref.address, color)

  7. @psifertex psifertex revised this gist Aug 22, 2023. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions pseudo_c.py
    Original file line number Diff line number Diff line change
    @@ -6,6 +6,7 @@ def c_source(bv, func):
    lines = ''
    settings = DisassemblySettings()
    settings.set_option(DisassemblyOption.ShowAddress, False)
    settings.set_option(DisassemblyOption.WaitForIL, True)
    obj = lineardisassembly.LinearViewObject.language_representation(bv, settings)
    cursor_end = lineardisassembly.LinearViewCursor(obj)
    cursor_end.seek_to_address(func.highest_address)
  8. @psifertex psifertex revised this gist Jun 13, 2023. 3 changed files with 21 additions and 2 deletions.
    File renamed without changes.
    23 changes: 21 additions & 2 deletions simple_ui.py
    Original file line number Diff line number Diff line change
    @@ -44,10 +44,29 @@ def qicon():
    button.setIcon(ico2)
    button.setText("PlayPause")
    msg_box.exec_()

    # Since Snippets now run on background threads, UI code needs to explicitly ensure that it is run on the main thread.
    #execute_on_main_thread(qpixmap2)
    #execute_on_main_thread(qpixmap)
    #execute_on_main_thread(basic)
    execute_on_main_thread(qicon)
    #execute_on_main_thread(qicon)

    from binaryninjaui import (UIAction, UIActionHandler)
    from binaryninja import log_info
    from PySide6.QtWidgets import QDialog, QLabel, QVBoxLayout

    def qd(context):
    dialog = QDialog(parent=context.widget)
    dialog.setWindowTitle("Hello Dialog")
    label = QLabel("Hello text")
    layout = QVBoxLayout()
    layout.addWidget(label)
    dialog.setLayout(layout)
    dialog.show()

    execute_on_main_thread_and_wait(lambda: qd(uicontext))

    # Note: outside of snippets you would use:
    # UIAction.registerAction("Simple")
    # UIActionHandler.globalActions().bindAction("Simple", UIAction(qd))
    # but snippets can provide the same UI context necessary to parent a QDialog
  9. @psifertex psifertex revised this gist May 21, 2023. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions new_file_from_selection.py
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    # new file from selection
    #
    # Opens the current selections contents into a new file

    # Opens the current selections contents into a new file / new tab
    # To modify it for a new tab
    import tempfile
    from binaryninjaui import UIContext

  10. @psifertex psifertex revised this gist May 9, 2023. 2 changed files with 32 additions and 0 deletions.
    18 changes: 18 additions & 0 deletions componentize_cpp.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,18 @@
    # componentize cpp
    #
    # Using the components API to sort CPP symbols into class hierarchies
    def ___sortCPPSymbols():
    bv.begin_undo_actions()
    cpp_comp = bv.get_component_by_path("C++ Classes")
    if not cpp_comp:
    cpp_comp = bv.create_component("C++ Classes")
    for func in bv.functions:
    if len(demangle_gnu3(bv.arch, func.name)[1]) < 2 or isinstance(demangle_gnu3(bv.arch, func.name)[1], str):
    continue
    namespace = demangle_gnu3(bv.arch, func.name)[1][0]
    comp = bv.get_component_by_path("C++ Classes/" + namespace)
    if comp is None:
    comp = bv.create_component(namespace, cpp_comp)
    comp.add_function(func)
    bv.commit_undo_actions()
    ___sortCPPSymbols()
    14 changes: 14 additions & 0 deletions componentize_sections.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,14 @@
    # componentize sections
    #
    # Using the components API to sort symbols in different sections into folders
    def ___sortSymbolsBySectionName():
    bv.begin_undo_actions()
    for func in bv.functions:
    sects = bv.get_sections_at(func.start)
    for sect in sects:
    comp = bv.get_component_by_path(sect.name)
    if comp is None:
    comp = bv.create_component(sect.name)
    comp.add_function(func)
    bv.commit_undo_actions()
    ___sortSymbolsBySectionName()
  11. @psifertex psifertex revised this gist May 9, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion unlifted_opcodes.py
    Original file line number Diff line number Diff line change
    @@ -5,7 +5,7 @@
    # build list of all unimplemented opcodes
    opcodes={}
    for f in bv.functions:
    for t in f.address_tags: # tuple of (arch, address, tag)
    for t in f.tags: # tuple of (arch, address, tag)
    data = t[2].data
    if "unimplemented" in data:
    opcode = data.split('"')[1]
  12. @psifertex psifertex revised this gist May 2, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion history_dump.py
    Original file line number Diff line number Diff line change
    @@ -6,7 +6,7 @@
    from binaryninja import log_info
    from PySide6.QtCore import QSettings

    settings = QtCore.QSettings()
    settings = QSettings()
    history = settings.value("script/history")
    for line in history:
    log_info(line)
  13. @psifertex psifertex revised this gist May 2, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion history_dump.py
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,7 @@

    import binaryninjaui #needed to load PySide
    from binaryninja import log_info
    from PySide6 import QSettings
    from PySide6.QtCore import QSettings

    settings = QtCore.QSettings()
    history = settings.value("script/history")
  14. @psifertex psifertex revised this gist Mar 30, 2023. 1 changed file with 3 additions and 7 deletions.
    10 changes: 3 additions & 7 deletions trigger_actions.py
    Original file line number Diff line number Diff line change
    @@ -4,11 +4,7 @@
    # Use the command-palette (CMD/CTL-P) to find action descriptions
    # Not compatible with headless of course

    from binaryninjaui import UIActionHandler, DockHandler
    from binaryninjaui import UIContext

    def triggerAction(action):
    handler = UIActionHandler().actionHandlerFromWidget(DockHandler.getActiveDockHandler().parent())
    handler.executeAction(action)

    action="About..."
    execute_on_main_thread_and_wait(lambda: triggerAction(action))
    action = "About..."
    execute_on_main_thread_and_wait(lambda: UIContext.activeContext().getCurrentActionHandler().executeAction(action))
  15. @psifertex psifertex revised this gist Mar 27, 2023. 1 changed file with 30 additions and 0 deletions.
    30 changes: 30 additions & 0 deletions batch_example.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,30 @@
    # batch example
    #
    # Intended to be run from the CLI, not as a snippet
    # Remove these top lines to run headlessly (requires a commercial license)

    #!/usr/bin/env python3
    import binaryninja
    import glob
    import filetype
    import os
    from tabulate import tabulate
    from collections import defaultdict

    binaryninja.disable_default_log()

    for file in glob.glob("./**/*", recursive=True):
    if os.path.isfile(file):
    guess = filetype.guess(file)
    if guess and guess.extension == "elf":
    print(f"Filename: {file}")
    try:
    with binaryninja.load(file, update_analysis=False) as bv:
    # Imports don't require analysis, otherwise remove update_analysis param
    syms = []
    for sym in bv.get_symbols_of_type(binaryninja.SymbolType.ImportAddressSymbol):
    syms.append([sym.name, hex(sym.address)])
    print(tabulate(syms, headers=["Import Name", "Offset"]))
    except:
    print("Invalid architecture.")
    print("\n")
  16. @psifertex psifertex revised this gist Mar 9, 2023. 1 changed file with 29 additions and 0 deletions.
    29 changes: 29 additions & 0 deletions show_exports.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,29 @@
    # show exports
    #
    # Open a new tab with a list of all exports, can be slow for extremely large files, modify to dump an external html for a perf improvement

    import time

    def genRow(sym, exportType):
    return f"<tr><td><a href=\"binaryninja:?expr={hex(sym.address)}\">{hex(sym.address)}</a></td><td>{sym.short_name}</td><td>{exportType}</td></tr>\n"

    begin = time.time()

    # [ Offset, Symbol, Type]
    html = "<h1>Exports</h1>\n\n"
    html += "<table><tr><th>Offset</th><th>Name</th><th>Type</th></tr>\n"
    html += f"<tr><td>{hex(bv.entry_point)}</td><td>{bv.entry_function.name}</td><td>Entry</td></tr>\n"
    for sym in bv.get_symbols_of_type(SymbolType.FunctionSymbol):
    if sym.binding == SymbolBinding.GlobalBinding:
    html += genRow(sym, "Export Function")
    for sym in bv.get_symbols_of_type(SymbolType.DataSymbol):
    if sym.binding == SymbolBinding.GlobalBinding:
    html += genRow(sym, "Export Data Var")
    html += "</table>"
    lines = len(html.split("\n"))
    generated=time.time()
    log_info(f"Lines: {lines}")
    log_info(f"Time: {generated - begin}")
    bv.show_html_report("Exports", html)
    render = time.time()
    log_info(f"Render: {render - generated }")
  17. @psifertex psifertex revised this gist Mar 7, 2023. 1 changed file with 12 additions and 0 deletions.
    12 changes: 12 additions & 0 deletions history_dump.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,12 @@
    # history dump
    #
    # Dump the entire history of the scripting console of this instance of Binary Ninja to the log

    import binaryninjaui #needed to load PySide
    from binaryninja import log_info
    from PySide6 import QSettings

    settings = QtCore.QSettings()
    history = settings.value("script/history")
    for line in history:
    log_info(line)
  18. @psifertex psifertex revised this gist Jan 31, 2023. 1 changed file with 25 additions and 0 deletions.
    25 changes: 25 additions & 0 deletions make_const.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,25 @@
    # make const
    #
    # Make the selected variable const

    def make_const(token, fn):
    if token.localVarValid:
    var = Variable.from_core_variable(fn, token.localVar)
    varType = var.type.mutable_copy()
    if isinstance(varType, PointerBuilder):
    print("got here")
    varType.target.const = True
    else:
    varType.const = True
    var.type = varType
    print(f"Made {repr(var)} have type {repr(var.type)}")
    elif current_token.type == InstructionTextTokenType.CodeSymbolToken:
    #TODO
    pass
    else:
    log_warn("Unhandled token")

    if not current_token or not current_function:
    log_warn("No valid variable token selected")
    else:
    make_const(current_token, current_function)
  19. @psifertex psifertex revised this gist Jan 16, 2023. 1 changed file with 5 additions and 4 deletions.
    9 changes: 5 additions & 4 deletions simple_ui.py
    Original file line number Diff line number Diff line change
    @@ -45,8 +45,9 @@ def qicon():
    button.setText("PlayPause")
    msg_box.exec_()

    #qpixmap2()
    #qpixmap()
    #basic()
    qicon()
    # Since Snippets now run on background threads, UI code needs to explicitly ensure that it is run on the main thread.
    #execute_on_main_thread(qpixmap2)
    #execute_on_main_thread(qpixmap)
    #execute_on_main_thread(basic)
    execute_on_main_thread(qicon)

  20. @psifertex psifertex revised this gist Oct 18, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion load_ida_map.py
    Original file line number Diff line number Diff line change
    @@ -3,7 +3,7 @@
    # Bare-bones script with no error checking to load a .map file created by IDA

    import re
    pattern = re.compile(r"\d+:([0-9A-Fa-f]+)\s+(.+)")
    pattern = re.compile(r"\d+:([0-9A-Fa-f]+)\s+(.+)$")
    mapfile = get_open_filename_input("map file:", "All Files (*)")
    if mapfile is not None and os.access(mapfile, os.R_OK):
    with open(mapfile, "r+", encoding="utf-8") as f:
  21. @psifertex psifertex revised this gist Oct 18, 2022. 1 changed file with 19 additions and 0 deletions.
    19 changes: 19 additions & 0 deletions load_ida_map.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,19 @@
    # load ida map
    #
    # Bare-bones script with no error checking to load a .map file created by IDA

    import re
    pattern = re.compile(r"\d+:([0-9A-Fa-f]+)\s+(.+)")
    mapfile = get_open_filename_input("map file:", "All Files (*)")
    if mapfile is not None and os.access(mapfile, os.R_OK):
    with open(mapfile, "r+", encoding="utf-8") as f:
    for line in f.readlines():
    matching = pattern.search(line)
    if not matching:
    continue
    addr = int(matching.group(1), 16)
    fnname = matching.group(2)
    if bv.get_function_at(addr):
    bv.define_user_symbol(Symbol(SymbolType.FunctionSymbol, addr, fnname))
    else:
    bv.define_user_symbol(Symbol(SymbolType.DataSymbol, addr, fnname))
  22. @psifertex psifertex revised this gist Oct 17, 2022. 4 changed files with 4 additions and 4 deletions.
    2 changes: 1 addition & 1 deletion clipboard.py
    Original file line number Diff line number Diff line change
    @@ -10,5 +10,5 @@
    # Alternate implementation that copies as a file offset (requires above lines
    #offset=s.data_offset + offset

    clip = PySide2.QtGui.QGuiApplication.clipboard()
    clip = PySide6.QtGui.QGuiApplication.clipboard()
    clip.setText('%x' % (fileoffset))
    2 changes: 1 addition & 1 deletion export_settings.py
    Original file line number Diff line number Diff line change
    @@ -3,7 +3,7 @@
    # Snippet that generates https://docs.binary.ninja/getting-started.html#all-settings

    import json
    from PySide2.QtGui import QGuiApplication
    from PySide6.QtGui import QGuiApplication
    settings = json.loads(binaryninja.Settings().serialize_schema())
    table = """|Category|Setting|Description|Type|Default|Scope|Key|
    |---|---|---|---|---|---|---|
    2 changes: 1 addition & 1 deletion read_dword.py
    Original file line number Diff line number Diff line change
    @@ -4,5 +4,5 @@

    dword = int.from_bytes(bv.read(here, 4), "big" if bv.endianness == Endianness.BigEndian else "little")

    clip = PySide2.QtGui.QGuiApplication.clipboard()
    clip = PySide6.QtGui.QGuiApplication.clipboard()
    clip.setText('%x' % (dword))
    2 changes: 1 addition & 1 deletion simple_ui.py
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    # Simple PySide UI elements/testing
    #
    import binaryninjaui
    from PySide2 import QtWidgets, QtGui, QtWidgets, QtCore
    from PySide6 import QtWidgets, QtGui, QtWidgets, QtCore

    def basic():
    popout = QtWidgets.QDialog()
  23. @psifertex psifertex revised this gist Sep 28, 2022. 1 changed file with 23 additions and 0 deletions.
    23 changes: 23 additions & 0 deletions unlifted_opcodes.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,23 @@
    # unlifted opcodes
    #
    # Script to summarize frequency of unlifted opcodes for a given binary

    # build list of all unimplemented opcodes
    opcodes={}
    for f in bv.functions:
    for t in f.address_tags: # tuple of (arch, address, tag)
    data = t[2].data
    if "unimplemented" in data:
    opcode = data.split('"')[1]
    if opcode in opcodes.keys():
    opcodes[opcode].append(t[1])
    else:
    opcodes[opcode] = [t[1]]

    # print them out, sorted by number of occurrences
    keys = list(opcodes.keys())
    for key in sorted(opcodes, key=lambda x: len(opcodes[x]), reverse=True):
    if len(opcodes[key]) <= 5:
    print(f"{len(opcodes[key])}: {key}: [{', '.join(hex(x) for x in opcodes[key])}]")
    else:
    print(f"{len(opcodes[key])}: {key}: [{', '.join(hex(x) for x in opcodes[key][0:5])}, ...]")
  24. @psifertex psifertex revised this gist Sep 20, 2022. 1 changed file with 18 additions and 0 deletions.
    18 changes: 18 additions & 0 deletions highlight_dangerous_calls.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,18 @@
    # highlight dangerous calls
    #
    # Highlight with theme-aware colors any "dangerous" functions:

    # Note that prefixed versions like "_system" will not be highlighted using this sample code.

    dangerous = ["strcpy", "gets"]
    sus = ["printf", "system", "exec"]

    for fnname in dangerous + sus:
    if fnname in dangerous:
    color = HighlightStandardColor.RedHighlightColor
    if fname in sus:
    color = HighlightStandardColor.OrangeHighlightColor
    for sym in bv.get_symbols_by_name(fnname):
    for ref in bv.get_code_refs(sym.address):
    ref.function.set_user_instr_highlight(ref.address, color)

  25. @psifertex psifertex revised this gist Jul 20, 2022. 1 changed file with 0 additions and 32 deletions.
    32 changes: 0 additions & 32 deletions unimplemented.py
    Original file line number Diff line number Diff line change
    @@ -1,32 +0,0 @@
    # checks all LLIL operations to find any unlimiplemented lifting
    #
    # Especially useful when building a new architecture and wanting to see what disassembly you're running into with real binaries to prioritize

    from binaryninja.lowlevelil import LowLevelILInstruction

    def visit(unimp, expr):
    for field in LowLevelILInstruction.ILOperations[expr.operation]:
    if field[1] == "expr":
    visit(unimp, getattr(expr, field[0]))
    if expr.operation in [LowLevelILOperation.LLIL_UNIMPL, LowLevelILOperation.LLIL_UNIMPL_MEM]:
    if hasattr(expr, "expr_index"):
    index = expr.expr_index
    else:
    index = 0
    dis = bv.get_disassembly(expr.address)
    mnemonic = dis.split(" ")[0]
    if mnemonic in unimp.keys():
    unimp[mnemonic].append([expr.address, index])
    else:
    unimp[mnemonic] = [[expr.address, index]]

    unimp = {}

    for llili in bv.llil_instructions:
    visit(unimp, llili)

    log_info(f"Found {len(unimp)} total unimplemented mnemonics")
    for k, v in sorted(unimp.items(), key=len):
    log_info(f"Unimplemented mnemonic: {k}:")
    for x in v:
    log_info(f" {hex(x[0])} / {x[1]}")
  26. @psifertex psifertex revised this gist Jul 20, 2022. 1 changed file with 20 additions and 0 deletions.
    20 changes: 20 additions & 0 deletions unimplemented_instructions.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,20 @@
    # unimplemented instructions
    #
    # Create a report showing unimplemented instructions

    from collections import defaultdict
    insts = defaultdict(lambda:0)
    addrs = {}
    for ins in bv.llil_instructions:
    if ins.operation == LowLevelILOperation.LLIL_UNIMPL:
    mnem = bv.get_disassembly(ins.address, ins.il_basic_block.arch).split()[0]
    insts[mnem + '-' + ins.il_basic_block.arch.name] = insts[mnem + '-' + ins.il_basic_block.arch.name] + 1
    addrs[mnem + '-' + ins.il_basic_block.arch.name] = ins.address

    lines = list(insts.items())
    lines = sorted(lines, key=lambda x: x[1], reverse=True)
    contents = "| Memonic-Arch | Count | Example Address |\n|---|---|---|\n"
    for mnem, count in lines:
    contents += f"|{mnem}|{count}|[{hex(addrs[mnem])}](binaryninja://?expr={hex(addrs[mnem])})|\n"

    bv.show_markdown_report("Unimplemented counts", contents, contents)
  27. @psifertex psifertex revised this gist Jul 2, 2022. 1 changed file with 12 additions and 0 deletions.
    12 changes: 12 additions & 0 deletions show_modifications.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,12 @@
    # show file modifications
    #
    # Use the get_modification API to show all places where bytes were modified in the file

    for offset in range(bv.start, len(bv) + bv.start):
    mod = bv.get_modification(offset,1)[0]
    if mod != ModificationStatus.Original:
    b = "0x" + bv.read(offset, 1).hex()
    if mod == ModificationStatus.Inserted:
    print(f"Looks like {b} was inserted at {hex(offset)}")
    else:
    print(f"Looks like {b} was written at {hex(offset)}")
  28. @psifertex psifertex revised this gist May 28, 2022. 1 changed file with 16 additions and 10 deletions.
    26 changes: 16 additions & 10 deletions save_search.py
    Original file line number Diff line number Diff line change
    @@ -2,15 +2,21 @@
    #
    # Save search results to a specified file

    # Currently hard-coded to HLIL, TODO: use an interaction API to let the user select from a drop-down.
    # Can just copy from export_to_text snippet
    search = interaction.get_text_line_input('Search test', 'Search text')
    output = interaction.get_save_filename_input("Output filename: ", "txt", "results.txt")

    if search and output:
    with open(output, 'wb') as f:
    for result in bv.find_all_text(bv.start, bv.end, search.decode('utf8'), graph_type=FunctionGraphType.HighLevelILFunctionGraph):
    addr = bytes(hex(result[0]) + "\t", 'utf8')
    targets = [FunctionGraphType.NormalFunctionGraph, FunctionGraphType.LowLevelILFunctionGraph, FunctionGraphType.MediumLevelILFunctionGraph, FunctionGraphType.HighLevelILFunctionGraph, FunctionGraphType.HighLevelLanguageRepresentationFunctionGraph]
    search_text = TextLineField("Search text")
    search_target = ChoiceField("Search target?", ["Assembly", "LLIL", "MLIL", "HLIL", "Pseudo C"])
    search_addresses = ChoiceField("Include addresses in output?", ["No", "Yes"])
    output_file = SaveFileNameField("Output filename: ", default_name=bv.file.filename + ".txt")
    choices = get_form_input(["Saved Search Plugin", search_text, search_target, output_file, search_addresses], "Saved Search")
    if choices and search_text.result and search_target.result and output_file.result:
    with open(output_file.result, 'wb') as f:
    target = targets[search_target.result]
    for result in bv.find_all_text(bv.start, bv.end, search_text.result, graph_type=target):
    if search_addresses.result and search_addresses.result == 1:
    addr = bytes(hex(result[0]) + "\t", 'utf8')
    else:
    addr = b""
    f.write(addr + bytes(str(result[2]), 'utf8') + b"\n")
    log_info(f"Search results saved to {output_file.result}")
    else:
    log_warn("Either search or filename not given.")
    log_warn("Search not saved, dialog cancelled or missing selection.")
  29. @psifertex psifertex revised this gist May 27, 2022. 1 changed file with 16 additions and 0 deletions.
    16 changes: 16 additions & 0 deletions save_search.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,16 @@
    # save search
    #
    # Save search results to a specified file

    # Currently hard-coded to HLIL, TODO: use an interaction API to let the user select from a drop-down.
    # Can just copy from export_to_text snippet
    search = interaction.get_text_line_input('Search test', 'Search text')
    output = interaction.get_save_filename_input("Output filename: ", "txt", "results.txt")

    if search and output:
    with open(output, 'wb') as f:
    for result in bv.find_all_text(bv.start, bv.end, search.decode('utf8'), graph_type=FunctionGraphType.HighLevelILFunctionGraph):
    addr = bytes(hex(result[0]) + "\t", 'utf8')
    f.write(addr + bytes(str(result[2]), 'utf8') + b"\n")
    else:
    log_warn("Either search or filename not given.")
  30. @psifertex psifertex revised this gist Apr 8, 2022. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion interesting_units.py
    Original file line number Diff line number Diff line change
    @@ -3,5 +3,6 @@
    # Log interesting components like the function and basic blocks with most incoming or outgoing edges

    log_info("Most connected function: " + repr(max(bv.functions, key=lambda x: len(x.callees) + len(x.callers))))
    log_info("Most connected bblock: " + repr(max(bv.basic_blocks, key=lambda x: len(x.incoming_edges) + len(x.outgoing_edges))))
    log_info("Most incoming callers: " + repr(max(bv.functions, key=lambda x: len(x.callers))))
    log_info("Most connected bblock: " + repr(max(bv.basic_blocks, key=lambda x: len(x.incoming_edges) + len(x.outgoing_edges))))
    log_info("Highest xrefs: " + repr(max(bv.functions, key=lambda x: len(x.callers))))