maximilianfeix/hexvero

The first look at a binary or app bundle: static, execution-free analysis with evidence. CLI, 15-tool MCP server and local web UI, Node only.

TypeScript

4

53 commits

updated Oct 15, 2026

See the code

See what people are saying

SourceMessageScoreDate

I built Hexvero: inspect Electron apps and native binaries from your AI coding assistant (r/coolgithubprojects)

Hey everyone — I’m building [Hexvero](https://github.com/maximilianfeix/hexvero), an open-source reverse-engineering toolkit that connects to AI assistants through MCP. The idea is to make it easier to explore what’s inside an app: inspect an Electron archive, find IPC handlers and channels, check…

1

Oct 14, 2026

I built a binary-analysis MCP server whose whole tool catalog is 3k tokens. The established one in the space is ~510k (r/mcp)

I wanted my coding agent to triage binaries and app bundles (ELF, PE, Mach-O, APK, Electron, .NET, Wasm…). The established MCP server for this, REA, is a much bigger and deeper project, but its `tools/list` alone is 1.95 MB, about 510k tokens in `o200k_base`. That’s before a single file is opened.…

4

Oct 14, 2026

README


The first look at a binary or app bundle: static facts with the evidence next to them, from Node alone. hexvero reads executables, app bundles, archives, databases and captures on your machine without executing them and tells you what it found, where it read it, how sure it is, and what it could not establish. One CLI, one 15-tool MCP server for coding agents, one local web UI. Engines are optional.

hexvero analyzing an Electron app in the terminal: findings with file and line, the IPC call sites of one channel, and the settings that changed between two builds

npx hexvero analyze ./app.asar          # what is it, what does it expose, where is the evidence
npx hexvero ui --open                   # the same in a local browser UI with hex view and module graph
npx hexvero setup --apply               # register the MCP server with the agent clients on this machine

Three things you can do in a minute

The inputs ship in examples/ and are generated by scripts/build-examples.mjs: a small Electron note-taking app and a small C program, each in two versions, written for these demos (MIT). The outputs below are real runs, trimmed only where marked; npm run e2e asserts them on every change. The recording above is produced by docs/demo/demo.tape.

1. Map an Electron app's attack surface, with the line each fact came from

$ hexvero analyze examples/notesdesk-1.4.0.asar
examples/notesdesk-1.4.0.asar
  asar · 6.1 KiB (6222 bytes) · sha256 875c2bb7fb7eebcd… · 11 ms

Summary
  Electron ASAR: 4 JavaScript modules, 6 IPC channels

Findings
  ○ nodeIntegration: true in a BrowserWindow  @ main.js:14
      Renderer code can require Node modules; XSS becomes RCE.
  ○ sandbox: false in a BrowserWindow  @ main.js:15
      The renderer process runs without the Chromium sandbox.
  ● 6 IPC channels  @ main.js:25
      app:quit, notes:list, notes:save, settings:get, settings:set, shell:open (collections ipcChannels, ipc)
  ● 6 members exposed to pages as window.notesdesk  @ preload.js:4
      getSetting, setSetting, listNotes, saveNote, quit, openInShell (collection contextBridge)
  ○ 1 preload script  @ main.js:12
      preload.js
  … (list of collections)

$ hexvero collection examples/notesdesk-1.4.0.asar ipc --text shell:open
ipc 1-2 of 2
  module      api               channel     line
  ----------  ----------------  ----------  ----
  main.js     ipcMain.on        shell:open  30
  preload.js  ipcRenderer.send  shell:open  9

A filled dot is a fact read from the file; a hollow dot is an inference from a pattern. Settings are reported as written in the source (webPreferences collection), never as assumed Electron defaults.

2. See what a native binary is, and who calls what

$ cc -O0 -o demo examples/native/demo-v2.c
$ hexvero collection demo imports
imports 1-5 of 5
  name
  -------
  _getenv
  _printf
  _puts
  _strlen
  _system

$ hexvero calls demo radare2 main --goal system
radare2 path main → sym.imp.system · found
main
  └ sym._run_tool
    └ sym.imp.system

  ! radare2 lists call instructions only: a function that ends in a jump to another function (a tail call, common in optimized builds) has no edge here, so a path may exist that is not shown. (callgraph_tail_calls_unknown)

Imports, libraries, symbols, sections and hardening facts (NX, PIE, RELRO, canaries, FORTIFY; ASLR, CFG, GS, Authenticode; code signature) come from hexvero's own parsers. Call graphs and cross-references come from radare2, rizin or Ghidra when one is installed. "Not reachable" is stated as definite only when the graph is complete: rizin's graph includes tail calls, radare2's does not and says so.

3. Explain what changed between two builds

$ hexvero compare examples/notesdesk-1.4.0.asar examples/notesdesk-1.5.0.asar ipcChannels
ipcChannels: 1 added, 1 removed, 0 changed, 5 unchanged · matched by channel
  + update:check
  - shell:open

$ hexvero compare examples/notesdesk-1.4.0.asar examples/notesdesk-1.5.0.asar webPreferences
webPreferences: 0 added, 0 removed, 2 changed, 2 unchanged · matched by file+key
  ~ main.js::nodeIntegration  value: true → false
  ~ main.js::sandbox  value: false → true

$ hexvero compare demo-v1 demo-v2 symbols
symbols: 3 added, 0 removed, 0 changed, 6 unchanged, 2 relocated · matched by name
  + _read_config
  + _getenv
  + _strlen
  2 item(s) only moved (value changed, e.g. _main value "0x100000588" → "0x1000005fc"); --strict counts them as changed

Nothing is ignored by default. The one built-in rule: in native binaries a symbol or section whose only difference is its address is counted as relocated and shown with an example; a value that changes from old to new is always a change. The output says when a comparison is partial.

Why hexvero

  • Zero-dependency parsers, any OS. Every format listed below is parsed by code in this repository. No JDK, no Python, no engine, and no file is ever executed.
  • Bounded by design. Every list is a page with a hard byte budget (maxBytes, default 256 KiB, measured on the real serialized size) and a next offset. A 541 MiB Electron app answers analyze over MCP in a 15 KB result (benchmarks). The complete result goes to a file with one command, never into a context window.
  • Evidence and honesty. Findings carry confirmed or inferred and, where a parser records it, the file and line; limitations say what was skipped, truncated or unreadable and why; an unknowns ledger keeps open questions.
  • 15 tools, 12.8 KB catalog. The MCP server reads like a workflow: open, analyze, page, read bytes, compare, recover, export.
  • A local UI. Verdict, findings, virtualized tables, a hex viewer with live decoding, a module graph for Electron apps. Keyboard-usable, loopback only.
  • Engines when you want them. radare2, rizin and Ghidra plug in behind one interface for disassembly, decompilation, cross-references and cached call graphs.

Install

Requires Node.js 22.12 or newer (24 recommended for SQLite support without warnings).

npm install -g hexvero        # the hexvero command
hexvero doctor                # checks Node, node:sqlite and optional engines

Or run without installing: npx hexvero <command>.

What it reads

Everything in this table works with Node alone. Engines add disassembly and decompilation on top.

TargetBuilt-in result
ELF (32/64, LE/BE)Sections, segments, symbols, dynamic imports/exports, needed libraries, RPATH, build-id, NX/PIE/RELRO/canary/FORTIFY
PE / PE32+Sections with entropy, imports (names and ordinals), exports and forwarders, PDB path, VERSIONINFO, ASLR/NX/CFG/GS/Authenticode
Mach-O (thin and fat)Load commands, segments, symbols, dylibs, UUID, platform/SDK, encryption, code signature, PIE/ARC/canary
Apple .app bundlesInfo.plist facts, executable, frameworks, entitlements, Objective-C classes/protocols, readable Swift names
Go binariesCompiler version, main module, dependencies with replacements, build settings, VCS revision
.NET assembliesAssembly and references, types with methods, P/Invoke, user strings, CIL method bodies with call targets and digests
Rust / C / C++ toolchainCompiler and rustc version strings, DWARF presence, build paths (confirmed only when a version string is present)
WebAssemblyTypes, imports/exports with correct index spaces, memories, tables, producers, WASI/Emscripten/wasm-bindgen
EVM bytecodePUSH-aware disassembly, dispatch selectors with jump targets, solc metadata, DELEGATECALL/SELFDESTRUCT
SQLite databasesTables, columns, row counts, sample rows, views, indexes, triggers (read-only via node:sqlite)
plist (XML and binary)Fully decoded property list
Android APKManifest, permissions, components with intent filters, deep links, native libs, DEX classes, resources.arsc, frameworks
Java class / JAR / DEXConstant pool, members, referenced APIs, manifest and packages; DEX classes, strings and SDK hints
Flutter appsEngine and Dart snapshot facts, library digests
Electron ASAR / JS appsPackage facts, module roles, resolved import graph, IPC channel sender/receiver matching, preload and webPreferences flags
JavaScript bundleswebpack / Rspack / browserify / esbuild module boundaries, requires, exports, endpoints, storage, vendor fingerprints
Source mapsSources grouped by package, mapping count, original position lookup, originals written back to disk
ZIP, HARZIP/ZIP64 entries; HAR hosts, methods, status buckets, API-like endpoints, secrets in URLs, auth headers, WebSockets
AnythingStrings (ASCII and UTF-16) with tags, bounded byte windows, entropy

CLI

hexvero analyze <path> [--json] [--out file.json]     summary, findings, limitations, overview
hexvero collection <path> <name> [--text s] [--limit n] [--offset n] [--max-bytes n]
hexvero strings <path> [--min n] [--utf16] [--tag url|path|email|ip|format|secret-ish|base64-ish]
hexvero bytes <path> <offset> [length]                hex dump (64 KiB per call)
hexvero compare <a> <b> <collection> [--key f[+g]] [--ignore f,g] [--strict]
                                                      added, removed, changed and relocated items between two builds
hexvero export <path> <out.json>                      the complete analysis
hexvero recover <bundle.js|map> <outdir>              split one JS bundle into module files, or write a source map's originals
hexvero provider <path> radare2|rizin|ghidra <op> [--address a] [--text s]
                                                      functions, disassemble, decompile, strings, imports,
                                                      xrefs, callers, callees, callgraph, search
hexvero calls <path> <engine> <root> [--direction callers|callees|both] [--depth n] [--goal g]
hexvero mcp · hexvero ui [--port n] [--open] · hexvero doctor · hexvero setup [--client c] [--apply] [--all]

Analysis commands take --json. Exit codes: 0 ok (including --help), 1 analysis failed (with a structured error), 2 usage. See docs/cli.md.

Web UI

hexvero ui --open

hexvero UI: the overview of an Electron app, each finding with its confidence and source line

hexvero UI: findings with their source lines, and the hex viewer decoding a selected byte

Starts a loopback server with a per-session token and opens the browser. Drop a file anywhere, or type a path on your machine. Each target gets a verdict line, a fact sheet, findings with confidence, the limitations, and one tab per collection that actually exists. The hex viewer loads windows on demand, decodes the selected bytes as u8…u64/f32/f64 in both byte orders and follows the arrow keys. Electron apps get a layered module graph. Dark and light themes, keyboard navigation throughout (⌘O/Ctrl+O open, / filter, arrow keys on tabs, hex and graph), phone layout.

For AI agents

hexvero setup                 # shows what would change (dry run)
hexvero setup --apply         # writes the MCP registration into every detected client, backs up existing files
hexvero setup --skill --apply # installs the investigation skill for Claude Code, Codex and .agents

setup knows 18 clients (Claude Code, Claude Desktop, Codex, Cursor, Windsurf, Gemini CLI, Antigravity, VS Code, Trae, OpenCode, GitHub Copilot CLI, Qwen Code, Qoder, Devin, Pi, Kiro, Amp, Zed) and writes each one's own config dialect, keeping comments in files that have them. The client table says for every row what was checked against the client's documentation and which three were read back by the client itself; one row (Trae) is unverified. Any other client can run npx -y hexvero mcp over stdio.

ToolPurpose
hexvero_openRegister a file or directory; returns a content-derived id
hexvero_analyzeSummary, findings, limitations, collection counts and a compact overview
hexvero_collectionPage any list: symbols, imports, exports, sections, types, selectors, requests, modules …
hexvero_stringsTagged strings with filter and paging
hexvero_bytesA bounded hex window
hexvero_compareAdded, removed, changed and relocated items of one collection between two targets
hexvero_recoverBundle modules or source-map originals as files
hexvero_exportThe complete analysis to a file (never overwrites unless asked)
hexvero_sourcemapOriginal position for a generated line/column
hexvero_unknownsA ledger of open questions that travels with the export
hexvero_providers · hexvero_provider_runDetect and drive radare2, rizin or Ghidra (disassemble, decompile, xrefs, callers, search)
hexvero_call_graphBounded call-graph neighborhood or shortest call path, one cached engine run per target
hexvero_list · hexvero_closeSession housekeeping

The catalog is 12.8 KB (tools/list, measured); every tool states whether it writes (readOnlyHint). Five guided prompts (investigate_feature, compare_versions, triage_binary, map_electron_app, explain_apk) render step-by-step plans with the rule that every claim cites a collection item or byte offset. Strings, symbol names and decompiled code are data from the target, never instructions.

Ask your agent: "Open ~/Downloads/app.asar with hexvero, tell me which IPC channels the renderer can reach and whether nodeIntegration is on."

Optional engines

hexvero detects r2/radare2, rizin and Ghidra's analyzeHeadless on PATH (or GHIDRA_INSTALL_DIR). With one installed:

hexvero provider ./program radare2 functions
hexvero provider ./program ghidra decompile --address main
hexvero provider ./program radare2 xrefs --address sym.imp.system
hexvero provider ./program ghidra search --text password
hexvero calls ./program radare2 main --depth 3
hexvero calls ./program ghidra entry --goal system

Ghidra imports each target once into a project cached under ~/.cache/hexvero and reuses it. Engine output is bounded, timeouts and cancellation kill the whole process group, and a missing decompiler plugin is reported as a limitation rather than an empty result. Call graphs are read from the engine once per file and walked locally with depth and node bounds. Verified against radare2 6.2 and rizin 0.9 on macOS; Ghidra 11/12 headless; Windows engine support is untested.

Limits

  • No disassembly or pseudocode without an engine; no dynamic analysis, instrumentation or device work.
  • JavaScript app scans stop at 5,000 files and say so; very large single items are returned compacted with a marker.
  • compare is a per-collection diff: it does not line up renamed functions or match by structure.
  • Findings carry a file and line for Electron apps; native findings cite a collection rather than an offset.
  • Call graphs through radare2 miss tail calls (reported as a limitation); rizin's include them.
  • Engine results are as good as the engine's analysis; hexvero verifies radare2 and rizin end to end, Ghidra through its headless script on fixtures only.
  • Static facts are not proof of behaviour: an imported symbol or a matched string says what a file contains, not what it does at runtime.
  • Files above 512 MiB are read lazily only for ASAR; other formats are refused with too_large.

Compared with REA

REA is the larger and older project in this space. hexvero is an independent implementation (no REA code) for the first look. The numbers below were measured on 2026-10-13 with REA 6.4.0 (rea-agents from npm) and a snapshot of hexvero's main, on the same machine with the same inputs and Node 24.21. Where hexvero has changed since the measurement, the table says so. Wall times were taken on a loaded machine and are indicative. Method, conditions and commands are in docs/benchmarks.md; the scripts are in docs/rea-compare.

REA 6.4.0hexvero
MCP catalog (tools/list result)170 tools, 1.95 MB (1.77 MB with its compact profile)15 tools, 12.8 KB
Electron example: IPC and window settingsall correct; 1 call, 1.3 MB of JSON to filterall correct; 1 call, 7.9 KB when measured (9.4 KB now, with bridge members)
Same example: what else was foundall 6 bridge members, handler source ranges, storage keys, evidence ids; 2 endpoints, one of them false and one missedfile and line per finding; bridge members were missing when measured and are listed now
Diff of the two example buildschannel changes, the bridge change, a new endpoint and storage key; the settings flip appears as a window removed and added; 3 calls, 450 KB, 78 itemschannel and setting changes exact in 2 calls, 1.2 KB; the bridge swap was missed when measured and is exact now with a third call
Native first look without an engineimports, libraries and signature detail through macOS system tools, with the commands recordedimports, libraries and decoded hardening flags from its own parsers
Call graphs and cross-referencesGhidra, Hopper or IDA (not run here)radare2, rizin or Ghidra
Large app (568 MB Electron archive)15,211 JavaScript files parsed, coverage marked partial; 821 s in one run, 1.0 GiB, 554 MB of JSON; needs --integrity-policy record-and-continue5,000 of 14,816 JavaScript files scanned, stated as a limitation; 14 s, 0.6 GiB, 25 KB
Source recovery from a bundledelegates to a Wakaru binary you supply, Linux x64 only (not run here)splits into module files on any OS; does not rewrite
Dynamic analysis, devices, firmwareyes, per its documentation (not run here)not in scope
Local UInot comparedweb UI with hex viewer and module graph

What this does and does not show:

  • The catalog size is the difference that does not depend on the machine or the input: about 150 times smaller, about 36 times if REA's output schemas are left out. In o200k_base tokens: 3,043 against 509,696.
  • REA is deeper. It parses large apps completely, keeps evidence ids and source ranges for every fact, pairs renderer calls with handlers, reports endpoints and storage for Electron archives, and covers dynamic analysis that hexvero does not attempt.
  • The timing on the large archive is not like for like: hexvero stops at 5,000 JavaScript files and builds no semantic graph. By default REA stops on that archive with a hash mismatch; the run above used the policy its error message names.
  • This comparison found gaps in hexvero that are closed on main: bridge members, URLs inside longer strings, and a call path hidden by a tail call. Through radare2 that path is still not found, but the answer now carries a limitation instead of a definite "not reached".
  • Earlier versions of this page said REA had 139 tools, aborted on 500 MB bundles and needed an engine for most targets. The first is out of date. The second misread an issue about a 15 MB bundle on REA 6.3.0 whose report was 500 MB; it is fixed in 6.4.0, which completed the 568 MB archive here. The third was wrong: JavaScript and Electron need only Node (run here), .NET and Go metadata need nothing more according to REA's documentation, and Mach-O headers, imports and signatures use macOS system tools, no engine.

Use REA when you need Hopper, IDA, JEB, Android devices over adb, browser capture, saved network captures, firmware, recorded Linux crashes, or complete graphs of very large apps. Use hexvero for the triage before that, for agents with small context windows, for radare2 or rizin, and for a UI.

How it works

packages/core     parsers (formats/*), JavaScript scanner, Session (targets, collections, paging, export), engine adapters
packages/mcp      McpServer with 15 tools over one Session
packages/server   loopback HTTP API + static hosting for the UI
packages/cli      hexvero command; ships the built UI and the agent skill
packages/web      React + Vite UI

Every parser returns facts plus findings and limitations. The Session flattens an analysis into named collections and pages them; CLI, MCP and HTTP are thin adapters over the same calls. Analyses run in a worker thread with a timeout and a heap cap. Tests build their fixtures byte by byte (packages/core/test/fixtures), so the suite runs anywhere in seconds. See docs/ARCHITECTURE-PLAN.md.

Contributing

npm install && npm run check      # typecheck, lint, format, unit tests, shape rules
npm run e2e                       # the four user flows on real inputs (compiler and engines if installed)
node packages/cli/bin/hexvero.js analyze /bin/ls

Adding a format is one parser in packages/core/src/formats/ plus one byte-built fixture test. Findings need a confidence, anything skipped becomes a limitation, every list is paged. CONTRIBUTING.md has the rules the review workflow enforces; issues labelled good first issue are sized for an afternoon. Found a wrong or missing fact? Open a bug report with the hexvero doctor output and, if you can share it, the file.

If hexvero told you something useful about a file, a star helps other people find it.

FAQ

Does hexvero run or upload my files?

No. Targets are parsed as bytes; nothing is executed. The UI server binds to 127.0.0.1 with a per-session token and rejects other hosts. There is no telemetry and no network call anywhere in the code base. When you use hexvero through a cloud-hosted agent, the results that agent requests are sent to its model provider like any other tool output; keep that in mind for files you would not share.

Is this a decompiler?

Not by itself. Built-in analysis gives you structure, symbols, strings, imports, metadata and hardening facts. For disassembly and pseudocode, install radare2, rizin or Ghidra and use hexvero provider or hexvero calls.

Is "static" the same as "safe"?

Parsing a hostile file is still processing attacker-controlled bytes. hexvero bounds every parser (sizes, counts, depths, worker timeout and heap cap) and its tests include truncated and oversized inputs, but no parser is proven safe against every crafted file. Treat results as evidence, not as a verdict.

What about Windows?

Parsers, the CLI, the MCP server and the UI run on Windows (CI runs the test suite on Windows and macOS). Engine process handling is tested on POSIX only.

Legal

Use hexvero only on software you own, that is open source, or that you are permitted to examine. See DISCLAIMER.md.

License

MIT

ai-agents
apk
binary-analysis
claude
cli
dotnet
electron
electron-security
elf
ghidra
mach-o
mcp
mcp-server
pe
radare2
reverse-engineering
security-tools
static-analysis
typescript
webassembly

maximilianfeix/hexvero

The first look at a binary or app bundle: static, execution-free analysis with evidence. CLI, 15-tool MCP server and local web UI, Node only.

TypeScript

4

53 commits

updated Oct 15, 2026

See the code

See what people are saying

SourceMessageScoreDate

I built Hexvero: inspect Electron apps and native binaries from your AI coding assistant (r/coolgithubprojects)

Hey everyone — I’m building [Hexvero](https://github.com/maximilianfeix/hexvero), an open-source reverse-engineering toolkit that connects to AI assistants through MCP. The idea is to make it easier to explore what’s inside an app: inspect an Electron archive, find IPC handlers and channels, check…

1

Oct 14, 2026

I built a binary-analysis MCP server whose whole tool catalog is 3k tokens. The established one in the space is ~510k (r/mcp)

I wanted my coding agent to triage binaries and app bundles (ELF, PE, Mach-O, APK, Electron, .NET, Wasm…). The established MCP server for this, REA, is a much bigger and deeper project, but its `tools/list` alone is 1.95 MB, about 510k tokens in `o200k_base`. That’s before a single file is opened.…

4

Oct 14, 2026

README


The first look at a binary or app bundle: static facts with the evidence next to them, from Node alone. hexvero reads executables, app bundles, archives, databases and captures on your machine without executing them and tells you what it found, where it read it, how sure it is, and what it could not establish. One CLI, one 15-tool MCP server for coding agents, one local web UI. Engines are optional.

hexvero analyzing an Electron app in the terminal: findings with file and line, the IPC call sites of one channel, and the settings that changed between two builds

npx hexvero analyze ./app.asar          # what is it, what does it expose, where is the evidence
npx hexvero ui --open                   # the same in a local browser UI with hex view and module graph
npx hexvero setup --apply               # register the MCP server with the agent clients on this machine

Three things you can do in a minute

The inputs ship in examples/ and are generated by scripts/build-examples.mjs: a small Electron note-taking app and a small C program, each in two versions, written for these demos (MIT). The outputs below are real runs, trimmed only where marked; npm run e2e asserts them on every change. The recording above is produced by docs/demo/demo.tape.

1. Map an Electron app's attack surface, with the line each fact came from

$ hexvero analyze examples/notesdesk-1.4.0.asar
examples/notesdesk-1.4.0.asar
  asar · 6.1 KiB (6222 bytes) · sha256 875c2bb7fb7eebcd… · 11 ms

Summary
  Electron ASAR: 4 JavaScript modules, 6 IPC channels

Findings
  ○ nodeIntegration: true in a BrowserWindow  @ main.js:14
      Renderer code can require Node modules; XSS becomes RCE.
  ○ sandbox: false in a BrowserWindow  @ main.js:15
      The renderer process runs without the Chromium sandbox.
  ● 6 IPC channels  @ main.js:25
      app:quit, notes:list, notes:save, settings:get, settings:set, shell:open (collections ipcChannels, ipc)
  ● 6 members exposed to pages as window.notesdesk  @ preload.js:4
      getSetting, setSetting, listNotes, saveNote, quit, openInShell (collection contextBridge)
  ○ 1 preload script  @ main.js:12
      preload.js
  … (list of collections)

$ hexvero collection examples/notesdesk-1.4.0.asar ipc --text shell:open
ipc 1-2 of 2
  module      api               channel     line
  ----------  ----------------  ----------  ----
  main.js     ipcMain.on        shell:open  30
  preload.js  ipcRenderer.send  shell:open  9

A filled dot is a fact read from the file; a hollow dot is an inference from a pattern. Settings are reported as written in the source (webPreferences collection), never as assumed Electron defaults.

2. See what a native binary is, and who calls what

$ cc -O0 -o demo examples/native/demo-v2.c
$ hexvero collection demo imports
imports 1-5 of 5
  name
  -------
  _getenv
  _printf
  _puts
  _strlen
  _system

$ hexvero calls demo radare2 main --goal system
radare2 path main → sym.imp.system · found
main
  └ sym._run_tool
    └ sym.imp.system

  ! radare2 lists call instructions only: a function that ends in a jump to another function (a tail call, common in optimized builds) has no edge here, so a path may exist that is not shown. (callgraph_tail_calls_unknown)

Imports, libraries, symbols, sections and hardening facts (NX, PIE, RELRO, canaries, FORTIFY; ASLR, CFG, GS, Authenticode; code signature) come from hexvero's own parsers. Call graphs and cross-references come from radare2, rizin or Ghidra when one is installed. "Not reachable" is stated as definite only when the graph is complete: rizin's graph includes tail calls, radare2's does not and says so.

3. Explain what changed between two builds

$ hexvero compare examples/notesdesk-1.4.0.asar examples/notesdesk-1.5.0.asar ipcChannels
ipcChannels: 1 added, 1 removed, 0 changed, 5 unchanged · matched by channel
  + update:check
  - shell:open

$ hexvero compare examples/notesdesk-1.4.0.asar examples/notesdesk-1.5.0.asar webPreferences
webPreferences: 0 added, 0 removed, 2 changed, 2 unchanged · matched by file+key
  ~ main.js::nodeIntegration  value: true → false
  ~ main.js::sandbox  value: false → true

$ hexvero compare demo-v1 demo-v2 symbols
symbols: 3 added, 0 removed, 0 changed, 6 unchanged, 2 relocated · matched by name
  + _read_config
  + _getenv
  + _strlen
  2 item(s) only moved (value changed, e.g. _main value "0x100000588" → "0x1000005fc"); --strict counts them as changed

Nothing is ignored by default. The one built-in rule: in native binaries a symbol or section whose only difference is its address is counted as relocated and shown with an example; a value that changes from old to new is always a change. The output says when a comparison is partial.

Why hexvero

  • Zero-dependency parsers, any OS. Every format listed below is parsed by code in this repository. No JDK, no Python, no engine, and no file is ever executed.
  • Bounded by design. Every list is a page with a hard byte budget (maxBytes, default 256 KiB, measured on the real serialized size) and a next offset. A 541 MiB Electron app answers analyze over MCP in a 15 KB result (benchmarks). The complete result goes to a file with one command, never into a context window.
  • Evidence and honesty. Findings carry confirmed or inferred and, where a parser records it, the file and line; limitations say what was skipped, truncated or unreadable and why; an unknowns ledger keeps open questions.
  • 15 tools, 12.8 KB catalog. The MCP server reads like a workflow: open, analyze, page, read bytes, compare, recover, export.
  • A local UI. Verdict, findings, virtualized tables, a hex viewer with live decoding, a module graph for Electron apps. Keyboard-usable, loopback only.
  • Engines when you want them. radare2, rizin and Ghidra plug in behind one interface for disassembly, decompilation, cross-references and cached call graphs.

Install

Requires Node.js 22.12 or newer (24 recommended for SQLite support without warnings).

npm install -g hexvero        # the hexvero command
hexvero doctor                # checks Node, node:sqlite and optional engines

Or run without installing: npx hexvero <command>.

What it reads

Everything in this table works with Node alone. Engines add disassembly and decompilation on top.

TargetBuilt-in result
ELF (32/64, LE/BE)Sections, segments, symbols, dynamic imports/exports, needed libraries, RPATH, build-id, NX/PIE/RELRO/canary/FORTIFY
PE / PE32+Sections with entropy, imports (names and ordinals), exports and forwarders, PDB path, VERSIONINFO, ASLR/NX/CFG/GS/Authenticode
Mach-O (thin and fat)Load commands, segments, symbols, dylibs, UUID, platform/SDK, encryption, code signature, PIE/ARC/canary
Apple .app bundlesInfo.plist facts, executable, frameworks, entitlements, Objective-C classes/protocols, readable Swift names
Go binariesCompiler version, main module, dependencies with replacements, build settings, VCS revision
.NET assembliesAssembly and references, types with methods, P/Invoke, user strings, CIL method bodies with call targets and digests
Rust / C / C++ toolchainCompiler and rustc version strings, DWARF presence, build paths (confirmed only when a version string is present)
WebAssemblyTypes, imports/exports with correct index spaces, memories, tables, producers, WASI/Emscripten/wasm-bindgen
EVM bytecodePUSH-aware disassembly, dispatch selectors with jump targets, solc metadata, DELEGATECALL/SELFDESTRUCT
SQLite databasesTables, columns, row counts, sample rows, views, indexes, triggers (read-only via node:sqlite)
plist (XML and binary)Fully decoded property list
Android APKManifest, permissions, components with intent filters, deep links, native libs, DEX classes, resources.arsc, frameworks
Java class / JAR / DEXConstant pool, members, referenced APIs, manifest and packages; DEX classes, strings and SDK hints
Flutter appsEngine and Dart snapshot facts, library digests
Electron ASAR / JS appsPackage facts, module roles, resolved import graph, IPC channel sender/receiver matching, preload and webPreferences flags
JavaScript bundleswebpack / Rspack / browserify / esbuild module boundaries, requires, exports, endpoints, storage, vendor fingerprints
Source mapsSources grouped by package, mapping count, original position lookup, originals written back to disk
ZIP, HARZIP/ZIP64 entries; HAR hosts, methods, status buckets, API-like endpoints, secrets in URLs, auth headers, WebSockets
AnythingStrings (ASCII and UTF-16) with tags, bounded byte windows, entropy

CLI

hexvero analyze <path> [--json] [--out file.json]     summary, findings, limitations, overview
hexvero collection <path> <name> [--text s] [--limit n] [--offset n] [--max-bytes n]
hexvero strings <path> [--min n] [--utf16] [--tag url|path|email|ip|format|secret-ish|base64-ish]
hexvero bytes <path> <offset> [length]                hex dump (64 KiB per call)
hexvero compare <a> <b> <collection> [--key f[+g]] [--ignore f,g] [--strict]
                                                      added, removed, changed and relocated items between two builds
hexvero export <path> <out.json>                      the complete analysis
hexvero recover <bundle.js|map> <outdir>              split one JS bundle into module files, or write a source map's originals
hexvero provider <path> radare2|rizin|ghidra <op> [--address a] [--text s]
                                                      functions, disassemble, decompile, strings, imports,
                                                      xrefs, callers, callees, callgraph, search
hexvero calls <path> <engine> <root> [--direction callers|callees|both] [--depth n] [--goal g]
hexvero mcp · hexvero ui [--port n] [--open] · hexvero doctor · hexvero setup [--client c] [--apply] [--all]

Analysis commands take --json. Exit codes: 0 ok (including --help), 1 analysis failed (with a structured error), 2 usage. See docs/cli.md.

Web UI

hexvero ui --open

hexvero UI: the overview of an Electron app, each finding with its confidence and source line

hexvero UI: findings with their source lines, and the hex viewer decoding a selected byte

Starts a loopback server with a per-session token and opens the browser. Drop a file anywhere, or type a path on your machine. Each target gets a verdict line, a fact sheet, findings with confidence, the limitations, and one tab per collection that actually exists. The hex viewer loads windows on demand, decodes the selected bytes as u8…u64/f32/f64 in both byte orders and follows the arrow keys. Electron apps get a layered module graph. Dark and light themes, keyboard navigation throughout (⌘O/Ctrl+O open, / filter, arrow keys on tabs, hex and graph), phone layout.

For AI agents

hexvero setup                 # shows what would change (dry run)
hexvero setup --apply         # writes the MCP registration into every detected client, backs up existing files
hexvero setup --skill --apply # installs the investigation skill for Claude Code, Codex and .agents

setup knows 18 clients (Claude Code, Claude Desktop, Codex, Cursor, Windsurf, Gemini CLI, Antigravity, VS Code, Trae, OpenCode, GitHub Copilot CLI, Qwen Code, Qoder, Devin, Pi, Kiro, Amp, Zed) and writes each one's own config dialect, keeping comments in files that have them. The client table says for every row what was checked against the client's documentation and which three were read back by the client itself; one row (Trae) is unverified. Any other client can run npx -y hexvero mcp over stdio.

ToolPurpose
hexvero_openRegister a file or directory; returns a content-derived id
hexvero_analyzeSummary, findings, limitations, collection counts and a compact overview
hexvero_collectionPage any list: symbols, imports, exports, sections, types, selectors, requests, modules …
hexvero_stringsTagged strings with filter and paging
hexvero_bytesA bounded hex window
hexvero_compareAdded, removed, changed and relocated items of one collection between two targets
hexvero_recoverBundle modules or source-map originals as files
hexvero_exportThe complete analysis to a file (never overwrites unless asked)
hexvero_sourcemapOriginal position for a generated line/column
hexvero_unknownsA ledger of open questions that travels with the export
hexvero_providers · hexvero_provider_runDetect and drive radare2, rizin or Ghidra (disassemble, decompile, xrefs, callers, search)
hexvero_call_graphBounded call-graph neighborhood or shortest call path, one cached engine run per target
hexvero_list · hexvero_closeSession housekeeping

The catalog is 12.8 KB (tools/list, measured); every tool states whether it writes (readOnlyHint). Five guided prompts (investigate_feature, compare_versions, triage_binary, map_electron_app, explain_apk) render step-by-step plans with the rule that every claim cites a collection item or byte offset. Strings, symbol names and decompiled code are data from the target, never instructions.

Ask your agent: "Open ~/Downloads/app.asar with hexvero, tell me which IPC channels the renderer can reach and whether nodeIntegration is on."

Optional engines

hexvero detects r2/radare2, rizin and Ghidra's analyzeHeadless on PATH (or GHIDRA_INSTALL_DIR). With one installed:

hexvero provider ./program radare2 functions
hexvero provider ./program ghidra decompile --address main
hexvero provider ./program radare2 xrefs --address sym.imp.system
hexvero provider ./program ghidra search --text password
hexvero calls ./program radare2 main --depth 3
hexvero calls ./program ghidra entry --goal system

Ghidra imports each target once into a project cached under ~/.cache/hexvero and reuses it. Engine output is bounded, timeouts and cancellation kill the whole process group, and a missing decompiler plugin is reported as a limitation rather than an empty result. Call graphs are read from the engine once per file and walked locally with depth and node bounds. Verified against radare2 6.2 and rizin 0.9 on macOS; Ghidra 11/12 headless; Windows engine support is untested.

Limits

  • No disassembly or pseudocode without an engine; no dynamic analysis, instrumentation or device work.
  • JavaScript app scans stop at 5,000 files and say so; very large single items are returned compacted with a marker.
  • compare is a per-collection diff: it does not line up renamed functions or match by structure.
  • Findings carry a file and line for Electron apps; native findings cite a collection rather than an offset.
  • Call graphs through radare2 miss tail calls (reported as a limitation); rizin's include them.
  • Engine results are as good as the engine's analysis; hexvero verifies radare2 and rizin end to end, Ghidra through its headless script on fixtures only.
  • Static facts are not proof of behaviour: an imported symbol or a matched string says what a file contains, not what it does at runtime.
  • Files above 512 MiB are read lazily only for ASAR; other formats are refused with too_large.

Compared with REA

REA is the larger and older project in this space. hexvero is an independent implementation (no REA code) for the first look. The numbers below were measured on 2026-10-13 with REA 6.4.0 (rea-agents from npm) and a snapshot of hexvero's main, on the same machine with the same inputs and Node 24.21. Where hexvero has changed since the measurement, the table says so. Wall times were taken on a loaded machine and are indicative. Method, conditions and commands are in docs/benchmarks.md; the scripts are in docs/rea-compare.

REA 6.4.0hexvero
MCP catalog (tools/list result)170 tools, 1.95 MB (1.77 MB with its compact profile)15 tools, 12.8 KB
Electron example: IPC and window settingsall correct; 1 call, 1.3 MB of JSON to filterall correct; 1 call, 7.9 KB when measured (9.4 KB now, with bridge members)
Same example: what else was foundall 6 bridge members, handler source ranges, storage keys, evidence ids; 2 endpoints, one of them false and one missedfile and line per finding; bridge members were missing when measured and are listed now
Diff of the two example buildschannel changes, the bridge change, a new endpoint and storage key; the settings flip appears as a window removed and added; 3 calls, 450 KB, 78 itemschannel and setting changes exact in 2 calls, 1.2 KB; the bridge swap was missed when measured and is exact now with a third call
Native first look without an engineimports, libraries and signature detail through macOS system tools, with the commands recordedimports, libraries and decoded hardening flags from its own parsers
Call graphs and cross-referencesGhidra, Hopper or IDA (not run here)radare2, rizin or Ghidra
Large app (568 MB Electron archive)15,211 JavaScript files parsed, coverage marked partial; 821 s in one run, 1.0 GiB, 554 MB of JSON; needs --integrity-policy record-and-continue5,000 of 14,816 JavaScript files scanned, stated as a limitation; 14 s, 0.6 GiB, 25 KB
Source recovery from a bundledelegates to a Wakaru binary you supply, Linux x64 only (not run here)splits into module files on any OS; does not rewrite
Dynamic analysis, devices, firmwareyes, per its documentation (not run here)not in scope
Local UInot comparedweb UI with hex viewer and module graph

What this does and does not show:

  • The catalog size is the difference that does not depend on the machine or the input: about 150 times smaller, about 36 times if REA's output schemas are left out. In o200k_base tokens: 3,043 against 509,696.
  • REA is deeper. It parses large apps completely, keeps evidence ids and source ranges for every fact, pairs renderer calls with handlers, reports endpoints and storage for Electron archives, and covers dynamic analysis that hexvero does not attempt.
  • The timing on the large archive is not like for like: hexvero stops at 5,000 JavaScript files and builds no semantic graph. By default REA stops on that archive with a hash mismatch; the run above used the policy its error message names.
  • This comparison found gaps in hexvero that are closed on main: bridge members, URLs inside longer strings, and a call path hidden by a tail call. Through radare2 that path is still not found, but the answer now carries a limitation instead of a definite "not reached".
  • Earlier versions of this page said REA had 139 tools, aborted on 500 MB bundles and needed an engine for most targets. The first is out of date. The second misread an issue about a 15 MB bundle on REA 6.3.0 whose report was 500 MB; it is fixed in 6.4.0, which completed the 568 MB archive here. The third was wrong: JavaScript and Electron need only Node (run here), .NET and Go metadata need nothing more according to REA's documentation, and Mach-O headers, imports and signatures use macOS system tools, no engine.

Use REA when you need Hopper, IDA, JEB, Android devices over adb, browser capture, saved network captures, firmware, recorded Linux crashes, or complete graphs of very large apps. Use hexvero for the triage before that, for agents with small context windows, for radare2 or rizin, and for a UI.

How it works

packages/core     parsers (formats/*), JavaScript scanner, Session (targets, collections, paging, export), engine adapters
packages/mcp      McpServer with 15 tools over one Session
packages/server   loopback HTTP API + static hosting for the UI
packages/cli      hexvero command; ships the built UI and the agent skill
packages/web      React + Vite UI

Every parser returns facts plus findings and limitations. The Session flattens an analysis into named collections and pages them; CLI, MCP and HTTP are thin adapters over the same calls. Analyses run in a worker thread with a timeout and a heap cap. Tests build their fixtures byte by byte (packages/core/test/fixtures), so the suite runs anywhere in seconds. See docs/ARCHITECTURE-PLAN.md.

Contributing

npm install && npm run check      # typecheck, lint, format, unit tests, shape rules
npm run e2e                       # the four user flows on real inputs (compiler and engines if installed)
node packages/cli/bin/hexvero.js analyze /bin/ls

Adding a format is one parser in packages/core/src/formats/ plus one byte-built fixture test. Findings need a confidence, anything skipped becomes a limitation, every list is paged. CONTRIBUTING.md has the rules the review workflow enforces; issues labelled good first issue are sized for an afternoon. Found a wrong or missing fact? Open a bug report with the hexvero doctor output and, if you can share it, the file.

If hexvero told you something useful about a file, a star helps other people find it.

FAQ

Does hexvero run or upload my files?

No. Targets are parsed as bytes; nothing is executed. The UI server binds to 127.0.0.1 with a per-session token and rejects other hosts. There is no telemetry and no network call anywhere in the code base. When you use hexvero through a cloud-hosted agent, the results that agent requests are sent to its model provider like any other tool output; keep that in mind for files you would not share.

Is this a decompiler?

Not by itself. Built-in analysis gives you structure, symbols, strings, imports, metadata and hardening facts. For disassembly and pseudocode, install radare2, rizin or Ghidra and use hexvero provider or hexvero calls.

Is "static" the same as "safe"?

Parsing a hostile file is still processing attacker-controlled bytes. hexvero bounds every parser (sizes, counts, depths, worker timeout and heap cap) and its tests include truncated and oversized inputs, but no parser is proven safe against every crafted file. Treat results as evidence, not as a verdict.

What about Windows?

Parsers, the CLI, the MCP server and the UI run on Windows (CI runs the test suite on Windows and macOS). Engine process handling is tested on POSIX only.

Legal

Use hexvero only on software you own, that is open source, or that you are permitted to examine. See DISCLAIMER.md.

License

MIT

ai-agents
apk
binary-analysis
claude
cli
dotnet
electron
electron-security
elf
ghidra
mach-o
mcp
mcp-server
pe
radare2
reverse-engineering
security-tools
static-analysis
typescript
webassembly

Project health

Latest release

0.3.0 · Oct 13, 2026

Releases this year

2