Files
magnus919_agent-skills/binary-analysis/references/triage-workflow.md
T

327 lines
12 KiB
Markdown

# Triage Workflow
Step-by-step methodology for triaging unknown binaries. Load this when the user
provides a binary and asks "what does this do?", "is this suspicious?", or
"analyze this." The triage workflow produces structured evidence — observations,
heuristics, and unknowns — without free-form narrative or unqualified
conclusions.
## Triage Philosophy
The triage is NOT a report. It is evidence collection organized into three
canonical categories:
1. **Observations**: Direct, deterministic facts from the backend. No
interpretation. No `confidence` field. These are true regardless of context.
2. **Heuristics**: Rule-derived interpretations with explicit `confidence`
scores. These are patterns, not proof.
3. **Unknowns**: Explicit unresolved questions tied to specific addresses. These
identify gaps, not failures.
The agent's role is to collect this evidence and then, separately, synthesize a
human-readable assessment. Never present an interpretation as an observation.
## When to Triage
Run a triage when:
- The binary is completely unknown (no prior knowledge of its purpose).
- The user asks "what does this binary do?"
- The user asks "is this suspicious?" or "is this malicious?"
- You need a broad survey before deciding where to deep-dive.
Skip triage when:
- The user asks a specific, narrow question ("decompile function main", "show
imports from ws2_32.dll"). Go straight to focused analysis.
- The binary is already well-understood and you're checking a specific
hypothesis.
## Triage Workflow (Step by Step)
### Step 1: Environment and Project Setup
Before touching the binary, verify the toolchain and create an isolated
workspace.
```bash
# Verify tools are available
binary doctor --json
# Create a project (name it after the binary or analysis purpose)
binary project create triage-<binary-name> --json
# Import in copy mode (default) for reproducibility
binary import /path/to/binary --project triage-<binary-name> --json
```
**Check the import response:**
- `import_mode`: "copy" means the sample is isolated. Good.
- `binary_sha256`: Record this. It's your evidence anchor — every finding ties
back to this hash.
- `format`: If "RAW", note this. Raw-format triage follows a different path
(see Step 6).
**If import returns `success: false`:**
- Exit code 5 (UNSUPPORTED_FORMAT): The file is not a recognized executable
format. Skip to Step 6 for RAW triage.
- Exit code 10 (IMPORT_FAILED): Backend could not load the file. Check
diagnostics. The file may be corrupted.
### Step 2: Analysis Profile Selection
```bash
binary analyze --project triage-<binary-name> --profile standard --json
```
| Profile | When to Use | Time Estimate |
|---------|-------------|---------------|
| `standard` | Default. Covers all structural queries needed for triage. | ~30s-2min |
| `quick` | Large binary (>50MB) or time-constrained. Skips deep function analysis. | ~10s-30s |
| `deep` | Focused security review. Runs all analyzers including data-flow. | ~2min-10min |
**Check the analyze response:**
- `success: true, partial: false` → Proceed to Step 3.
- `success: true, partial: true` → Review diagnostics. Note which analyzers
failed. Proceed with bounded results — they are still evidence.
- `success: false, partial: true` → Timeout (exit code 12). The binary may be
very large or contain deeply nested control flow. Try `--profile quick` or
increase `--timeout`.
- `success: false, partial: false` → Hard failure (exit code 11). Check
diagnostics. The project is now FAILED. Run `binary project clean` to reset.
### Step 3: Run the Automated Triage
```bash
binary triage --project triage-<binary-name> --json
```
The triage command runs the rule engine against backend data and produces the
three-category output. This is your primary evidence source.
**Read the triage output systematically:**
#### 3a. Review Observations (`data.observations[]`)
Observations are bare facts. Scan for:
- **Entry point characteristics**: Address, section location. Is the entry
point in `.text` (normal) or an unusual section?
- **Section layout**: Count, names, flags. Note any RWX sections immediately.
- **Import count and sources**: How many DLLs/libs? Which ones?
- **Export count**: Does this binary export functions? (It may be a library or
have plugin capabilities.)
- **String density and categories**: Many error strings? URLs? File paths?
Registry keys?
- **Compiler/package identification**: Strings like "GCC:", "MSVC", "Go build
ID", "rustc" identify the toolchain.
#### 3b. Review Heuristics (`data.heuristics[]`)
Heuristics are rule-derived interpretations. Each has a `confidence` score.
**Sort by risk and confidence:**
- HIGH confidence + high risk_score → Flag prominently in your assessment.
- LOW confidence + high risk_score → Flag as "possible" with explicit caveat.
- HIGH confidence + low risk_score → Note but don't overstate.
- LOW confidence + low risk_score → Typically noise. Acknowledge but don't
center your assessment on it.
**Common heuristic categories:**
- **API-based**: Suspicious API combinations (process injection, credential
access, network enumeration).
- **Structure-based**: Unusual section flags, high entropy, missing imports.
- **Capability-based**: Networking, cryptography, file system, keylogging,
anti-debugging.
- **Compilation-based**: Packer signatures, known compiler fingerprints, debug
build indicators.
#### 3c. Review Unknowns (`data.unknowns[]`)
Unknowns are specific gaps — things the backend could not resolve:
- **Indirect call targets**: `call eax` where the target is computed at
runtime. The backend can't follow this.
- **Unresolved imports**: Symbols resolved via `GetProcAddress`/`dlsym`.
- **Encrypted/obfuscated regions**: Areas the decompiler couldn't penetrate.
Each unknown has an `address` and a `question`. These are your todo list for
deeper analysis — each one is a candidate for `binary decompile` or `binary
disassemble`.
### Step 4: Follow Up with Focused Analysis
The triage output tells you where to dig deeper. Prioritize:
1. **High-confidence suspicious heuristics** → Run `binary suspicious-apis` for
detailed API risk scoring.
2. **High-confidence capability heuristics** → Run `binary capability-map` to
map functional areas with evidence sources.
3. **Unresolved unknowns at key addresses** → Run `binary decompile` or
`binary disassemble` on the surrounding function.
4. **Unusual import patterns** → Run `binary imports` for full resolution
status, then `binary callees` on suspicious functions.
5. **Interesting strings** → Run `binary xrefs` on the string's address to
find which code references it.
```bash
# Deepen API analysis
binary suspicious-apis --project triage-<binary-name> --json
# Map capabilities with evidence
binary capability-map --project triage-<binary-name> --json
# Investigate a specific function
binary decompile --project triage-<binary-name> function:<name> --json
binary xrefs --project triage-<binary-name> function:<name> --json
binary callers --project triage-<binary-name> function:<name> --json
binary callees --project triage-<binary-name> function:<name> --json
```
### Step 5: Check Diagnostics
Always review diagnostics before presenting findings:
```bash
binary diagnostics --project triage-<binary-name> --json
```
Diagnostics reveal:
- **Timeouts**: Some analyzers didn't finish. Your results are incomplete.
- **Backend limitations**: Certain analyses aren't supported for this format or
architecture.
- **Partial failures**: Specific modules failed but analysis continued.
- **Memory or output limits**: Results were truncated.
**Never silently ignore diagnostics.** If a diagnostic says "function analysis
incomplete for 5 of 200 functions," mention this when you present the function
count. Incomplete evidence is still evidence, but it must carry that caveat.
### Step 6: RAW Format Triage
When `binary metadata` reports `format: "RAW"`, the standard triage workflow
may produce limited results. Adjust:
1. **String analysis is your primary tool:**
```bash
binary strings --project triage-<binary-name> --min-length 6 --json
```
Scan for:
- Compiler/OS identification strings
- Error messages (reveal functionality)
- URLs, IP addresses, file paths
- Function names from stripped debug info
- Format signatures (maybe it's a known container format)
2. **Entropy analysis via `binary sections`:**
High entropy across the entire file → compressed or encrypted. Low entropy
with visible strings → flat firmware or raw code.
3. **Byte-level analysis:**
```bash
binary bytes --project triage-<binary-name> 0x0 256 --json
```
Look at the first 256 bytes for any magic bytes or structure.
4. **Check for known firmware formats** (see [firmware.md](firmware.md)).
5. **Check for packing** (see
[packed-and-obfuscated.md](packed-and-obfuscated.md)).
6. If nothing works, report the RAW format, file size, entropy, and any
identifiable strings. Flag as `unknown` with the file's physical
characteristics.
### Step 7: Synthesize Findings
After collecting all evidence, synthesize it into a human-readable assessment.
**This is your (the agent's) work — not the CLI's output.**
Structure your synthesis:
```
## CLI Evidence
### Binary Identity
- Format: PE, x86-64, 45,632 bytes
- SHA-256: <hash>
- Compiled: 2024-03-15 (PE timestamp)
- Compiler: MSVC 19.35 (from .rdata strings)
### Structural Observations
- 3 sections: .text (rx), .rdata (r), .data (rw)
- Entry point: 0x140001000 (.text)
- 47 imports from 5 DLLs
- 12 exports (DLL project)
### Security Heuristics
- process-injection (risk_score: 8, confidence: HIGH)
Evidence: VirtualAlloc + WriteProcessMemory + CreateRemoteThread
- credential-access (risk_score: 6, confidence: MEDIUM)
Evidence: CredEnumerateW, CryptUnprotectData
### Unknowns
- Indirect call at 0x140002a80: target not statically resolvable
- GetProcAddress call at 0x140003c10: runtime-resolved APIs unknown
## Agent Assessment
The binary is a DLL that exhibits API patterns consistent with process
injection (confidence: HIGH based on the VirtualAlloc → WriteProcessMemory →
CreateRemoteThread chain) and possible credential harvesting (confidence:
MEDIUM based on DPAPI decryption APIs). The binary uses MSVC and was likely
compiled in early 2024.
Limitations: Several calls are resolved at runtime via GetProcAddress,
meaning the static analysis cannot determine their targets. The
credential-access assessment is based on API presence, not confirmed behavior.
Dynamic analysis would be needed to confirm.
```
## Triage Red Flags (Immediate Action Items)
Some findings should be flagged immediately, even before completing the full
triage:
| Finding | Action |
|---------|--------|
| RWX section | Flag as suspicious. Load [packed-and-obfuscated.md](packed-and-obfuscated.md). |
| High entropy + small import table | Strong packing indicator. Load [packed-and-obfuscated.md](packed-and-obfuscated.md). |
| TLS callbacks / .init_array / __mod_init_func pointing to unusual code | Possible anti-analysis. Note and investigate with `binary decompile`. |
| Known packer signatures in section names (.upx0, .aspack, etc.) | Identify the packer. See [packed-and-obfuscated.md](packed-and-obfuscated.md). |
| Process injection API chain (VirtualAlloc + WriteProcessMemory + CreateRemoteThread) | HIGH confidence suspicious. Flag prominently. |
| Service/driver creation APIs (CreateService, NtLoadDriver) | Possible persistence mechanism. Flag. |
| Network listeners (bind, listen, accept) | Possible backdoor. Flag. |
## Triage Output Limits
The triage command respects result count limits:
- Default: 100 results per category (observations, heuristics, unknowns)
- Maximum: 1000 per category
If the limit is hit, the output is truncated and a diagnostic is emitted.
**Always check `diagnostics` for truncation warnings.** If results were
truncated, report the truncation in your assessment.
## Following Up After Triage
A triage is a starting point, not an endpoint. After presenting the triage
findings, ask the user which direction they want to go:
- **"I want to understand function X"** → Load
[function-analysis.md](function-analysis.md).
- **"How certain are these findings?"** → Load
[evidence-and-confidence.md](evidence-and-confidence.md).
- **"Is this definitely malicious?"** → Explain that static analysis provides
evidence, not verdicts. Offer to produce a report via `binary export-report`.
- **"This looks packed"** → Load
[packed-and-obfuscated.md](packed-and-obfuscated.md).
- **"This is firmware"** → Load [firmware.md](firmware.md).
- **"Generate a report"** → Load [reporting.md](reporting.md).