Written against the build you can download today.
Command line
Everything the interface computes, klarion-cli computes too, because they are one engine with two front ends. Nineteen commands, every one of which emits JSON, so Klarion drops into a pipeline without anything having to scrape a table.
Requires Klarion Pro or Team — except licence and update, which have to run without one
The shape of every command
$ klarion-cli [global options] <command> [command options] [arguments]stdout carries data; stderr carries everything else. Analysis progress, warnings and diagnostics never contaminate the thing you are piping, so klarion-cli report app.exe --json > report.json produces a file that parses, and you still see the progress on your terminal while it runs.
Commands are one of two kinds, marked on each below. View-only commands read the file and answer; they are near-instant on any size of image because nothing is recovered that the format did not already state. Analysis commands run the full pipeline first. On a small binary that is milliseconds; on a large one with symbols it is the bulk of the wait, and it is why --referenced-only turns a fast strings into a slow one.
Global options
- -v, --verbose
- Raise the log level one step. Repeatable.
- -q, --quiet
- Silence everything below errors.
- --log-level L
- trace, debug, info, warning, error, critical or off.
- --no-colour
- Disable ANSI colour in diagnostics.
- -h, --help
- This help, or help for one command.
- --version
- Print the version and exit.
klarion-cli help <command> prints the same reference you are reading, for one command, from the binary you have installed. Where this page and that disagree, the binary is right.
The first five minutes
Four commands answer most of what you want to know about a file you have never seen. None of them runs analysis, so all four return before you have finished reading the previous one.
$ klarion-cli info target.exepath target.exeformat pe (PE32+ executable (Windows GUI) x64 (AMD64))architecture x86_64bitness 64-bitendianness littleimage base 0x0000000140000000address range 0x0000000140001000 - 0x000000014005935cfile size 352.0 KiB (360448 bytes) entry points (1): 0x00000001400019c0 image-entry entry sections 8segments 8symbols 340relocations 366imported modules 56overlay noneThen sections for the memory map, imports for what it depends on, and strings for what it says. If the file will not open at all, loaders tells you what this build was compiled to recognise, which is a different question from what is wrong with your file.
Asking a real question
analyze is the one to reach for when you want to know whether Klarion understood the binary at all. It prints what it found and, more usefully, the evidence it found each thing by:
$ klarion-cli analyze target.exeAnalysis finished in 21 ms. Functions discovered: 498 (symbols: 0, exception data: 428, call targets: 57, prologue/gap scan: 13) Thunks detected: 40 No-return functions: 0 Jump tables recovered: 0 Unresolved indirect branches: 49 Xrefs built: 2409 Strings found: 765 C++ classes recovered: 4 (names applied: 5) Decode failures: 0Read that breakdown rather than the total. 428 functions from exception data means the image carries a reliable unwind table; a binary where nearly everything came from the prologue and gap scan is one Klarion is guessing about, and the Unresolved indirect branches count is the honest measure of what it could not follow. Nothing here is rounded up.
With a target in mind, xrefs is the workhorse — it takes a symbol name, so you rarely need an address first:
$ klarion-cli xrefs target.exe CreateFileW0x0000000140029de8 CreateFileW references to (2): 0x000000014000ca5c read (in sub_14000ca10+0x4c) 0x000000014000deaa read (in sub_14000da7c+0x42e) references from (0):Into a pipeline
Every command takes --json, so the useful ones compose with whatever you already use. Functions over a size threshold, sorted, as a list of addresses:
$ klarion-cli functions app.exe --min-size 64 --json | jq -r '.[].entry'A whole-image dump for something downstream to index:
$ klarion-cli report app.exe --json > report.jsonAnd the CI check that catches a regression in the engine rather than in your build: run analyze over a corpus and fail on a non-zero exit or a count that dropped.
$ klarion-cli analyze app.exe --quietEvery command
Nineteen, in the order you would meet them rather than alphabetically: what the file is, what is in it, what the analysis made of it, what you can do with two of them, and the two that act on the installation rather than on a binary.
loaders
view only$ klarion-cli loaders [--json]Every file-format loader and every architecture this build has registered. Takes no file.
Run this first when a file will not open. What Klarion recognises is a build-time decision, independent of anything in the file you are holding, and this is the command that tells you what the build decided.
- --json
- Machine-readable output.
info
view only$ klarion-cli info <file>Format, architecture, bitness, byte order, image base, entry points, size, counts, overlay, every format-specific property the loader exposes, and any load diagnostics.
Runs no analysis, so it is near-instant even on a huge image. This is where the Authenticode verdict appears on a signed PE.
- --format <name>
- Force a loader instead of auto-detecting.
- --arch <name>
- Force an architecture. Also picks the slice of a universal Mach-O.
- --json
- Machine-readable output.
sections
view only$ klarion-cli sections <file>Every section: name, address range, size, permissions, semantic role and file offset.
A section is a unit of meaning and may have no virtual mapping at all — a debug-only section, for instance. Use segments for the mapping-only view.
- --format <name>
- Force a loader instead of auto-detecting.
- --arch <name>
- Force an architecture. Also picks the slice of a universal Mach-O.
- --json
- Machine-readable output.
- --no-align
- Tab-separated output even on a terminal.
segments
view only$ klarion-cli segments <file>Every segment: the raw units of mapping between file bytes and virtual addresses, with their permissions.
A segment carries no name and no declared role, so the name column is synthesised as #<index> and the type is inferred from permissions and zero-fill.
- --format <name>
- Force a loader instead of auto-detecting.
- --arch <name>
- Force an architecture. Also picks the slice of a universal Mach-O.
- --json
- Machine-readable output.
- --no-align
- Tab-separated output even on a terminal.
symbols
view only$ klarion-cli symbols <file>Every symbol the loader recovered: name, address, kind, binding, which table it came from, size and owning library.
View-only, so this reflects exactly what the format's own tables say — including on a stripped binary, where it will be mostly imports and exports.
- --imports-only
- Only symbols that are imports.
- --exports-only
- Only symbols that are exports.
- --filter <text>
- Case-insensitive substring match on the name.
- --sort <key>
- address (default) or name.
- --format <name>
- Force a loader instead of auto-detecting.
- --arch <name>
- Force an architecture. Also picks the slice of a universal Mach-O.
- --json
- Machine-readable output.
- --no-align
- Tab-separated output even on a terminal.
imports
view only$ klarion-cli imports <file>Imported symbols grouped by the module that provides them, with delay-loaded and weak dependencies marked.
- --format <name>
- Force a loader instead of auto-detecting.
- --arch <name>
- Force an architecture. Also picks the slice of a universal Mach-O.
- --json
- Machine-readable output.
exports
view only$ klarion-cli exports <file>Every export: name, address, ordinal, and the forwarder target where an export forwards to another module.
- --format <name>
- Force a loader instead of auto-detecting.
- --arch <name>
- Force an architecture. Also picks the slice of a universal Mach-O.
- --json
- Machine-readable output.
- --no-align
- Tab-separated output even on a terminal.
strings
view only$ klarion-cli strings <file>Printable text: address, encoding, byte length and the decoded text of each run.
Plain use scans the loaded image and runs no analysis. --referenced-only has to know what the program points at, so it runs the full pipeline first — which is the difference between a fast command and a slow one.
- --min-length N
- Shortest run accepted, in characters. Default 4.
- --encoding <e>
- ascii, utf8, utf16 or all. Default all.
- --referenced-only
- Only strings something in the image refers to. Runs analysis.
- --format <name>
- Force a loader instead of auto-detecting.
- --arch <name>
- Force an architecture. Also picks the slice of a universal Mach-O.
- --json
- Machine-readable output.
- --no-align
- Tab-separated output even on a terminal.
search
view only$ klarion-cli search <file> "48 8B ?? 24"Searches the whole file for a byte pattern and prints the address and containing section of each hit.
The pattern is space-separated hex by default, where ?? (or ?) matches any single byte. With --text it is matched as literal text instead. Only the first hit is reported unless you pass --all.
- --text
- Treat the pattern as literal text, not hex.
- --utf16
- Encode --text as UTF-16LE instead of 8-bit.
- --all
- List every match instead of stopping at the first.
- --format <name>
- Force a loader instead of auto-detecting.
- --arch <name>
- Force an architecture. Also picks the slice of a universal Mach-O.
- --json
- Machine-readable output.
dotnet
view only$ klarion-cli dotnet <file>The CLI metadata of a managed image: CLR header, assembly manifest and references, the type and method tables, and the string heaps.
Exits with a failure status when the file is not a managed image, so it doubles as a test for one.
- --types
- List every type the module defines.
- --methods
- List every method, with its token, RVA and body extent.
- --refs
- List assembly references and referenced members.
- --strings
- List the #US literals — what ldstr loads.
- --heap-strings
- List the #Strings identifiers.
- --format <name>
- Force a loader instead of auto-detecting.
- --arch <name>
- Force an architecture. Also picks the slice of a universal Mach-O.
- --json
- Machine-readable output.
- --no-align
- Tab-separated output even on a terminal.
disasm
view only$ klarion-cli disasm <file> [--function main --all]Disassembles from an address, or the whole of one function: address, bytes, text.
Starts at the image's first entry point unless told otherwise. Give either --address or --function, not both. This runs no full analysis: a function is recovered on the spot by one recursive traversal from its entry.
- --address <addr>
- Start at this hex or decimal address.
- --function <ref>
- Start at this function: a symbol name or an address.
- --count N
- Decode N instructions. Default 64.
- --all
- Decode the whole function instead of --count.
- --no-bytes
- Omit the raw byte column.
- --att
- AT&T syntax instead of Intel.
- --format <name>
- Force a loader instead of auto-detecting.
- --arch <name>
- Force an architecture. Also picks the slice of a universal Mach-O.
- --json
- Machine-readable output.
analyze
runs analysis$ klarion-cli analyze <file>Runs the full pipeline and prints the counts: functions and the evidence each was found by, thunks, no-return functions, jump tables, unresolved indirect branches, cross-references, strings, classes, decode failures and elapsed time.
This is the CI smoke test for the engine. Run it over a corpus of known-good binaries and watch for a non-zero exit or a suspicious drop in any count.
- --quiet
- Print only the counts, one per line.
- --format <name>
- Force a loader instead of auto-detecting.
- --arch <name>
- Force an architecture. Also picks the slice of a universal Mach-O.
- --json
- Machine-readable output.
functions
runs analysis$ klarion-cli functions <file> --sort sizeEvery recovered function: entry, name, size, basic-block count, how it was discovered, a confidence and its flags.
Progress goes to stderr; stdout carries only the table. That split is what lets you pipe the output without a scraping layer.
- --sort <key>
- address (default), name or size.
- --min-size N
- Omit functions smaller than N bytes.
- --format <name>
- Force a loader instead of auto-detecting.
- --arch <name>
- Force an architecture. Also picks the slice of a universal Mach-O.
- --json
- Machine-readable output.
- --no-align
- Tab-separated output even on a terminal.
xrefs
runs analysis$ klarion-cli xrefs <file> CreateFileWEvery cross-reference pointing at a target, and every one originating from it, each annotated with the function it falls inside.
The target is a hex or decimal literal, an image symbol name, or a name analysis generated such as sub_401000.
- --format <name>
- Force a loader instead of auto-detecting.
- --arch <name>
- Force an architecture. Also picks the slice of a universal Mach-O.
- --json
- Machine-readable output.
diff
runs analysis$ klarion-cli diff <a> <b> [--apply-names --save]Analyses both files and matches their functions on several independent signals, then propagates matches along the call graph to a fixed point.
Every match carries a 0–100 confidence and is classified identical, moved or changed. Nothing ambiguous is reported: a signal shared by more than one candidate on either side matches nothing, because a wrong match costs far more than a missing one.
- --min-confidence N
- Drop matches below this confidence. 0–100, default 50.
- --changed-only
- Report only pairs classified changed.
- --show-unmatched
- Also list the functions that were not matched.
- --no-names
- Ignore symbol names as a signal, to see what the structural and call-graph signals achieve alone.
- --limit N
- Print at most N match rows. 0 means no limit, and is the default.
- --apply-names
- Port names from A onto the functions they matched in B.
- --name-confidence N
- Minimum confidence to port a name. Default 80 — higher than --min-confidence, because a listed match is a suggestion and a ported name is a claim.
- --replace-names
- Also replace meaningful names B already had. Never a name the analyst set.
- --save
- Write the ported names to B's project file. Nothing reaches disk without it.
- --show-ported
- List every name that was ported.
- --show-skipped
- List the names that were not ported, with the reason for each.
- --format <name>
- Force a loader instead of auto-detecting.
- --arch <name>
- Force an architecture. Also picks the slice of a universal Mach-O.
- --json
- Machine-readable output.
- --no-align
- Tab-separated output even on a terminal.
script
runs analysis$ klarion-cli script <file> <script.lua>Runs the analysis pipeline, then executes a Lua script against the result.
Names and comments a script writes are held in memory; --save writes the project file. Byte patches are separate and reach a file only with --patched-out.
- --save
- Write the project file when the script finishes.
- --patched-out <path>
- Write a copy of the binary with the script's byte patches applied. Refused if it made none.
- --lang <lua|python>
- Override the language implied by the extension. Python needs a build with an embedded interpreter, which release builds are not.
- --format <name>
- Force a loader instead of auto-detecting.
- --arch <name>
- Force an architecture. Also picks the slice of a universal Mach-O.
- --json
- Machine-readable output.
report
runs analysis$ klarion-cli report <file> --json > report.jsonEverything, in one document: file info, sections, segments, symbols, imports and exports, functions with their basic blocks, strings and the analysis report.
Intended to feed a pipeline. Note the one inconsistency in the whole CLI: here --format selects the output format (json, markdown or html) and --loader forces the loader, where every other command spells that --format.
- --format <fmt>
- json (default), markdown or html.
- --loader <name>
- Force a loader. Called --format everywhere else.
- --arch <name>
- Force an architecture.
- --json
- Shorthand for --format json.
licence
view only$ klarion-cli licence activate <key>What this machine is licensed for, and how to change it: activate from the key on your receipt, install a licence file, remove one, or print the machine fingerprint.
The one command that runs without a licence, because the command that installs a licence cannot sit behind the gate it exists to open. That matters on a build agent, in a container or over SSH, where the answer cannot be “open a menu”. `activate` needs the network; `install` never does, and takes a file from a path or from standard input.
- status
- Edition, key, seats and expiry. The default.
- activate <key>
- Exchange the key from your receipt for a licence file.
- install <file|->
- Install a licence file you already have.
- deactivate
- Remove the licence from this machine. Local only.
- fingerprint
- This machine's fingerprint, for offline activation.
- --server <url>
- Activate against a staging or self-hosted server.
- --json
- Machine-readable output.
update
changes this install$ klarion-cli update <package>Verifies a signed update package and applies it to an installation, moving it from one release to the next without re-running the installer.
The other command that runs without a licence, and for a sharper reason than licence does: a security fix has to reach someone running Klarion Free, and an updater that stops working when a subscription lapses turns a billing problem into an unpatched installation. It opens no socket — Klarion never checks for updates by itself, so you fetch the package and hand it to this command. The package is verified against a public key compiled into the build before a byte of it is unpacked, and the installation is untouched until every replacement file has been staged and checked against the digest that was signed; if the machine loses power partway through, --recover finishes or undoes it. Nothing to apply yet: the download page publishes installers only, and the first update package will appear there beside the release it moves you to.
- --root <dir>
- The installation to update. Defaults to the one this klarion-cli is part of.
- --verify
- Check the package and print what it contains. Changes nothing.
- --dry-run
- Plan against the installation and print the plan. Changes nothing.
- --status
- Report whether an earlier update was left unfinished.
- --recover
- Finish or undo an update that was interrupted.
- --force
- Apply even while Klarion appears to be running.
- --json
- Machine-readable output.
Exit codes
0 on success and non-zero on failure, with 3 reserved for “this is not licensed”. That separation is deliberate and worth using: a pipeline can tell a licence problem from an analysis problem without parsing the message, and the Python module raises with the same exit_code for the same reason.
Going further
When a command stops being enough, the same engine is reachable from a script, from your own Python, and from an agent.