SuperAPI AI Directory

re-variant

Security & auditing

View repository

Updated 10-15 17:28

About this skill

Binary variant/patch comparison

When to use / when not to use

  • Use: before/after patch comparison (vulnerability localization), family variant correlation, sample tracing, N-day analysis
  • Use: same-source code reuse identification (determining shared functions/library code)
  • Use: fix coverage audit—compare fixed samples across multiple branch versions one by one, verifying which versions fixed it and which did not
  • Use: vulnerability research—full reconstruction of structural and behavioral differences before and after the fix (patch → defect pattern)
  • Don't use: in-depth single-sample analysis (follow the [[re-binary-core]] general path)
  • Don't use: no comparison baseline (only a single sample, no second version to compare against)—follow the single-sample path first
  • Don't use: behavioral-level comparison (dynamic behavior differences go to [[re-behavior]]; this skill only compares static code structure)
  • Don't use: comparison requiring semantic-level reconstruction (first use [[re-cpp-abi]] to reconstruct ABI/symbol semantics, then return to this skill for difference localization)

Tool preparation

BinDiff / Diaphora (function matching plugins)

  • BinDiff (Google/Zynamics line, Windows commercial tool, official installer): primarily an IDA plugin; since BinDiff 6 it officially supports Ghidra (requires the BinExport plugin for export); BinExport also supports Binary Ninja—the export format can bridge multiple decompilers
  • Diaphora: multi-platform open source (git clone https://github.com/joxeankoret/diaphora), primarily an IDA plugin (Ghidra-side support is continuously progressing)
  • Alternatives without an IDA environment: qbindiff (Quarkslab, reads BinExport/quokka exports), ghidriff (headless Ghidra diff)
  • Typical workflow: BinExport export within the decompiler → load the two samples into the tool → match → export results (similarity/change set) as a report; the BinExport plugin and decompiler versions must match (see [[gotchas]])
  • Verification: the plugin can be loaded within the decompiler and can export/import matching results

radiff2 / rz-diff (rizin command-line diff)

  • For the installation matrix see [[re-radare2]] (including the radare2 compatibility layer); after installation verify with rizin -v rizin -v
  • Verification: rz-diff --version; radiff2 ships with the radare2 package
  • Purpose: fast function-level comparison and batch scripting without a GUI (see the step 1 commands); radiff2 does quick byte/instruction-level checks (radiff2 -s radiff2 -s bytes,-C instructions)—use it for quick checks on small files, use rz-diff for function-level comparison

readelf (symbol alignment aid)

  • For installation and verification see [[re-cpp-abi]] tool preparation
  • Purpose: readelf -s(symbol table/dynamic symbols) and readelf -d(dynamic section) for import/export alignment and pairing verification

Pairing aids (pre-comparison)

  • Hashing/baseline: sha256sum; architecture/compiler determination: [[re-imports]] import table and toolchain fingerprint—confirm comparability before pairing (same architecture/same toolchain, see pitfalls 1, 3)
  • Verification: sha256sum --version
  • Packer check: [[re-packer-id]] identifies packing; unpack packed samples first ([[re-unpack-simple]]) before comparing—packer code pollutes matching (see the counterexample group in [[gotchas]])

Operating steps

Execute in order, archive the output of each step (path + sha256, see [[re-triage]]); the final difference list is passed according to the [[re-analyze/analysis-contract]] data contract.

  1. Pairing preparation (confirm comparability before comparing):

    • Record baseline: sha256 of both samples, architecture (x86_64/ARM, etc.), bitness, compiler fingerprint (version string/imported library set, [[re-imports]])
    • Comparability determination: same architecture + same toolchain → full function-level matching; cross-architecture/different compiler → downgrade to logic-layer comparison (call graph/constants/strings), annotate limitations
    • Bitness/endianness: mixed 32/64-bit or mixed big/little-endian are not compared directly—unify to the same bitness and endianness before matching
    • Symbol state: strip/not stripped determines the matching strategy (if not stripped, align directly by symbol, see step 2)
  2. Function matching:

    # 命令行快速对比(rizin 系):函数级匹配(-B 自动分析后按函数对比,含相似度列)
    rz-diff -B -t functions sample_v1 sample_v2 | head -30
    
    • Matching dimensions: instruction hash (identical code), call graph (subgraph isomorphism), import/export alignment, string/constant references
    • Data reference dimension: global constant/string references serve as function identity fingerprints—more stable than instruction hashes after refactoring/reordering, a reliable anchor for cross-version matching
    • Tool (BinDiff/Diaphora) output: matched / changed / new / deleted function sets
    • Output interpretation: matched means identical, changed means modified, new/deleted means functionality added/removed—changed goes to step 3, new/deleted indicate differences in functionality surface, give follow-up actions for each category
    • Missing symbols after strip: rely on structural matching (entry features/call patterns/constant anchoring)—for matching dimension weight adjustment see pitfall 1 and [[decision-tree]]
    • Address difference handling: function addresses differ after PIE/relocation—align by symbol name/function order, do not compare raw addresses (see pitfall 2)
    • Name/comment migration: tools support migrating names/comments of already-named functions to matched functions—annotate known samples first and then compare, semi-automatically recover symbols of new samples; annotate the source of migrated names, use only as auxiliary anchors (see [[gotchas]] symbol group)
  3. Patch diff (N-day):

    • Compare before and after the fix → changed function set = vulnerability point candidates
    • Classify changed functions by semantics: newly added validation/boundary handling (high priority) > behavior changes > pure refactoring (low priority)—sort candidates by category ([[decision-tree]] grading)
    • Deep dive into changed functions: new conditional branches/new validation/newly added calls ([[re-ghidra]] / [[re-ida]] decompilation)
    • Infer vulnerabilities: defect patterns in the old code (missing validation/out-of-bounds/use-after-free)
    • Fix pattern recognition: common fix patterns (length checks/null pointer checks/tightening out-of-bounds limits) are correspondingly categorized with defect patterns—quickly screen candidate functions by pattern
    • Fix coverage verification: compare fixed samples one by one across multi-branch versions to confirm the coverage of vulnerability fixes (which versions fixed it and which did not)
    • Output: vulnerable functions + defect pattern inference (with confidence annotation, for evidence grading see [[decision-tree]])
  4. Variant tracing:

    • Pairwise comparison of samples within a family → similarity matrix → clustering (shared function ratio threshold)
    • Evolution chain: sort samples by timeline/similarity (early vs late variants)
    • Shared unique functions = family signature (connects with [[re-attribution]] capability evidence)
    • Unique function determination: first compute the shared set within the family, then take functions missing from out-of-family samples as family markers—avoid treating common code as a marker
    • Association strength: comprehensively determined by the number of shared unique functions + similarity distribution; no single metric is used as the sole basis
    • Sample size control: when there are many family samples, the pairwise matrix is O(n²)—first perform coarse clustering by features (architecture/size/import set), then compare in detail within clusters
    • Threshold calibration: manually spot-check and calibrate similarity scores before setting the clustering line (see Pitfall 5) to avoid inflated associations
  5. Output a difference list and verification:

    • Format: changed function table (function name/address/change type/similarity) + conclusion
    • Pass according to the [[re-analyze/analysis-contract]] data contract (downstream consumption)
    • False-positive prevention verification: exclude shared library code (cross-check against the [[re-imports]] dependency list), recheck with a different compiler (differences from compiling the same source with different compilers), and cross-validate across tools ([[decision-tree]] grading)
    • Reproducible report: the difference list includes tools and versions, similarity thresholds, and exclusion rules; important conclusions are stored with matching export files + manual decompilation review records

Cross-domain joint

  • [[re-binary-core]] gateway: home of this skill (selection tree "patch/N-day comparison" branch)
  • [[re-ghidra]] / [[re-ida]]: decompilation foundation (deep dive into changed functions)
  • [[re-attribution]]: connecting capability evidence for variant association
  • [[re-imports]]: import table/toolchain fingerprint (pairing comparability and shared library exclusion)
  • [[re-cpp-abi]]: symbol semantics recovery (prerequisite for semantic-level comparison)
  • [[re-packer-id]] / [[re-unpack-simple]]: unpack packed samples before comparison (packer code pollutes matching)
  • [[re-analyze/analysis-contract]]: the difference list is passed according to the data contract

Common pitfalls and traps

  • Compiler differences interfere with matching: Symptom—the same source code compiled by different compilers is judged dissimilar; Cause—optimization/code generation differences; Countermeasure—weight by call graph and constant references, and reduce the weight of instruction hashing
  • Missing symbols after strip: Symptom—no function names at all; Cause—symbol stripping; Countermeasure—structural matching (entry features/call patterns) and import table anchoring
  • Cross-architecture comparison degrades: Symptom—low match rate between x86 and ARM; Cause—different instruction sets; Countermeasure—compare only the logic layer (call graph/constants/strings), and note cross-architecture limitations
  • Large numbers of similarities cause false associations: Symptom—shared library code causes inflated similarity; Cause—common dependencies (libc/frameworks); Countermeasure—exclude shared library symbols and compare only business code ([[re-imports]] dependency cross-check)
  • Patch diff localization bias: Symptom—many changed functions, and the vulnerability point is drowned out; Cause—the patch contains refactoring; Countermeasure—filter by change semantics (prioritize newly added validation/boundary handling)
  • Raw address comparison fails: Symptom—the same function is reported as different; Cause—address drift due to PIE/relocation/version differences; Countermeasure—align by symbols/function order, not raw addresses
  • "No diff found" ≠ "not fixed": Symptom—patch comparison has no changed functions; Cause—the fix is inlined into adjacent functions or reorganized across functions; Countermeasure—expand the comparison scope (including instruction hashes after inline expansion), and cross-check with version history
  • Name migration contamination: Phenomenon—the function names of the new sample appear to be known; Cause—migrated from matching sources rather than real symbols; Countermeasure—annotate the source of migrated names, use them only as auxiliary anchors, and confirm key functions through decompilation
  • Symbol table forged: Phenomenon—symbol names are complete but the structure does not match; Cause—obfuscation/anti-analysis tools rename (forge symbols); Countermeasure—subject symbol alignment results to structural confirmation (call patterns/constants), and do not trust the symbol names themselves
  • Blind spot of comparing only .text: Phenomenon—function matches are all identical but behavior differs; Cause—data segment differences (configuration/constant tables) are excluded from the comparison scope; Countermeasure—include the data reference dimension in matching, and compare constant tables/configuration structures separately
  • For decision branches (comparability/matching level/candidate ranking), see [[decision-tree]]; for matching algorithms and tool boundaries, see [[gotchas]]