instrumented android session orchestrator.
AVD/physical device management, UI automation via uiautomator2, Frida instrumentation, mitmproxy traffic interception, static analysis, evidence collection. daemon mode with JSON-RPC API and MCP server for agent-driven automation.
git clone https://github.com/AbuCTF/Golem.git
cd Golem
./setup.shhandles everything: system deps, Java, Android SDK, system image, KVM, Python package, static analysis tools. works on arch, debian/ubuntu, fedora, opensuse, void, alpine.
flags: --no-system, --no-sdk, --no-static, --verify.
The current release is v0.7.1. See CHANGELOG.md for the release notes.
The Python package and CLI also use host-native Android SDK paths on Linux,
macOS, and Windows. setup.sh is the Linux bootstrap; on other hosts install
the Android SDK/tooling normally and use pip install -e '.[all,dev]'.
Prepare the latest Play build for the default x86_64 emulator:
golem doctor
golem target com.example.app --workspace ./targets/com.example.appThis resolves the current Play version, downloads and validates its base/split
APK set with gplaydl, decompiles every DEX with JADX, decodes every base/split
manifest and Smali tree with apktool, and writes target.json, analysis.json,
and a source-linked review.md queue. To install and launch in the same command:
golem target com.example.app \
--workspace ./targets/com.example.app \
--session example-test \
--headedUse --arch arm64 for an arm64 physical device. Golem checks the APK/device
ABI intersection before installation and returns a specific recovery hint for
missing splits, signing conflicts, version downgrades, and storage failures.
golem create test-phone --headed
golem create headless-a
golem list
golem status test-phone
golem observe test-phone
golem tap test-phone "Settings"
golem type test-phone "search query"
golem fill test-phone "Search" "hello world"
golem press test-phone back
golem swipe test-phone up
golem screenshot test-phone evidence.png
golem screen test-phone
golem diff test-phone
golem health test-phone
golem shell test-phone "pm list packages -3"
golem install test-phone target.apk
golem launch test-phone com.target.app
golem app-info test-phone com.target.app
golem logcat test-phone --package com.target.app --clear
golem deeplink test-phone 'example://open/item'
golem cert-install test-phone
golem proxy-on test-phone --port 8082
golem proxy-off test-phone
golem frida-scripts
golem instrument test-phone com.target.app network_monitor intent_intercept --duration 15
golem frida-messages test-phone --script network_monitor --json
golem checkpoint test-phone save before-login
golem persona "hunt-seed-001"
golem evidence test-phone list
golem evidence test-phone capture --desc "login screen"
golem analyze target.apk
golem analyze base.apk config.x86_64.apk config.xxhdpi.apk
golem doctor --profile dynamic --json
golem pull com.example.app --arch x86_64
golem target com.example.app --session test-phone --headed
golem experiment test-phone start "exported service" "an untrusted caller can write a private marker"
golem close test-phone
golem destroy test-phone
# start the daemon
golem daemon
# start the MCP server (stdio)
golem mcpfrom golem import Pool
async with Pool() as pool:
s = await pool.create("test-phone", headless=True)
# observe interactive elements (indexed for tap targets)
elements = await s.observe()
for el in elements:
print(f"[{el.idx}] {el.cls} — {el.text}")
# tap by index, text, or resource ID
await s.tap(0)
await s.tap("Settings")
await s.tap("id:com.android.settings:id/search_bar")
# type and fill
await s.fill("Search", "wifi")
await s.type_text("hello", clear=True)
# screen state after every action
state = await s.screen_state()
# screen diffing — what changed since last observe
diff = await s.observe_diff()
print(diff.summary())
# screenshots
png = await s.screenshot("evidence.png")
# shell commands
output = await s.shell("dumpsys activity top")
# app lifecycle
await s.app_install("target.apk")
await s.app_start("com.target.app")
apps = await s.app_list()
# atomic early instrumentation: spawn paused, load hooks, resume
await s.frida_instrument("com.target.app", "network_monitor", "crypto_monitor")
messages = await s.frida_messages("crypto_monitor")
await s.frida_detach()
# bounded runtime diagnostics and reproducible reset points
await s.logcat(clear=True)
await s.open_uri("example://open/item")
logs = await s.logcat(package="com.target.app", lines=500)
await s.checkpoint("save", "before-login")
# proxy + cert
await s.proxy_install_cert()
await s.proxy_configure(port=8082)
# evidence collection
eid = await s.capture_screenshot_evidence("login bypass")
items = s.evidence.list()
# health monitoring
status = await s.health_check()
await s.close()
# reopen later — device still running
s = await pool.get("test-phone", launch=True)from golem import Pool, SessionGroup
async with Pool() as pool:
group = SessionGroup(pool, "idor-test")
await group.add("account-a")
await group.add("account-b")
await group.install_all("target.apk")
await group.launch_all("com.target.app")
# observe all sessions in parallel
results = await group.observe_all()
# run arbitrary action across all sessions
results = await group.run_all(lambda s: s.tap("Profile"))from golem.bbp_bridge import GolemHuntSession
async with GolemHuntSession("hunt-opensea-001", persona_seed="opensea-a") as hunt:
await hunt.prepare("com.opensea.app", workspace="./targets/com.opensea.app")
await hunt.instrument(["network_monitor", "crypto_monitor", "intent_intercept"])
elements = await hunt.observe()
await hunt.tap("Sign in")
eid = await hunt.capture("login screen reached")
messages = await hunt.frida_messages("crypto_monitor")
hunt.add_finding(
title="hardcoded API key in crypto init",
category="info-disclosure",
severity="medium",
description="AES key derived from static seed",
evidence_ids=[eid],
)
print(hunt.findings_summary())| Feature | Description |
|---|---|
| Device management | AVD create/boot/shutdown/checkpoints + USB/network ADB devices with runtime ABI discovery |
| Session isolation | Separate profile dir, u2 connection, Frida bridge, proxy per session |
| UI automation | observe, tap, type, fill, press, swipe, scroll_to, wait_element |
| Screen diffing | What changed between observations (activity, elements, count) |
| Context tracking | Ring buffer of screen states (50) and actions (200) |
| Frida instrumentation | Atomic spawn/load/resume, attach retries, process discovery, detach diagnostics, and eight reusable scripts |
| Runtime diagnostics | Bounded package-aware logcat, installed/runtime metadata, deep-link launches, and clean state checkpoints |
| Traffic interception | mitmproxy with tmpfs CA cert install for Android 11+ |
| Play acquisition | Latest/version-pinned gplaydl downloads, split validation, hashes, ABI inventory, versioned workspaces |
| Static analysis | Whole split-set JADX plus apktool Smali, APKiD, redacted secret locations, endpoints, manifest security context, source-linked review queue |
| Device personas | 6 real device profiles, 6 carriers, seeded PRNG for consistent fingerprints |
| Evidence store | Screenshot, traffic, Frida, shell, observe capture with index |
| Health monitoring | Battery, memory, disk, u2 responsiveness, periodic checks |
| Multi-session | Parallel observe/screenshot/action, SessionGroup, cross-account testing |
| Experiment ledger | Persistent hypotheses, preconditions, actions, observations, evidence links, and explicit outcomes |
| MCP server | 43 acquisition, analysis, experiment, job, UI, runtime, Frida, proxy, health, and evidence tools over stdio; long jobs can be polled or cancelled and large reports default to bounded summaries |
| JSON-RPC daemon | Local Unix socket server (loopback TCP fallback on Windows) for persistent session management |
| BBP bridge | GolemHuntSession for bug-bounty harness integration |
| Script | What it hooks |
|---|---|
ssl_bypass |
TrustManager, OkHttp CertificatePinner, Conscrypt, WebViewClient SSL errors, NetworkSecurityConfig |
root_bypass |
su paths, Runtime.exec, PackageManager (root packages), Build.TAGS, RootBeer |
emulator_bypass |
Build.*, TelephonyManager, SystemProperties, sensors, emulator files |
crypto_monitor |
Cipher.doFinal, SecretKeySpec, MessageDigest, Mac, IvParameterSpec |
webview_dump |
addJavascriptInterface, loadUrl, evaluateJavascript, shouldOverrideUrlLoading |
intent_intercept |
startActivity, sendBroadcast, startService, ContentResolver.query, deep links |
sharedprefs_monitor |
getString/putString reads and writes with interesting-key filtering |
network_monitor |
passive URL, Android/OkHttp connection, response, error, and cleartext-policy diagnostics with query redaction |
| Profile | Model |
|---|---|
| Pixel 7 | Google Tensor |
| Pixel 6 Pro | Google Tensor |
| Galaxy S23 Ultra | Samsung Exynos |
| Galaxy A54 | Samsung Exynos |
| Redmi Note 12 | Xiaomi Snapdragon |
| OPPO A58 | OPPO MediaTek |
each persona bundles matching IMEI, Android ID, build props, SIM info, carrier. same seed = same identity.
See Agent workflow for the artifact contract, recovery rules, and a complete acquisition-to-report sequence.
MIT

