Skill v1.0.1
Automated scan100/100+2 new
version: "1.0.1" name: decompiler description: Reverse-engineer and modify native binaries, Java archives, Android APKs, and Dex files with a single decompiler CLI that drives IDA Pro, Ghidra, Binary Ninja, angr, or JADX via DecLib. Use whenever the user asks to decompile, disassemble, inspect Java/Android classes or resources, look up cross references, rename functions or variables, define or change types, sync work between decompilers, search strings or functions, or otherwise inspect a binary file. Also use for multi-binary workflows (load several binaries at once and switch between them with --id).
decompiler — DecLib CLI for LLMs
The decompiler command is a thin client that talks to a long-running DecompilerServer (IDA / Ghidra / Binary Ninja / angr / JADX). The first load of a binary spawns a server in the background; every subsequent call reuses that server, so repeated decompile/disassemble/xref_* calls are fast.
Setup (once per environment)
pip install declib # installs the `decompiler` and `declib` entry points
That's it — the decompiler CLI drives every backend headlessly via DecLib and does not need any plugins installed inside IDA/Ghidra/Binary Ninja to run. angr needs no host tool at all (it's a pure Python dependency) and is the fastest way to verify the pipeline end-to-end. JADX is optional and requires the official JADX 1.5.6+ distribution plus Java 17+. Check it with decompiler backend status jadx --json. DecLib finds it through JADX_HOME or jadx on PATH; Gradle is only needed when developing DecLib itself from a source checkout.
Mental model
| Concept | Description | |
|---|---|---|
| Server | A headless declib --server process holding a single binary open. Identified by a short ID. | |
| Client | Every decompiler <subcommand> call is a short-lived client that picks a server, does one thing, and exits. | |
| Registry | decompiler list / the shared registry under the declib state dir. Each record has id, backend, binary_path, socket_path, pid. Use decompiler list --show-registry to print just the path. | |
| Identity | Native backends expose lifted integer addresses. JADX exposes stable class, field, and full method-descriptor ref strings; never treat them as addresses or omit a method descriptor. |
First moves on a new binary
For an APK, DEX, AAB, XAPK, JAR, or class file, select JADX and begin with classes, methods, and the manifest/resources. Copy complete ref values from JSON list output; descriptors make overloaded methods unambiguous.
decompiler backend status jadx --jsondecompiler load ./challenge.apk --backend jadxdecompiler manifest --rawdecompiler class list --filter 'challenge|MainActivity' --jsondecompiler method list --class 'com.example.MainActivity' --jsondecompiler method source \'com.example.MainActivity->checkFlag(Ljava/lang/String;)Z' --rawdecompiler method xrefs \'com.example.MainActivity->checkFlag(Ljava/lang/String;)Z' --jsondecompiler resource list --filter 'xml|json|assets' --json
JADX xrefs can trigger whole-program usage analysis and consume substantially more time and memory than listing or decompiling one class. Use them after narrowing to an interesting method.
Always prefer IDA Pro when it's available (--backend ida) — it generally produces the cleanest decompilation and the most accurate type recovery. If IDA fails to load the binary (missing license, unsupported file type, decompiler error), fall back to --backend ghidra, then --backend angr as a last resort.
Always start with `list_functions` and `list_strings` — the same binary can have the entry named main (angr), FUN_00101c5c (Ghidra), or sub_101c5c (IDA). Don't assume main exists.
decompiler load ./target --backend ida # prefer IDA; fall back to ghidra if it failsdecompiler list_functions # enumerate every function — pick a real entrydecompiler list_functions --filter 'main|auth' # or narrow by regexdecompiler list_strings --filter 'flag|pass' # find interesting string constants
If load fails, read the reported server-log tail before trying another backend; startup child failures are surfaced immediately. Use --timeout SECONDS to fit a strict task budget or allow an unusually slow initial analysis. Successful JSON output includes the persistent log_path.
Typical first-hour workflow on a stripped binary:
decompiler load ./bin --backend ida(fall back to--backend ghidra,
then --backend angr, if IDA can't open the binary)
decompiler list_functions→ note non-stub function names + sizesdecompiler list_strings→ look for error messages, user prompts,
format strings — they often point at the interesting code
decompiler xref_to "Welcome"→ jump from a string to its usersdecompiler decompile <addr>on whichever function came out of steps 3–4
When several independent reads are already known, send them as a JSONL batch to avoid paying Python startup, server discovery, and connection setup for each command:
decompiler batch --stdin --json <<'EOF'{"id":"functions","argv":["list_functions","--filter","main|auth"]}{"id":"strings","argv":["list_strings","--filter","flag|pass"]}{"id":"main","argv":["decompile","main"]}EOF
The batch selects one server with the usual outer --id, --binary, or --backend flags. Operations use ordinary CLI argument arrays, inherit that server, return an independent result/error, and continue after failures by default. Use --stop-on-error when later operations depend on earlier ones.
Core workflow
decompiler load ./fauxware --backend ida # start a server (prefer IDA)decompiler list_functions # enumerate functions (do this first)decompiler list_strings --filter 'pass|key' # strings the decompiler identifieddecompiler xref_to SOSNEAKY # who references this string?decompiler decompile authenticate # by name (from list_functions)decompiler disassemble 0x40071d # by absolute addressdecompiler xref_to authenticate # every code+data referencedecompiler get_callers authenticate # call-sites only (subset of xref_to)decompiler xref_from main # what does main call?decompiler rename func sub_400662 trampoline # rename a functiondecompiler rename var v2 auth_result --function main # rename a localdecompiler create-type "struct Point { int x; int y; }" # define a new typedecompiler retype main buf "Point *" # set a variable's typedecompiler stop --all
Running multiple binaries concurrently
Each binary gets its own server ID:
decompiler load ./my-binary # id=abc1234decompiler load ./my-binary-2 # id=def5678decompiler list# ID BACKEND PID BINARY# abc1234... angr 4213 .../my-binary# def5678... angr 4217 .../my-binary-2decompiler decompile main --id abc1234decompiler decompile main --binary ./my-binary-2 # or target by path
When more than one server matches, the CLI refuses and prints a disambiguation list. Narrow with --id, --binary, or --backend. If you want to restart the server for a binary cleanly, use load ... --replace which stops the old server and starts a new one (vs --force which adds a second server alongside the existing one).
Choosing a backend
Default: IDA Pro. Use --backend ida whenever IDA is installed and licensed — its decompilation is the most reliable across architectures. Only switch backends if IDA fails to load the binary (the load call errors, or analysis stalls); fall through in this order: ida → ghidra → angr. Use binja only when explicitly requested.
decompiler load ./my-binary --backend ida # PREFERRED: IDA Pro (needs install + license)decompiler load ./my-binary --backend ghidra # FALLBACK: needs GHIDRA_INSTALL_DIRdecompiler load ./my-binary --backend angr # LAST RESORT: pure-Python, always availabledecompiler load ./my-binary --backend binja # Binary Ninja, needs licensedecompiler load ./challenge.apk --backend jadx # Java/Android managed code
If the IDA load fails (e.g. unsupported file format, decompiler error), re-issue load with --backend ghidra — load is idempotent per backend, so this leaves any other server alone and just brings up a Ghidra one alongside.
--backend is also accepted on the inspection/mutation subcommands to narrow which server to target when multiple backends are loaded for the same binary.
Full subcommand reference
| Subcommand | Purpose | Key flags | |
|---|---|---|---|
load <bin> | Start a server on the binary. Idempotent: returns existing unless --force/--replace. | --backend, --id, --force, --replace, --project-dir, --json | |
list | Show all running servers and the registry path. | --show-registry, --json | |
batch | Run JSONL operations over one persistent server connection. | --file/--stdin, --stop-on-error, server selector, --json | |
stop | Shut down one or all servers. --save flushes analysis to disk first; --discard drops unsaved edits. | --id, --binary, --all, --save, --discard, --json | |
save | Persist backend analysis to disk so renames/types/comments survive a reload. | --path, --id, --binary, --backend, --json | |
list_functions | Enumerate every function (ADDR, SIZE, NAME). | --filter REGEX, --json | |
class list/source/xrefs | List, decompile, or find references to managed-code classes. | stable class ref, --filter, --max-chars, --json | |
method list/source/xrefs | List, decompile, or find callers/callees for JVM/Dex methods. | full method ref, --class, --direction, --max-chars, --json | |
field list/xrefs | List managed-code fields or their references. | stable field ref, --class, --filter, --json | |
resource list/get | List or decode Android/JVM resources. Binary reads default to at most 1 MiB. | path, --filter, --raw, --max-chars, --max-bytes, --json | |
manifest | Decode AndroidManifest.xml. | --raw, --max-chars, --json | |
decompile <target> | Pseudocode for a function (name or address). | --raw, --map-lines, --lines, --grep, --context, --max-chars, --output, --json | |
disassemble <target> | Assembly for a function. | --raw, same | |
xref_to <target> | Every reference (code + data) to the target. | --decompile, same | |
xref_from <target> | Functions that target calls. | same | |
comment set/append <addr> <text> | Set (replace) or append a comment at an address. --decompiled attaches it to the pseudocode view. | --decompiled, same + --json | |
comment get/delete <addr> | Read or remove the comment at an address. | same | |
comment list | List every comment in the binary. | --filter REGEX, same | |
rename func <target> <new> | Rename a function. | same + --json | |
rename var <old> <new> --function <f> | Rename a local variable inside a function. | same | |
global list | List global variables (ADDR, SIZE, TYPE, NAME). | --filter REGEX, same | |
global get/rename/retype <addr> [...] | Read, rename, or retype the global at an address. | same | |
signature get <func> | Print a function's full C prototype. | same | |
signature set <func> "<C prototype>" | Set return type + argument types/names from a prototype. | same | |
create-type "<C definition>" | Define a new struct/enum/typedef from a C string and add it to the type database. | same + --json | |
retype <func> <var> <type> | Set the type of a function's local variable or argument. | same | |
sync <func> --from-id <src> | Copy a function's work (names, return/arg types, stack-var names+types, referenced user types) from one running server into another for the same binary. | dest: --id/--binary/--backend; --json | |
list_strings | Strings the decompiler found (may be incomplete — see below). | --filter, --min-length N, same | |
get_callers <target> | Call-sites only — subset of xref_to. | same | |
search bytes <hex> | Find a raw byte pattern. | --max, same | |
search string <text> | Find a string's bytes in memory. | --encoding, --max, same | |
search instruction <regex> | Regex-search disassembly across all functions. | --max, same | |
imports | List imported symbols (external functions/data). | --filter REGEX, same | |
define function/code/data <addr> | Repair analysis: create a function, disassemble bytes, or define data. | --type, --size (data), same | |
undefine <addr> | Clear code/data at an address (removes a function if one starts there). | --size, same | |
patch set <addr> <hex> | Patch bytes at an address. | same | |
patch get/delete <addr> | Show or revert the patch at an address (IDA). | same | |
patch list | List all byte patches (IDA). | same | |
eval "<expr>" | UNSAFE: evaluate a Python expression in the backend process. | same | |
exec "<code>" / exec --file <p> | UNSAFE: run Python in the backend process. | --file, same | |
read int/string/struct <addr> [...] | Typed reads: decode memory as an integer, C string, or defined struct. | --size, --signed, --endian, --max-len, --encoding, same + --json | |
read_memory <addr> <size> | Read raw bytes from the binary at <addr>. Default output is a hexdump. | --format {hexdump,hex,raw}, same + --json (base64-encoded bytes) | |
backend status jadx | Check the optional Java/JADX runtime without loading an input. | --json | |
install-skill | Install this file for Claude Code or Codex. | --agent, --dest, --force, --json |
xref_to vs get_callers
xref_toasks the backend for every reference — code and data. On
Ghidra with --decompile this includes global variables and string references. Rows include a kind field (Function, GlobalVariable, ...). xref_to also accepts strings and raw addresses: if the target isn't a function, it's looked up in list_strings first, then queried as a raw-address xref — so you can go straight from list_strings --filter "admin" to xref_to admin to find who reads that constant.
get_callersis the narrower call-sites-only view: only functions that
contain a call to the target. When you want "who calls this?" reach for get_callers; when you want "who touches this in any way?" reach for xref_to.
comment — annotate addresses
Comments are the primary way to leave durable notes for later (or for another agent). They are keyed by address and come in two flavors: disassembly comments (default) and decompiler/pseudocode comments (--decompiled).
decompiler comment set 0x71d "entry point; parses argv" # replacedecompiler comment append 0x71d "calls authenticate()" # add a linedecompiler comment set 0x664 "SOSNEAKY backdoor" --decompileddecompiler comment get 0x71d # print it (exit 1 if none)decompiler comment list --filter backdoor # find comments by textdecompiler comment delete 0x71d
Addresses accept the usual lifted/absolute/decimal forms. comment set writes through the backend, so on IDA the address must be inside a function (disassembly comments elsewhere aren't supported by IDA's API). angr implements comment writes but not reads/enumeration yet — comment get/list return nothing there. Pair with save to make comments durable across reloads.
global and signature — globals and full prototypes
decompiler global list --filter 'key|flag' # ADDR SIZE TYPE NAMEdecompiler global get 0x4008 --jsondecompiler global rename 0x4008 g_secret_keydecompiler global retype 0x4008 "char[32]"decompiler signature get main # int main(int argc, char **argv)decompiler signature set main "int main(int argc, char **argv)"
signature set takes a full C prototype and applies the return type plus each argument's type and name. IDA applies the whole prototype atomically (it can also change the parameter count); Ghidra/Binary Ninja retype and rename the parameters they already recognize. angr sets argument names but not types. global retype is fully supported on IDA/Binary Ninja and best-effort on Ghidra; angr has no global-variable store (its global commands return nothing).
Persistence — durable artifacts (save, stop --save)
By default a server holds the binary open in memory; IDA discards edits on stop, while Ghidra persists on close. To make renames/types/comments durable regardless of backend, save explicitly, or stop with --save:
decompiler load ./fauxware --backend ida --project-dir ./projdecompiler rename func authenticate auth_checkdecompiler save # writes ./proj/ida/fauxware.i64decompiler stop --id <id>decompiler load ./fauxware --backend ida --project-dir ./proj # reopens saved DBdecompiler list_functions --filter auth_check # the rename is still there# {"saved": true, "path": null}decompiler stop --all --save # flush every server before shutting downdecompiler stop --id <id> --discard # drop unsaved edits (revert to analyzed state)
Reuse the same `--project-dir` across load/reload so the backend finds its saved database (IDA .i64, Ghidra project, Binary Ninja .bndb). angr is purely in-memory: save exits 2 (not implemented) and there is nothing to reload. IDA reopens the saved .i64 directly (no re-analysis), so a reload after save is fast and lossless.
eval / exec — UNSAFE backend scripting (escape hatch)
When the abstracted API doesn't cover what you need, drop to the backend's own Python. This runs arbitrary code inside the backend process — it is not portable and not sandboxed. deci is the live DecompilerInterface; the backend API is reachable through it (idaapi importable; deci.flat_api on Ghidra; deci.project on angr; deci.bv on Binary Ninja).
decompiler eval "deci.name" # -> 'ida'decompiler eval "len(list(deci.functions.keys()))" # count functionsdecompiler exec "print(hex(deci.binary_base_addr))" # captured stdoutdecompiler exec "result = [f.name for _,f in deci.functions.items()][:5]"decompiler exec --file ./my_ida_script.py # run a whole script
eval returns the expression's repr; exec captures stdout and the value of a variable named result. On error, the CLI prints the traceback and exits non-zero. Prefer the first-class commands (rename, retype, comment, ...) when they exist — reach for eval/exec only for backend-specific gaps.
patch — modify bytes
decompiler patch set 0x401200 "9090" # NOP out two bytesdecompiler patch get 0x401200 # show the patch (IDA)decompiler patch list # every patch (IDA)decompiler patch delete 0x401200 # revert to original bytes (IDA)
patch set works on IDA, Ghidra, and Binary Ninja; angr has no user-patch store (exit non-zero). Patch tracking — get/list/delete (revert) — is IDA-only today; on other backends those return no results. Pair with save to persist patches.
define / undefine — repair analysis
When auto-analysis misses a function or mislabels code as data (common on obfuscated or hand-written binaries), fix it:
decompiler define function 0x401200 # create a function IDA/Ghidra misseddecompiler define code 0x401200 # disassemble bytes into an instructiondecompiler define data 0x4040 --type int # define typed datadecompiler define data 0x4040 --size 8 # or a raw 8-byte itemdecompiler undefine 0x401200 --size 32 # clear code/data; removes a function here
A realistic repair sequence is undefine → define code → define function. Supported on IDA, Ghidra, and Binary Ninja; angr's CFG-based model has no define/undefine primitives (those commands exit 2).
search and imports — discovery
decompiler search bytes "7f454c46" # raw byte pattern (hex)decompiler search bytes "48 89 e5" # spaces are ignoreddecompiler search string "SOSNEAKY" # a string's bytes in memorydecompiler search instruction "call.*puts" # regex over disassemblydecompiler imports --filter 'alloc|free' # imported symbols
search bytes/string use the backend's native memory search (IDA, Ghidra, Binary Ninja); angr has no byte-search API, so those exit 2 (not implemented). search instruction is client-side (it greps each function's disassembly), so it works on every backend — but it disassembles as it goes, so keep --max modest on large binaries. imports is available on IDA, angr, and Binary Ninja; Ghidra returns not implemented for now.
read — typed reads (int / string / struct)
read_memory gives you raw bytes; read decodes them so you don't have to byte-twiddle. All three subcommands work on every backend (decoding happens client-side over read_memory).
decompiler read int 0x4040 # pointer-sized int (little-endian)decompiler read int 0x4040 --size 4 --signed # 4-byte signeddecompiler read int 0x4040 --endian bigdecompiler read string 0x8e0 # NUL-terminated C stringdecompiler read string 0x8e0 --max-len 512 --encoding latin-1decompiler create-type "struct Point { int x; int y; }"decompiler read struct 0x4050 Point # decode each member# struct Point @ 0x4050 (size 8):# +0x0 x int = 5 (05000000)# +0x4 y int = 10 (0a000000)
Address semantics — absolute and lifted both work
Every address argument accepts lifted (relative to the image base, e.g. 0x2320), absolute/loaded (0x402320), or decimal. The CLI normalizes absolute addresses to the backend's lifted form, so read_memory 0x402320 and read_memory 0x2320 return the same byte — no more "Ghidra rejects the absolute address" surprises. The heuristic: an address >= the image base is treated as absolute and rebased; a smaller one is already lifted. JSON output always echoes the resolved addr/addr_hex (lifted).
read_memory — raw bytes at an address
read_memory <addr> <size> reads <size> bytes from the loaded binary's mapped memory starting at <addr>. It goes through the backend's own memory accessor, so it returns whatever the decompiler currently has loaded for that address (post-relocation, post-mapping) — not the raw bytes from the on-disk ELF/PE/Mach-O. Use it when you need to:
- Inspect a constant table, jump table, or vtable that the decompiler
rendered as dword_<addr> / unk_<addr>.
- Read a string the backend's string detector missed (cross-check
against list_strings first; if absent, dump bytes manually).
- Verify the actual bytes behind a global the decompiler shows as an
opaque symbol.
- Pull a magic header / signature out of
.rodatato confirm a file
format or library version.
decompiler read_memory 0x4008e0 64 # default: hexdumpdecompiler read_memory 0x4008e0 64 --format hex # one-line hex blobdecompiler read_memory 0x4008e0 64 --format raw > bytes # raw bytes to a filedecompiler read_memory 0x4008e0 64 --json # base64-encoded payload
JSON output includes both size (actual bytes returned) and requested_size — backends may produce short reads when the request straddles the end of a mapped segment. In text mode the CLI prints a # short read: ... notice on stderr in that case. If the address is unmapped or uninitialized, the CLI exits non-zero with a message saying the backend couldn't satisfy the read; try a smaller size or confirm the address with list_functions / xref_to.
Address formats follow the same rules as everywhere else: hex (0x4008e0), decimal (4197088), or lifted (0x8e0) all work.
Editing types and syncing across decompilers
create-type parses a C type definition and adds it to the binary's type database. retype then points a variable at it (or at any built-in type). Both work on every backend; refer to the struct by name, with */[] for pointers and arrays:
decompiler create-type "struct Point { int x; int y; }"decompiler create-type "enum Color { RED, GREEN=5, BLUE }"decompiler retype main buf "Point *" # stack var or argument, by name
sync copies one function's work from a source server into a destination server for the same binary — handy when you reverse a function in one tool and want it mirrored in another. It transfers the function name, return/argument types, stack-variable names and types, and any user-defined types those reference. The source is chosen with --from-id; the destination with the usual --id/--binary/--backend:
decompiler load ./fauxware --backend ida # id=ida123 (do your work here)decompiler load ./fauxware --backend ghidra # id=ghi456decompiler rename func 0x71d auth_check --id ida123decompiler retype 0x71d buf "Point *" --id ida123decompiler sync 0x71d --from-id ida123 --id ghi456 # push it into Ghidra
Addresses and stack-variable offsets are normalized, so the function and its variables re-key correctly even when the two backends name them differently. Pass a function address (most robust) or a name.
list_strings may be incomplete
list_strings returns exactly what the backend's own string detector surfaced — the CLI does not second-guess the decompiler. Fidelity varies (angr < ghidra < ida); angr in particular misses most of .rodata. If the output looks thin, check the binary file directly with an external scanner:
strings -a -n 4 ./target # classic strings(1)rabin2 -z ./target # radare2: ASCII data-section scanreadelf -p .rodata ./target # ELF-specific, per section
Use those to confirm a specific constant exists, then come back and decompile / xref_to its address inside the CLI. --min-length defaults to 4.
Machine-readable output
Pass --json on any subcommand to get a structured payload suitable for downstream parsing — ideal when an LLM wants to chain commands. Every JSON payload that mentions an address provides both addr (int, lifted) and addr_hex (hex string, also lifted):
decompiler list_functions --filter '^main$' --json# [{"addr": 1821, "size": 184, "name": "main", "addr_hex": "0x71d"}]decompiler list_strings --filter 'flag' --json# [{"addr": 4197168, "string": "flag{...}", "addr_hex": "0x4008e0"}]decompiler decompile main --json# {"addr": 1821, "decompiler": "angr", "text": "void main(...){...}", "addr_hex": "0x71d"}# Terminal-friendly form of decompile: skip JSON wrapping entirely.decompiler decompile main --raw
When statement addresses matter for comments, patches, or control-flow reasoning, request the backend's pseudocode line mapping:
decompiler decompile main --map-lines --json# "line_map": [{"line": 4, "addrs": [1849], "addrs_hex": ["0x739"]}, ...]
Lines can map to multiple instructions, and presentation-only lines may have no mapping. --map-lines requires --json and cannot be combined with --raw.
Never send a giant function to model context repeatedly. Shape one decompilation at the CLI:
# Inspect one or several 1-based inclusive source ranges.decompiler decompile processClientInput --lines 1450:1700 --json# Search pseudocode and include nearby control flow.decompiler decompile processClientInput \--grep 'firmware|readlog' --context 8 --ignore-case --json# Save the complete result once; the response contains path/hash/size, not text.decompiler decompile processClientInput --output /tmp/processClientInput.c --json
Shaped JSON reports original and output sizes, source ranges, match lines, and whether --max-chars truncated the result. Repeat --lines or --grep in one request instead of decompiling the same function again. Existing output files require --force-output before they are overwritten.
Gotchas and tips
- First `load` is slow (backend analysis pass). Subsequent calls on the
same server are fast.
- Exit codes: every CLI command exits
0on success and1
on failure (including "rename didn't find the old name"). Exit 2 means the feature is not implemented for the selected backend (e.g. save on angr) — distinct from a real error so scripts can branch on it. Use && safely.
- Stripped binaries: use
list_functionsbeforedecompileto find
the real entry. main may not exist; look for non-default names (sub_XXXX, FUN_..., entry, etc.) with plausible sizes and xrefs.
- Backend main-naming varies: angr promotes the entry to
main,
Ghidra leaves FUN_00101c5c, IDA emits sub_101c5c. Always resolve via list_functions or a known entry address, not by assuming main.
- Invalid addresses fail with a clear message distinguishing "no
function starts here" from "decompiler engine failed". The CLI does not auto-round-trip invalid addresses.
- Address formats:
0x71d,0x40071d, and1821all resolve the
same function in fauxware. Names are also accepted wherever an address is.
- Servers persist until explicitly stopped (
decompiler stop --all)
or the host reboots; decompiler list always reflects live processes.
- Registry path:
decompiler list --show-registryprints just the
directory so you can clean up manually if you ever need to (e.g. after a kill -9).
- Project/database files: by default they live in
<user-cache>/declib/projects/<binary>-<hash>/, not next to the binary. Pass --project-dir <path> to load to override, or --project-dir "" to restore the legacy "write next to the binary" behavior.
Library-level API (for Python scripts)
Everything the CLI does is also available as a library:
from declib.api.decompiler_client import DecompilerClientclient = DecompilerClient.discover_from_registry(binary_path="./fauxware")for addr, func in client.functions.items():if func.name == "main":print(client.decompile(addr).text)
The new core APIs (list_strings(filter=...), get_callers(target), disassemble(addr), read_memory(addr, size)) are on both the local DecompilerInterface and the DecompilerClient proxy. read_memory returns bytes (or None if the backend can't satisfy the read), so you can hexdump, decode, or feed the result straight into struct parsers without going through the CLI.