Skip to content

v4.10.0

Choose a tag to compare

@github-actions github-actions released this 14 Oct 19:19
· 37 commits to master since this release
af37541

Changed

  • (labels) Read every mangled symbol through the demangle package, pinned at ==0.5.1, where an installed
    plugin cannot replace a built-in scheme unless the caller asks. ItaniumDemangler, MsvcDemangler,
    MachoDemangler and rust_demangler keep their public functions and wrap it, replacing the pycxxfilt build, the
    vendored MSVC and Rust demanglers and the swift demangle subprocess. Measured on the bundled fixtures: every
    Itanium, MSVC and Rust label is unchanged; 32 of 7,937 stored labels change, all Swift names a host without a Swift
    toolchain left mangled. Reproduced on the bundled fixtures. Cost: C++ names demangle slower per name than the
    native build did; labelling the fixtures end to end went 381 ms to 297 ms cold, measured on 0.5.0. (#379, #383)

  • (labels) Bump demangle to ==0.6.0. Labels move on non-ASCII and ARM64e names: Itanium and Swift source-name
    lengths count UTF-8 bytes, as GCC and Clang mangle them, so _ZN4main5caféE reads as main::café where it was
    refused, _Z4cafév is now refused, and $s4main5caféV1fyyF reads as main.café.f() -> () instead of
    maincaféV.f() -> (). MSVC ARM64e __ptrauth names, refused before, now demangle, and Swift ~Deinitable is
    rendered by name. Rust gains bounds checks on oversized lengths with no output change found. Measured on the
    bundled fixtures:
    8,282 labels, 0 changed; no fixture name is non-ASCII. Reproduced by reviewer. (#387)

  • (labels) Rust names follow rustc-demangle: a lowercase .llvm. suffix is kept (foo::bar.llvm.1234567890abcdef),
    _RIC1aKh_E reads as a::<0>, _ZN3$u$E as $u$, and _ZN17h0000000000000000E, a hash and nothing else, is
    refused instead of labelled "" and stays mangled rather than read as C++. MSVC now reads void f(...) and
    operator co_await, and still refuses malformed names llvm-undname reads past (a literal length that disagrees
    with its bytes, a non-hexadecimal unnamed-namespace discriminator, a scope fragment ? does not open, a
    conversion operator with parameters). None of these names occurs in the bundled fixtures.

  • (binja) Read instruction lengths from the core instead of rendering each block's disassembly text.
    _instructionAddresses iterated each block, which builds text tokens for every instruction, and ran twice per
    block; it now calls get_instruction_length with the block's architecture, decodes each block once and serves
    getInstructionBytes from one read per block. Measured on 2247ee6 with Binary Ninja 6.0 on a 2,329-function
    x64 PE, not bundled, median of three:
    exportBinaryView 7.39 s to 2.15 s, report identical. Cost: one bytes
    object per instruction is held for the lifetime of the interface. (#378)

Removed

  • (labels) The vendored MSVC demangler, the vendored Rust demangler and the Swift subprocess machinery,
    2,519 lines under src/, with the NOTICE section for the demangler behaviour they reimplemented.
    primeSwiftSymbols stays as a no-op for callers.

Fixed

  • Keep a shrink-wrapped function's frame setup in the function whose early-exit check runs ahead of it: on AArch64
    the code before an unnamed, uncalled exception record or prologue seeds the function, and on x86 a jcc into a
    prologue-only candidate stays in the function unless that candidate sits aligned behind padding. Reproduced on the
    bundled #385 fixtures. On python.org's 3.13.7 python313.dll (not bundled), PDB as truth, against ddffed4:
    ARM64 without the PDB gains 24 entries, splits inside functions fall from 55 to 23, and __gsfailure joins
    __security_check_cookie (pinned in a test); x86 loses nothing. (#384)

  • Stop decoding a function at a switch table it reads. MSVC puts the table right after the last block, and when that
    block ends in a call that does not return, decoding ran on into the table and the next function; a function that
    reached a table before its dispatch is analyzed again with the table known. A two-level switch's byte table is
    booked as data, so the gap scan no longer starts functions in it. Same DLL, x86: 2 functions recovered, none
    lost, starts inside functions 116 to 43. The official sqlite 3.54 DLL drops 44 (x86) and 1 (x64) false starts that
    sat inside switch tables. (#384)

  • (intel) Bound Go 1.22+ type-switch jump tables by the and mask on their index (capped at 0xFF, and tightened
    by any cmp on the index behind it). Writes to an unrelated register no longer drop the index tie, so since the
    backtrack window is address-ordered the tie can now reach a cmp in another block. Measured on the issue's
    go1.24.7 reproducer, windows and linux amd64 (not bundled):
    image/draw.DrawMask drops from 1,065 / 549 blocks
    to 238. Malpedia CI corpus: 154/155 files identical; one Akira ELF goes -13/+4, as a recovered table removes a
    false function that had split a gap. Bundled fixtures keep their golden results. (#363)

  • (intel) Resume the gap scan past a failed candidate on binaries padded with nops. The int3 resume from #338
    never fires on GCC/Clang ELFs, so when the failed candidate's straight-line code ends in ret/jmp and only
    nops follow up to a 16-byte boundary, the scan now resumes there. Only capstone's nop counts, so old i386
    binutils fillers (lea esi, [esi], mov esi, esi) are not padding: Delphi emits lea eax, [eax] inside code.
    Reproduced by reviewer on 866 stripped x86/x64 ELFs against their symbol tables: +464/-2 true starts,
    +2/-6 false starts. Malpedia CI corpus, before #380: 152/155 files identical, +111 functions, none lost. (#381)

Compatibility

  • Function boundaries move on AArch64 images with shrink-wrapped functions, on x86 code that jumps over an early
    ret into a prologue, on functions with a switch table behind a call that does not return (any architecture),
    and on x86/x64 two-level switches. (#384)

What's Changed

Changes

  • perf(binja): read instruction lengths instead of rendering block text by @r0ny123 in #378
  • fix(intel): bound jump tables by and masks in Go type switches by @r0ny123 in #380
  • refactor(labels): read symbols through the demangle package by @r0ny123 in #379
  • test: add msvc function-boundary fixtures for #384 by @danielplohmann in #385
  • build(labels): pin demangle 0.5.1 so plugins cannot replace built-ins by @r0ny123 in #383
  • fix(intel): resume the gap scan past nop padding after a failed candidate by @r0ny123 in #381
  • fix: keep shrink-wrapped prologues, early-return bodies and switch tables in their function by @r0ny123 in #386
  • build(labels): bump demangle to 0.6.0, take #383 review nits by @r0ny123 in #387
  • build: release 4.10.0 by @danielplohmann in #390

Full Changelog: v4.9.0...v4.10.0