Skip to content

Repository files navigation

MALTER

MALTER is a CLI-first malware analysis pipeline for defensive research and reporting. It does not execute samples on the local host. Static analysis runs locally, and any dynamic execution must be delegated to a CAPEv2 sandbox.

MALTER is the analysis executor and normalizer, and Codex is the default orchestration layer for every analysis run. mal-agent analyze always writes Codex-ready artifacts, especially summary.json, agent_brief.json, next_actions.json, and analysis_runbook.md; there is no separate non-Codex analysis mode.

Safety Rules

  • Do not execute samples on the local host.
  • Dynamic analysis is allowed only through CAPEv2.
  • Analyzer failures are recorded per analyzer and should not stop the whole pipeline.
  • summary.json is the source of truth.
  • LLM or Codex output must not overwrite analyzer findings.
  • Static string IOCs remain candidates unless confirmed by stronger evidence.

Current Features

  • mal-agent analyze for sample analysis.
  • mal-agent analyze-dir for sequential batch analysis.
  • mal-agent serve for the minimal HTTP API.
  • mal-agent worker for processing durable API jobs separately from the API process.
  • mal-agent report for regenerating readable outputs from summary.json.
  • mal-agent validate-artifacts for report quality gating and safe release-bundle regeneration.
  • mal-agent cape-check for CAPEv2 connectivity checks.
  • mal-agent tool-check for cached local tool and adapter discovery.
  • mal-agent agent-brief for regenerating mandatory Codex orchestration artifacts from summary.json.
  • mal-agent ida-session for Codex-guided iterative IDA MCP follow-up on suspicious imports/functions.
  • mal-agent cape-session for Codex-guided CAPEv2 telemetry interpretation, IOC promotion, and evidence linking.
  • mal-agent codex-orchestrate for running the IDA MCP and CAPEv2 Codex follow-up loops together.
  • mal-agent completion-harness for final-design regression checks across tests, safe fixtures, artifacts, docs, and the durable worker queue.
  • File triage and profile routing for PE, .NET PE, ELF, Mach-O, scripts, documents, archives, APKs, and unknown files.
  • Common static analyzers: hash, magic, strings, entropy, IOC candidates, and YARA.
  • PE metadata via pefile, optional LIEF metadata for PE/ELF/Mach-O when available.
  • .NET CLR metadata parsing, optional ILSpyCmd decompilation, and suspicious API scan.
  • PDF/document static metadata, qpdf JSON collection when available, OOXML macro/external relationship hints, OLE directory hints, VBA compressed source recovery, static VBA expression deobfuscation, and suspicious keyword/object hints.
  • Script static metadata and suspicious keyword/encoded blob hints.
  • APK ZIP metadata, Android binary XML/text manifest parsing, reconstructed manifest XML, resource reference resolution, DEX/native library/certificate listing, and permission extraction.
  • ZIP/TAR/GZIP archive listing, optional 7-Zip/py7zr listing, and suspicious entry flags without unrestricted extraction.
  • Bounded archive child extraction and recursive static triage.
  • IDA MCP direct integration with optional IDA auto-launch, related binary follow-up, and export import through mal-agent ida-import.
  • Codex-guided IDA MCP static follow-up and Codex-guided CAPEv2 dynamic follow-up.
  • Grounded LLM report generation through local codex exec, using the normal local Codex login/OAuth state instead of an API key stored by MALTER.
  • CAPEv2 client and report normalizer.
  • Generated YARA syntax/self-match validation and optional benign corpus false-positive checks.
  • Correlation records for static/dynamic IOC overlap and related finding context.
  • Export writers for exports/iocs.csv, exports/iocs.stix.json, exports/markdown/report.md, and generated YARA.
  • Minimal HTTP API backed by a pluggable durable queue and a separate worker command.
  • Independent analyzer status values: ok, skipped, unavailable, error.
  • Evidence-linked IOC records where possible.
  • Codex orchestration artifacts: agent_brief.json, next_actions.json, analysis_runbook.md.

Project Layout

src/mal_agent/
  cmd/            CLI entrypoints
  core/           models, config, pipeline, profiles, triage, artifacts
  analyzers/      internal analyzers and external tool adapters
  generators/     IOC, ATT&CK, report, YARA, Codex orchestration writers
  templates/      report templates
docs/
tests/
examples/
packaging/

Installation

python -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install -e .[dev]

Configuration

The CLI reads mal-agent.toml when present, then overlays supported environment variables.

yara_rule_dirs = ["examples/rules"]
yara_benign_corpus_limit = 0 # 0 = scan the complete configured corpus
capa_path = "capa"
capa_rules_path = ".tools/capa-rules"
capa_signatures_path = ".tools/capa-src/sigs"
# 0 disables a fixed deadline; the no-progress watchdog remains active.
capa_timeout_sec = 0
capa_no_progress_timeout_sec = 7200
ilspycmd_path = "ilspycmd"
qpdf_path = "qpdf"
seven_zip_path = "7z"
dnlib_reader_path = "tools/dnlib_reader/bin/malter-dnlib-reader.exe"
strings_min_length = 6
archive_listing_limit = 0 # 0 = retain the complete listing
archive_extract_children = true
archive_max_depth = 3
archive_max_file_count = 500
archive_max_total_extracted_size = 1073741824
archive_max_compression_ratio = 1000.0
generated_yara_benign_corpus_dir = ""

[quality]
auto_orchestrate = true
max_ida_targets = 0 # 0 = no MALTER-side cap
max_callers_per_import = 0 # 0 = no MALTER-side cap
ida_review_batch_size = 50 # batch/checkpoint size, not a coverage cap

[queue]
backend = "sqlite"
external_base_url = ""
external_token = ""
external_token_env = "MAL_AGENT_QUEUE_TOKEN"
timeout_sec = 30
lease_duration_sec = 180
heartbeat_interval_sec = 20
max_pending_jobs = 1000
max_request_bytes = 262144
request_read_timeout_sec = 30
min_free_disk_bytes = 5368709120
hot_terminal_jobs = 10000

[cape]
base_url = "https://cape.example.org"
token = ""
verify_tls = true
poll_interval_sec = 10
max_wait_sec = 1200
request_full_logs = true
api_cap = 0 # omit CAPE api-cap; CAPE-native limits may still apply

[ida_mcp]
enabled = true
auto_launch = true
ida_path = ""
host = "127.0.0.1"
port = 13337
endpoint_path = "/mcp"
token = ""
launch_args = ["-A"]
launch_timeout_sec = 90
launch_port_search_limit = 20
launch_visible = false
terminate_on_timeout = true
terminate_on_completion = true
analyze_related_binaries = true
auto_launch_related = true
related_max_binaries = 0
related_search_dirs = ["sample_dir"]
related_include_system_dirs = false
related_port_step = 1
load_tool = ""
functions_tool = "list_funcs"
imports_tool = "imports"
xrefs_tool = "xrefs_to"
decompile_tool = "decompile"
max_xref_targets = 0
max_decompile_functions = 0
max_function_records = 0
max_import_records = 0
max_xref_records = 0
max_decompiler_records = 0

[codex_cli]
enabled = true
executable = "codex"
model = "gpt-5.6-sol"
reasoning_effort = "max"
timeout_sec = 0 # no fixed wall-clock deadline
no_progress_timeout_sec = 1800
sandbox = "read-only"
approval_policy = "never"
max_prompt_chars = 0 # no MALTER-side character truncation
max_output_chars = 0 # no MALTER-side character truncation

[llm]
enabled = true
provider = "codex_cli"
input_mode = "full"
max_input_iocs = 0
max_input_evidence = 0

[dotnet_mcp]
enabled = false
endpoint = ""
token = ""
tool_name = "analyze_dotnet"
timeout_sec = 60
use_tools_call = true

[document_mcp]
enabled = false
endpoint = ""
token = ""
tool_name = "analyze_document"
timeout_sec = 60
use_tools_call = true

[script_mcp]
enabled = false
endpoint = ""
token = ""
tool_name = "analyze_script"
timeout_sec = 60
use_tools_call = true

[archive_mcp]
enabled = false
endpoint = ""
token = ""
tool_name = "analyze_archive"
timeout_sec = 60
use_tools_call = true

[apk_mcp]
enabled = false
endpoint = ""
token = ""
tool_name = "analyze_apk"
timeout_sec = 60
use_tools_call = true

[unknown_mcp]
enabled = false
endpoint = ""
token = ""
tool_name = "analyze_unknown"
timeout_sec = 60
use_tools_call = true

Environment overrides:

  • MAL_AGENT_YARA_RULE_DIRS
  • MAL_AGENT_CAPA_PATH
  • MAL_AGENT_CAPA_RULES_PATH
  • MAL_AGENT_CAPA_SIGNATURES_PATH
  • MAL_AGENT_CAPA_TIMEOUT_SEC
  • MAL_AGENT_ILSPYCMD_PATH
  • MAL_AGENT_QPDF_PATH
  • MAL_AGENT_SEVEN_ZIP_PATH
  • MAL_AGENT_DNLIB_READER_PATH
  • MAL_AGENT_AUTO_ORCHESTRATE
  • MAL_AGENT_MAX_IDA_TARGETS
  • MAL_AGENT_MAX_CALLERS_PER_IMPORT
  • MAL_AGENT_LLM_INPUT_MODE
  • MAL_AGENT_CODEX_CLI_MODEL
  • MAL_AGENT_CODEX_CLI_REASONING_EFFORT
  • MAL_AGENT_CODEX_CLI_TIMEOUT_SEC
  • MAL_AGENT_CODEX_CLI_MAX_PROMPT_CHARS
  • MAL_AGENT_CODEX_CLI_MAX_OUTPUT_CHARS
  • MAL_AGENT_STRINGS_MIN_LENGTH
  • MAL_AGENT_ARCHIVE_LISTING_LIMIT
  • MAL_AGENT_ARCHIVE_EXTRACT_CHILDREN
  • MAL_AGENT_ARCHIVE_MAX_DEPTH
  • MAL_AGENT_ARCHIVE_MAX_FILE_COUNT
  • MAL_AGENT_ARCHIVE_MAX_TOTAL_EXTRACTED_SIZE
  • MAL_AGENT_YARA_BENIGN_CORPUS_DIR
  • MAL_AGENT_CAPE_BASE_URL
  • MAL_AGENT_CAPE_TOKEN
  • MAL_AGENT_CAPE_VERIFY_TLS
  • MAL_AGENT_CAPE_POLL_INTERVAL_SEC
  • MAL_AGENT_CAPE_MAX_WAIT_SEC
  • MAL_AGENT_IDA_MCP_ENDPOINT
  • MAL_AGENT_IDA_MCP_TOKEN
  • MAL_AGENT_IDA_MCP_ENABLED
  • MAL_AGENT_IDA_PATH
  • MAL_AGENT_IDA_MCP_HOST
  • MAL_AGENT_IDA_MCP_PORT
  • MAL_AGENT_IDA_MCP_ENDPOINT_PATH
  • MAL_AGENT_IDA_MCP_AUTO_LAUNCH
  • MAL_AGENT_IDA_MCP_LAUNCH_TIMEOUT_SEC
  • MAL_AGENT_IDA_MCP_LAUNCH_PORT_SEARCH_LIMIT
  • MAL_AGENT_IDA_MCP_LAUNCH_VISIBLE
  • MAL_AGENT_IDA_MCP_TERMINATE_ON_TIMEOUT
  • MAL_AGENT_IDA_MCP_ANALYZE_RELATED_BINARIES
  • MAL_AGENT_IDA_MCP_RELATED_MAX_BINARIES
  • MAL_AGENT_IDA_MCP_RELATED_INCLUDE_SYSTEM_DIRS
  • MAL_AGENT_CODEX_CLI_ENABLED
  • MAL_AGENT_CODEX_CLI_EXECUTABLE
  • MAL_AGENT_CODEX_CLI_MODEL
  • MAL_AGENT_CODEX_CLI_TIMEOUT_SEC
  • MAL_AGENT_CODEX_CLI_SANDBOX
  • MAL_AGENT_CODEX_CLI_APPROVAL_POLICY
  • MAL_AGENT_CODEX_CLI_MAX_PROMPT_CHARS
  • MAL_AGENT_CODEX_CLI_MAX_OUTPUT_CHARS
  • MAL_AGENT_LLM_ENABLED
  • MAL_AGENT_LLM_PROVIDER
  • MAL_AGENT_LLM_MAX_INPUT_IOCS
  • MAL_AGENT_LLM_MAX_INPUT_EVIDENCE
  • MAL_AGENT_DOTNET_MCP_ENABLED, MAL_AGENT_DOTNET_MCP_ENDPOINT, MAL_AGENT_DOTNET_MCP_TOKEN, MAL_AGENT_DOTNET_MCP_TOOL_NAME, MAL_AGENT_DOTNET_MCP_TIMEOUT_SEC, MAL_AGENT_DOTNET_MCP_USE_TOOLS_CALL
  • MAL_AGENT_DOCUMENT_MCP_ENABLED, MAL_AGENT_DOCUMENT_MCP_ENDPOINT, MAL_AGENT_DOCUMENT_MCP_TOKEN, MAL_AGENT_DOCUMENT_MCP_TOOL_NAME, MAL_AGENT_DOCUMENT_MCP_TIMEOUT_SEC, MAL_AGENT_DOCUMENT_MCP_USE_TOOLS_CALL
  • MAL_AGENT_SCRIPT_MCP_ENABLED, MAL_AGENT_SCRIPT_MCP_ENDPOINT, MAL_AGENT_SCRIPT_MCP_TOKEN, MAL_AGENT_SCRIPT_MCP_TOOL_NAME, MAL_AGENT_SCRIPT_MCP_TIMEOUT_SEC, MAL_AGENT_SCRIPT_MCP_USE_TOOLS_CALL
  • MAL_AGENT_ARCHIVE_MCP_ENABLED, MAL_AGENT_ARCHIVE_MCP_ENDPOINT, MAL_AGENT_ARCHIVE_MCP_TOKEN, MAL_AGENT_ARCHIVE_MCP_TOOL_NAME, MAL_AGENT_ARCHIVE_MCP_TIMEOUT_SEC, MAL_AGENT_ARCHIVE_MCP_USE_TOOLS_CALL
  • MAL_AGENT_APK_MCP_ENABLED, MAL_AGENT_APK_MCP_ENDPOINT, MAL_AGENT_APK_MCP_TOKEN, MAL_AGENT_APK_MCP_TOOL_NAME, MAL_AGENT_APK_MCP_TIMEOUT_SEC, MAL_AGENT_APK_MCP_USE_TOOLS_CALL
  • MAL_AGENT_UNKNOWN_MCP_ENABLED, MAL_AGENT_UNKNOWN_MCP_ENDPOINT, MAL_AGENT_UNKNOWN_MCP_TOKEN, MAL_AGENT_UNKNOWN_MCP_TOOL_NAME, MAL_AGENT_UNKNOWN_MCP_TIMEOUT_SEC, MAL_AGENT_UNKNOWN_MCP_USE_TOOLS_CALL
  • MAL_AGENT_QUEUE_BACKEND
  • MAL_AGENT_QUEUE_BASE_URL
  • MAL_AGENT_QUEUE_TOKEN
  • MAL_AGENT_QUEUE_TOKEN_ENV
  • MAL_AGENT_QUEUE_TIMEOUT_SEC

Do not hardcode CAPE tokens, profile MCP tokens, API keys, or other secrets in code. codex_cli uses the local codex exec command and the normal local Codex login/OAuth state. LLM report text is generated through codex exec, not by MALTER calling an OpenAI-compatible HTTP API.

CLI Usage

Default maximum-quality analysis. This runs local static analyzers, configured deep adapters, CAPEv2 dynamic analysis through the sandbox, Codex-guided IDA/CAPE follow-up loops, and then regenerates the grounded report from the enriched summary.json:

mal-agent analyze .\sample.exe

The quality defaults use gpt-5.6-sol with max reasoning and apply no MALTER-side Codex prompt/output/time cap or IDA target/record cap. 0 means “unlimited by MALTER”; Codex, IDA MCP, CAPEv2, the account, and the operating environment can still enforce native limits. Archive extraction retains path/depth/file-count/total-size safety guards.

Run the baseline pipeline without automatic Codex follow-up loops when explicitly needed:

mal-agent analyze .\sample.exe --no-orchestrate

Static-only analysis, with deep and dynamic stages disabled:

mal-agent analyze .\sample.exe --no-deep --no-dynamic

The CLI prints live pipeline progress at the default INFO log level:

[pipeline] running: file_triage
[pipeline] analyzer dotnet_metadata: skipped
[pipeline] analyzer ida_mcp: unavailable
[pipeline] analyzer cape: error

Use --log-level ERROR when only failures and final exit status should be shown.

Write artifacts to a specific directory:

mal-agent analyze .\sample.exe --output-dir .\artifacts\sample-run

Resume from an existing summary.json and regenerate final artifacts:

mal-agent analyze .\sample.exe --output-dir .\artifacts\sample-run --resume

Fetch CAPEv2 dynamic results from an existing task/report:

mal-agent analyze .\sample.exe --cape-task-id 1234
mal-agent analyze .\sample.exe --cape-report .\cape_report.json

Batch analyze a directory:

mal-agent analyze-dir .\samples --output-dir .\artifacts

analyze-dir snapshots batch_manifest.json before analysis and excludes its artifact subtree, so generated reports cannot be ingested as new samples. The sample directory itself cannot also be the output directory.

Run the HTTP API:

mal-agent serve --host 127.0.0.1 --port 8080 --jobs-dir .\.malter_api

Process queued API jobs in a separate worker:

mal-agent worker --jobs-dir .\.malter_api

For single-process local testing only:

mal-agent serve --host 127.0.0.1 --port 8080 --jobs-dir .\.malter_api --embedded-worker

Check CAPEv2 connectivity:

mal-agent cape-check

Cache local tool and adapter availability:

mal-agent tool-check --output-dir .malter_tools

Regenerate report artifacts:

mal-agent report .\artifacts\<sha256>\summary.json

Validate delivery readiness and rebuild the safe report package:

mal-agent validate-artifacts .\artifacts\<sha256>

Regenerate Codex orchestration artifacts:

mal-agent agent-brief .\artifacts\<sha256>

Run Codex-guided IDA MCP follow-up after baseline analysis. When codex_cli.enabled=true, this invokes local codex exec to prioritize IDA MCP targets before MALTER performs allowlisted xrefs_to, decompile, and disasm calls:

mal-agent ida-session .\artifacts\<sha256>

If the artifact's saved IDA MCP endpoint is stale and ida_mcp.auto_launch=true, ida-session relaunches IDA, refreshes ida_mcp.json, then continues targeted xref/decompiler review.

Run Codex-guided CAPEv2 follow-up after dynamic analysis. When codex_cli.enabled=true, this invokes local codex exec to review normalized CAPE telemetry and return follow-up priorities as JSON:

mal-agent cape-session .\artifacts\<sha256>

cape-session reads summary.json and cape_report.json, creates analyzer_results/codex_cape_session.json, links CAPE telemetry to evidence IDs, promotes CAPE-observed IOCs to confirmed, and refreshes summary.json, iocs.json, report.md, and Codex next actions. If dynamic telemetry is missing or failed, it submits to CAPEv2 by default; use --no-submit to inspect existing artifacts only, or --cape-task-id/--cape-report to attach known CAPE results.

Manually rerun both Codex follow-up loops for an existing artifact directory:

mal-agent codex-orchestrate .\artifacts\<sha256>

This is the explicit rerun form of the follow-up stage already enabled by a default analyze. It runs the IDA MCP static follow-up where the profile supports it and the CAPEv2 dynamic follow-up, refreshes the final LLM report, and writes analyzer_results/codex_orchestration.json.

Run the final-design completion harness:

mal-agent completion-harness --output-dir .tmp\completion_harness

Import real IDA MCP JSON output:

mal-agent ida-import .\artifacts\<sha256> .\ida_mcp_export.json

Analysis Pipeline

mal-agent analyze <sample> follows this path. Codex orchestration is part of the default pipeline, not an optional mode:

config load
sample validation
hash calculation
artifact directory creation
run_state.json initialization
file triage
AnalyzerProfile selection
common static analyzers
format metadata analyzers and optional external adapters
default CAPEv2 dynamic analysis attempt
post-CAPE static and dynamic capa rule matching
IOC / ATT&CK / Detection / Correlation / Confidence / Recommendation generation
generated YARA creation and validation
summary.json, iocs.json, report.md generation
exports generation
mandatory Codex orchestration artifact generation

Implemented routing:

File family Profile Current behavior
PE PeProfile PE metadata, optional LIEF, YARA, common static, CAPEv2, then static/dynamic capa
.NET PE DotNetProfile CLR metadata parsing, optional dnlib reader, optional ILSpyCmd, PE/common static
ELF ElfProfile Magic triage, optional LIEF metadata and hardening interpretation, common static, IDA MCP when configured
Mach-O MachOProfile Magic triage, optional LIEF metadata and code-signing/entitlement interpretation, common static, IDA MCP when configured
Script ScriptProfile Safe text decoding, suspicious keyword/encoded blob hints, common static
PDF/Office/RTF DocumentProfile Safe static keyword/object hints, optional qpdf JSON, OOXML/OLE macro and relationship hints, recovered VBA source, no host opening
Archives ArchiveProfile Limited ZIP/TAR/GZIP/7-Zip-style listing, bounded child extraction, recursive static triage
APK ApkProfile APK ZIP metadata, text/binary manifest parsing and XML reconstruction, DEX/native library/certificate listing
Unknown UnknownProfile Hash, magic, strings, entropy, IOC candidates, YARA

Artifacts

Each run writes to artifacts/<sha256>/ by default, or to --output-dir when supplied.

Always generated:

  • summary.json: full normalized source of truth
  • iocs.json: compact IOC projection
  • report.md: analyst-readable report
  • run_state.json: run status
  • agent_brief.json: compact Codex-readable brief
  • next_actions.json: recommended next analysis actions
  • analysis_runbook.md: human-readable continuation runbook
  • tool_inventory.json: cached local tool/module/adapter inventory
  • report_quality.json: machine-readable delivery quality gate

Codex orchestration markers are also always present:

  • summary.json.agent_summary.orchestrator = "codex"
  • summary.json.agent_summary.orchestration_mode = "codex_required"
  • summary.json.orchestration_hints.mode = "codex_required"
  • agent_brief.json.orchestration_mode = "codex_required"
  • next_actions.json.orchestration_mode = "codex_required"

Conditionally generated:

  • generated.yara: conservative draft rule when enough stable strings exist
  • cape_report.json: saved when CAPE data is loaded or fetched

Export artifacts:

  • exports/iocs.csv
  • exports/iocs.stix.json
  • exports/markdown/report.md
  • exports/yara/generated.yar when a generated rule exists
  • exports/manifest.json
  • exports/release_manifest.json
  • exports/malter_report_bundle.zip: safe report bundle that excludes the sample file and raw/extracted/unpacked payloads
  • completion_harness.json and completion_harness.md when mal-agent completion-harness is run

Analyzer outputs live under analyzer_results/, for example:

analyzer_results/
  hash.json
  file_triage.json
  tool_inventory.json
  magic.json
  strings.json
  entropy.json
  ioc_candidates.json
  yara.json
  common_static.json
  pe_metadata.json
  lief_metadata.json
  dotnet_metadata.json
  document_metadata.json
  archive_metadata.json
  script_metadata.json
  apk_metadata.json
  capa.json
  ida_mcp.json
  codex_ida_session.json
  yara_validation.json

Report Delivery Quality

Every generated report now includes a delivery gate in report_quality.json. It validates that the human report is consistent with summary.json, the full iocs.json payload matches, evidence IDs are unique and resolvable, required artifacts exist, enum values are rendered as analyst-readable text, weak IOC candidates are downgraded, partial IDA MCP coverage is disclosed, failed dynamic analysis is visible, generated YARA is marked as draft-only when appropriate, and safety warnings remain present.

Current summaries use schema 1.1.0 and emit the canonical detections field. Schema 1.0.0 summaries using detection are migrated on read; newly written artifacts never emit both keys.

Readiness values:

Readiness Meaning
final_ready No blockers or warnings were detected by the delivery gate.
triage_ready The artifact package is coherent, but warnings such as partial IDA coverage, missing analyzer evidence, or draft YARA require analyst follow-up.
not_ready A blocker exists; do not distribute the report until fixed.

exports/release_manifest.json records file hashes and explicitly marks the sample as excluded. exports/malter_report_bundle.zip contains only delivery artifacts such as summary.json, iocs.json, report.md, report_quality.json, analyzer JSON, IOC exports, and runbook files.

Codex Analysis Agent

Codex is not a MALTER internal analyzer. It is the default orchestration layer for MALTER artifacts and decides what to do next from the generated JSON/Markdown outputs. A normal analyze run always produces the Codex files below and marks summary.json.orchestration_hints.mode as codex_required.

Primary files for Codex:

  • agent_brief.json
  • next_actions.json
  • analysis_runbook.md
  • summary.json
  • analyzer_results/*.json
  • cape_report.json when present

next_actions.json can recommend actions such as:

  • run_ida_deep_analysis
  • run_codex_ida_session
  • inspect_ida_functions
  • inspect_ida_xrefs
  • run_dotnet_decompile
  • inspect_dotnet_resources
  • run_cape_dynamic
  • inspect_cape_network
  • inspect_cape_process_tree
  • inspect_pdf_embedded_files
  • inspect_archive_children
  • generate_final_report

See docs/codex_analysis_agent.md for operating rules.

Completion Harness

mal-agent completion-harness is the project-level gate for checking whether the implemented pipeline still matches the final design. It does not execute malware or open active content. It creates benign/synthetic fixtures and runs only static MALTER analysis plus queue processing.

The harness currently checks:

  • python -m pytest unless --skip-pytest is set
  • PE, .NET PE, unknown, PDF, OLE/VBA, archive, and APK profile routing
  • required artifacts: summary.json, iocs.json, report.md, run_state.json, agent_brief.json, next_actions.json, analysis_runbook.md
  • delivery artifacts: report_quality.json, exports/release_manifest.json, and exports/malter_report_bundle.zip
  • normalized analyzer statuses
  • Codex-ready summary fields
  • VBA compressed source recovery into raw/vba/
  • static VBA expression deobfuscation
  • APK manifest XML reconstruction/preservation and resource reference resolution
  • ELF/Mach-O LIEF interpretation and Mach-O Objective-C/Swift semantic hints
  • SQLite durable queue, external HTTP queue adapter, and separate worker processing
  • resume regeneration from an existing summary.json
  • cached tool inventory creation/reuse
  • README/docs blocker scan for open-work markers

Output:

.tmp/completion_harness/
  completion_harness.json
  completion_harness.md
  iteration_1/
    fixtures/
    artifacts/
    jobs/

CAPEv2 Behavior

Dynamic analysis is enabled by default and never runs the sample on the local host. It uses CAPEv2 only, through one of these paths:

  • default mal-agent analyze <sample> submission when CAPE is configured
  • --cape-task-id <id>
  • --cape-report <path-or-url>
  • --no-dynamic disables the CAPEv2 dynamic stage for that run

Behavior:

  • --cape-report loads an existing CAPE JSON report and avoids live submission.
  • --cape-task-id polls and fetches an existing CAPE task/report.
  • Default analyze submits the sample to CAPEv2 using the configured API.
  • CAPE failures are recorded in dynamic.status and do not discard static results.

MALTER never falls back to local sample execution.

Optional Tool Behavior

  • Missing capa: status is unavailable; the run continues.
  • Missing YARA rules or yara-python: status is unavailable; the run continues.
  • Missing LIEF: lief_metadata is unavailable; PE metadata still uses pefile.
  • Missing dnlib reader: internal CLR metadata still runs; dnlib_status is unavailable.
  • Missing ILSpyCmd: CLR metadata and dnlib reader still run; ilspycmd_status is unavailable.
  • Missing qpdf: document keyword/object hints still run; qpdf_status is unavailable.
  • Missing 7-Zip/py7zr: ZIP/TAR/GZIP still list via Python stdlib; 7z/RAR/ISO listing is unavailable.
  • Missing IDA MCP direct access with default deep analysis: analyzer status is unavailable; import real IDA MCP JSON with mal-agent ida-import.
  • IDA MCP auto-launch starts IDA for static loading only. The IDA MCP plugin must open the configured MCP tool endpoint, usually /mcp. If the configured port is busy, MALTER searches for the next free port before launching a new IDA process. If the port does not open before launch_timeout_sec, MALTER records an analyzer error and terminates the IDA process it launched by default. Related binary follow-up defaults to every resolved file in the configured search directories (related_max_binaries = 0), while system directories remain excluded unless explicitly enabled.
  • Native PE/ELF/Mach-O deep analysis routes to IDA MCP. .NET, document, script, archive, APK, and unknown profiles route to their profile-specific static deep adapter and optional <family>_mcp endpoint instead of IDA.
  • Missing profile-specific MCP endpoints do not block analysis; MALTER records the endpoint as unavailable in analyzer_results/profile_deep.json and keeps the profile static fallback summary.
  • Missing CAPEv2 config with default dynamic analysis: dynamic analysis records an error while static artifacts remain.
  • Missing local Codex CLI: LLM report generation and Codex-guided follow-up decisions are skipped or unavailable; analyzer findings remain authoritative.

Resource safety without silent coverage loss

  • Queue workers use owner leases and heartbeats. Only an expired lease can be reclaimed; starting another worker does not requeue live work.
  • Equivalent queued, running, or completed requests are idempotent by default. Use force=true only when a genuinely new analysis is intended.
  • CAPE submission intent and task IDs are persisted in raw/cape/submission.json. File-creation POST requests are never retried automatically; an ambiguous response requires an explicit --cape-task-id recovery.
  • Nested archives share one tree-wide file/byte/compression ledger. Content hashes prevent archive cycles, and unprocessed entries are recorded as deferred in raw/archive/traversal_ledger.json instead of being silently omitted.
  • IDA target and decompiler work is written to per-target shards and checkpoints. The configured batch size controls memory and restart granularity, not total coverage.
  • capa and Codex have no fixed deadline at the quality-first defaults. Their watchdog fires only after output, CPU, and I/O all show no progress for the configured interval.
  • Admission pauses before the queue/artifact volume crosses the free-disk watermark. Old terminal queue metadata is moved to jobs_archive.sqlite3 rather than discarded.

Current Implementation Boundaries

Implemented:

  • Python internal analyzers
  • PE metadata
  • optional LIEF metadata for PE/ELF/Mach-O with hardening/code-signing interpretation and Mach-O Objective-C/Swift semantic hints
  • YARA scanner
  • generated YARA validator
  • cached tool inventory writer
  • capa adapter
  • CAPEv2 client/report normalization
  • .NET CLR metadata parser, dnlib reader adapter, and optional ILSpyCmd adapter
  • PDF keyword/object hints, OOXML parser, OLE/CFB stream parser, VBA source recovery/static expression deobfuscation, and optional qpdf adapter
  • script metadata analyzer
  • APK ZIP analyzer and Android binary XML/text manifest parser with XML reconstruction and resource reference resolution
  • ZIP/TAR/GZIP listing plus optional 7-Zip/py7zr listing
  • bounded archive child extraction and recursive static triage
  • IDA MCP direct /mcp adapter, optional IDA auto-launch, uncapped-by-default resolved related-binary follow-up, and export import
  • live grounded LLM report generation through local codex exec when configured
  • front-loaded deep routing metadata in analyzer_results/deep_routing.json and profile-specific analyzer_results/profile_deep.json
  • correlation engine
  • export writers
  • sequential batch CLI, minimal HTTP API, SQLite durable queue, external HTTP queue adapter, and separate worker CLI
  • Codex orchestration artifact writer

Design boundaries:

  • VBA analysis uses bounded static expression evaluation and never executes macros.
  • APK XML and resource references are resolved from AndroidManifest.xml, res/values/*.xml, and resources.arsc when present.
  • SQLite is the built-in durable queue; queue.backend=http connects the worker/API flow to an external broker service.

Testing

python -m pytest

Smoke test:

python -m mal_agent.cmd.cli analyze tests\fixtures\samples\minimal_pe.exe --output-dir .tmp\test_artifacts

The test suite uses tiny benign fixtures and does not ship malware.

Packaging

PyInstaller is the first packaging target:

python -m pip install -e .[build]
pyinstaller packaging\pyinstaller.spec

Documentation

  • docs/malter_pipeline_final.md: full pipeline design and current implementation status
  • docs/codex_analysis_agent.md: Codex orchestration rules
  • docs/cape_lab.md: CAPEv2 lab notes, when present

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages