def scan(bv): rules = [RULES_DIR] paths = Settings().get_string("yara.customRulesPath") for path in paths.split(";"): rules.append(path.strip()) ys = YaraScan(bv, directories=rules) ys.start()
def get_load_settings_for_data(self, data): load_settings = Settings("mapped_load_settings") if use_default_loader_settings: load_settings = self.registered_view_type.get_default_load_settings_for_data( data) # specify default load settings that can be overridden (from the UI) overrides = [ "loader.architecture", "loader.platform", "loader.entryPoint", "loader.imageBase", "loader.segments", "loader.sections" ] for override in overrides: if load_settings.contains(override): load_settings.update_property( override, json.dumps({'readOnly': False})) # override default setting value load_settings.update_property("loader.imageBase", json.dumps({'default': 0})) load_settings.update_property("loader.entryPoint", json.dumps({'default': 0})) # # add custom arch setting # load_settings.register_setting("loader.my_custom_arch.customLoadSetting", # '{"title" : "My Custom Load Setting",\ # "type" : "boolean",\ # "default" : false,\ # "description" : "My custom load setting description."}') return load_settings
def __init__(self, view, label, callback): super().__init__(label) self.callback = callback self.view = view font_name = Settings().get_string('ui.font.name') font_size = Settings().get_integer('ui.font.size') button_font = QFont(font_name, font_size) fm = QFontMetrics(button_font) self.setFont(button_font) self.setFixedWidth(fm.horizontalAdvance(label) + 10) QObject.connect(self, SIGNAL('clicked()'), self.callback)
def __init__(self, view): super().__init__() self.view = view if view.arch is None: view.session_data['emulator.registers'] = [] view.session_data['emulator.registers'] = [ (r, 0) for r in view.arch.full_width_regs ] view.session_data['emulator.registers.model'] = self self.font_name = Settings().get_string('ui.font.name') self.font_size = Settings().get_integer('ui.font.size')
def init(self): self.arch = Architecture['EVM'] self.platform = Architecture['EVM'].standalone_platform self.max_function_size_for_analysis = 0 file_size = len(self.raw) # Find swarm hashes and make them data evm_bytes = self.raw.read(0, file_size) # code is everything that isn't a swarm hash code = IntervalSet([Interval(0, file_size)]) log_debug('Finding swarm hashes') swarm_hashes = self.find_swarm_hashes(evm_bytes) for start, sz in swarm_hashes: self.add_auto_segment( start, sz, start, sz, (SegmentFlag.SegmentContainsData | SegmentFlag.SegmentDenyExecute | SegmentFlag.SegmentReadable | SegmentFlag.SegmentDenyWrite)) code -= IntervalSet([Interval(start, start + sz)]) for interval in code: if isinstance(interval, int): continue self.add_auto_segment( interval.lower_bound, interval.upper_bound, interval.lower_bound, interval.upper_bound, (SegmentFlag.SegmentReadable | SegmentFlag.SegmentExecutable)) log_debug('Building CFG with evm_cfg_builder') cfg = CFG(evm_bytes, remove_metadata=False) log_debug('Finished building CFG with evm_cfg_builder') Function.set_default_session_data('cfg', cfg) log_debug("registering VsaNotification") self.register_notification(VsaNotification()) log_debug("specifiying entry point and functions") self.add_entry_point(0) for function in cfg.functions: function_start = (function._start_addr + 1 if function._start_addr != 0 else 0) self.define_auto_symbol( Symbol(SymbolType.FunctionSymbol, function_start, function.name)) self.add_function(function_start) # disable linear sweep Settings().set_bool('analysis.linearSweep.autorun', False, view=self, scope=SettingsScope.SettingsUserScope) return True
def get_setting(self, name: str): if name in self.settings: return self.settings[name] val = Settings().get_string("seninja." + name) self.settings[name] = val return val
def show(self): if Settings().get_bool("bn-callgraph.showColorLeaves"): nodes_dst = set([edge[1] for edge in self.edges]) nodes_src = set([edge[0] for edge in self.edges]) leaves = nodes_dst - nodes_src for leave in leaves: self.nodes[ leave].highlight = enums.HighlightStandardColor.RedHighlightColor if Settings().get_bool("bn-callgraph.showIndirectCalls"): for fun in self.nodes: if isinstance(fun, UndeterminedFunction): self.nodes[ fun].highlight = enums.HighlightStandardColor.BlueHighlightColor self.graph.show("Callgraph starting from {} @ {:#x}".format( demangle_name(self.bv, self.root_function.name), self.root_function.start))
def __init__(self, view: BinaryView): QAbstractTableModel.__init__(self) BinaryDataNotification.__init__(self) self.view = view self.memory_view = view.session_data.get('emulator.memory.view') self.font_name = Settings().get_string('ui.font.name') self.font_size = Settings().get_integer('ui.font.size') if self.memory_view is None: return self.memory_view.register_notification(self) if self.view.session_data.get('emulator.memory') is None: self.view.session_data['emulator.memory'] = [ seg for seg in self.memory_view.segments ]
def show(self): if Settings().get_bool("bn-callgraph.showColorLeaves"): nodes_dst = set([edge[1] for edge in self.edges]) nodes_src = set([edge[0] for edge in self.edges]) leaves = nodes_dst - nodes_src for leave in leaves: self.nodes[leave].highlight = enums.HighlightStandardColor.RedHighlightColor self.graph.show("Callgraph starting from {}".format(self.root_function.name))
def run_vsa(thread, view, function): cfg = function.session_data.cfg log_info(str(cfg)) cfg_function = cfg.get_function_at(function.start - 1 if function.start != 0 else 0) log_info(str(cfg_function)) hash_id = cfg_function.hash_id thread.task.progress = '[VSA] Analyzing...' to_process = [ cfg.get_basic_block_at(function.start - 1 if function.start != 0 else 0) ] seen = set() i = 3 while to_process: thread.task.progress = '[VSA] Processing Basic Blocks{}'.format('.' * i) i += (i + 1) % 4 basic_block = to_process.pop() seen.add(basic_block) end = basic_block.end.pc outgoing_edges = basic_block.outgoing_basic_blocks(hash_id) if outgoing_edges is not None: for outgoing_edge in outgoing_edges: if (view.get_function_at(outgoing_edge.start.pc + 1) is None and outgoing_edge not in seen): to_process.append(outgoing_edge) dest_branches = function.get_indirect_branches_at(end) current_branches = {dest.dest_addr for dest in dest_branches} if outgoing_edge.start.pc not in current_branches: current_branches.add(outgoing_edge.start.pc) function.set_user_indirect_branches( end, [(view.arch, dest) for dest in current_branches if (not basic_block.ends_with_jumpi or outgoing_edge.start.pc != end + 1)]) if function.start == 0: max_function_size, _ = Settings().get_integer_with_scope( 'analysis.limits.maxFunctionSize', scope=SettingsScope.SettingsDefaultScope) if max_function_size: view.max_function_size_for_analysis = max_function_size else: view.max_function_size_for_analysis = 65536
def __init__(self, bv, root_function): self.bv = bv self.nodes = {} self.edges = set() self.graph = FlowGraph() self.root_function = root_function root_node = FlowGraphNode(self.graph) if Settings().get_bool("bn-callgraph.showColorRoot"): root_node.highlight = enums.HighlightStandardColor.GreenHighlightColor root_node.lines = [self._build_function_text(root_function)] self.graph.append(root_node) self.nodes[root_function] = root_node
def plugin_fs_finder(bv): # Find format strings fs_finder = FormatStringFinder( bv, Settings().get_bool(setting_1_should_highlight_variable_trace)) fs_finder.find_format_strings() # Get results and print them in the logs view and in a markdown report md = fs_finder.get_results_string() log_info(md) md = '<span style="color:red">(use the \'Log View\' for clickable addresses)</span>\n' + md title = f"FormatStringFinder results for {os.path.basename(bv.file.filename)}" bv.show_markdown_report(title=title, contents=md)
def get_load_settings_for_data(self, data): load_settings = Settings("mapped_load_settings") if use_default_loader_settings: load_settings = self.registered_view_type.get_default_load_settings_for_data( data) # specify default load settings that can be overridden (from the UI) overrides = [ "loader.architecture", "loader.platform", "loader.entryPoint", "loader.imageBase", "loader.segments", "loader.sections" ] for override in overrides: if load_settings.contains(override): load_settings.update_property( override, json.dumps({'readOnly': False})) # override default setting value load_settings.update_property("loader.imageBase", json.dumps({'default': 0})) load_settings.update_property("loader.entryPoint", json.dumps({'default': 0})) return load_settings
def get_load_settings_for_data(cls, data): # This method is optional. If provided this is where the Load Settings for a BinaryViewType are specified. Binary Ninja provides # some default read-only load settings which are: # ["loader.architecture", "loader.platform", "loader.entryPoint", "loader.imageBase", "loader.segments", "loader.sections"] # The default load settings are provided for consistency and convenience. # The default load settings are always generated with a read-only indication which is respected by the UI. # The read-only indication is a property that consists of a JSON name/value pair ("readOnly" : true). load_settings = None if not use_default_loader_settings: # Create a new named Settings container for the load settings. Settings("mapped_load_settings") else: # Optionally, perform light-weight parsing of the 'Raw' BinaryView to extract required information for load settings generation. # This allows finer control of the settings provided as well as their default values. # For example, the view.relocatable property could be used to control the read-only attribute of "loader.imageBase" view = cls.registered_view_type.parse(data) # Populate settings container with default load settings # Note: `get_default_load_settings_for_data` automatically tries to parse the input if the `data` BinaryViewType name does not match the # cls BinaryViewType name. In this case a parsed view is already being passed. load_settings = cls.registered_view_type.get_default_load_settings_for_data( view) # Specify default load settings that can be overridden (from the UI). overrides = [ "loader.architecture", "loader.platform", "loader.entryPoint", "loader.imageBase", "loader.segments", "loader.sections" ] for override in overrides: if load_settings.contains(override): load_settings.update_property( override, json.dumps({'readOnly': False})) # Override the default setting values. load_settings.update_property("loader.imageBase", json.dumps({'default': 0})) load_settings.update_property("loader.entryPoint", json.dumps({'default': 0})) # Specify additional custom settings. load_settings.register_setting( "loader.my_custom_arch.customLoadSetting", '{"title" : "My Custom Load Setting",\ "type" : "boolean",\ "default" : false,\ "description" : "My custom load setting description."}') return load_settings
def __init__(self): super(LinearMLILViewType, self).__init__("Linear MLIL", "Linear MLIL") settings = Settings() settings.register_group('linearmlil', 'Linear MLIL') settings.register_setting('linearmlil.priority', ('''{ "description": "Set the priority for the Linear MLIL view.", "title": "View Priority", "default" : 100, "type" : "number", "id" : "priority" }'''))
def scan(self, start, length, rule): self.reader.seek(start) data = self.reader.read(length) matches = rule.match(data=data, timeout=Settings().get_integer("yara.timeout")) for match in matches: name = match.rule # Include the description field if its present in the metadata try: description = f"{name}: {match.meta['description']}" except KeyError: description = f"{name}" tag = self.bv.create_tag(self.bv.tag_types["YARA Matches"], description, True) for address, var, value in match.strings: address += start # Display data values correctly in the report if value.isascii(): value = value.decode("ascii") elif self.bv.endianness == Endianness.BigEndian: value = hex(int.from_bytes(value, "big")) else: value = hex(int.from_bytes(value, "little")) self.results.append({ "address": address, "name": name, "string": var, "value": value }) # Add either an address or data tag to the location funcs = self.bv.get_functions_containing(address) if 0 < len(funcs): # Ensure the tag is not placed in the middle of an instruction address = get_instruction_containing(funcs[0], address) funcs[0].add_user_address_tag(address, tag) else: self.bv.add_user_data_tag(address, tag)
def __init__(self): super(LinearMLILViewType, self).__init__("Linear MLIL", "Linear MLIL") settings = Settings() settings.register_group("linearmlil", "Linear MLIL") settings.register_setting( "linearmlil.priority", ("""{ "description": "Set the priority for the Linear MLIL view.", "title": "View Priority", "default" : 100, "type" : "number", "id" : "priority" }"""), ) settings.register_setting("linearmlil.debug", ("""{ "description": "Turn on debug reports for Linear MLIL view.", "title": "Show Debug Graphs", "default": true, "type": "boolean", "id": "debug" } """))
def run(self): log_info("Scanning binary view for matching YARA signatures") # TODO: Scan the raw binary data from the Raw view instead of by segment. # This would require mapping the addresses from the Raw view to the PE/ELF views. # raw = self.bv.get_view_of_type("Raw") # reader = BinaryReader(raw) # data = reader.read(raw.end) try: for idx, rule in enumerate(self.rules): if len(self.bv.segments) == 0: # Scan binary without segments self.scan(self.bv.start, self.bv.end, rule) else: # Scan by segment for segment in self.bv.segments: if self.cancelled: return self.scan(segment.start, segment.data_length, rule) self.progress = f"{self.progress_banner} matching on rules ({round((idx / len(self.rules)) * 100)}%)" except yara.TimeoutError: log_warn( "YARA scan exceeded timeout limit. Consider changing the timeout in settings." ) except yara.Error as err: log_error("Error matching on YARA rules: {}".format(str(err))) show_message_box("Error", "Check logs for details", icon=MessageBoxIcon.ErrorIcon) if 0 < len(self.results): if Settings().get_bool("yara.displayReport"): self.display_report() else: log_info("YARA scan finished with no matches.")
def isswitchy(fn): for h in fn.hlil.instructions: if h.operation == HighLevelILOperation.HLIL_SWITCH: if len(h.cases) > Settings().get_integer("tagteam.largeSwitch"): return True return False
#!/usr/bin/env python # -*- coding: utf-8 -* from binaryninja import PluginCommand, Settings, interaction, log_info, HighLevelILOperation Settings().register_group("tagteam", "Tag Team Plugin") Settings().register_setting( "tagteam.largeFunc", """ { "title" : "Number of basic blocks", "type" : "number", "default" : 40, "description" : "Functions with more than this number of basic blocks will be considered large" } """) Settings().register_setting( "tagteam.largeSwitch", """ { "title" : "Large Switch Statement", "type" : "number", "default" : 10, "description" : "Functions with switch statements with more than this many cases are considered switchy" } """) Settings().register_setting( "tagteam.complexFunc", """ { "title" : "Complex Function", "type" : "number", "default" : 40,
def graph_slice( view: BinaryView, ns: MediumLevelILBasicBlock, ne: MediumLevelILBasicBlock, slice: List[List[BasicBlockEdge]], collection: ReportCollection, title: str = '', ): if not Settings().get_bool('linearmlil.debug'): return graph = FlowGraph() ns_node = FlowGraphNode(graph) ns_node.lines = [f'Start: {ns.start}'] ne_node = FlowGraphNode(graph) ne_node.lines = [f'End: {ne.start}'] nodes = {ns.start: ns_node, ne.start: ne_node} graph.append(ns_node) graph.append(ne_node) for path in slice: for edge in path: source = edge.source if source.start in nodes: source_node = nodes[source.start] else: source_node = FlowGraphNode(graph) source_node.lines = [f'Block: {source.start}'] nodes[source.start] = source_node graph.append(source_node) target = edge.target if target.start in nodes: target_node = nodes[target.start] else: target_node = FlowGraphNode(graph) target_node.lines = [f'Block: {target.start}'] nodes[target.start] = target_node graph.append(target_node) if next( ( e for e in source_node.outgoing_edges if e.target == target_node ), None ): continue source_node.add_outgoing_edge( edge.type, target_node ) if collection is not None: if not title: title = f'Slice: {ns}->{ne}' report = FlowGraphReport(title, graph, view) collection.append(report) else: show_graph_report('Graph Slice', graph)
from binaryninja import Settings Settings().register_group("seninja", "SENinja") Settings().register_setting( "seninja.memory.symb_address_mode", """ { "title" : "Symbolic access mode", "type" : "string", "default" : "limit_pages", "description" : "Select the policy to use when a memory access from symbolic address occurs.", "enum": ["concretization", "limit_pages", "fully_symbolic"] } """) Settings().register_setting( "seninja.memory.limit_pages_limit", """ { "title" : "Limit pages, page limit", "type" : "number", "default" : 3, "description" : "If the symbolic access policy is set to 'limit_pages', the maximum width of a symbolic access (in pages)." } """) Settings().register_setting( "seninja.memory.concretize_unconstrained", """ { "title" : "Concretize unconstrained memory accesses", "type" : "boolean", "default" : true, "description" : "When a memory access on a unconstrained symbolic address occurs, allocate a new page and concretize the address to it." } """)
def callgraph(bv, current_function): bv.update_analysis_and_wait() graph = GraphWrapper(bv, current_function) show_indirect = False if Settings().get_bool("bn-callgraph.showIndirectCalls"): show_indirect = True visited = set() stack = [current_function] while stack: func = stack.pop() calls = set() indirect_calls = set() external_calls = set() for llil_block in func.llil: for llil in llil_block: if llil.operation.name in {"LLIL_CALL", "LLIL_TAILCALL"}: if llil.dest.possible_values.type.name in { "ImportedAddressValue", "UndeterminedValue" }: if llil.dest.operation.name == "LLIL_LOAD" and llil.dest.src.possible_values.type.name == "ConstantPointerValue": # External function is_in_binary = False dst_addr = llil.dest.src.possible_values.value if dst_addr != 0: dst_fun_addr_raw = bv.read( dst_addr, bv.arch.address_size) if len(dst_fun_addr_raw ) == bv.arch.address_size: # Its in the binary. Probably a shared library that exports a symbol that uses dst_fun_addr = int.from_bytes( dst_fun_addr_raw, "little" if bv.arch.endianness.name == "LittleEndian" else "big") dst_funs = bv.get_functions_at( dst_fun_addr) for dst_fun in dst_funs: calls.add(dst_fun) is_in_binary = True if not is_in_binary: # The function is not here symb = bv.get_symbol_at(dst_addr) if symb is not None: external_calls.add( ExternalFunction(symb.name)) elif llil.dest.possible_values.type.name == "UndeterminedValue" and show_indirect: # Indirect call indirect_calls.add( UndeterminedFunction(llil.address)) elif llil.dest.possible_values.type.name == "ConstantPointerValue": dst_funs = bv.get_functions_at( llil.dest.possible_values.value) for dst_fun in dst_funs: calls.add(dst_fun) elif show_indirect: # Indirect call indirect_calls.add(UndeterminedFunction(llil.address)) for child_func in calls | indirect_calls | external_calls: graph.add(child_func, func) if child_func not in visited: if not isinstance(child_func, UndeterminedFunction) and not isinstance( child_func, ExternalFunction): stack.append(child_func) visited.add(func) graph.show()
import os import json from binaryninja import PluginCommand, BackgroundTaskThread, BinaryViewType, Settings, log_info from .src import FormatStringFinder from .tests import run_fs_tests __all__ = [] # ==================== # Settings # Register group group_id = "format_string_finder" Settings().register_group(group_id, "Format String Finder") # Register setting 1 setting_1_id = "should_highlight_variable_trace" setting_1_should_highlight_variable_trace = group_id + "." + setting_1_id setting_1_properties = { "description": "Highlight instructions that are used in the trace of the format parameter origin.", "title": "Should Highlight Variable Trace", "default": False, "type": "boolean", "id": setting_1_id } Settings().register_setting(setting_1_should_highlight_variable_trace, json.dumps(setting_1_properties)) # Register setting 2
def getPriority(self, data, filename): if data.executable: # Higher priority will make this view the default priority = Settings().get_integer('linearmlil.priority', data) return priority return 0
def generate_graph( view: BinaryView, region: MediumLevelILAstNode, collection: ReportCollection = None, title: str = '' ): if not Settings().get_bool('linearmlil.debug'): return graph = FlowGraph() def add_children(node: MediumLevelILAstNode) -> FlowGraphNode: node_node = FlowGraphNode(graph) graph.append(node_node) node_line = node.type if node.type == 'block': node_line += f': {node.block}' if node.type == 'break': node_line += f': {node.start}' elif node.type in ('seq', 'case'): node_line += f': {node.start}' for child in node.nodes: child_node = add_children(child) node_node.add_outgoing_edge( BranchType.UnconditionalBranch, child_node ) elif node.type == 'cond': node_line += f': {node.condition}' child = add_children(node[True]) node_node.add_outgoing_edge( BranchType.TrueBranch, child ) if node[False] is not None: child = add_children(node[False]) node_node.add_outgoing_edge( BranchType.FalseBranch, child ) elif node.type == 'switch': for child in node.cases: child_node = add_children(child) node_node.add_outgoing_edge( BranchType.UnconditionalBranch, child_node ) elif node.type == 'loop': node_line += f': {node.loop_type} {node.condition}' child_node = add_children(node.body) node_node.add_outgoing_edge( BranchType.UnconditionalBranch, child_node ) node_node.lines = [node_line] return node_node # iterate over regions and create nodes for them # in the AST add_children(region) if collection is not None: if not title: title = f' {region.type}: {region.start}' report = FlowGraphReport(title, graph, view) collection.append(report) else: show_graph_report('Current AST', graph)
def iscomplex(fn): return cc(fn) > Settings().get_integer("tagteam.complexFunc")
from binaryninja import (DisassemblyTextLine, InstructionTextToken, InstructionTextTokenType, FlowGraph, FlowGraphNode, BranchType, enums, PluginCommand, Settings, BackgroundTaskThread, demangle) Settings().register_group("bn-callgraph", "BN CallGraph") Settings().register_setting( "bn-callgraph.showColorRoot", """ { "title" : "Colorize Root", "type" : "boolean", "default" : true, "description" : "Show root node in green" } """) Settings().register_setting( "bn-callgraph.showColorLeaves", """ { "title" : "Colorize Leaves", "type" : "boolean", "default" : true, "description" : "Show leaves node in red" } """) Settings().register_setting( "bn-callgraph.showIndirectCalls", """ { "title" : "Show Indirect Calls", "type" : "boolean", "default" : true, "description" : "Show indirect calls as undetermined nodes in the graph"
def islarge(fn): return len(fn.basic_blocks) >= Settings().get_integer("tagteam.largeFunc")
from binaryninja import Settings Settings().register_group("yara", "Yara") Settings().register_setting( "yara.customRulesPath", """ { "title" : "Custom YARA Rules Path", "type" : "string", "default" : "", "description" : "Absolute path to a directory containing custom YARA rule files (*.yar, *.yara). Use a semicolon to delimit multiple paths." } """) Settings().register_setting( "yara.timeout", """ { "title" : "Scan Timeout", "type" : "number", "default" : 60, "description" : "Timeout for running a YARA scan. A value of 0 disables this feature. The default value is 60 seconds. Time is specified in seconds." } """) Settings().register_setting( "yara.displayReport", """ { "title" : "Show YARA Report", "type" : "boolean", "default" : true, "description" : "Display a report of the YARA results when the scan is finished." } """)