Malware Analysis Module
July 18, 2026 · View on GitHub
Introduced in v4.0 and available in the latest tagged v6.0.0 release, Malware Analysis is backed by the isolated MalwareGraph service. All static, unpacking, string, decompilation, and AI output is analyst-assistance data. Validate every finding before using it in incident response, detection deployment, customer reporting, or attribution.
For the full walkthrough article see docs/publication-drafts/adversarygraph-v4-malware-analysis.md.
Contents
- What the module does
- Architecture and deployment modes
- Technology stack
- Screenshots
- Safety model
- Configuration reference
- API endpoints reference
- Use cases
What the module does
The Malware Analysis module connects AdversaryGraph to an isolated MalwareGraph service for static malware analysis. It gives analysts a single workflow dashboard where every extracted hash, IP, domain, ATT&CK technique, string, function, and family lead is a clickable entity that links back into the AdversaryGraph graph (IOC Library, IOC Intelligence, ATT&CK Navigator, Compare Groups, Detection Studio).
Core capabilities available today:
| Capability | Route / action |
|---|---|
| Sample submission (raw file or password-protected ZIP) | POST /api/malwaregraph/analyses |
| Analysis status and first-analysis data | GET /api/malwaregraph/analyses/{job_id} |
| Structured report (IOCs, TTPs, hashes, behaviors) | GET /api/malwaregraph/analyses/{job_id}/report |
| String extraction with length and reference filters | GET /api/malwaregraph/analyses/{job_id}/strings |
| Hex / ASCII / strings file preview | GET /api/malwaregraph/analyses/{job_id}/files/preview |
| Static unpack | POST /api/malwaregraph/analyses/{job_id}/unpack |
| Dynamic/runtime unpack (policy-gated) | POST /api/malwaregraph/analyses/{job_id}/unpack/runtime |
| Obfuscation technique analysis | POST /api/malwaregraph/analyses/{job_id}/obfuscation-analysis |
| AI full analysis pipeline | POST /api/malwaregraph/analyses/{job_id}/ai-full-analysis |
| Debug session (static analyst-guided plan) | POST /api/malwaregraph/analyses/{job_id}/debug-sessions |
| Debug workspace (AI-assisted step-through) | POST /api/malwaregraph/analyses/{job_id}/debug-workspaces |
| Decompilation metadata | POST /api/malwaregraph/analyses/{job_id}/decompilation |
| Analysis workflow graph | GET /api/malwaregraph/analyses/{job_id}/workflow-graph |
| Analysis and service logs | GET /api/malwaregraph/analyses/{job_id}/logs, GET /api/malwaregraph/logs |
| LLM provider enumeration | GET /api/malwaregraph/llm/providers |
| Custom LLM prompt completion | POST /api/malwaregraph/llm/complete |
| Service health | GET /api/malwaregraph/health |
Architecture and deployment modes
┌─────────────────────────────────────────────────────────────────────────────┐
│ AdversaryGraph │
│ React UI ──── FastAPI backend ──── PostgreSQL / Redis / Celery │
│ │ │ │
│ Malware Analysis └──────── MalwareGraphClient ─────────┐ │
│ page (MalwareAnalysis.tsx) (proxy client, port 8000) │ │
└──────────────────────────────────────────────────────────────┼─────────────┘
│ REST API
▼
┌─────────────────────────────────────┐
│ MalwareGraph service (port 8100) │
│ FastAPI · static analysis workers │
│ quarantine storage · job queue │
└─────────────────────────────────────┘
Integrated mode
The default. The AdversaryGraph frontend talks to the AdversaryGraph backend
(/api/malwaregraph/*), which proxies every request to the MalwareGraph service
through MalwareGraphClient. The MalwareGraph service is on the
malwaregraph_internal Docker network, not exposed on the host.
Standalone mode
The MalwareGraph service is accessed directly at http://localhost:8100 from the
host or from scripts. Use this when running analysis separately from
AdversaryGraph or for scripted batch processing. The same analysis.json
contract is produced in both modes.
Technology stack
This section lists the real libraries, binaries, and code modules used by the current implementation.
| Area | Actually used | What it does |
|---|---|---|
| MalwareGraph API | fastapi, uvicorn[standard], python-multipart, malwaregraph.api.app | Serves analysis, upload, strings, unpack, decompilation, debug, runtime, report, logs, and provider endpoints. |
| Models and settings | pydantic, pydantic-settings, malwaregraph.core.models, malwaregraph.core.config | Defines API contracts, job records, graph entities, debug/session models, and environment configuration. |
| Archive handling | Python zipfile, pyzipper, malwaregraph.services.archive | Extracts ZIP/password-protected ZIP samples into isolated MalwareGraph storage. |
| Hashing | Python hashlib, malwaregraph.services.hashing | Calculates MD5, SHA1, and SHA256. |
| Static PE/file parser | Python struct, math, pathlib, malwaregraph.services.static_analysis | Performs file classification, entropy, PE header parsing, sections, imports, resources, and first-analysis artifacts. |
| Packer detection | malwaregraph.services.packing, malwaregraph.services.unpack | Uses UPX markers, section names, entropy, import scarcity, overlay and encoded-payload heuristics. |
| Static depacking | upx-ucl binary, Python subprocess, zlib, bz2, lzma, base64, XOR scanner in malwaregraph.services.unpack | Runs upx -d for UPX and tries bounded recovery of compressed, encoded, or XOR-like payloads. |
| Runtime depacking | unipacker==1.0.8, child-process wrapper in malwaregraph.services.unpack | Handles policy-gated PE32 runtime unpack attempts. Qiling is referenced only as a planned/profile name and is not installed. |
| Strings and decoding | Python re, base64, JSON helpers, malwaregraph.services.strings | Extracts ASCII/UTF-16LE strings, decodes base64/hex candidates, scores entropy, and classifies string findings. |
| IOC extraction | Python re, ipaddress, urllib.parse, malwaregraph.services.iocs | Extracts URLs, domains, IPs, hashes, emails, registry paths, and related IOC values. |
| Android/APK static support | Python zipfile, malwaregraph.services.android | Performs static APK/archive inspection and Android-related IOC/string extraction. |
| Disassembly | capstone>=5, malwaregraph.services.workflow_graph, malwaregraph.services.debug_workspace | Disassembles x86/x64 PE code and builds workflow/function traces. Falls back to hex preview if Capstone is unavailable. |
| Decompile view | malwaregraph.services.decompilation | Builds MalwareGraph's internal static pseudocode artifact from PE metadata, WinAPI strings, entrypoint details, and sections. It is not Ghidra/IDA/RetDec output. |
| Debug workspace | malwaregraph.services.debug_workspace, in-memory workspace dictionary | Provides function stepping, breakpoints, register/stack snapshots, memory regions, API hooks, graph export, and AI debug context. |
| AI calls | httpx, malwaregraph.services.ai.factory | Calls Claude, OpenAI, Gemini, MiniMax/OpenAI-compatible, local OpenAI-compatible endpoints, or AdversaryGraph's LLM proxy. |
| AdversaryGraph proxy | httpx, backend/app/services/malwaregraph.py, backend/app/api/routes/malwaregraph.py | Proxies /api/malwaregraph/* from AdversaryGraph to MalwareGraph. |
| Frontend | React 18, Vite, TypeScript, Tailwind CSS, @tanstack/react-query, axios, react-router-dom | Implements the malware dashboard, strings view, unpacker, dynamic analysis, and debugger/decompiler UI. |
| Container runtime | Docker Compose, python:3.12-slim, node:22-slim, read-only service, dropped capabilities, no-new-privileges, PID/CPU/memory limits | Runs MalwareGraph in a restricted container and builds the bundled MalwareGraph UI. |
Not currently bundled as malware-analysis engines: Ghidra, IDA Pro, RetDec,
pefile, CAPE/Cuckoo, YARA, Volatility, Frida, or Qiling execution.
Screenshots
The v4 screenshot folder name is historical. These screenshots remain
representative for the tagged v6.0.0 Malware Analysis workflow unless a newer
module-specific screenshot is listed. They show the implemented workflow rather
than mockups. The complete asset list is tracked in
docs/assets/malware-analysis-v4/manifest.md,
and validation.json records the
dimension and nonblank checks for each 1920x1200 PNG.
Malware Analysis dashboard

Shows case controls, service status, safety record, first-analysis results, hash-check entry point, and section entropy visualization.
Hash-check feed results

Shows the post-action feed result panel. The result window is wide enough for VirusTotal-style counts, reputation, family names, local IOC feed results, and external feed summaries without cutting the content.
First-analysis tools

Shows the first-analysis action area: static facts, section entropy, hash check, string analysis, unpacking, debug, report, and dynamic-analysis entry points.
Report, IOCs, and TTPs

Shows the report section where extracted IOCs, clickable ATT&CK TTP tags, suspicious functions, analyst notes, and validation gaps are kept with evidence.
String Analyzer - all strings

Shows extracted ASCII/UTF-16 string review, target metrics, category counts, and filters for analyst-controlled triage.
String Analyzer - smart IOC/TTP leads

Shows smart mode, where the module groups registry keys, commands, URLs, domains, hashes, suspicious API names, and ATT&CK-style leads for faster review.
String Analyzer - AI mode

Shows AI string-analysis mode and provider selection. The visible result depends on local LLM availability; the UI keeps the provider state separate from the raw string evidence.
Unpacker - packed sample

Shows a packed Windows sample profile with packer name, entropy, obfuscation flags, file versions, static unpack controls, AI unpack action, and dynamic unpack gate.
Unpacker - deobfuscation actions

Shows follow-on deobfuscation controls for strings and code after packer triage, plus navigation back to the case and onward to strings, debug, and dynamic analysis.
Debugger - decompilation IDE

Shows the IDE-style decompilation pane, recovered static APIs, entrypoint details, controls, breakpoints, and dynamic-analysis safety gate.
Debugger - after step

Shows debugger state after one safe symbolic function step. This verifies that the UI can create a workspace, advance function-level analysis, and keep step results visible.
Debugger - OllyDbg CPU view

Shows the CPU-style debug panel with disassembly, registers, stack, memory/API context, and AI notes for the current function.
Debugger - function graph

Shows the AIDebug function graph and selected function context used to move between recovered code blocks and AI-assisted explanations.
Dynamic Analysis - safety gate

Shows runtime target selection, AI provider selection, and the explicit dynamic-analysis disclaimer before any isolated runtime session is prepared.
Dynamic Analysis - runtime session

Shows the prepared isolated runtime session, runtime profile, control state, and behavior categories that are expected from dynamic output.
Dynamic Analysis - function workflow

Shows the loaded full function workflow, function stepping controls, branch context, and AI feedback-loop entry points for summarizing dynamic behavior.
Safety model
The default execution policy is static-only. Submitted samples are never executed unless dynamic analysis is explicitly enabled.
| Control | Default | Notes |
|---|---|---|
| Sample execution | Disabled | Requires dynamic_analysis: true in the request AND MALWAREGRAPH_ENABLE_DYNAMIC_DEBUG=true in service env |
| Outbound internet from analysis jobs | Disabled | Workers have no external network route |
| Binary upload to third-party services | Disabled | Only hash lookups by default |
| Host filesystem access | Denied | Workers use dedicated quarantine and scratch volumes |
runtime_debug_disclaimer_accepted | false | Must be explicitly true for any runtime-debug action |
| Network between MalwareGraph and AdversaryGraph DB | Denied | malwaregraph_internal is isolated |
Do not upload real malware samples to public or shared AdversaryGraph installations. Use a private self-hosted deployment.
Configuration reference
Set in .env at the AdversaryGraph project root:
MALWAREGRAPH_URL=http://malwaregraph:8100 # service URL (default for Docker Compose)
MALWAREGRAPH_API_KEY= # optional API key sent as X-API-Key
MALWAREGRAPH_REQUEST_TIMEOUT_SECONDS=30 # short-request timeout
MALWAREGRAPH_UPLOAD_TIMEOUT_SECONDS=180 # sample upload timeout
MALWAREGRAPH_LONG_TIMEOUT_SECONDS=300 # AI/RE/unpack operation timeout
MALWAREGRAPH_STORAGE_DIR=/malwaregraph-storage # shared storage for saved layers
For standalone testing from the host machine, override the URL:
MALWAREGRAPH_URL=http://localhost:8100
API endpoints reference
All proxy routes live under /api/malwaregraph/.
Health and providers
| Method | Path | Notes |
|---|---|---|
GET | /api/malwaregraph/health | Returns service version and status dict |
GET | /api/malwaregraph/llm/providers | Returns available LLM provider list |
GET | /api/malwaregraph/logs | Returns service-level log lines (?limit=N) |
Analysis lifecycle
| Method | Path | Notes |
|---|---|---|
GET | /api/malwaregraph/analyses | List all jobs |
POST | /api/malwaregraph/analyses | Submit a sample (multipart: file, optional password, case_id, dynamic_analysis, runtime_debug_disclaimer_accepted) |
GET | /api/malwaregraph/analyses/{job_id} | Get job status and first-analysis data |
GET | /api/malwaregraph/analyses/{job_id}/report | Get full structured report |
GET | /api/malwaregraph/analyses/{job_id}/logs | Get per-job log lines (?limit=N) |
GET | /api/malwaregraph/analyses/{job_id}/workflow-graph | Get workflow/debug graph (nodes and edges) |
Static analysis tools
| Method | Path | Notes |
|---|---|---|
GET | /api/malwaregraph/analyses/{job_id}/strings | String extraction (sample_ref, min_chars, max_chars, ai, ai_provider) |
GET | /api/malwaregraph/analyses/{job_id}/files/preview | File preview (mode: strings, ascii, hex; limit; sample_ref) |
POST | /api/malwaregraph/analyses/{job_id}/unpack | Static unpack job (sample_ref, dynamic_analysis, runtime_debug_disclaimer_accepted) |
POST | /api/malwaregraph/analyses/{job_id}/unpack/runtime | Runtime/dynamic unpack (policy-gated) |
POST | /api/malwaregraph/analyses/{job_id}/obfuscation-analysis | Obfuscation technique analysis (sample_ref, ai_provider) |
POST | /api/malwaregraph/analyses/{job_id}/decompilation | Decompilation metadata (sample_ref) |
POST | /api/malwaregraph/analyses/{job_id}/save-unpacked | Save unpacked layers to MALWAREGRAPH_STORAGE_DIR |
AI analysis
| Method | Path | Notes |
|---|---|---|
POST | /api/malwaregraph/analyses/{job_id}/ai-full-analysis | Full pipeline AI analysis (sample_ref, ai_provider, dynamic_analysis, prefer_unpacked_output) |
POST | /api/malwaregraph/llm/complete | Direct LLM prompt completion (provider: local|claude|openai|gemini|minimax, model, system, prompt) |
Debug sessions and workspaces
| Method | Path | Notes |
|---|---|---|
POST | /api/malwaregraph/analyses/{job_id}/debug-sessions | Create analyst-guided debug session |
POST | /api/malwaregraph/analyses/{job_id}/runtime-debug-sessions | Create dynamic runtime debug session (policy-gated) |
POST | /api/malwaregraph/analyses/{job_id}/debug-workspaces | Create AI-assisted debug workspace (ai_provider) |
GET | /api/malwaregraph/debug-workspaces/{session_id} | Get workspace state and steps |
POST | /api/malwaregraph/debug-workspaces/{session_id}/step | Advance workspace by one step |
POST | /api/malwaregraph/debug-workspaces/{session_id}/ai-assistant | Run AI assistant in workspace context |
POST | /api/malwaregraph/runtime-debug-sessions/{session_id}/step | Advance runtime debug session |
Use cases
The examples below describe analyst workflows. Do not commit or download live malware into this repository. For real Windows malware, use only authorized samples supplied by your organization, incident-response case, or a controlled private corpus, and store them outside the Git worktree in an isolated malware lab. Keep samples password-protected at rest and run dynamic workflows only in a disposable runtime profile.
Scenario set — Five Windows malware-analysis profiles
Use these profiles to validate the module from low risk to high complexity.
| Profile | Sample type | What it validates | Recommended handling |
|---|---|---|---|
| Clean control | Known-good Windows PE such as a signed Microsoft utility or your own compiled benign PE | Baseline file identity, PE parsing, entropy, imports, strings, false-positive control, report wording | Safe static analysis. Do not mark it malicious; use it to compare UI output and AI caution. |
| Simple packed sample | Authorized Windows PE packed once with a common packer such as UPX | Packer detection, high entropy, static unpack output, before/after hashes and entropy delta | Start with static unpack. Dynamic execution should not be needed if static unpack succeeds. |
| Packed malware requiring runtime unpack | Authorized Windows malware where static unpack fails and unpacking needs runtime state or memory dump | Runtime-unpack planning, policy gate, validation gaps, unpacked target selection, AI explanation of why runtime is required | Enable dynamic only inside a disposable sandbox or VM profile with no production network route. |
| Obfuscated code malware | Authorized Windows malware or script-backed PE with encoded strings, API hashing, encrypted config, or control-flow tricks | Obfuscation analysis, AI string classification, suspicious API/function explanation, validation-gap tracking | Keep output as hypotheses until deobfuscation or runtime stepping confirms behavior. |
| Multilayer packed malware | Authorized Windows malware with nested packers, loaders, droppers, or staged payload extraction | Layer tracking, repeated unpack/inject-file workflow, per-layer target selection, IOC/TTP correlation across layers | Analyze one layer at a time. Save each unpacked/deobfuscated output as a new target and preserve evidence links. |
Suggested sample-corpus layout outside the repo:
/malware-lab/samples/windows/
clean-control/
simple-packed/
runtime-unpack-required/
obfuscated-code/
multilayer-packed/
For each sample, keep a companion note with:
- source and authorization
- original filename
- SHA256
- password, if zipped
- expected profile
- whether runtime execution is permitted
- network policy:
none,fakenet,sinkhole, or restricted lab internet
Never use public demo infrastructure for these profiles. The dynamic and
runtime-unpack profiles require MALWAREGRAPH_ENABLE_DYNAMIC_DEBUG=true,
dynamic_analysis=true, and explicit acceptance of the runtime-debug
disclaimer.
Use case 1 — Submit a raw sample for first triage
When: An analyst receives a suspicious .exe, .dll, or script and wants
an immediate static metadata overview.
Steps:
curl -X POST http://localhost:8000/api/malwaregraph/analyses \
-F "file=@suspicious.exe" \
-F "case_id=MAL-2026-001"
The response returns a job_id. Use it for all subsequent steps:
curl http://localhost:8000/api/malwaregraph/analyses/{job_id}
Review: file_type, hashes (MD5/SHA256/imphash/tlsh/ssdeep), entropy,
packer_hints, sections, imports.
In the UI: Open Malware Analysis, set a case name and case ID, attach the file, and click Create case and run first analysis.
Use case 2 — Submit a password-protected malware ZIP
When: A vendor or threat intel team delivers samples inside an encrypted
archive using the conventional password infected.
Steps:
curl -X POST http://localhost:8000/api/malwaregraph/analyses \
-F "file=@samples.zip" \
-F "password=infected" \
-F "case_id=MAL-2026-002"
The intake service extracts the archive inside an isolated container with path
traversal, file count, and decompression-bomb limits. Each extracted file
becomes a separate analysis target accessible via the sample_ref parameter.
In the UI: The file picker shows a ZIP password field when a .zip file is
selected. Default password is pre-filled as infected.
Use case 3 — Check what targets were extracted from an archive
When: The submitted archive contained multiple payloads and the analyst needs to identify which target to analyze.
curl http://localhost:8000/api/malwaregraph/analyses/{job_id}
Look at the first_analyses array — each entry has a target_entity_id
(e.g., archive--file--0001, archive--file--0002) and the file type, name,
size, and entropy of the extracted file.
Subsequent API calls use ?sample_ref=archive--file--0002 to target a specific
extracted file. In the UI, the target selector dropdown lists all extracted
targets.
Use case 4 — Extract strings from a sample
When: An analyst wants to identify hardcoded URLs, IP addresses, registry keys, commands, mutex names, or encoded configuration.
curl "http://localhost:8000/api/malwaregraph/analyses/{job_id}/strings?\
sample_ref=archive--file--0001&min_chars=4"
Useful filters:
| Parameter | Purpose |
|---|---|
min_chars | Filter out short noise strings (default: 4) |
max_chars | Cap on string length for focused extraction |
sample_ref | Target a specific extracted file |
ai=false | Return raw strings without AI classification (faster) |
ai=true | Add AI classification into IOC categories, commands, WinAPI calls |
The response strings array includes the raw string, its offset, and
(when ai=true) an AI-assigned category.
Use case 5 — Run AI string classification
When: Raw string extraction returns thousands of entries and the analyst needs them categorized into IOC candidates, commands, WinAPI calls, registry keys, network indicators, and evasion strings.
curl "http://localhost:8000/api/malwaregraph/analyses/{job_id}/strings?\
sample_ref=archive--file--0001&ai=true&ai_provider=local"
AI string categories returned by MalwareGraph:
ioc_ip,ioc_domain,ioc_url,ioc_emailcommand,powershell,registry_key,file_path,mutexwinapi,android_apicredential,encryption_keynetwork_c2,persistence,defense_evasion,execution
Switch to a more capable model for complex obfuscated code:
... &ai=true&ai_provider=claude
Use case 6 — Preview file content in hex, ASCII, or strings mode
When: An analyst wants a quick look at the binary content to spot magic bytes, decode a configuration block, or verify a sample type by eye.
# Strings mode (default)
curl "http://localhost:8000/api/malwaregraph/analyses/{job_id}/files/preview?\
sample_ref=archive--file--0001&mode=strings&limit=200"
# Hex mode — useful for magic byte inspection and section analysis
curl "http://localhost:8000/api/malwaregraph/analyses/{job_id}/files/preview?\
mode=hex&limit=64"
# ASCII mode — plaintext decoders, base64 blobs, script payloads
curl "http://localhost:8000/api/malwaregraph/analyses/{job_id}/files/preview?\
mode=ascii&limit=200"
In the UI, the preview panel sits below the first-analysis metadata with a Strings / ASCII / Hex toggle.
Use case 7 — Unpack a packed executable
When: Static triage shows high entropy, a known packer (UPX, MPRESS, custom), or a stub-only import table — indicating the real payload is unpacked at runtime.
curl -X POST "http://localhost:8000/api/malwaregraph/analyses/{job_id}/unpack?\
sample_ref=archive--file--0001"
The unpack job attempts static decompression first. On success, the unpacked
layer becomes a new sample_ref target (e.g., archive--file--0002). Re-run
strings, preview, and AI analysis against the unpacked target.
To save the unpacked layers to disk for external tooling:
curl -X POST http://localhost:8000/api/malwaregraph/analyses/{job_id}/save-unpacked
Layers are written to MALWAREGRAPH_STORAGE_DIR/{job_id}/. Returns 404 if no
layers exist yet.
Use case 8 — Run obfuscation technique analysis
When: A script (PowerShell, JScript, VBScript) or shellcode uses encoding, string splitting, variable substitution, API hashing, or layered encryption that the strings pass misses.
curl -X POST "http://localhost:8000/api/malwaregraph/analyses/{job_id}/obfuscation-analysis?\
sample_ref=archive--file--0001&ai_provider=local"
The response includes an obfuscation score (0–1), a list of identified
techniques (e.g., xor_encode, base64, string_concat, api_hash), and
AI-generated recommendations for next steps.
Use a more capable provider for complex multi-stage obfuscated loaders:
... &ai_provider=claude
Use case 9 — Run the full AI analysis pipeline
When: An analyst wants a comprehensive AI-assisted pass over a completed static analysis: IOC classification, ATT&CK TTP mapping, behavior summary, family hypotheses, and detection drafts — all in one operation.
curl -X POST "http://localhost:8000/api/malwaregraph/analyses/{job_id}/ai-full-analysis?\
sample_ref=archive--file--0001&ai_provider=claude&prefer_unpacked_output=true"
The prefer_unpacked_output=true flag tells the AI pipeline to use the deepest
available unpacked layer instead of the original archive, which produces better
TTP mapping when a packer was present.
Expected output sections:
ioc_analysis— normalized IOCs with type and confidencettp_candidates— ATT&CK technique IDs, tactic, evidence stringsbehavior_summary— capability narrativefamily_hypotheses— malware family leads with evidencedetection_draft— raw YARA/Sigma snippet ideasanalyst_notes— AI-identified uncertainty and recommended next steps
In the UI: Click Run full AI analysis in the AI Analysis panel.
Use case 10 — Retrieve the structured analysis report
When: The analyst has completed static triage and wants the canonical
analysis.json-style report with all normalized findings.
curl http://localhost:8000/api/malwaregraph/analyses/{job_id}/report
The report includes:
sample— names, hashes (MD5/SHA1/SHA256/imphash/tlsh/ssdeep), file type, sizeiocs— each with type, value, source stage, confidenceattack_mappings— technique ID, tactic, evidence, review statebehaviors— capability descriptions with supporting artifact refsfamily_hypotheses— malware family leads with evidence categoriessafety— records whether the sample was executed, the sandbox profile, and whether binary upload to third-party services occurredai_assistance— all AI output marked as untrusted analyst assistance
Use case 11 — Get the workflow/debug graph
When: An analyst wants to understand the analysis stages, their dependencies, and any debug-plan nodes in a visual graph layout.
curl http://localhost:8000/api/malwaregraph/analyses/{job_id}/workflow-graph
The response contains a nodes array (each with type, stage, label, and
status) and an edges array. Render this in a graph viewer or import it into
the AdversaryGraph Investigation Graph. For static-only jobs, the graph
shows triage, strings, unpack, deobfuscation, and AI-synthesis nodes. Debug
nodes populate only when a debug session or dynamic evidence exists.
Use case 12 — Create an analyst-guided debug session
When: The analyst wants to map the reversing plan — which functions to examine, what breakpoints to set, what memory addresses to dump — without needing a live runtime environment.
curl -X POST "http://localhost:8000/api/malwaregraph/analyses/{job_id}/debug-sessions?\
sample_ref=archive--file--0001"
The response returns a session_id. Each session stores a planned debug
sequence. On a deployment with runtime debug enabled, the same session can
drive live execution. Use the session for tracking analyst notes and building
the reversing evidence trail before escalating to dynamic analysis.
Use case 13 — Use an AI-assisted debug workspace
When: An analyst wants step-by-step AI guidance through a reversing or triage sequence — the AI proposes what to examine next, explains what it sees, and updates its plan as the analyst advances.
# Create the workspace
curl -X POST "http://localhost:8000/api/malwaregraph/analyses/{job_id}/debug-workspaces?\
sample_ref=archive--file--0001&ai_provider=local"
# Retrieve workspace state
curl http://localhost:8000/api/malwaregraph/debug-workspaces/{session_id}
# Advance by one step
curl -X POST http://localhost:8000/api/malwaregraph/debug-workspaces/{session_id}/step
# Ask the AI assistant a question in workspace context
curl -X POST "http://localhost:8000/api/malwaregraph/debug-workspaces/{session_id}/ai-assistant?\
ai_provider=claude"
Each step advances the AI analysis to the next stage, storing both the
AI output and the evidence it references. The full step history is returned by
the workspace GET endpoint.
Use case 14 — Request decompilation metadata
When: An analyst needs pseudo-code for key functions, an import table reconstruction, or a call graph to understand the control flow of a PE or ELF binary.
curl -X POST "http://localhost:8000/api/malwaregraph/analyses/{job_id}/decompilation?\
sample_ref=archive--file--0001"
The response includes a functions array. Each function entry contains:
name, virtual_address, size, pseudocode, calls_to, called_by,
capability_tags, and evidence_refs.
Use the function names and pseudocode alongside strings output to identify C2 communication routines, encryption implementations, and persistence mechanisms without running the binary.
Use case 15 — Pivot extracted IOCs into AdversaryGraph IOC Intelligence
When: The analysis report identifies an IP, domain, or URL and the analyst wants to see its enrichment history, known threat actor associations, and VirusTotal reputation — all within AdversaryGraph.
The analysis.json report iocs array returns each IOC with its type
and value. From the malwareShared module these translate to routes:
| IOC type | AdversaryGraph route |
|---|---|
| IP / domain / URL | /ioc?value={value} (IOC Investigation) |
| Hash | /ioc-library?q={sha256} |
| Any IOC | /ioc-library?q={value} |
In the Malware Analysis page, clicking any IOC entity calls iocNodeUrl or
iocInvestigationUrl and opens the entity in the correct view.
API pattern for automation:
import httpx, urllib.parse
report = httpx.get(f"http://localhost:8000/api/malwaregraph/analyses/{job_id}/report").json()
for ioc in report.get("iocs", []):
if ioc["type"] in ("ip", "domain", "url"):
enrich = httpx.get(
f"http://localhost:8000/api/ioc/investigate",
params={"value": ioc["value"]}
).json()
print(ioc["value"], enrich.get("risk_score"))
Use case 16 — Pivot ATT&CK TTPs into the Navigator
When: The AI full analysis or report produces ATT&CK technique candidates and the analyst wants to see them on the MITRE ATT&CK matrix, compare them to known actor profiles, or export a Navigator layer.
From the report attack_mappings array, each entry has a technique_id
(e.g., T1059.001). Navigate to:
/navigator?technique=T1059.001
Or use the matrix comparison view to see which tracked groups share those techniques:
/compare?techniques=T1059.001,T1055,T1027
In the MalwareAnalysis page, clicking a TTP badge calls ttpNavigatorUrl
and opens the technique in the ATT&CK Matrix view.
Use case 17 — Send a custom LLM prompt about a sample
When: The analyst wants to ask a specific question about the binary — for example, "does this function look like a configuration decoder?" or "what YARA rule would match this string cluster?" — without waiting for the full AI pipeline.
curl -X POST http://localhost:8000/api/malwaregraph/llm/complete \
-H "Content-Type: application/json" \
-d '{
"provider": "claude",
"system": "You are a malware reverse engineering assistant. Be concise and technical.",
"prompt": "The following strings were extracted from a PE sample. Classify each as an IOC, WinAPI call, C2 indicator, or persistence key:\n\nCreateRemoteThread\nHKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\n192.168.1.100:4444\nMozilla/5.0 (compatible; MSIE 10.0)"
}'
Valid provider values: local, claude, openai, gemini, minimax.
The model field is optional — omit it to use the provider's configured
default. The system prompt is optional (max 50 000 chars). The prompt
field is required (max 800 000 chars for large string dumps).
Use case 18 — Monitor analysis progress with logs
When: A job is taking longer than expected, an unpack or AI step failed silently, or the analyst is troubleshooting why a sample shows no findings.
# Per-job log with the most recent 100 lines
curl "http://localhost:8000/api/malwaregraph/analyses/{job_id}/logs?limit=100"
# Service-level log (worker startup, queue errors, API exceptions)
curl "http://localhost:8000/api/malwaregraph/logs?limit=200"
Look for:
[ERROR]entries indicating a worker crash or timeout- Packer/unpacker log lines showing why static unpack failed
- AI provider error codes if the LLM call was rejected
- Stage transitions (
static_triage → strings → unpack → ai_synthesis)
For real-time tailing inside Docker:
docker compose logs -f malwaregraph
Use case 19 — Run the full workflow against the standalone service directly
When: AdversaryGraph is not running or the analyst is scripting a batch pipeline and wants to hit MalwareGraph directly without the proxy layer.
Set MALWAREGRAPH_URL=http://localhost:8100 in the environment, or target
the standalone service directly. All proxy routes have exact equivalents on
the standalone service under /api/v1/ (analyses, debug, etc.) and /api/
(health, llm).
import httpx, time
BASE = "http://localhost:8100"
with open("malware.exe", "rb") as f:
resp = httpx.post(
f"{BASE}/api/v1/analyses",
files={"file": ("malware.exe", f, "application/octet-stream")},
data={"dynamic_analysis": "false"},
timeout=180,
)
job_id = resp.json()["job_id"]
# Poll until complete
while True:
status = httpx.get(f"{BASE}/api/v1/analyses/{job_id}").json()
if status.get("status") not in ("queued", "running"):
break
time.sleep(3)
report = httpx.get(f"{BASE}/api/v1/analyses/{job_id}/report").json()
strings = httpx.get(f"{BASE}/api/v1/analyses/{job_id}/strings",
params={"sample_ref": "archive--file--0001", "min_chars": 6}).json()
Results produced in standalone mode are import-compatible with AdversaryGraph
because both use the same analysis.json contract.
Use case 20 — Enable and use dynamic unpack (policy-gated)
When: Static unpack consistently fails (the packer requires execution to resolve imports, decrypt stages, or reconstruct the PE header) and the analyst has a dedicated, network-isolated sandbox deployment.
Prerequisites:
- Set
MALWAREGRAPH_ENABLE_DYNAMIC_DEBUG=truein the MalwareGraph service environment. - Confirm the service is running in a disposable VM or microVM with no production network route.
- Review and accept the runtime disclaimer in the UI or set
runtime_debug_disclaimer_accepted=truein the API request.
curl -X POST "http://localhost:8000/api/malwaregraph/analyses/{job_id}/unpack/runtime?\
sample_ref=archive--file--0001&dynamic_analysis=true&runtime_debug_disclaimer_accepted=true"
After the dynamic unpack completes, new sample_ref targets appear in the
analysis. Re-run strings and AI analysis against the unpacked output for a
significantly better signal-to-noise ratio compared to the packed binary.
Never enable dynamic analysis on a host that has access to production networks, databases, or shared infrastructure.
Use case 21 — Associate an analysis job with an investigation case
When: Multiple samples are related to the same incident and the analyst
wants to track them as a single case (e.g., INC-2026-042).
curl -X POST http://localhost:8000/api/malwaregraph/analyses \
-F "file=@loader.dll" \
-F "case_id=INC-2026-042"
curl -X POST http://localhost:8000/api/malwaregraph/analyses \
-F "file=@c2-config.bin" \
-F "case_id=INC-2026-042"
All jobs in the list endpoint can be filtered by case_id. In the UI, the
case ID is saved in browser local storage alongside the human-readable case
name (adversarygraph.malwareCases.v1), so the name persists across page
reloads and survives a backend restart.
Use case 22 — Check available LLM providers before analysis
When: The analyst wants to know which AI providers are currently
reachable before choosing ai_provider for a long-running AI full analysis.
curl http://localhost:8000/api/malwaregraph/llm/providers
A healthy response lists each configured provider with its availability flag.
A provider is considered unavailable if its API key is missing or the endpoint
is unreachable. Use local if no external providers are configured — this
points to a local OpenAI-compatible model server.
Use case 23 — Run analysis against a specific extracted file (multi-target)
When: An archive contained three files — a loader, a payload, and a config blob — and the analyst needs to repeat every analysis step on the payload specifically.
# First, identify all targets
curl http://localhost:8000/api/malwaregraph/analyses/{job_id} | \
python3 -c "import sys,json; [print(x['target_entity_id'], x.get('file_type'), x.get('sha256')) \
for x in json.load(sys.stdin).get('first_analyses', [])]"
# Example output:
# archive--file--0001 zip a1b2c3...
# archive--file--0002 pe d4e5f6... ← the payload
# archive--file--0003 data 789abc...
# Now run strings on the payload only
curl "http://localhost:8000/api/malwaregraph/analyses/{job_id}/strings?\
sample_ref=archive--file--0002&ai=true&ai_provider=local"
# And AI full analysis on the payload
curl -X POST "http://localhost:8000/api/malwaregraph/analyses/{job_id}/ai-full-analysis?\
sample_ref=archive--file--0002&ai_provider=claude"
Every API endpoint that accepts sample_ref defaults to archive--file--0001
if omitted. Always set sample_ref explicitly when working with a specific
extracted target.
Use case 24 — Export analysis findings for detection engineering
When: The analyst has completed a malware analysis and wants to hand off IOCs, ATT&CK mappings, and detection drafts to the detection engineering team.
Step 1 — Get the full report:
curl http://localhost:8000/api/malwaregraph/analyses/{job_id}/report > analysis-report.json
Step 2 — Extract IOCs as CSV:
import json, csv, sys
report = json.load(open("analysis-report.json"))
with open("iocs.csv", "w", newline="") as f:
w = csv.DictWriter(f, fieldnames=["type", "value", "confidence", "source_stage"])
w.writeheader()
for ioc in report.get("iocs", []):
w.writerow({k: ioc.get(k, "") for k in w.fieldnames})
Step 3 — Push ATT&CK mappings into Navigator:
Navigate to /navigator in AdversaryGraph and import the
attack_mappings technique IDs. Accepted and validated techniques can be
compared against existing actor profiles in the Compare view to identify
overlaps with tracked groups.
Step 4 — Open detection drafts in Detection Studio:
The AI full analysis detection_draft section contains raw YARA and Sigma
candidates. Open Detection Studio (/detection-studio) and paste them for
validation and refinement with the malware evidence already on-screen.
Error codes and troubleshooting
| HTTP code | Cause | Fix |
|---|---|---|
422 | Invalid provider value in LLM complete, or missing required file in submit | Check the provider pattern (local|claude|openai|gemini|minimax); attach a file |
502 | MalwareGraph service unreachable or returned invalid JSON | Check docker compose ps malwaregraph; inspect with GET /api/malwaregraph/health |
504 | Request to MalwareGraph timed out | Increase MALWAREGRAPH_LONG_TIMEOUT_SECONDS for heavy AI/RE operations; check worker load |
404 on save-unpacked | No unpacked layers exist for this job | Run unpack first; wait for it to complete |
500 on save-unpacked | Storage volume permission error | Check MALWAREGRAPH_STORAGE_DIR is writable by the API container |
Job stuck in running | Worker crash or AI provider rate limit | Tail logs: docker compose logs -f malwaregraph |
| Empty strings result | min_chars too high, or sample is fully packed | Lower min_chars to 3; run unpack first, then re-run strings on the unpacked target |
| AI returns no TTPs | Sample is a loader stub with no visible behavior | The real payload is packed; unpack first, then re-run AI analysis with prefer_unpacked_output=true |
Accepted sample types
The upload endpoint accepts:
.zip .exe .dll .apk .dex .so .elf .bin .dat
.ps1 .js .vbs .bat .cmd
application/zip
application/vnd.android.package-archive
application/octet-stream
For any format not listed, submit as raw bytes with content type
application/octet-stream. The file classifier will attempt identification
via magic bytes and entropy analysis regardless of the provided extension.