Skip to content

Instantly share code, notes, and snippets.

@psifertex
Last active August 1, 2025 04:25
Show Gist options
  • Save psifertex/6fbc7532f536775194edd26290892ef7 to your computer and use it in GitHub Desktop.
Save psifertex/6fbc7532f536775194edd26290892ef7 to your computer and use it in GitHub Desktop.

Revisions

  1. psifertex revised this gist Aug 1, 2025. 2 changed files with 138 additions and 0 deletions.
    69 changes: 69 additions & 0 deletions decrease_variadic_param.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,69 @@
    # Decrease variadic function parameters
    #
    def find_call_in_instruction(instr):
    if isinstance(instr, Localcall):
    return instr

    if hasattr(instr, 'operands'):
    for operand in instr.operands:
    if hasattr(operand, 'operation'):
    result = find_call_in_instruction(operand)
    if result:
    return result
    return None

    # Use the current address directly
    addr = here

    call_instr = find_call_in_instruction(current_il_instruction)
    if not call_instr:
    log_error("No call instruction found at current position")
    else:

    target_addr = None
    if hasattr(call_instr.dest, 'value'):
    target_addr = call_instr.dest.value.value
    elif hasattr(call_instr.dest, 'constant'):
    target_addr = call_instr.dest.constant

    if not target_addr:
    log_error("Cannot determine target function address")
    else:
    target_func = bv.get_function_at(target_addr)
    if not target_func:
    log_error("Cannot find target function")
    else:
    func_type = target_func.type
    if not func_type.has_variable_arguments.value:
    log_error("Target function is not variadic")
    else:
    # Check for existing call type adjustment
    existing_adjustment = current_function.get_call_type_adjustment(addr)
    if existing_adjustment is None:
    log_error("No existing call type adjustment to decrease")
    else:
    # Use the existing adjustment
    builder = existing_adjustment.mutable_copy()
    current_params = list(builder.parameters)

    # Count only the added variadic parameters (those beyond the original function's parameter count)
    original_param_count = len(func_type.parameters)

    if len(current_params) <= original_param_count:
    log_error("No variadic parameters to remove")
    else:
    removed_param = current_params.pop()
    builder.parameters = current_params

    # If we're back to original params, remove the adjustment
    if len(current_params) == original_param_count:
    current_function.set_call_type_adjustment(addr, None)
    log_info(f"Removed parameter '{removed_param.name}' from variadic function call at 0x{addr:x} (reset to original)")
    else:
    # Finalize the type builder to get an immutable Type object
    finalized_type = builder.immutable_copy()
    current_function.set_call_type_adjustment(addr, finalized_type)
    log_info(f"Removed parameter '{removed_param.name}' from variadic function call at 0x{addr:x} (remaining: {len(current_params)} params)")

    # Wait for analysis to complete
    current_function.analyze()
    69 changes: 69 additions & 0 deletions increase_variadic_param.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,69 @@
    # Increase variadic function parameters
    #
    from time import sleep

    def find_call_in_instruction(instr):
    if isinstance(instr, Localcall):
    return instr

    if hasattr(instr, 'operands'):
    for operand in instr.operands:
    if hasattr(operand, 'operation'):
    result = find_call_in_instruction(operand)
    if result:
    return result
    return None

    # Use the current address directly
    addr = here

    call_instr = find_call_in_instruction(current_il_instruction)
    if not call_instr:
    log_error("No call instruction found at current position")
    else:

    target_addr = None
    if hasattr(call_instr.dest, 'value'):
    target_addr = call_instr.dest.value.value
    elif hasattr(call_instr.dest, 'constant'):
    target_addr = call_instr.dest.constant

    if not target_addr:
    log_error("Cannot determine target function address")
    else:
    target_func = bv.get_function_at(target_addr)
    if not target_func:
    log_error("Cannot find target function")
    else:
    func_type = target_func.type
    if not func_type.has_variable_arguments.value:
    log_error("Target function is not variadic")
    else:
    # Check for existing call type adjustment
    existing_adjustment = current_function.get_call_type_adjustment(addr)
    if existing_adjustment is not None:
    # Use the existing adjustment as base
    builder = existing_adjustment.mutable_copy()
    else:
    # Use the original function type as base
    builder = func_type.mutable_copy()

    # Get current parameter count
    current_param_count = len(builder.parameters)
    new_param_num = current_param_count + 1
    new_param_name = f"arg{new_param_num}"
    void_ptr_type = Type.pointer(bv.arch, Type.void())
    new_param = FunctionParameter(void_ptr_type, new_param_name)

    # Get current params and append new one
    params = list(builder.parameters)
    params.append(new_param)
    builder.parameters = params

    # Finalize the type builder to get an immutable Type object
    finalized_type = builder.immutable_copy()

    current_function.set_call_type_adjustment(addr, finalized_type)
    current_function.analyze()

    log_info(f"Added parameter '{new_param_name}' to variadic function call at 0x{addr:x} (total: {len(params)} params)")
  2. psifertex revised this gist Jun 19, 2025. 1 changed file with 4 additions and 1 deletion.
    5 changes: 4 additions & 1 deletion trigger_actions.py
    Original file line number Diff line number Diff line change
    @@ -7,4 +7,7 @@
    from binaryninjaui import UIContext

    action = "About..."
    execute_on_main_thread_and_wait(lambda: UIContext.activeContext().getCurrentActionHandler().executeAction(action))
    ctx = UIContext.activeContext()
    view = ctx.getCurrentView()
    handler = view.actionHandler()
    execute_on_main_thread_and_wait(lambda: handler.executeAction(action))
  3. psifertex revised this gist Apr 15, 2025. 1 changed file with 42 additions and 0 deletions.
    42 changes: 42 additions & 0 deletions rename_from_logs.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,42 @@
    # rename from logs
    #
    # Extract function name from log calls (credit: whitequark https://gist.github.com/whitequark/03594daa69710089b55720cee688d556#file-log_func_rename-py)

    logger = "LogFuncRename"

    def log_func_rename(log_function, name_index):
    func_votes = defaultdict(lambda: defaultdict(lambda: 0))
    for call_site in log_function.caller_sites:
    if not isinstance(call_site.hlil.operands[0], HighLevelILConstPtr):
    continue
    if call_site.hlil.operands[0].constant != log_function.start:
    continue
    if name_index >= len(call_site.hlil.instruction_operands):
    log_warn(f"Call site {call_site.address:#x}: Too few operands", logger)
    continue
    name_param = call_site.hlil.instruction_operands[name_index]
    if isinstance(name_param, HighLevelILConstPtr):
    name, _ = name_param.string
    func_votes[call_site.function][name] += 1
    else:
    log_warn(f"Call site {call_site.address:#x}: Non-constant name argument", logger)
    for caller, votes in func_votes.items():
    if len(votes) == 1:
    func_name = next(iter(votes.keys()))
    log_info(f"Function {caller.start:#x}: Unanimously renaming to {func_name}", logger)
    elif len(votes) > 1:
    func_name, _ = max(votes.items(), key=lambda x: x[1])
    other_func_names = set(votes.keys()) - {func_name}
    log_info(f"Function {caller.start:#x}: Renaming by majority to {func_name} "
    f"(potentially inlined: {', '.join(other_func_names)})", logger)
    caller.name = func_name

    log_func_f = AddressField("Name or address of logging function", current_view)
    name_index_f = IntegerField("Zero-based index of caller name parameter")
    if get_form_input([log_func_f, name_index_f], "Logging function parameters"):
    log_func = current_view.get_function_at(log_func_f.result)
    if log_func is None:
    log_error(f"No function at address {log_func_f.result:#x}", logger)
    else:
    log_func_rename(log_func, name_index_f.result)
    Comment
  4. psifertex revised this gist Apr 3, 2025. 1 changed file with 17 additions and 0 deletions.
    17 changes: 17 additions & 0 deletions demangle_this.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,17 @@
    # demangle this
    #
    # demangle all functions and add an extra "this" parameter

    # First, open the file with the demangle types disabled, then run this snippet.

    for f in bv.functions:
    demangleResult = demangle_gnu3(bv.arch,f.symbol.raw_name)
    demangleType = demangleResult[0]
    if demangleType is not None:
    # we have type info, insert void* as first param
    if isinstance(demangleType, FunctionType):
    returnType = demangleType.return_value
    newParams = [Type.pointer(bv.arch, Type.void())] + demangleType.parameters
    newType = FunctionType.create(ret = returnType, params = newParams)
    f.type = newType
    print(f"Replaced {f} type with {newType}")
  5. psifertex revised this gist Mar 19, 2025. 1 changed file with 31 additions and 4 deletions.
    35 changes: 31 additions & 4 deletions export_settings.py
    Original file line number Diff line number Diff line change
    @@ -5,19 +5,29 @@
    import json
    from PySide6.QtGui import QGuiApplication
    settings = json.loads(binaryninja.Settings().serialize_schema())
    table = """|Category|Setting|Description|Type|Default|Scope|Key|
    header = """|Category|Setting|Description|Type|Default|Scope|Key|
    |---|---|---|---|---|---|---|
    """
    table = ""

    excludeEnum = [ "analysis.unicode.blocks", "python.interpreter", "ui.theme"]
    excludeEnum = [ "analysis.unicode.blocks", "python.interpreter", "ui.theme.name", "files.universal.architecturePreference",
    "ui.sidebar.defaultWidgets", "ui.view.graph.carousel", "ui.view.graph.il", "ui.view.linear.carousel",
    "ui.view.linear.il"]
    allscope = set(["SettingsProjectScope", "SettingsUserScope", "SettingsResourceScope"])

    # Build temp structure to group entries by cat
    table_entries = []
    cat_entries = {}

    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']
    cat = key.split(".")[0]
    if (key.count(".") > 1):
    cat += "." + key.split(".")[1]
    default = settings[category]['settings'][setting]['default']
    if isinstance(default, list):
    default = "[" + ', '.join(["`%s`" % x for x in sorted(default)]) + "]"
    @@ -31,15 +41,32 @@
    else:
    scope = allscope
    scope = "[" + ', '.join(["`%s`" % x for x in sorted(scope)]) + "]"
    table += f"|{category}|{title}|{description}|`{typ}`|{default}|{scope}|<a id='{key}'>{key}</a>|\n"

    entry = f"|{cat}|{title}|{description}|`{typ}`|{default}|{scope}|<a id='{key}'>{key}</a>|\n"

    # Store sub-entries with their parent
    sub_entries = []
    if settings[category]['settings'][setting].get("enum") and key not in excludeEnum:
    for idx, enum in enumerate(settings[category]['settings'][setting]["enum"]):
    if settings[category]['settings'][setting].get("enumDescriptions"):
    description = " enum: " + settings[category]['settings'][setting]["enumDescriptions"][idx]
    else:
    description = " "
    table += f"| | |{description}|`enum`|`{enum}`| | |\n"
    sub_entries.append(f"| | |{description}|`enum`|`{enum}`| | |\n")

    # Add to our categorized entries
    if cat not in cat_entries:
    cat_entries[cat] = []
    cat_entries[cat].append((entry, sub_entries))

    # Sort by cat and build the final table
    for cat in sorted(cat_entries.keys()):
    for entry, sub_entries in cat_entries[cat]:
    table += entry
    for sub_entry in sub_entries:
    table += sub_entry

    table = header + table
    show_markdown_report("Settings Documentation", "Below table added to the clipboard:\n\n"+table)
    log_info("Saving result to the clipboard.")
    clip = QGuiApplication.clipboard()
  6. psifertex revised this gist Nov 20, 2024. 1 changed file with 7 additions and 18 deletions.
    25 changes: 7 additions & 18 deletions pseudo_c.py
    Original file line number Diff line number Diff line change
    @@ -2,21 +2,10 @@
    #
    # Adapt as necessary to save to file for example, though File / Export will also work

    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)
    body = bv.get_next_linear_disassembly_lines(cursor_end)
    cursor_end.seek_to_address(func.highest_address)
    header= bv.get_previous_linear_disassembly_lines(cursor_end)
    for line in header:
    lines += str(line) + '\n'
    for line in body:
    lines += str(line) + '\n'
    return lines

    print(c_source(bv, current_function))
    def c_source(func):
    lines = func.pseudo_c_if_available.get_linear_lines(func.hlil.root)
    return '\n'.join(map(str, lines))

    proto = f'{" ".join(map(str,current_function.type.get_tokens_before_name()))} {current_function.name}{ "".join(map(str,current_function.type.get_tokens_after_name()))}'
    print(proto)
    print(c_source(current_function))
  7. 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}")
  8. 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"]):
  9. 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)
  10. 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))
  11. 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>
  12. 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)

  13. 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)
  14. 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
  15. 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

  16. 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()
  17. 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]
  18. 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)
  19. 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")
  20. 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))
  21. 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")
  22. 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 }")
  23. 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)
  24. 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)
  25. 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)

  26. 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:
  27. 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))
  28. 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()
  29. 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])}, ...]")
  30. 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)