"Real hackers were so multi-lingual they often knew more platforms than they had pants."
-- Dave Aitel
Marco Ivaldi (raptor) is a seasoned security researcher and tech leader with over 25 years of experience in offensive security. He is the technical director and co-founder of HN Security, a boutique firm specializing in tailored offensive security services. A prolific exploit writer, Marco is also a Phrack author and a core developer of the OSSTMM, the open methodology for security testing. He has been recognized as one of Microsoft's Most Valuable Security Researchers and was selected to compete in its Zero Day Quest. He is a member of the Hex-Rays Contributor Program. His career began in the '90s, when he co-founded Linux&C, the first Italian magazine dedicated to Linux and open source.
In a hurry? I got you. Here's what's new in my vulnerability divination 🔮 suite:
As usual, you can get the latest releases from Hex-Rays community plugins, crates.io, or GitHub.
First, a quick recap of my previous work, for those who missed the earlier episodes.
I've been doing binary vulnerability research against closed-source targets since the '90s, both professionally and as a hobby. Over the past couple of years, I've built a pipeline around IDA Pro's GUI-free automation capabilities to audit binaries at scale, across architectures. I described its building blocks in my previous article on this topic.
The centerpiece is haruspex, an idalib-rs based headless plugin that extracts IDA's decompiler pseudocode into per-function flat files. It's fast enough to process a 19 MB MIPS binary encompassing 7436 functions in less than 20 seconds (including the initial analysis stage) on my MacBook Air M3. Not bad!
That output feeds a layered static analysis stack. My custom Semgrep C/C++ ruleset and weggli patterns treat the pseudocode as source code, flagging candidate vulnerability hotspots automatically. Interesting hits then go to LLM agents for deeper triage, before I move to manual static and dynamic confirmation. Its siblings rhabdomancer and augur complete my vulnerability divination suite, by marking calls to potentially insecure API functions in the IDB and extracting strings and related pseudocode as needed, respectively.
Why IDA? Because of decompiler quality, performance, and integration capabilities, the latter courtesy of the award-winning idalib Rust bindings. Across x86, ARM, MIPS, and other architectures, the output is clean enough for pattern-matching tools to work on it reliably. Just last July, I batch-decompiled 280 binaries in less than 14 minutes, and Semgrep parsed more than 98% of the resulting pseudocode lines. Meanwhile, the idalib headless API lets Rust drive the whole analysis without a GUI in the loop.
This pipeline finds real bugs, such as the Zyxel local privilege escalation I covered last year. And I've spoken with other researchers who use a similar setup to hunt for vulnerabilities at scale, so I guess I'm not the only haruspex in town. 🐑
Last May, I joined the Hex-Rays Contributor Program. In a nutshell, the program aims to recognize and support people who design, build, and maintain IDA plugins, applications, and content, idalib-based tools explicitly included. Contributors may get a free IDA license that includes all decompilers and dedicated support from the Hex-Rays team.
For me, this means that I can keep developing my tools against the latest IDA releases on macOS, Windows, and Linux, without worrying about how to keep my license current. In return, contributors must keep their projects compatible with the latest IDA SDK within four months of each release. They must also maintain good installation, quality, and documentation standards, support their users, and provide product feedback to Hex-Rays. Sounds like a fair deal to me, especially since I was already doing most of these things anyway. 🤷
The first tangible result of this collaboration is a major release of my cross-platform vulnerability divination suite, which is now fully compatible with IDA 9.5. If you're building something cool on top of IDA, I encourage you to apply as well!
As customary, while working on my tools I kept contributing new features to the idalib Rust bindings, which in the meantime moved to their own home under the idalib-rs GitHub organization. My most relevant contributions since my last article are listed below:
.h files generated by haruspex and augur. It allows idalib-rs based tools to dump the type definitions that IDA has recovered or that you've applied to the IDB, both globally and for the types used by a specific decompiled function. Ever tried to make sense of a pile of pseudocode full of *(_DWORD *)(a1 + 0x48) without the corresponding struct definition? Well, me too 😵💫You can find the full history in the official changelog of the project. As always, I encourage you to contribute to idalib-rs!
A sneak peek of rhabdomancer doing its magic on Linux/AArch64
A lot has happened since my last article on IDA 9.2. Back then, my tools were at v0.7.x. One year, a few hundred commits, and more than a dozen releases later, rhabdomancer, haruspex, and augur have all reached v1. Yes, I'm finally confident enough to call them stable. 🎉
The full details are in each project's changelog (rhabdomancer, haruspex, augur). Here are the highlights.
All three tools now officially support IDA 9.5, via the v0.11.x releases of the idalib Rust bindings. Along the way, they also tracked IDA 9.3, 9.3sp1, 9.3sp2, 9.4, and 9.4sp1, with a compatibility matrix in each README.md that maps every IDA build to the latest compatible release of each tool.
IDA 9.4 brought a couple of decompiler changes that directly affected my pipeline:
IDA 9.5 kept me busy as well. It no longer garbles names by default, so names that aren't valid C identifiers are kept verbatim, and the decompiler wraps them in backticks. That's more faithful to the binary, but once again it breaks Semgrep and weggli. I dealt with it by restoring IDA 9.4's behavior in haruspex and augur, via a new IDA configuration API I contributed to idalib-rs. In rhabdomancer, I handled the breaking change by improving the function name matching logic.
.h header files alongside the pseudocode. This means that structs, enums, and typedefs recovered by IDA are available to downstream tools and LLM agents, which makes their analysis more accurate.decompile function tells apart functions that can't be decompiled from errors that affect all functions, decompile_to_file now returns a DumpedFunction with the paths of the files it wrote, and DumpedFunction::copy_to copies the output elsewhere without decompiling again. augur is built entirely on top of this API..plt stubs are traced back to their callers, and calls in library code recognized by IDA (e.g., via FLIRT signatures) are labeled (lib). Function names are now matched regardless of platform decorations (e.g., the ARM64EC #memcpy kept by IDA 9.5, Apple's stat$INODE64, or 32-bit _WinExec@8), glibc aliases (e.g., __libc_system, __isoc23_sscanf), Universal CRT wrappers (e.g., __o_malloc), and IDA's numeric suffixes (e.g., memset_0).wordexp, chroot, syscall, ioctl, the *spawn*, CreateProcess, and LoadLibrary families, and many more. The default configuration is now embedded in the binary, and you can point the RHABDOMANCER_CONFIG environment variable to your own customized copy..plt segment address ranges to avoid repeated FFI round trips, and fetches each function's name only once.stdcall names). Turns out these were hiding in plain sight 🙈.plt references in crafted or unusual binaries.Under the hood, I also enabled clippy restriction lints, added semver-checks to CI to enforce semantic versioning, pinned GitHub Actions to hashes, and wrote (with Claude's help) a lot of new unit, integration, and regression tests, with dedicated test binaries for edge cases such as binaries without functions, binaries without a decompiler, and functions too big to decompile.
Talk is cheap, so I've put my claims to the test. I ran the old v0.9.3 releases on IDA 9.3 and the new v1.2.0 releases on IDA 9.5 against the same set of real-world targets, on the same MacBook Air M3. Times include IDA's initial auto-analysis, and each run started from a clean state, without any pre-existing IDB.
| Tool | Target | Arch | Size | v0.9.3 on IDA 9.3 | v1.2.0 on IDA 9.5 | Speedup |
|---|---|---|---|---|---|---|
| rhabdomancer | 280 ELF binaries (batch) | AArch64 | 14 MB | 356 secs | 342 secs | +4% |
| haruspex | 280 ELF binaries (batch) | AArch64 | 14 MB | 827 secs | 744 secs | +11% |
| augur | 36 ELF binaries (batch) | AArch64 | 2.2 MB | 39.3 mins | 124 secs | +1802% |
The last figure in the table is especially remarkable: a staggering +1802% speedup means that augur is now 19x faster, thanks to single-pass decompilation.
Finally, Semgrep 1.183.0 parses about 99.1% of lines in the pseudocode generated by haruspex v1.2.0, which marks a slight increase compared to the 98.3% measured with the older haruspex/IDA version.
A word of caution: these numbers measure the combined effect of my code changes and of the IDA upgrade, so they don't tell you which side deserves the credit. Also note that augur now decompiles every function upfront to recover more strings, which costs some time on binaries with few string references, but pays off handsomely on large ones. Your mileage may vary, so please run your own benchmarks and let me know how they fare!
The v1 milestone is by no means the end of the road. My TODO list is as long as ever, and the most ambitious goals on my roadmap are tighter LLM integration, to make the triage of static analysis findings even smoother, and a LibAFL fuzzing stage downstream of static analysis, to confirm candidate vulnerabilities automatically.
Meanwhile, you can download my updated tools from Hex-Rays community plugins, crates.io, or GitHub. Take them for a spin against your favorite targets, open issues, and submit pull requests!
I'd like to thank Hex-Rays for welcoming me into their Contributor Program and for inviting me to write on their blog once again. It's been an honor.
As always, huge thanks to idalib-rs's maintainers, and especially to Sam L. Thomas (@xorpse), for reviewing my pull requests and for keeping this awesome project alive and kicking. ✊