disassembled_functions Attribute Documentation

June 18, 2026 · View on GitHub

Overview

The disassembled_functions attribute is an optional output of the blint binary analysis tool. It provides disassembled machine code and related analysis for functions identified in the binary's metadata (e.g., from symbol tables). This feature requires the nyxstone library for disassembly.

When disassembly is enabled, blint also derives a top-level callgraph metadata object from each function's direct_call_targets list. This provides a compact graph view without re-processing assembly text.

For a pass-by-pass explanation of callgraph computation and analyst triage workflow, see docs/CALLGRAPH.md.

Prerequisites

  • LLVM 18 and g++ must be installed. Alternatively, use the blint container image.
  • blint extended with nyxstone library must be installed (pip install blint[extended]).
  • Invoke blint cli with --disassemble
  • Use --export-callgraph-mermaid to additionally emit per-binary Mermaid files (*-callgraph.mmd) and embed diagrams in blint-output.html.
  • Use --export-callgraph-graphml to emit GraphML files (*-callgraph.graphml) for tools like Gephi/Cytoscape.
  • Use --export-callgraph-gexf to emit GEXF files (*-callgraph.gexf) for Gephi-focused workflows.

Structure

The disassembled_functions attribute is a dictionary where each key is a unique string identifying the function by its virtual address and name, in the format "0xADDRESS::FUNCTION_NAME" (e.g., "0x140012345::simple_add"). Using both address and name prevents collisions in cases where multiple functions might share the same name (e.g., in different modules or due to symbol stripping). The value for each key is another dictionary containing the following fields:

Field NameTypeDescription
nameStringThe name of the function.
addressStringThe virtual address of the function entry point (hexadecimal string, e.g., "0x12345").
assemblyStringThe full disassembled code of the function, with instructions separated by newlines.
assembly_hashStringA SHA-256 hash of the entire assembly string.
instruction_hashStringA SHA-256 hash of the newline-separated list of instruction mnemonics (e.g., "push", "mov", "call").
instruction_countIntegerThe total number of instructions disassembled.
instruction_metricsDictionaryA map of specific instruction types to their counts.
direct_callsList of StringsA legacy list of function names identified as targets of direct calls only (e.g., call 0x123456 where 0x123456 resolves to a known function name). Indirect heuristics and tailcalls are not included here.
has_indirect_callBooleanTrue if the function contains instructions like call rax or call [rax+0x10].
has_pacBooleanTrue if the function contains ARM64 Pointer Authentication Code (PAC) instructions (e.g., pacibsp, autibsp, retaa). These instructions sign and authenticate pointers (typically return addresses) to mitigate Return-Oriented Programming (ROP) attacks.
has_system_callBooleanTrue if the function contains system call instructions (e.g., syscall, int 0x80).
has_security_featureBooleanTrue if the function contains instructions related to security features (e.g., endbr64, endbr32).
has_crypto_callBooleanTrue if the function's disassembly text contains patterns indicating cryptographic operations (based on blint.lib.indicators.CRYPTO_INDICATORS).
has_gpu_callBooleanTrue if the function's disassembly text contains patterns indicating GPU-related operations (based on blint.lib.indicators.GPU_INDICATORS).
has_loopBooleanTrue if the function contains conditional jumps that target addresses earlier within the disassembled range (indicating a potential loop).
regs_readList of StringsA list of unique register names that are read within the disassembled function code. This provides a high-level view of all registers whose values influence the function's execution.
regs_writtenList of StringsA list of unique register names that are written to within the disassembled function code. This indicates registers whose values are modified by the function.
used_simd_reg_typesList of StringsA list of SIMD register types such as FPU, MMX, SSE/AVX etc.
instructions_with_registersList of DictionaryA detailed list providing register usage information for each individual instruction within the function.
function_typeStringA classification of the function based on heuristics. Possible values include: "PLT_Thunk", "Simple_Return", "Has_Syscalls", "Has_Indirect_Calls", or "Has_Conditional_Jumps". If a function doesn't fit these specific categories but is not a simple return, this field will be an empty string.
proprietary_instructionsList of Strings(Apple Silicon Only) A list of categories for proprietary instructions found (e.g., "GuardedMode", "AMX"). This indicates the use of non-standard hardware features.
sreg_interactionsList of Strings(Apple Silicon Only) A list of categories for interactions with proprietary System Registers (e.g., "SPRR_CONTROL", "PAC_KEYS"). This signals manipulation of low-level security and hardware configuration.

instruction_metrics Sub-structure

The instruction_metrics dictionary contains counts for specific categories of instructions found during disassembly:

Field NameTypeDescription
call_countIntegerNumber of call instructions.
conditional_jump_countIntegerNumber of conditional jump instructions (e.g., je, jne, jg, jle).
xor_countIntegerNumber of xor instructions.
shift_countIntegerNumber of shift/rotate instructions (e.g., shl, shr, rol, ror).
arith_countIntegerNumber of arithmetic/logical instructions (e.g., add, sub, imul, and, or).
ret_countIntegerNumber of ret instructions.
jump_countIntegerNumber of jmp instructions.
simd_fpu_countIntegerNumber of simd instructions.
unique_regs_read_countIntegerNumber of unique registers read within the function (aggregated from all instructions).
unique_regs_written_countIntegerNumber of unique registers written to within the function (aggregated from all instructions).

instructions_with_registers Sub-structure

Each element in the instructions_with_registers list is a dictionary corresponding to a single disassembled instruction. It contains:

Field NameTypeDescription
regs_readList of StringsA list of register names that are read as part of this specific instruction's operation.
regs_writtenList of StringsA list of register names that are written to as part of this specific instruction's operation.

Understanding Register Usage

The regs_read and regs_written fields (both globally for the function and per-instruction) provide a first-pass approximation of register usage. They analyze the textual representation of the assembly instruction.

  • regs_read: Indicates registers whose current value is used by the instruction.
    • Example: add rax, rbx
      • regs_read: ["rax", "rbx"] (The values in rax and rbx are inputs to the addition).
  • regs_written: Indicates registers whose value is modified or set by the instruction.
    • Example: add rax, rbx
      • regs_written: ["rax"] (The result of rax + rbx is stored back into rax).
  • Implicit Operands: Some instructions implicitly read or write specific registers (e.g., push/pop use rsp; call/ret affect rsp and rip). The analysis attempts to account for common implicit behaviors for certain instruction types, but coverage might not be exhaustive.
  • Memory Operands: Instructions accessing memory via addresses calculated from registers (e.g., mov rax, [rbx + rcx*2]) indicate that rbx and rcx are read (used for address calculation). The destination rax is written.
  • Limitations: This analysis is based on parsing the assembly text string. It provides a good approximation for common instructions but might be inaccurate for highly complex or obfuscated code, or for instructions not explicitly handled in the parsing logic.

Use Cases

  1. Binary Fingerprinting and Diffing: Compare binaries by matching functions based on their assembly_hash or instruction_hash. This helps identify identical or modified functions between different versions or variants of a binary.
  2. Vulnerability Detection: Identify functions containing specific instruction patterns or sequences associated with known vulnerabilities or insecure coding practices. The instruction_metrics can highlight functions with unusual numbers of certain instructions.
  3. Malware Analysis: Analyze the disassembled code for suspicious patterns, such as indirect calls (has_indirect_call), system calls (has_system_call), or cryptographic operations (has_crypto_call). The assembly field allows for manual inspection.
  4. Code Similarity Analysis: Group functions based on their assembly_hash or instruction_hash to find duplicated or similar code blocks within a binary.
  5. Function Characterization: Quickly assess the nature of a function using the function_type field and boolean flags (has_loop, has_security_feature, etc.). This can help prioritize analysis or identify specific types of functions (e.g., PLT thunks).
  6. Call Graph Approximation: Use the top-level callgraph object, which is derived from per-function direct_call_targets entries (including kind values such as direct, indirect_hint, and tailcall) and confidence scoring.
    • blint now emits this approximation directly in top-level metadata as callgraph with deterministic node IDs and counted edges.
    • Ambiguous targets (same symbol name in multiple internal nodes) and unresolved targets are emitted under callgraph.external.
  7. Security Feature Verification: Confirm the presence of control-flow integrity features.
    • Intel CET: Check has_security_feature for endbr instructions.
    • ARM64 PAC: Check has_pac for pointer signing instructions. This confirms that protections claimed in headers are actually compiled into the code.
  8. Register Usage Analysis:
    • Identify Potential Arguments: Functions that read specific registers (especially those used for argument passing conventions like rcx, rdx, r8, r9 on Windows x64 or rdi, rsi, rdx, rcx, r8, r9 on System V AMD64 ABI) at the beginning might be taking arguments via those registers.
    • Track Data Flow: By examining instructions_with_registers, you can trace how data moves through registers within a function. For example, seeing rax written by one instruction and then read by a subsequent one.
    • Detect Register Preservation: Check if a function modifies callee-saved registers (like rbx, rbp, r12-r15 on x64) without restoring them, which might violate calling conventions or indicate specific behavior.
    • Spot Unusual Register Patterns: Functions that read or write an unusually large number of registers might be complex, perform context switching, or manipulate state extensively.
  9. Analyzing Proprietary Hardware Features (Apple Silicon):

The proprietary_instructions and sreg_interactions fields provide powerful insights into how software leverages Apple's custom silicon features. This is critical for security research, anti-tampering analysis, and performance tuning on macOS and iOS.

  • Detecting Advanced Security Hardening:

    • Use Case: A kernel extension or system daemon uses hardware-enforced memory permissions that are stronger than standard ARM features.
    • blint Findings: The sreg_interactions list contains "SPRR_CONTROL" or "GXF_CONTROL".
    • Analysis: This indicates the function is setting up or entering a "Guarded Execution" mode (GXF) or manipulating the Secure Page Table (SPRR). This code is highly security-sensitive and is likely part of Apple's core operating system defenses, such as protecting kernel memory or DRM components.
  • Identifying Anti-Debugging and Anti-Emulation:

    • Use Case: A protected application wants to detect if it's being run under a debugger or in an emulator. It does this by reading hardware performance counters, which behave differently in virtualized environments.
    • blint Findings: The sreg_interactions list contains "PERF_COUNTERS".
    • Analysis: This is a strong indicator of an anti-analysis technique. The function is likely measuring execution time or specific hardware events to detect anomalies caused by debuggers or emulators
  • Finding Performance-Critical Code:

    • Use Case: A high-performance application uses Apple's custom matrix co-processor for machine learning or signal processing tasks.
    • blint Findings: The proprietary_instructions list contains "AMX" (Apple Matrix Coprocessor).
    • Analysis: This function is a candidate for performance analysis. It directly leverages specialized hardware, and any changes to it could have significant performance implications.
  • Locating Kernel-Level Pointer Authentication Logic:

    • Use Case: The kernel is configuring Pointer Authentication (PAC) keys to protect its own function pointers from being overwritten in an attack.
    • blint Findings: The sreg_interactions list contains "PAC_KEYS".
    • Analysis: This function is manipulating the hardware keys used for pointer signing and authentication. It is a critical part of the system's control-flow integrity and a high-value target for security researchers.

Examples

Example 1: Standard x64 Arithmetic

Consider a simple function that adds two numbers passed in rcx and rdx, stores the result in rax, and returns.

Disassembly Snippet:

simple_add:
   push rbp
   mov rbp, rsp
   mov rax, rcx  ; Move first argument (rcx) to rax
   add rax, rdx  ; Add second argument (rdx) to rax
   pop rbp
   ret

Corresponding disassembled_functions Entry (Simplified):

{
  "0x140012345::simple_add": {
    "name": "simple_add",
    "address": "0x140012345",
    "assembly": "push rbp\nmov rbp, rsp\nmov rax, rcx\nadd rax, rdx\npop rbp\nret",
    "instruction_count": 6,
    "instruction_metrics": {
      "arith_count": 1,
      "call_count": 0,
      ...
      "unique_regs_read_count": 4,
      "unique_regs_written_count": 3
    },
    "regs_read": ["rbp", "rsp", "rcx", "rdx"],
    "regs_written": ["rbp", "rsp", "rax"],
    "instructions_with_registers": [
      {
        "regs_read": ["rbp", "rsp"],
        "regs_written": ["rsp"]
      },
      {
        "regs_read": ["rsp"],
        "regs_written": ["rbp"]
      },
      {
        "regs_read": ["rcx"],
        "regs_written": ["rax"]
      },
      {
        "regs_read": ["rax", "rdx"],
        "regs_written": ["rax"]
      },
      {
        "regs_read": ["rsp"],
        "regs_written": ["rbp", "rsp"]
      },
      {
        "regs_read": ["rax", "rsp"],
        "regs_written": ["rsp"]
      }
    ]
    ...
  }
}

Example 2: Analyzing ARM64 PAC Compliance

This example demonstrates how has_pac helps analysts confirm that a critical function is protected by Return Address Signing.

Disassembly Snippet (ARM64 Windows):

secure_function:
   pacibsp                      ; Sign return address (LR) using SP and Key B
   stp    x29, x30, [sp, #-16]! ; Save frame pointer and signed LR to stack
   mov    x29, sp               ; Setup frame pointer
   ...
   ldp    x29, x30, [sp], #16   ; Restore frame pointer and signed LR
   autibsp                      ; Authenticate return address (check signature)
   ret                          ; Return (will fault if signature mismatch)

Corresponding disassembled_functions Entry:

{
  "0x140001000::secure_function": {
    "name": "secure_function",
    "has_pac": true,
    "assembly": "pacibsp\nstp x29, x30, [sp, #-16]!\nmov x29, sp\n...\nldp x29, x30, [sp], #16\nautibsp\nret",
    "instruction_metrics": { ... },
    ...
  }
}

Analysis: The presence of has_pac: true and the visible pacibsp/autibsp instructions confirm that this function is actively hardened against ROP attacks. If has_pac were false for a critical function in a high-security binary, it would indicate a potential vulnerability or build misconfiguration.

Example 3: Analyzing an Apple Silicon Security Function

Consider a hypothetical function on macOS that configures memory permissions.

_configure_secure_memory:
   stp    x29, x30, [sp, #-16]!
   mov    x29, sp
   mrs    x0, s3_6_c15_c1_0  // Read SPRR_CONFIG_EL1
   orr    x0, x0, #1         // Set the SPRR_CONFIG_EN bit
   msr    s3_6_c15_c1_0, x0  // Write back to enable SPRR
   ldp    x29, x30, [sp], #16
   ret

Corresponding disassembled_functions attribute:

{
  "0x1000abcde::_configure_secure_memory": {
    "name": "_configure_secure_memory",
    "address": "0x1000abcde",
    "assembly": "stp x29, x30, [sp, #-16]!\nmov x29, sp\nmrs x0, s3_6_c15_c1_0\norr x0, x0, #1\nmsr s3_6_c15_c1_0, x0\nldp x29, x30, [sp], #16\nret",
    "proprietary_instructions": [],
    "sreg_interactions": [
      "SPRR_CONTROL"
    ],
    "regs_read": ["x29", "x30", "sp", "x0"],
    "regs_written": ["x29", "x30", "sp", "x0"],
    "instructions_with_registers": [
      // ...
      {
        "regs_read": [],
        "regs_written": ["x0"]
      },
      // ...
    ]
    ...
  }
}

Disassembly Approach: Strengths and Limitations

blint's disassembly engine is designed for resilience, especially when analyzing stripped firmware (extracted using binwalk) and other non-standard binaries.

[ Get Function Address & Size from Metadata ]
                |
                V
 [ Fetch Bytes (Dual-Strategy) ]
  /                           \
[1. Manual Segment Lookup]   [2. LIEF VA-to-Offset] --> (Whichever succeeds first)
                |
                V
 [ Disassembly Heuristic ]
  /         |          \
[ LLVM Tuple? ] [ MIPS16? ] [ microMIPS? ] --> (For each mode, also try small offsets 0-3 and smaller disassembly)
                |
                V
 [ SUCCESS ] -> [ Analyze Instructions & Generate Metrics ]
  • Strength: Hybrid Byte Fetching. blint first attempts a manual, segment-based calculation to find a function's bytes, which is highly effective for firmware where symbol addresses may not align with LIEF's standard view. If that fails, it falls back to LIEF's canonical virtual_address_to_offset method, which correctly handles standard ELF files. This hybrid approach maximizes the number of functions found.
  • Strength: Multi-Mode Disassembly. Thanks to nyxstone and LLVM, blint supports a range of architecture and cpu supported by LLVM. For MIPS binaries, blint doesn't just assume one instruction set. It intelligently attempts to disassemble a function as MIPS32, then MIPS16, and finally microMIPS. This is crucial for correctly analyzing firmware binaries that mix standard and compressed instruction sets.
  • Strength: Mach-O and PE coverage. nyxstone only initializes LLVM's ELF object backend, so blint remaps Mach-O (*-apple-*) and PE (*-windows-msvc) target triples onto an equivalent ELF triple for the disassembler instance only — instruction decoding is identical across object formats — while keeping the real llvm_target_tuple in the metadata. This is what enables disassembly of macOS/iOS and Windows binaries.
  • Strength: Imported-call resolution. For Mach-O, blint resolves calls made through __stubs (via the indirect symbol table) and the GOT (via the dyld binding table) to their demangled imported symbol names. These are recorded at call sites and surfaced in the callgraph as external import edges instead of being misattributed to internal functions.
  • Strength: Offset Probing. blint also probes small offsets (0-3 bytes) from the reported function address. This handles common cases where a symbol points to a data word or is slightly misaligned from the first true instruction.
  • Limitation: Heuristic Register Analysis. The regs_read and regs_written attributes are generated by a pattern-matching heuristic, not a full data-flow analysis. They provide a good overview but may be inaccurate for complex instructions or unusual addressing modes. They are best used for initial triage, not for precise exploit development.