Android environment integrity inspection tool for root, hook, bootloader, SELinux, virtualization, and attestation signals
See the code
On-device Android environment integrity diagnostics backed by Kotlin and native probes.
Download Nightly · Documentation · SDK Integration
DuckDetector collects and correlates security-relevant evidence on an Android device. It looks for boot-state changes, root and SU artifacts, runtime injection or hooking, mount namespace anomalies, modified system properties, suspicious applications, KeyStore and attestation inconsistencies, and signs of virtualization.
The app combines a Jetpack Compose interface, feature-oriented Gradle modules, and a native C++/assembly library. The same detectors are also published as a UI-free SDK AAR that other applications can embed. Results are diagnostic signals, not an authoritative statement that a device is secure or compromised.
main and are also posted to the Telegram channel; there is no stable release yet.Each card shows one of these statuses:
| Status | Meaning |
|---|---|
| Danger, Warning | The detector observed evidence; the card lists what it saw. |
| All Clear | The probes ran and found none of the evidence they look for. This does not prove that the device is unmodified. |
| Info (Support) | A probe was unsupported or unavailable on this device, so the absence of findings says nothing. |
| Info (Error) | The check failed; the card says why. |
To see what a detector looks for and why that counts as evidence, open its evidence record from Detector coverage.
Current feature areas, each linked to its evidence record, which explains what the detector looks for, why that counts as evidence, and what it cannot see:
ADB Runtime · Bootloader · Custom ROM · Dangerous Apps · Kernel Check · LSPosed · Memory · Mount · Native Root · Play Integrity Fix · Root Managers · SELinux · SU · System Properties · TEE · Virtualization · Zygisk
The SELinux area also retains an app-zygote SID-table query/registration experiment. Repeated discrepancies are supporting warnings; synchronized policy hiding can leave no discrepancy. Controlled privileged-context writes run in a disposable child with malformed/stock controls; recognition and ordinary-app timing are supporting warnings, an app-zygote AVC lookup-count profile is informational only, and negative observations cannot exclude hidden policy. Access failures and incomplete experiments remain explicit coverage states. See the SELinux evidence record for source references and device-validation limits.
Play Integrity Fix refers to local indicators associated with integrity-spoofing modifications; DuckDetector does not return an official Google Play Integrity API verdict. The TEE area evaluates Android KeyStore and attestation evidence, including certificate chains, security levels, revocation data, and selected KeyMint, StrongBox, and Soter behavior where supported.
Native Root also offers an optional fsnotify SRCU timing experiment, disabled by default. It requires an explicit choice, uses a temporary private dynamic permission on supported Android 12–14 systems, and reports repeatable contention as supporting evidence. Its thresholds await device calibration; it does not identify KernelSU, and its private-process deadline cannot recover a blocked kernel.
Supporting modules provide the dashboard, device information, settings, update checks, notifications, license information, and common UI infrastructure.
NativeActivity runs before MainActivity and records early mount and virtualization evidence./proc inspection, timing measurements, linker/runtime visibility, or architecture-specific assembly.| Area | Details |
|---|---|
| Android | Android 10 or later (minSdk 29); compiled and targeted against Android API 37. |
| ABIs | Native syscall paths are provided for arm64-v8a, armeabi-v7a, x86, and x86_64. Some timing and virtualization trap probes are available only on arm64-v8a. |
| Privileges | Root access is not required. Android permissions and platform visibility rules still limit what the app can observe. |
| Device variance | OEM changes, kernel configuration, Android version, and sandbox policy may cause a probe to be unsupported, unavailable, or lower-confidence. |
| Network use | Core device scans run locally. TEE revocation checks always include the bundled snapshot; downloading Google's current revocation feed requires user consent. The app also checks GitHub for Nightly updates after a cold start and when requested from Settings. With GitHub acceleration on, which Chinese-language users are asked about and Settings can switch, those checks and the Nightly download go through the third-party gh-proxy.com service instead. |
Duck-Detector-Refactoring/
├─ app/ # UI composition root: activities, dashboard cards, notifications
├─ sdk/runtime/ # Headless composition root: detector catalog, scan API, process hooks, native library registry
├─ sdk/aar/ # The distributable SDK: every headless module fused into one AAR
├─ core/ # Contracts and shared infrastructure: evidence, native payloads, platform access, reports, scan lifecycle, detector, UI
├─ capability/ # Evidence collection shared by several detectors
├─ feature/ # One detector or supporting feature per directory, split into layers
├─ samples/sdk-consumer/ # A separate application built from the published AAR alone
├─ build-logic/ # Convention plugins, module boundary and dependency validation, generated assets
├─ uihelper/ # Pinned Git submodule: MIUIX / Material UI, included as a separate Gradle build
├─ docs/ # Architecture overview, guides, and follow-ups
├─ .github/policies/ # Module, native, detector touch point, reflection, and text protocol policies
├─ .github/scripts/ # Repository guards and their self-tests
├─ gradle/ # Version catalog and Gradle wrapper files
├─ scripts/ # Detector scaffold and repository maintenance scripts
├─ build.gradle.kts
└─ settings.gradle.kts
Each detector is a set of Gradle modules under feature/<name>/:
domain: result models, report models, status definitions, and judgement rules (pure JVM)data: probes, parsers, repositories, JNI bridges, and Android service accesspresentation: the card model, its mapping from the domain report, and the report projection (pure JVM)detector: the one headless <Name>Detector object that binds the other layers for the SDK and the appui: the Compose card, the prompts for the detector's consents, and its integration with the dashboardDependencies point from :app to the SDK, from both to features, from features to capabilities, and from capabilities to core; features never depend on each other. Every module declares exactly the dependencies it uses. The build and CI enforce these rules. See the architecture overview.
A new detector touches only its own directory and its one line in DetectorCatalog, plus one registry line and one policy entry when it has a native unit; the app generates its dashboard cards from the ui modules. One command creates all of it, in a state that builds and reports "Not evaluated" until its probe is written:
python3 scripts/new_detector.py <name> --description "what it looks for and why that is evidence"
Continue with Adding a detector. To run the detectors inside another application, see Using the SDK.
Native sources are organized into units. Each unit lives in src/main/cpp/<unit>/ of the module that owns it, is compiled as its own object library and is linked into libduckdetector.so; Kotlin code reaches native results through the JNI bridges of that same module.
Dependency and tool versions are defined in gradle.properties and gradle/libs.versions.toml.
Initialize the UI submodule after cloning (or clone with --recurse-submodules). It is an
independent Gradle build; the detector SDK never depends on it:
git submodule update --init --recursive
# macOS / Linux
./gradlew :app:assembleDebug
# Windows
gradlew.bat :app:assembleDebug
For the standard local validation path:
./gradlew unitTest :app:assembleDebug :app:lintDebug buildHealth
for s in .github/scripts/check-*.py; do python3 "$s"; done
The same build also produces the headless SDK as one AAR, without any UI, published to sdk/aar/build/repository (see Using the SDK):
./gradlew :sdk:aar:publish
The Nightly release includes the SDK AAR alongside the APK.
Before contributing, read CONTRIBUTING.md and CODING_STANDARDS.md.
The build uses the ciRelease signing configuration only when all four variables are set:
ANDROID_KEYSTORE_PATHANDROID_KEYSTORE_PASSWORDANDROID_KEY_ALIASANDROID_KEY_PASSWORDWithout the complete set, the local release build is signed with the debug key. Do not treat such an artifact as an official release.
/proc, SELinux, mount namespaces, and other low-level interfaces vary by device and OS build.This software is provided "as is", without warranty of any kind. It is intended for education, diagnostics, and security research. The developers are not liable for damage, data loss, or system instability resulting from its use. Interpret heuristic findings in the context of the device, OS build, and available probe coverage.
DuckDetector is licensed under the Apache License 2.0.
129 followers · starred Jun 2026
591 followers · starred Apr 2026
290 followers · starred May 2026
102 followers · starred Apr 2026
Android environment integrity inspection tool for root, hook, bootloader, SELinux, virtualization, and attestation signals
See the code
On-device Android environment integrity diagnostics backed by Kotlin and native probes.
Download Nightly · Documentation · SDK Integration
DuckDetector collects and correlates security-relevant evidence on an Android device. It looks for boot-state changes, root and SU artifacts, runtime injection or hooking, mount namespace anomalies, modified system properties, suspicious applications, KeyStore and attestation inconsistencies, and signs of virtualization.
The app combines a Jetpack Compose interface, feature-oriented Gradle modules, and a native C++/assembly library. The same detectors are also published as a UI-free SDK AAR that other applications can embed. Results are diagnostic signals, not an authoritative statement that a device is secure or compromised.
main and are also posted to the Telegram channel; there is no stable release yet.Each card shows one of these statuses:
| Status | Meaning |
|---|---|
| Danger, Warning | The detector observed evidence; the card lists what it saw. |
| All Clear | The probes ran and found none of the evidence they look for. This does not prove that the device is unmodified. |
| Info (Support) | A probe was unsupported or unavailable on this device, so the absence of findings says nothing. |
| Info (Error) | The check failed; the card says why. |
To see what a detector looks for and why that counts as evidence, open its evidence record from Detector coverage.
Current feature areas, each linked to its evidence record, which explains what the detector looks for, why that counts as evidence, and what it cannot see:
ADB Runtime · Bootloader · Custom ROM · Dangerous Apps · Kernel Check · LSPosed · Memory · Mount · Native Root · Play Integrity Fix · Root Managers · SELinux · SU · System Properties · TEE · Virtualization · Zygisk
The SELinux area also retains an app-zygote SID-table query/registration experiment. Repeated discrepancies are supporting warnings; synchronized policy hiding can leave no discrepancy. Controlled privileged-context writes run in a disposable child with malformed/stock controls; recognition and ordinary-app timing are supporting warnings, an app-zygote AVC lookup-count profile is informational only, and negative observations cannot exclude hidden policy. Access failures and incomplete experiments remain explicit coverage states. See the SELinux evidence record for source references and device-validation limits.
Play Integrity Fix refers to local indicators associated with integrity-spoofing modifications; DuckDetector does not return an official Google Play Integrity API verdict. The TEE area evaluates Android KeyStore and attestation evidence, including certificate chains, security levels, revocation data, and selected KeyMint, StrongBox, and Soter behavior where supported.
Native Root also offers an optional fsnotify SRCU timing experiment, disabled by default. It requires an explicit choice, uses a temporary private dynamic permission on supported Android 12–14 systems, and reports repeatable contention as supporting evidence. Its thresholds await device calibration; it does not identify KernelSU, and its private-process deadline cannot recover a blocked kernel.
Supporting modules provide the dashboard, device information, settings, update checks, notifications, license information, and common UI infrastructure.
NativeActivity runs before MainActivity and records early mount and virtualization evidence./proc inspection, timing measurements, linker/runtime visibility, or architecture-specific assembly.| Area | Details |
|---|---|
| Android | Android 10 or later (minSdk 29); compiled and targeted against Android API 37. |
| ABIs | Native syscall paths are provided for arm64-v8a, armeabi-v7a, x86, and x86_64. Some timing and virtualization trap probes are available only on arm64-v8a. |
| Privileges | Root access is not required. Android permissions and platform visibility rules still limit what the app can observe. |
| Device variance | OEM changes, kernel configuration, Android version, and sandbox policy may cause a probe to be unsupported, unavailable, or lower-confidence. |
| Network use | Core device scans run locally. TEE revocation checks always include the bundled snapshot; downloading Google's current revocation feed requires user consent. The app also checks GitHub for Nightly updates after a cold start and when requested from Settings. With GitHub acceleration on, which Chinese-language users are asked about and Settings can switch, those checks and the Nightly download go through the third-party gh-proxy.com service instead. |
Duck-Detector-Refactoring/
├─ app/ # UI composition root: activities, dashboard cards, notifications
├─ sdk/runtime/ # Headless composition root: detector catalog, scan API, process hooks, native library registry
├─ sdk/aar/ # The distributable SDK: every headless module fused into one AAR
├─ core/ # Contracts and shared infrastructure: evidence, native payloads, platform access, reports, scan lifecycle, detector, UI
├─ capability/ # Evidence collection shared by several detectors
├─ feature/ # One detector or supporting feature per directory, split into layers
├─ samples/sdk-consumer/ # A separate application built from the published AAR alone
├─ build-logic/ # Convention plugins, module boundary and dependency validation, generated assets
├─ uihelper/ # Pinned Git submodule: MIUIX / Material UI, included as a separate Gradle build
├─ docs/ # Architecture overview, guides, and follow-ups
├─ .github/policies/ # Module, native, detector touch point, reflection, and text protocol policies
├─ .github/scripts/ # Repository guards and their self-tests
├─ gradle/ # Version catalog and Gradle wrapper files
├─ scripts/ # Detector scaffold and repository maintenance scripts
├─ build.gradle.kts
└─ settings.gradle.kts
Each detector is a set of Gradle modules under feature/<name>/:
domain: result models, report models, status definitions, and judgement rules (pure JVM)data: probes, parsers, repositories, JNI bridges, and Android service accesspresentation: the card model, its mapping from the domain report, and the report projection (pure JVM)detector: the one headless <Name>Detector object that binds the other layers for the SDK and the appui: the Compose card, the prompts for the detector's consents, and its integration with the dashboardDependencies point from :app to the SDK, from both to features, from features to capabilities, and from capabilities to core; features never depend on each other. Every module declares exactly the dependencies it uses. The build and CI enforce these rules. See the architecture overview.
A new detector touches only its own directory and its one line in DetectorCatalog, plus one registry line and one policy entry when it has a native unit; the app generates its dashboard cards from the ui modules. One command creates all of it, in a state that builds and reports "Not evaluated" until its probe is written:
python3 scripts/new_detector.py <name> --description "what it looks for and why that is evidence"
Continue with Adding a detector. To run the detectors inside another application, see Using the SDK.
Native sources are organized into units. Each unit lives in src/main/cpp/<unit>/ of the module that owns it, is compiled as its own object library and is linked into libduckdetector.so; Kotlin code reaches native results through the JNI bridges of that same module.
Dependency and tool versions are defined in gradle.properties and gradle/libs.versions.toml.
Initialize the UI submodule after cloning (or clone with --recurse-submodules). It is an
independent Gradle build; the detector SDK never depends on it:
git submodule update --init --recursive
# macOS / Linux
./gradlew :app:assembleDebug
# Windows
gradlew.bat :app:assembleDebug
For the standard local validation path:
./gradlew unitTest :app:assembleDebug :app:lintDebug buildHealth
for s in .github/scripts/check-*.py; do python3 "$s"; done
The same build also produces the headless SDK as one AAR, without any UI, published to sdk/aar/build/repository (see Using the SDK):
./gradlew :sdk:aar:publish
The Nightly release includes the SDK AAR alongside the APK.
Before contributing, read CONTRIBUTING.md and CODING_STANDARDS.md.
The build uses the ciRelease signing configuration only when all four variables are set:
ANDROID_KEYSTORE_PATHANDROID_KEYSTORE_PASSWORDANDROID_KEY_ALIASANDROID_KEY_PASSWORDWithout the complete set, the local release build is signed with the debug key. Do not treat such an artifact as an official release.
/proc, SELinux, mount namespaces, and other low-level interfaces vary by device and OS build.This software is provided "as is", without warranty of any kind. It is intended for education, diagnostics, and security research. The developers are not liable for damage, data loss, or system instability resulting from its use. Interpret heuristic findings in the context of the device, OS build, and available probe coverage.
DuckDetector is licensed under the Apache License 2.0.
129 followers · starred Jun 2026
591 followers · starred Apr 2026
290 followers · starred May 2026
102 followers · starred Apr 2026