Skip to content

Analyzer SDK

External analyzers are discovered from the r2inspect.analyzers Python entry point group. A plugin may expose a BaseAnalyzer subclass or a callable that receives the registry.

A complete minimal package is available in the external plugin example.

Analyzer class

from typing import Any

from r2inspect.abstractions.base_analyzer import BaseAnalyzer
from r2inspect.registry import AnalyzerCategory, AnalyzerSpec


class ExampleAnalyzer(BaseAnalyzer):
    spec = AnalyzerSpec(
        id="vendor.example",
        version="1.0.0",
        category=AnalyzerCategory.DETECTION,
        formats=frozenset({"PE"}),
        architectures=frozenset({"x86", "x86_64"}),
        output_schema="vendor.example/v1",
    )

    def analyze(self) -> dict[str, Any]:
        return {"available": True, "detected": False, "evidence": []}

Register it in the plugin package:

[project.entry-points."r2inspect.analyzers"]
example = "vendor_plugin:ExampleAnalyzer"

The loader reads AnalyzerSpec without constructing the analyzer. Classes without a spec retain the legacy metadata-method discovery path; callable entry points should call registry.register() with an explicit category and formats.

Constructors must accept the runtime dependencies used by the analyzer factory: adapter, r2, config, and filepath/filename as applicable. Do not open sessions or perform analysis at import time. Return JSON-compatible values and preserve extraction errors as explicit unavailable/failed results.

Run the conformance suite against a class or installed entry point before publishing a plugin:

r2inspect-plugin-check vendor_plugin:ExampleAnalyzer
r2inspect-plugin-check example --sample ./fixture.exe

The second form constructs the analyzer through the production factory, runs it against the sample, and rejects non-mapping or non-JSON output. The command emits r2inspect.plugin-conformance/v1 JSON and exits non-zero on any failed check.

Independent backends

Backend packages can expose an independent implementation through the r2inspect.backends entry point group. The factory receives filename plus optional configuration and must return a BinaryInspector: analyze, close, __enter__, and __exit__ are required and checked when the backend loads:

[project.entry-points."r2inspect.backends"]
pe-core = "vendor_pe_core:create_backend"

r2inspect --backend consensus --consensus-backend pe-core sample.bin runs radare2 and the independent backend and preserves field-level disagreements as backend_disagreement records instead of silently choosing a value. Each record contains the canonical field path, both backend names and values, severity, and status. Built-in pe-core, elf-core, and macho-core implementations provide this independent structural view without radare2 or optional Python parsers.

Optional engines

The deep and forensic profiles run capa and FLOSS when those executables are on PATH. Deep mode bounds FLOSS to static strings; forensic mode runs full FLOSS extraction and preserves exact capa/FLOSS stdout in its evidence bundle. Missing tools produce dependency_unavailable analyzer outcomes. capa rules are exposed as report capabilities and FLOSS strings as report artifacts. Each external-tool result records the executable path, version, SHA-256 digest, and rule-source digest when applicable.

External commands use bounded temporary-file capture instead of pipes. The defaults limit the captured preview to 4 MiB, each output spool to 256 MiB, resident memory to 1 GiB, child processes to 32, CPU time to 120 seconds, and wall time to the analyzer timeout. Override them with R2INSPECT_CMD_MAX_OUTPUT_BYTES, R2INSPECT_CMD_MAX_SPOOL_BYTES, R2INSPECT_CMD_MAX_MEMORY_MB, R2INSPECT_CMD_MAX_PROCESSES, R2INSPECT_CMD_CPU_SECONDS, and R2INSPECT_CMD_TIMEOUT_SECONDS. Set R2INSPECT_CMD_OUTPUT_DIR to relocate temporary output or R2INSPECT_CMD_SANDBOX_PREFIX to prepend an argv-only sandbox/container wrapper. Set R2INSPECT_CAPA_RULES to select and fingerprint a capa rules path.

Signed YARA pack directories contain manifest.json and are verified before any rule is compiled. Use r2inspect rules build|sign|verify|install|list|update to manage them. Installed packs carry their trusted public key; direct pack paths require R2INSPECT_RULE_PACK_PUBLIC_KEY. Manifest entries that resolve outside the pack directory, including symlink escapes, are rejected. The YARA compile cache is invalidated by the verified manifest digest or by a digest of the rule contents for unpackaged directories.