def quick_get_func(fpath='./tests', symbol='_fizzbuzz'): if sys.argv[1:]: fpath = sys.argv[1] if sys.argv[2:]: symbol = sys.argv[2] update_analysis = True callbacks = None options = {'analysis.mode': 'controlFlow'} # minimal analysis for speed #bv = BinaryViewType.get_view_of_file(fpath) # prefer the .bndb, if it exists if os.path.exists(fpath + '.bndb'): fpath = fpath + '.bndb' if not os.path.exists(fpath): raise Exception('%s doesnt exist' % fpath) print('opening %s in binaryninja' % fpath) t0 = time.perf_counter() bv = binaryninja.open_view(fpath, update_analysis, callbacks, options) if not bv: raise Exception('binary ninja didnt return analysis on -%s-' % fpath) #bv.update_analysis_and_wait() #func = bv.get_functions_by_name(symbol)[0] t1 = time.perf_counter() print('analysis complete after %fs' % (t1 - t0)) func = None if symbol: funcs = [f for f in bv.functions if f.name.lower() == symbol.lower()] if funcs: func = funcs[0] #if not funcs: # raise Exception('binary ninja didnt return func on -%s-' % symbol) return (bv, func)
bn.debuginfo.DebugInfoParser.register("dummy extra debug parser 2", lambda bv: bv.view_type != "Raw", lambda di, bv: None) # Test fetching parser list and fetching by name print(f"Availible parsers: {len(list(bn.debuginfo.DebugInfoParser))}") for p in bn.debuginfo.DebugInfoParser: if p == parser: print( f" {bn.debuginfo.DebugInfoParser[p.name].name} (the one we just registered)" ) else: print(f" {bn.debuginfo.DebugInfoParser[p.name].name}") # Test calling our `is_valid` callback bv = bn.open_view(filename, options={"analysis.experimental.parseDebugInfo": False}) if parser.is_valid_for_view(bv): print("Parser is valid") else: print("Parser is NOT valid!") quit() # Test getting list of valid parsers, and DebugInfoParser's repr print("") for p in bn.debuginfo.DebugInfoParser.get_parsers_for_view(bv): print(f"`{p.name}` is valid for `{bv}`") print("") # Test calling our `parse_info` callback debug_info = parser.parse_debug_info(bv) # debug_info = bv.debug_info
fpath = sys.argv[1] wf = Workflow().clone('MyWorkflow') a = Activity('MyActivity', action=action_func) result = wf.register_activity(a) assert result == True # the topology as we expected? subactivities = wf.subactivities('core.function.basicAnalysis') assert subactivities[0] == 'core.function.generateLiftedIL' assert subactivities[1] == 'core.function.resetIndirectBranchesOnFullUpdate' # insert, finalize workflow result = wf.insert('core.function.resetIndirectBranchesOnFullUpdate', ['MyActivity']) assert result == True result = wf.register() assert result == True # did we change the topology as expected? subactivities = wf.subactivities('core.function.basicAnalysis') assert subactivities[0] == 'core.function.generateLiftedIL' assert subactivities[1] == 'MyActivity' assert subactivities[2] == 'core.function.resetIndirectBranchesOnFullUpdate' print('open_view()') bv = binaryninja.open_view(fpath, \ options={'workflows.enable':True, 'workflows.functionWorkflow':'MyWorkflow'}) print('done')
#!/usr/bin/env python # do multiple binaries opened reference the same architecture instance? import binaryninja bv0 = binaryninja.open_view('/bin/ls') assert bv0 bv1 = binaryninja.open_view('/bin/ln') assert bv1 bv2 = binaryninja.open_view('/bin/cp') assert bv2 print('binary views: %d %d %d' % (id(bv0), id(bv1), id(bv2))) print('architectures: %d %d %d' % (id(bv0.arch), id(bv1.arch), id(bv2.arch)))
def main(): with binaryninja.open_view("tdpServer") as bv: # Step 1. Check if our target has at least one source and one sink we care about source_symbols = [] sink_symbols = [] symbols = bv.get_symbols_of_type( binaryninja.SymbolType.ImportedFunctionSymbol) for symbol in symbols: if symbol.name in sources: source_symbols.append(symbol) elif symbol.name in sinks: sink_symbols.append(symbol) if (set(sinks) & set([sink.name for sink in sink_symbols])) and ( set(sources) & set([source.name for source in source_symbols])): print( "This target contains interesting source(s) and sink(s). Continuing analysis..." ) else: print( "This target does not contain interesting source(s) and sink(s). Done." ) return # Step 2. Find functions that calls at least one source and one sink source_callers = [] sink_callers = [] interesting_functions = [] for source in source_symbols: func = bv.get_function_at(source.address) source_callers.extend(func.callers) for sink in sink_symbols: func = bv.get_function_at(sink.address) sink_callers.extend(func.callers) interesting_functions = (set(source_callers) & set(sink_callers)) if len(interesting_functions) <= 0: print("\nNo interesting functions found to analyze. Done.") return else: print("\nFound {} interesting functions to analyze:".format( len(interesting_functions))) for func in interesting_functions: print(" {}".format(func.name)) # Step 3. Dig into interesting functions for func in interesting_functions: print("\nAnalyzing function: {}".format(func.name)) source_args = [] sink_args = [] # using HLIL for bb in func.hlil: for ins in bb: hlil_call_ins = None if ins.operation == binaryninja.HighLevelILOperation.HLIL_CALL: hlil_call_ins = ins elif ins.operation == binaryninja.HighLevelILOperation.HLIL_ASSIGN and ins.src.operation == binaryninja.HighLevelILOperation.HLIL_CALL: hlil_call_ins = ins.src else: continue call_target = bv.get_function_at( hlil_call_ins.dest.constant) if call_target.name in sources: param1 = hlil_call_ins.params[0] print(" >> 0x{:x} : {}({}, ...)".format( ins.address, call_target.name, param1)) if param1.operation == binaryninja.HighLevelILOperation.HLIL_ADDRESS_OF: param1 = param1.src source_args.append(param1.var) elif call_target.name in sinks: param1 = hlil_call_ins.params[0] print(" >> 0x{:x} : {}({}, ...)".format( ins.address, call_target.name, param1)) if param1.operation == binaryninja.HighLevelILOperation.HLIL_ADDRESS_OF: param1 = param1.src sink_args.append(param1.var) if len(set(sink_args) & set(source_args)) > 0: print( " [!] Alert: Function {} appears to contain a vulnerable `system` call pattern!" .format(func.name))
def main(target): with binaryninja.open_view(target) as bv: for sink in sinks: # 1. Find symbols we want to analyze symbols = [] symbol_list = bv.get_symbols_by_name(sink) for symbol in symbol_list: if symbol.type == binaryninja.SymbolType.ImportedFunctionSymbol: symbols.append(symbol) if len(symbols) <= 0: continue # 2. Find all cross references for each symbol for symbol in symbols: callers = bv.get_callers(symbol.address) if len(callers) <= 0: continue for caller in callers: func = caller.function addr = caller.address # 3. For each location where a sink is called, grab the parameters. mlil_index = func.mlil.get_instruction_start(addr) mlil_ins = func.mlil[mlil_index] hlil_ins = mlil_ins.hlil params = [] if hlil_ins.operation == binaryninja.HighLevelILOperation.HLIL_CALL: params = hlil_ins.params # 4. Isolate `format` parameter if len(params) >= (sinks[sink] + 1): fparam = params[sinks[sink]] if fparam.expr_type and fparam.expr_type.const: debug_print("0x{:08x} : {} is SAFE!".format( hlil_ins.address, fparam)) continue target_var = None try: if fparam.operation == binaryninja.HighLevelILOperation.HLIL_VAR: target_var = fparam.var elif fparam.operation == binaryninja.HighLevelILOperation.HLIL_DEREF: target_var = fparam.src.var else: debug_print( ">> Unsupported type: {} at 0x{:08x}". format(fparam.operation.name, hlil_ins.address)) continue except AttributeError as e: debug_print( ">> ERROR: 0x{:08x} : {}\n>> {}".format( hlil_ins.address, fparam, e)) continue # 5. Locate definition (initialization) and uses of the `format` parameter definitions = func.hlil.get_var_definitions(target_var) uses = func.hlil.get_var_uses(target_var) if len(uses) >= 1: debug_print("\nFunction: {}".format(func.name)) for definition in definitions: debug_print( "Def of {} at 0x{:x} - {} ({})".format( definition.dest, definition.address, definition, definition.operation.name)) for use in uses: debug_print( "Use of {} at 0x{:x} - {} ({})".format( use.var, use.address, use.instr, use.instr.operation.name)) # 6. Check if our `format` parameter is used in a tracked `source` function, in the tracked paramater slot if use.instr.operation == binaryninja.HighLevelILOperation.HLIL_CALL: if str(use.instr.dest) in sources: source_call_ins = use.instr.dest params = source_call_ins.params target_param_index = sources[str( use.instr.src.dest)] if len(params) >= (target_param_index + 1): target_param = params[ target_param_index] if type( target_param ) == binaryninja.function.Variable and p == target_var: print( "ALERT! - Function: {} : 0x{:X}" .format( func.name, func.start)) elif use.instr.operation == binaryninja.HighLevelILOperation.HLIL_VAR_INIT: if str(use.instr.src.dest) in sources: source_call_ins = use.instr.src params = source_call_ins.params target_param_index = sources[str( use.instr.src.dest)] if len(params) >= (target_param_index + 1): target_param = params[ target_param_index] for p in target_param.prefix_operands: if type( p ) == binaryninja.function.Variable and p == target_var: print( "ALERT! - Function: {} : 0x{:X}" .format( func.name, func.start))
#!/usr/bin/env python3 # does Zain's new .in_loop attribute of BasicBlock work? import sys import binaryninja fpath = (sys.argv[1:] and sys.argv[1]) or './tests' symname = (sys.argv[2:] and sys.argv[2]) or '_some_loops' bv = binaryninja.open_view(fpath, True, None, {'analysis.mode':'controlFlow'}) func = next((f for f in bv.functions if f.name == symname), None) if not func: print('function %s not found, available are:' % symname) print(', '.join([f.name for f in bv.functions])) sys.exit(-1) print('-------- analyzing function %s --------' % func.name) for (i,bb) in enumerate(func.basic_blocks): print('b%d: %s.in_loop = %s' % (i, bb, bb.in_loop))
int main() { // printf takes a format string and prints it to standard out printf("Hello world!"); // return 0 means that we ran correctly and have no errors return 0; } """ # can we compile it to HLIL? (gecil = "good enough c to il") good_enough_hlil_func, imaginary_memory_space = gecil(main_function_source) # And, with a possible location for that function in the binary, possible_binary_location = 0x401149 bv = bn.open_view("example") bv.update_analysis_and_wait() bn_func = bv.get_function_at(possible_binary_location) hlil_func = bn_func.hlil # can we match the graphs, # (here I'm demonstrating matching on basically an exact match, # because graph theory is hard, but also string arguments!) match = True instruction_generator = hlil_func.instructions for geil in good_enough_hlil_func.instructions: if geil.operation is hlil.HighLevelILOperation.HLIL_COMMENT: continue bnil = next(instruction_generator)
result.append('\t%s["%s"]' % (block_id(block), label)) # block identifier to block identifier for src in func.basic_blocks: for edge in src.outgoing_edges: dst = edge.target result.append('\t%s --> %s' % (block_id(src), block_id(dst))) return '\n'.join(result) if __name__ == '__main__': fpath = '../testbins/tests-linux-x64-elf' if sys.argv[1:]: fpath = sys.argv[1] sym_name = 'collatz_message' if sys.argv[2:]: sym_name = sys.argv[2] sym_name = sym_name.lower() with binaryninja.open_view(fpath) as bview: functions = [f for f in bview.functions if f.name.lower() == sym_name] function = functions[0] print('```mermaid') print(func_to_mermaid(function)) print('```')
bv.platform = plat bv.add_function(0, plat=plat) bv.update_analysis_and_wait() assert len(bv.functions)==1 f = bv.functions[0] print('IL available after creation:') print_func(f) bv.create_database('/tmp/tmp.bndb') print('-------- IL available from .bndb with get_view_of_file(update_analysis=False)') bv = binaryninja.BinaryViewType.get_view_of_file('/tmp/tmp.bndb', update_analysis=False) assert len(bv.functions)==1 f = bv.functions[0] print_func(f) print('-------- IL available from .bndb with get_view_of_file(update_analysis=True)') bv = binaryninja.BinaryViewType.get_view_of_file('/tmp/tmp.bndb', update_analysis=True) assert len(bv.functions)==1 f = bv.functions[0] print_func(f) print('-------- IL available from .bndb with open_view()') with binaryninja.open_view('/tmp/tmp.bndb') as bv: assert len(bv.functions)==1 f = bv.functions[0] #f.reanalyze() print_func(f)
import binaryninja from miasm.core.graph import DiGraph def bbid(bb): return 'b%d' % bb.index def miasm_graph_from_binja(func): G = DiGraph() for src in func.basic_blocks: for dst in [edge.target for edge in src.outgoing_edges]: G.add_edge(bbid(src), bbid(dst)) return G bv = binaryninja.open_view(sys.argv[1], True, None, {'analysis.mode':'controlFlow'}) for func in bv.functions: print('-------- analyzing function %s --------' % func.name) # get binja's answer # answer_binja = set() for bb in func.basic_blocks: if bb.in_loop: answer_binja.add(bbid(bb)) print('binja says:', answer_binja) # get miasm's answer # answer_miasm = set()
# 1.474496s controlFlow (16.929012% of full) # 2.059575s basic (23.646425% of full) # 6.303724s intermediate (72.374422% of full) # 8.709878s full (100.000000% of full) import sys, time import binaryninja fpath = '/bin/ls' if len(sys.argv) == 1 else sys.argv[1] update_analysis = True callbacks = None options = {} # cause plugins to load, whatever else to get initialized which could interfere in measurements print('initial open') with binaryninja.open_view(fpath) as bv: pass modes = ['controlFlow', 'basic', 'intermediate', 'full'] results = [] for mode in modes: options = {'analysis.mode': mode} print('opening with %s' % options) t0 = time.perf_counter() with binaryninja.open_view(fpath, update_analysis, callbacks, options) as bv: t1 = time.perf_counter() results.append(t1 - t0) print('done! %fs' % results[-1])