What this is: binary-analysis is an Agent Skill that gives an AI agent a deterministic, read-only workflow for inspecting unfamiliar PE, ELF, and Mach-O files using Ghidra-backed static analysis. It can identify binary structure, enumerate imports and strings, decompile functions, explore references/call paths, run bounded heuristics, and export evidence-backed reports.
Get it from: the magnus919/agent-skills GitHub repository. The skill lives at binary-analysis/.
An agent handling an unfamiliar executable needs more than strings, a one-line verdict, or a polished hallucination. It needs a bounded, reproducible workflow that can preserve evidence, name uncertainty, and leave behind an audit trail.
This demo uses a harmless, transparent Mach-O fixture. Its source is included in this gist. The fixture is not executed. It contains three deliberately inspectable code paths:
- write a small local configuration file,
- resolve a documentation-only hostname, and
- dynamically look up a symbol in the system library.
Those paths are guarded by argc == -1, an impossible normal-process condition. They exist so a static-analysis agent has something concrete to inspect.
- Create an isolated analysis project and copy an unknown binary into it.
- Record its SHA-256 and identify its format, architecture, sections, imports, functions, and strings.
- Move from a string or import to a named function, a decompilation, and its callers/callees.
- Run rule-derived capability and suspicious-API checks without turning a heuristic into a verdict.
- Export a durable Markdown report with the project’s provenance and diagnostics.
The commands in commands.sh are the complete capture sequence. They assume the skill’s binary CLI is on PATH, with Java 21, Ghidra 12.1.2+, and PyGhidra 3.1+ installed.
clang -Wall -Wextra -Werror -O0 -g telemetry_fixture.c -o telemetry_fixture
binary doctor --json
binary project create telemetry-fixture --json
binary import telemetry_fixture --project telemetry-fixture --json
binary analyze --project telemetry-fixture --profile standard --jsonA real capture of this fixture reported a 51,232-byte ARM-64 Mach-O with SHA-256:
9f2892a12a3fcddca1cc14944db7cd3abc7b1a49fd3ae77c9a2b88bff57a2eaf
The captured response envelopes identified the backend as Ghidra 12.1.2, with success: true, partial: false, and no diagnostics for every command shown below.
{
"format": "Mach-O",
"architecture": "ARM-64",
"endianness": "LITTLE",
"size_bytes": 51232,
"entry_point": "0x1000004f8"
}binary strings --project telemetry-fixture --contains telemetry --json{
"items": [
{
"text": "https://telemetry.example.invalid/v1/event",
"encoding": "ASCII",
"address": "0x1000007c1",
"length": 42
},
{
"text": "telemetry.example.invalid",
"encoding": "ASCII",
"address": "0x100000821",
"length": 25
}
],
"total": 5
}That is an observation: the strings occur in the binary. It is not evidence that the executable made a network connection.
[
{"name": "main", "address": "0x1000004f8", "size_bytes": 140, "confidence": "HIGH"},
{"name": "write_demo_config", "address": "0x100000584", "size_bytes": 164, "confidence": "HIGH"},
{"name": "resolve_demo_endpoint", "address": "0x100000628", "size_bytes": 116, "confidence": "HIGH"},
{"name": "resolve_optional_symbol", "address": "0x10000069c", "size_bytes": 108, "confidence": "HIGH"}
]binary decompile --project telemetry-fixture function:write_demo_config --jsonint write_demo_config(void)
{
int iVar1;
size_t sVar2;
ssize_t sVar3;
iVar1 = _open(kConfigPath, 0x601);
if (iVar1 < 0) {
return -1;
}
sVar2 = _strlen("{\"demo\":true}\n");
sVar3 = _write(iVar1, "{\"demo\":true}\n", sVar2);
_close(iVar1);
return (int)sVar3;
}The actual decompiler output contained an explicit warning about an unknown calling convention and unresolved local-variable storage. A trustworthy agent carries that caveat forward rather than silently cleaning it away.
binary callees --project telemetry-fixture function:main --jsonThe capture found direct calls to _puts, _fputs, _fputc, write_demo_config, resolve_demo_endpoint, and resolve_optional_symbol.
This supports an agent assessment such as: the binary contains code paths for local configuration writing, hostname resolution, and dynamic-symbol lookup. It does not establish that those paths execute. In this fixture, the supplied source proves they are intentionally unreachable in normal use.
binary triage --project telemetry-fixture --json
binary suspicious-apis --project telemetry-fixture --json
binary capability-map --project telemetry-fixture --jsonTriage recorded deterministic observations including Mach-O format, ARM-64 architecture, SHA-256, standard analysis profile, and 12 mapped sections. The suspicious-API rule set applied ten rules and returned zero matches.
The capability map emitted LOW-confidence candidates, including networking from the URL string and file-system from the configuration path. It also produced clearly over-broad LOW-confidence candidates from generic Mach-O strings. That is useful behavior to expose publicly: a good agent reports a heuristic as a heuristic, checks its evidence, and does not call this fixture privileged or malicious.
binary export-report --project telemetry-fixture --type triage --format markdown --json
binary project status telemetry-fixture --jsonThe report export succeeded. The project finished in READY state with one binary and no project lock. The full local capture includes the report, all JSON envelopes, and the project audit trail.
An agent can turn a vague request such as “what is this executable doing?” into a disciplined chain:
unknown file
→ identity and hash
→ structural survey
→ focused strings/imports/functions
→ decompilation and call relationships
→ evidence / heuristic / unknown separation
→ report with provenance
That is different from simply asking a model to speculate about a binary. The CLI owns deterministic operations. The agent chooses the next question and explains the limits of the answer.
This is a live capture from a work-in-progress real Ghidra/PyGhidra adapter patch, not a claim about every currently distributed binary-analysis installation. The capture is real, but the adapter implementation still requires release hardening before this exact workflow should be presented as a stable, generally available interface. In particular, the project’s existing fixture-oriented test suite needs completion work and Ghidra timeout enforcement needs a process boundary rather than a helper thread.
All command outputs above came from a live local run against the SHA-256 shown here. The public excerpts were deliberately field-selected: timestamps, UUIDs, host-local file paths, temporary compiler paths, and report locations were omitted. The source and exact commands are included so anyone can reproduce the analysis in their own environment.
This is static analysis only. The target was never executed, loaded as a library, uploaded, or exposed through a network listener.