# Copyright (c) 2019-2026 Vector 35 Inc # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. from binaryninja.function import DisassemblyTextRenderer, InstructionTextToken from binaryninja.flowgraph import FlowGraph, FlowGraphNode from binaryninja.enums import InstructionTextTokenType from binaryninjaui import FlowGraphWidget, ViewType # Flow graph class for creating a graph with both assembly and Low Level IL in one view class DisassemblyAndLowLevelILGraph(FlowGraph): def __init__(self, func): super(DisassemblyAndLowLevelILGraph, self).__init__() self.function = func il = func.low_level_il self.il_function = il # Support user annotations for this graph self.uses_block_highlights = True self.uses_instruction_highlights = True self.includes_user_comments = True self.allows_patching = True self.shows_secondary_reg_highlighting = True def populate_nodes(self): # Create disassembly text renderers for assembly and IL func = self.function il = self.il_function asm_renderer = DisassemblyTextRenderer(func) il_renderer = DisassemblyTextRenderer(il) # First create nodes for every block in the function nodes = {} for block in il: node = FlowGraphNode(self) node.basic_block = block nodes[block.start] = node self.append(node) # Construct graph for block in il: il_renderer.basic_block = block # Add outgoing edges for the node for edge in block.outgoing_edges: nodes[block.start].add_outgoing_edge(edge.type, nodes[edge.target.start]) # Get instruction starts for assembly instructions in the block start_addr = il[block.start].address asm_block = func.get_basic_block_at(start_addr) if asm_block is None: # If first IL instruction was removed in IL translation, the previous call will fail. Find # the block that contains the instruction for i in func.basic_blocks: if start_addr >= i.start and start_addr < i.end: asm_block = i break asm_instrs = [] if asm_block is not None: asm_renderer.basic_block = asm_block start = asm_block.start end = asm_block.end addr = start while addr < end: asm_instrs.append(addr) data = func.view.read(addr, min(asm_block.arch.max_instr_length, end - addr)) info = asm_block.arch.get_instruction_info(data, addr) addr += info.length if info.length == 0: break # Iterate through instructions in this block and add disassembly lines lines = [] for i in block: # Display assembly instructions at or before the current IL instruction while len(asm_instrs) > 0 and i.address >= asm_instrs[0]: for asm_lines, length in asm_renderer.get_disassembly_text(asm_instrs[0]): lines.append(asm_lines) asm_instrs = asm_instrs[1:] # Display IL instruction for il_lines, length in il_renderer.get_disassembly_text(i.instr_index): lines.append(il_lines) # Go through lines and add addresses to them for line in lines: if line.il_instruction is None: # For assembly lines, show address line.tokens.insert( 0, InstructionTextToken(InstructionTextTokenType.AddressDisplayToken, "%.8x" % line.address, line.address) ) line.tokens.insert(1, InstructionTextToken(InstructionTextTokenType.TextToken, " ")) else: # For IL lines, show IL instruction index line.tokens.insert( 0, InstructionTextToken( InstructionTextTokenType.AnnotationToken, "%8s" % ("[%d]" % line.il_instruction.instr_index) ) ) line.tokens.insert(1, InstructionTextToken(InstructionTextTokenType.AnnotationToken, " => ")) nodes[block.start].lines = lines def update(self): return DisassemblyAndLowLevelILGraph(self.function) # Flow graph widget subclass that displays the graphs described above class DisassemblyAndLowLevelILView(FlowGraphWidget): def __init__(self, parent, data): # Start view with entry function self.data = data self.function = data.entry_function if self.function is None: graph = None else: graph = DisassemblyAndLowLevelILGraph(self.function) super(DisassemblyAndLowLevelILView, self).__init__(parent, data, graph) def navigate(self, addr): # Find correct function based on most recent use block = self.data.get_recent_basic_block_at(addr) if block is None: # If function isn't done analyzing yet, it may have a function start but no basic blocks func = self.data.get_recent_function_at(addr) else: func = block.function if func is None: # No function contains this address, fail navigation in this view return False return self.navigateToFunction(func, addr) def navigateToFunction(self, func, addr): if func == self.function: # Address is within current function, go directly there self.showAddress(addr, True) return True # Navigate to the correct function self.function = func graph = DisassemblyAndLowLevelILGraph(func) self.setGraph(graph, addr) return True # View type for the new view class DisassemblyAndLowLevelILViewType(ViewType): def __init__(self): super(DisassemblyAndLowLevelILViewType, self).__init__("Asm -> LLIL", "Assembly to Low Level IL") def getPriority(self, data, filename): if data.executable: # Use low priority so that this view is not picked by default return 1 return 0 def create(self, data, view_frame): return DisassemblyAndLowLevelILView(view_frame, data) # Register the view type so that it can be chosen by the user ViewType.registerViewType(DisassemblyAndLowLevelILViewType())