PARALLAX — About the Project

Inspiration

I wanted to reverse-engineer a real Android banking trojan (SharkBot) and got frustrated by how every tool gives you a verdict but not the story. Static analyzers dump permission lists. Sandbox tools dump raw logs. Threat-intel platforms dump IOC lists. None of them explain why a sample is dangerous in a way a junior analyst can verify.

Around the same time, band-sdk shipped — a Python SDK for real multi-agent chatrooms. That clicked:

What if 8 specialized AI agents argued about an APK in a chatroom, then proved each other right or wrong with real Frida hooks?

The verdict wouldn't be one model's opinion — it'd be the consensus of agents that ran real code against the sample.

That's PARALLAX.


What it does

PARALLAX ingests an Android APK and runs two layers of analysis in parallel:

  1. Static — extracts permissions, intent filters, embedded URLs, cryptographic constants, suspicious API usage (SmsManager, AccessibilityService, Cipher, DexClassLoader).
  2. Dynamic — runs the APK inside an instrumented Frida sandbox, with hypothesis-driven JavaScript hooks that prove or disprove specific behavioral claims.

A Hook Planner agent maps each analytical hypothesis to a concrete Frida JS payload from a curated API dictionary — never hallucinated, only from a whitelisted api_signatures.json of 15 high-signal Android APIs.

A two-layer risk score combines the static signal density with the dynamic observation count into an explainable verdict — BENIGN → LOW → MEDIUM → HIGH → CRITICAL. Every score component is traceable back to a specific finding.

The result: an analyst sees not just "this APK is 9.2 CRITICAL" but "agent X hypothesized SMS exfil, agent Y's Frida hook observed SmsManager.sendTextMessage called with destination +1-XXX from com.target.app, evidence attached."


How I built it

Architecture. Backend in FastAPI + Pydantic v2, frontend in React 19 + Vite + Tailwind, persistence in PostgreSQL + Neo4j + Qdrant (vector) + Redis (queue) + MinIO (raw APK + Frida logs).

The 8 agents

Hosted as band-sdk processes that join a real band.ai chatroom. Each agent filters incoming @mentions, performs its specialty, and replies to the thread:

Agent Role
StaticAnalyst Permissions, intent filters, code structure
DynamicAnalyst Frida hooks in the live sandbox
HookPlanner Maps hypotheses → Frida JS payloads from api_signatures.json
RiskScorer Two-layer risk aggregation
Reasoner Reconciles static + dynamic, writes the narrative
ThreatHunter Cross-references IOCs against the threat-graph
IOCEnricher Enriches URLs, hashes, IPs
ReportWriter Bundles evidence + transcript

The hook grammar

The Hook Planner LLM's output is wrapped in <<<HOOK_START>>>...<<<HOOK_END>>> markers, validated for structure, then injected into Frida via:

setImmediate(() => {
  globalThis.SESSION_ID = "<UUID>";
});

Each hook fires a send() payload with hypothesis_id, captured args, return value, exception info, and caller package derived from the stack trace.

Rate limiting

Each hook checks a per-hook counter — >100 fires in 60s → suppress send(), log locally.


Challenges I ran into

  • API hallucination. The first version of the Hook Planner invented method overloads. Fixed by constraining the LLM to a strict api_signatures.json whitelist and explicitly telling it to emit empty scripts for unsupported behavior (reason: "native code, requires Phase 3.5").

  • Vercel-only deploy, no backend. The live demo at parallax-mocha-beta.vercel.app has no /api/v1/* route. Solved by adding a ?demo=true mode that loads realistic seeded data (1,247 mock submissions, 8-node TAIG graph, ticker of synthetic system events).

  • band-sdk tier limits. Pro tier blocks agent creation on the /me namespace. Initial probes returned 401 — turned out the right path was /api/v1/agent, not /api/v1/me.

  • LLM ease-curve typing. Framer Motion's ease array is typed as a tuple, not number[]. Caught only by tsc -b on a tsx file.

  • Context compression. Multiple times during development, the working context window hit its limit mid-task and the assistant had to recover from a summary. Wrote durable facts to memory so the next session could pick up cleanly.


Accomplishments that I'm proud of

  • An 8-agent chatroom that runs against a real APK. Not a demo of "agents talking about a fake APK" — actual band-sdk Python processes joining a real room, @mention-ing each other, and producing a real evidence bundle.

  • Explainable two-layer risk scoring. Every score component is traceable to a specific finding (static signal + dynamic observation), not a black-box predict_proba() call.

  • No hallucinated hooks. Strict api_signatures.json whitelist means the LLM cannot generate Frida payloads for APIs we haven't vetted. Wrong-but-confident → "empty script + reason comment."

  • A frontend that doesn't lie about being live. Instead of pretending to call a real backend, the ?demo=true mode loads realistic seeded data and tells the analyst exactly that it's a demo.

  • End-to-end evidence trail. From hypothesis → hook → observation → score → narrative, every step is persisted and queryable.


What I learned

  • Multi-agent systems need a real protocol, not a prompt. Treating agents as REST endpoints that @mention each other gave me a clean way to compose them without one agent becoming a god-prompt.

  • Whitelists beat guardrails. Telling the LLM "don't hallucinate" doesn't work. Giving it api_signatures.json and saying "emit empty + reason if not in this list" works.

  • Frontend honesty matters more than frontend polish. A ?demo=true mode that openly tells the user "this is seeded data, not a live backend" is more credible than a fake live system that 404s on every API call.

  • Memory is a tool, not a vibe. Saving durable facts (band-sdk tier limits, Frida version, mock-data shape) into persistent memory meant context-compression handoffs didn't lose critical state.

  • Static analysis alone lies. Dynamic alone lies. Together they're useful. The two-layer risk score is what makes the verdict defensible.


What's next for PARALLAX

  • The verdict is no longer the artifact — the debate is. Tomorrow's malware analyst doesn't want a 9.2 CRITICAL stamp; they want to scrub through 8 agents arguing about whether the SMS sender really is malicious, see which Frida hook settled the argument, and replay the transcript to teach the next junior hire. PARALLAX becomes the Loom for malware reasoning.

  • An agent room per APK, replayable forever. Every submission becomes a persistent, time-ordered, signed transcript — agents, hypotheses, hooks, observations, scores. Drop it in front of a regulator, a court, or a customer. The verdict is reproducible from the transcript alone.

  • Native code is the last frontier. Right now we hand-wave at JNI with an empty hook. Ghidra + radare2 in the loop means the agents reason over decompiled .so libraries the same way they reason over SmsManager.sendTextMessage today. Banking trojans that hide their payload in native code stop being a blind spot.

  • A shared threat-graph for the community. Every PARALLAX submission enriches a global Neo4j graph: families ↔ indicators ↔ samples ↔ hypotheses ↔ observations. The hundredth analyst to look at SharkBot shouldn't start from scratch — they should inherit a 100-debate-deep consensus.

  • Self-tuning hook dictionary. The 15-API api_signatures.json whitelist is hand-curated today. Tomorrow it grows automatically: every new confirmed finding teaches the system which Android APIs are signal-rich and which are noise. The whitelist becomes a living knowledge base, not a static file.

  • Multi-agent consensus as the audit trail. When an agent room disagrees, we surface the disagreement — not paper over it. "StaticAnalyst said LOW, DynamicAnalyst said HIGH because hook X observed Y." That disagreement is the most valuable signal in the system, because that's where the next unknown family will reveal itself.

  • From analyst tool to incident-response co-pilot. A SOC analyst pastes a phishing URL, PARALLAX reverse-engineers the dropper, runs the 8-agent debate, opens a Frida sandbox, and walks back with: "This is SharkBot variant X, here are the IOCs, here is the Frida evidence, here is what the 8 agents disagreed about, and here is what I'd block at the gateway."

Built With

Share this project:

Updates

Submission history