Workflow plugins allow you to extend or modify Binary Ninja’s analysis pipeline. You can add custom analysis passes, modify IL during analysis, or implement specialized analysis techniques.
Workflows are registered by name and contain ordered activities:
from binaryninja import Workflow, Activity# Get a workflowworkflow = Workflow.Instance("core.function.baseAnalysis")# List activitiesfor activity in workflow.activities: print(f"{activity.name}: {activity.description}")# Activity graph shows dependenciesprint(workflow.graph)
# Insert after an activityworkflow.insert("core.function.generateLowLevelIL", "myplugin.activity")# Insert before an activity workflow.insert_before("core.function.generateLowLevelIL", "myplugin.activity")# Replace an activityworkflow.replace("core.function.basicBlockAnalysis", "myplugin.customBasicBlock")# Remove an activityworkflow.remove("core.function.advancedAnalysisCache")
Removing or replacing core activities can break analysis. Only do this if you understand the implications.
from binaryninja import LowLevelILOperationdef optimize_il(analysis_context): llil = analysis_context.llil for block in llil: for i, instr in enumerate(block): # Find redundant operations if instr.operation == LowLevelILOperation.LLIL_SET_REG: src = instr.src if src.operation == LowLevelILOperation.LLIL_REG: # Remove mov reg, reg if instr.dest == src.src: instr.replace(llil.nop())
IL modification must happen before analysis is complete. Use early activities like those after LLIL generation.
# Clone the default workflowcustom_workflow = Workflow.Instance("core.function.baseAnalysis").clone("myWorkflow")# Modify itcustom_workflow.insert("core.function.generateLowLevelIL", "myplugin.activity")custom_workflow.remove("core.function.advancedAnalysisCache")# Register itcustom_workflow.register()# Use it for a functionfunc.workflow = custom_workflow
def taint_analysis(analysis_context): mlil = analysis_context.mlil tainted = set() for block in mlil: for instr in block: # Mark sources as tainted if is_source(instr): tainted.add(instr.dest) # Propagate taint for operand in instr.operands: if operand in tainted: if instr.dest: tainted.add(instr.dest) # Check sinks if is_sink(instr): for operand in instr.operands: if operand in tainted: report_issue(instr)
Custom calling convention detection
Detect non-standard calling conventions:
def detect_calling_convention(analysis_context): func = analysis_context.function llil = analysis_context.llil # Analyze first basic block for parameter passing entry = llil.basic_blocks[0] params = analyze_register_usage(entry) # Set detected calling convention if matches_custom_cc(params): func.calling_convention = my_custom_cc
Dead code elimination
Remove unused code:
def eliminate_dead_code(analysis_context): mlil = analysis_context.mlil # Find live variables live = compute_live_variables(mlil) # Remove dead assignments for block in mlil: for instr in block: if instr.operation == MediumLevelILOperation.MLIL_SET_VAR: if instr.dest not in live: instr.replace(mlil.nop())