Repository navigation
v4.11.0
Added
- (utility)
extract_stringsreads Go and Rust strings at their real length, from the paired length load or a
(pointer, length) header, as UTF-8, withdata_addron the string. Addsmode="rust", picked when the Rust score
and a/rustc/<commit>/path agree; Go mode still needs a pclntab. A reference without a usable length keeps the
NUL-terminated read, stopped at the next referenced address. Measured on0575a23, reproduced by reviewer on
bundled fixtures:rust_pe_gnu2657 -> 2909 refs, strings over 200 chars 820 -> 1, 3 pointer reads lost. Cost:
an in-image/rustc/path putsmirai_x64in Rust mode and shortens 13 of its 162 strings, 16 before #402 (tail-merged C strings
such as"\r\n"->"\r"); string extraction onrust_pe_gnuwent 0.66 s -> 1.12 s on the reviewer's machine. (#382)
Changed
- (core) Keep the per-instruction state of large images compact: a lone code reference is stored bare, not in a
one-element set (AddressRefs),code_mapreadsins2fn, mnemonics are interned and pointer escaping drops the
capstone detail. Measured onaf37541, not bundled: RSS after analysis libjvm.so 7,289 to 4,015 MB. Reproduced
by reviewer: peak RSS -26 to -35% on libpython3.14.so, libcrypto.so.3 and an x64 and an ARM64 PE, CPU 1-7% lower,
reports identical. Cost:code_mapcan no longer be assigned, writes through it land inins2fn, and its values
are function starts; lonecode_refs_from/code_refs_toentries read as frozensets that onlyadd()and
discard()change. (#396) - (core) Suspend the cyclic garbage collector while a buffer is analysed. Analysis builds millions of long-lived
objects and almost no cycles, so every full collection walked the growing heap and freed nothing. Measured on
af37541, not bundled, reproduced by reviewer: analysis CPU on libpython -27% under CPython 3.12 (14.4 to 10.5 s),
-5 to -7% under 3.14, whose incremental collector already does less; reports identical. Cost: the collector is off
process-wide during analysis, and atoDict()right after pays one heap walk (1.0 to 2.2 s on 3.12). (#396)
Fixed
- (loaders) Start a mapped ELF at its
PT_LOADsegments where the sections miss them, so the ELF header is
mapped. With no section headers (as UPX leaves them) the base was 0; with a header-only firstPT_LOAD(lld, no
.rodataor build-id) it sat a page above the header. Either waydisassembleBufferlost the header and found no
code or the wrong architecture. Measured onb34154e: the 18 bundled ELF fixtures with section headers zeroed
map at their sectioned base and size, was 3 (three overranMAX_IMAGE_SIZE). Reproduced on bundled fixtures. No
cost: base and functions unchanged on the shipped fixtures, 264 Debian armhf/armel and 50 x86 system ELFs. (#394) - (loaders) Keep a mapped ELF buffer's dynamic symbols. lief read
disassembleBuffer()'s image by file offset and
so missed the dynamic table of any segment mapped above its offset;ElfFileLoader.unmapImagenow rebuilds the file
layout from the PT_LOADs when the table is found at its address but not at its offset. Measured onaf37541: 7
built x86/x64/AArch64 libraries, exported-function labels in buffer mode 0 -> 33 (file 33); the ELF fixtures gain
their imports in buffer mode (19, 16, 3), functions/blocks/instructions bit-identical on every pre-existing fixture
in every mode. Reproduced on bundled fixtures. Cost: one extra lief parse per mapped ELF buffer. (#395) - (intel) Keep the body behind an early
retin its function when the body is analyzed first. A body whose
prologue outranked its entry in the candidate order was a function by the time the entry's jcc reached it; the jcc
now takes it back when the entry's own code runs without a gap into theretin front of it, at most 0x100 bytes
past the entry, and analyzes it again if the entry does not keep it. Reproduced by reviewer on 494 benign PEs (not
bundled): 70 starts fewer in 55 files, all bodies behind their entry'sret(_RTC_CheckEsp,__isa_available
helpers, LLVM shrink-wrapped functions), runtime unchanged. (#388, #389) - (utility) Go mode no longer loses the whole report to an
OverflowErrorwhen a string header's length word is
out of range; sized reads stop at 64 KiB. Lengths that cut a C string short now fall back to it, so cgo code keeps
open("/dev/watchdog", 2)whole. Bundled fixtures with a pclntab header planted in padding:mirai_x64errored
on master and now has 125 refs (162 unplanted); in Go mode 1-character reads are dropped. go1.24.7 amd64 and 386
builds, not bundled, not reproduced: unchanged by the fallback, 4 two-character reads gained on 386. (#382) - (utility) Go and Rust mode keep a C string whose last character the compiler also stores as a string of its
own ("="in the tail of"url="); the NUL-terminated read stopped at the suffix's reference. Adds a 1 MB go1.24.7
linux/amd64 fixture so Go mode is tested on a real pclntab. Measured on1cec0bc, reproduced by reviewer on
bundled fixtures:mirai_x64with a planted pclntab header or/rustc/path keepsurl=andCookie:(3 refs
longer; in Rust mode 13 strings shortened against unplanted, was 16);rust_pe_gnuand the Go fixture are
unchanged. Chains of suffixes ("\r\n\r\n","\r\n","\n") are still cut. (#402) - (ida) The IDAPython backend no longer fails the whole export when IDA passes no name for an import by ordinal.
_cbEnumImportsconcatenated the missing name and raisedTypeError; it now writesmodule!#<ordinal>, as the
ida_domainbackend already does. A stubbedenum_import_namestest reproduces it. IDA 9.4 names such an import
itself (mcritord_23), so it is not known which IDA versions reach the error. No recovery output changes. (#400)
Compatibility
- (intel) x86 function boundaries move where an early-return body was analyzed before its entry: the body now joins
the entry instead of standing as a function of its own. (#388, #389) - (utility) String output changes on every binary extracted in Go or Rust mode: strings stop at their real
length instead of running into the next literal, some are added or dropped, and UTF-8 ones carry the new
utf8type. Nothing in mcrit, mcrit-plugin or capa readstypeordata_addr. (#382)
What's Changed
Changes
- fix(loaders): take the ELF base from PT_LOAD when the sections miss the header by @r0ny123 in #394
- fix(loaders): read a mapped ELF buffer's dynamic symbols through its … by @r0ny123 in #395
- perf(core): keep large images' per-instruction state compact by @r0ny123 in #396
- fix(intel): keep an early-return body in its function when it is analyzed first by @r0ny123 in #389
- feat(utility): read Go and Rust strings at their real length by @r0ny123 in #382
- ci: move the dangerous-triggers ignore onto the trigger line by @danielplohmann in #399
- fix(ida): name an import by ordinal when IDAPython passes no name by @danielplohmann in #400
- ci: allow the arm title scope by @r0ny123 in #401
- fix(utility): keep tail-merged C strings in Go and Rust mode by @r0ny123 in #402
- build: release 4.11.0 by @danielplohmann in #403
Dependency and tooling updates
- build(deps-dev): bump ruff from 0.16.9 to 0.16.10 in the dev-dependencies group by @dependabot[bot] in #393
- build(deps): bump https://github.com/astral-sh/ruff-pre-commit from v0.16.9 to 0.16.10 in the hooks group by @dependabot[bot] in #392
- build(deps): bump the actions group with 2 updates by @dependabot[bot] in #391
Full Changelog: v4.10.0...v4.11.0