Detects Play Integrity Fix, PlayIntegrityFork, TrickyStore, and the newer wave of bypass modules (inject-s v4.5 companion streaming, autopif4 Canary fingerprints, TS-Enhancer-Extreme, PIF-Hybrid Rust edition, Treat Wheel root hider) on Android, plus in-app hardware key-attestation validation that counters the 2026 STRONG-integrity keybox-spoofing stack. Native C++ detection engine with a custom bytecode VM, runtime string obfuscation, and Kotlin UI.
- Play Integrity Fix: maps scan for PIF classes &
InMemoryDexClassLoaderDEX regions,custom.pif.prop/custom.pif.json, module dirs, known props - PIF Companion Streaming (inject-s v4.5): Zygisk companion IPC sockets, memfd-backed dex regions, module installed without
pif.json - Pixel Canary Fingerprint (autopif4): monthly Canary build IDs, vendor partition /
ro.build.idmismatch, brand vs fingerprint cross-check - PIF Pure Rust (PIF-Hybrid): module.prop markers ("Pure Rust Edition", "zero DobbyHook", "Enginex0"), Rust crate libs in maps
- TrickyStore / KeyboxHub:
keybox.xml,target.txt,security_patch.txtunder/data/adb/tricky_store/, KeyboxHub auto-rotation paths, maps artifacts - TS-Enhancer-Extreme: anti-detection module that masquerades the bootloader as locked; module deploy path, config dir,
tseed/tsees/tseevRust binaries - Zygisk / Magisk / KernelSU / APatch: maps scan for zygisk libs (incl. ReZygisk, ZygiskNext, Shamiko, NoHello), env vars,
rwxpanomalies, root-manager packages viaPackageManager(Magisk + KernelSU + APatch + classic SuperSU/Koush/Kingo + cloakers), su binary paths (incl./system_ext/bin/suon A11+), busybox, legacy SuperUser/SuperSU APKs in/system - Root Hiders: mount namespace divergence, OverlayFS on
/system, SELinux context anomalies, elevated rwxp anonymous mapping count - Treat Wheel: closed-source "Shamiko for ReZygisk" root hider (written in C to dodge
__cxa_atexitunload detection);/proc/self/mapsscan for thetreat_wheel/zygisk/module mapping - Key Attestation Anomaly: generates an attested EC key in
AndroidKeyStoreand runs several low-false-positive checks. (1a) Cryptographically validates the certificate chain: leaf-hacking spoof modes (TrickyStore/TrickyStoreOSS/TEESimulator?mode) mutate the TEE leaf's attestation extension, which breaks the issuer's signature. (anti-replay) Verifies the attestation echoes the random challenge nonce we passed, defeating cached/replayed certs. (1b) Parses the attestationRootOfTrust(deviceLocked/verifiedBootState) and flags a contradiction when it claims a locked/verified device while the engine's own root-hider signals prove tampering. (1c) Anchors the chain to Google's hardware-attestation roots (RSA valid through 2042 and ECDSA P-384 valid through 2035, SHA-256-pinned offline): a self-consistent chain that does not terminate at a genuine Google root is a software/fake-keybox spoof. (1d, opt-in) For a Google-anchored chain, checks each cert serial against Google's revocation list to catch generation-mode spoofers (a leaked-but-revoked real keybox). 1d is the only check that touches the network and is OFF by default behind a user toggle, so the app stays network-silent otherwise. Fails safe throughout: absence of attestation (old/AOSP devices), parse failure, an unrecognised root, or an offline revocation fetch never flags. - Frida / Xposed: TCP connect probe to
127.0.0.1:27042and:27043, maps scan for gadget libs,gmain/gum-js-loopthread names, parent process cmdline, Objection. The TCP probe replaces the legacy/proc/net/tcpscan, which Android 10+ filters to empty foruntrusted_app. - Property Spoofing: cross-validation of
ro.build.fingerprintvsro.product.*, security patch vs kernel date, board (Pixel boards must run on Tensor SoCs), property read timing - Bootloader:
ro.boot.verifiedbootstate,ro.boot.flash.locked,ro.boot.veritymode,vbmeta.device_state,ro.debuggable,ro.secure,sys.oem_unlock_allowed - Debuggers:
TracerPidfrom/proc/self/status,FLAG_DEBUGGABLE(release builds only) - APK Tampering: SHA-256 of the signing cert obtained via
PackageManager.GET_SIGNING_CERTIFICATES(API 28+), pinned in native code. Skipped in debug builds.
PIF runs as a Zygisk module and:
- Hooks
__system_property_read_callbackto spoof build properties (ro.build.fingerprint,ro.build.version.security_patch, etc.) - Injects
classes.dexat runtime intocom.google.android.gms.unstableviaInMemoryDexClassLoader - Uses reflection to modify
android.os.Buildfields and inject a customKeyStoreSpiprovider - TrickyStore extends this by modifying key attestation certificate chains using stolen/leaked keybox files
Newer forks add: streaming the dex payload over a Zygisk companion IPC channel (no on-disk JSON, defeats config-file scans); rotating monthly Pixel Canary fingerprints from automation; pure-Rust rewrites that ditch DobbyHook and so escape libdobby signature scans; and TS-Enhancer-Extreme, which actively patches VerifiedBootHash and security-patch props to make the bootloader look locked.
MainActivity is UI-only. DetectionRunner owns the worker executor and the JNI binding to the native engine. The native engine runs in two phases:
- Debug/instrumentation checks run first (fail-fast):
isTraced(), Frida VM + TCP-connect probe + thread scan, parent process check, debuggable flag (release only) - Tampering checks run in randomized order each time: Zygisk VM + PackageManager root-app probe + su-binary scan, PIF VM + companion-streaming probe, mount namespace, OverlayFS, SELinux, bootloader props, APK signature (SHA-256), TrickyStore paths, property consistency, Pixel Canary fingerprint, TS-Enhancer-Extreme, PIF Pure Rust, Treat Wheel
After the native call returns, DetectionRunner runs the key-attestation probe (KeyAttestationProbe, written in Kotlin because the AndroidKeyStore attestation API lives in Java land) and ORs ATTEST_ANOMALY into the bitmask. Its contradiction check reuses the root-hider bit the native engine already computed; its chain-anchor check uses the SHA-256-pinned Google roots in AttestationRoots. The optional online revocation check (1d) runs only when the user enables the toggle (SharedPreferences, default off), so the app stays network-silent by default. The flag bit is still registered in nativeAllFlagsMask() so the SSOT assertion holds; the pure validation/parse/anchor logic is isolated in AttestationAnalysis and unit-tested (including against real Google attestation bytes and the pinned root fingerprints). This is the only detection signal produced outside the native engine.
VM-based checks (Frida, Zygisk, PIF) use a custom bytecode interpreter with rolling-key XOR-encoded opcodes, which are harder to hook than direct function calls. Sensitive strings are base64+XOR encoded and only decoded at runtime.
JNI helpers use a LocalRef<T> RAII wrapper for ref hygiene, and security checks (signature, debuggable) fail closed: any JNI lookup error fires the detection bit so a hooked-JNI environment can't suppress it.
untrusted_app on Android 10+ cannot read /proc/net/tcp (filtered to empty) or access() paths under /data/adb/* (denied). Detectors that target those signals use alternatives: TCP connect probes for ports, PackageManager for root managers, and in-process /proc/self/maps for module presence.
The native function returns a bitmask: 0 means clean, any set bit indicates a detection.
0x0001 DEBUGGER 0x0040 TRICKYSTORE 0x0400 CANARY_FP
0x0002 FRIDA 0x0080 PROP_SPOOF 0x0800 TSEE
0x0004 ZYGISK 0x0100 ROOT_HIDER 0x1000 PIF_RUST
0x0008 PIF 0x0200 PIF_STREAM 0x2000 TREAT_WHEEL
0x0010 BOOTLOADER 0x4000 ATTEST_ANOMALY
0x0020 SIGNATURE
Flag values are owned by the native side; DetectionRunner.verifyFlagsInSync() asserts at startup that the Kotlin mirrors match nativeAllFlagsMask().
./gradlew assembleDebug # debug build (signature check skipped)
./gradlew assembleRelease # release build (requires signing config)
./gradlew test # run unit tests
./gradlew lintDebug # run lint checksRequires NDK r25+, Android Studio Hedgehog or later, SDK 35, Kotlin 2.0+. The manifest declares INTERNET (used by the Frida TCP probe and the opt-in attestation revocation check) and a <queries> block listing root-manager package names (Android 11+ visibility requirement).
If you build a release with your own signing key, update
EXPECTED_CERT_SHA256innative-lib.cppto the lowercase hex SHA-256 of your cert's DER bytes:apksigner verify --print-certs your-release.apk | grep "SHA-256"Debug builds skip the signature check, so you don't need to update anything for local development.
- Android 7.0+ (API 24)
See CONTRIBUTING.md.
| Name | GitHub |
|---|---|
| Ir0nByte | @IR0NBYTE |
GPL-3.0. Derivative works must stay open source.
This tool is for defensive security research. Use it only on devices you own or have permission to test.

