dumbtv
dumbtv가 ADB로 Fire TV에서 광고·추적·업데이트 알림을 제거, 루트·앱 변경 없이, 무료 오픈소스
Android에서 AirTag·Tile·SmartTag·Find My 추적기를 실시간으로 감지하고, 키 회전 복제까지 탐지하는 완전 오프라인 앱
Android app that flags AirTags, Tile, SmartTags & Find My trackers travelling WITH you over time, plus an experimental detector for key-rotating clones. Fully offline, no internet permission.
보안Kotlin · CGitHub @KaraZajac온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
Temporal counter-tracking: detect AirTags/Tile/SmartTag/Find My trackers following you over time. On-device, private. Sibling to OVERWATCH.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A native Android (Kotlin) app that watches for personal item-trackers — Apple
AirTags, Tile, Samsung Galaxy SmartTags, and Google Find My Device / DULT tags —
that are travelling with you over time. It is the temporal counterpart to
OVERWATCH:
OVERWATCH is spatial — what surveillance is watching this place, right now.
VIGIL is temporal — what has been with me, across time and places.
A tracker being near you means nothing — trackers are everywhere. The signal is
persistence: the same device seen at many of your distinct locations, over a
sustained window. VIGIL is built around that idea.README 전문 보기 ↗What's been following you?
A native Android (Kotlin) app that watches for personal item-trackers — Apple AirTags, Tile, Samsung Galaxy SmartTags, and Google Find My Device / DULT tags — that are travelling with you over time. It is the temporal counterpart to OVERWATCH:
OVERWATCH is spatial — what surveillance is watching this place, right now. VIGIL is temporal — what has been with me, across time and places.
A tracker being near you means nothing — trackers are everywhere. The signal is persistence: the same device seen at many of your distinct locations, over a sustained window. VIGIL is built around that idea.
⚠️ Prototype / work in progress. The scanning, parsing, temporal store, co-movement engine, allowlist, and learned baseline are in place; the app builds in CI. Wire-format offsets come from published reverse-engineering (see the paper, §3); they and the detection thresholds need validation against real hardware (see Status).
VIGIL requests no INTERNET permission at all. There is no server, no account,
no telemetry. Every tracker, every sighting, and the entire learned baseline live
in an on-device SQLite database. VIGIL never transmits them and cloud backup is
off; they leave the phone only if you export an evidence report yourself.
Sightings older than 14 days are deleted while VIGIL is watching (when you start
it, then every 6 hours), so they stay on the phone while it's stopped. Alerts, with
their locations, stay until you clear Alert history, and learned places stay until
you clear the app's storage.
Detection listens to what trackers already broadcast and never connects to
them. The one exception is the user-initiated "Make it ring" action, which
connects to a tracker you already suspect and asks it to play a sound (the way
Apple and the DULT standard let someone find a tag that isn't theirs). Your
phone's Bluetooth scan can send generic scan requests, as any scan does, but VIGIL
sends no data anywhere.
Four native BLE wire formats today, plus the emerging unified DULT format. VIGIL recognises each ecosystem and, where the ecosystem signals it, filters to the separated-from-owner state — the only state in which a following tracker is detectable (see the paper, §2.1). VIGIL takes that flag on trust, so a modified tag that always claims its owner is nearby is never escalated. Tile has no such flag, so every Tile is checked.
| Ecosystem | BLE signature | Separated-state signal | Passive re-link window |
|---|---|---|---|
| Apple Find My / AirTag | mfg data, company 0x004C, type 0x12 | status byte "maintained" bit cleared | ~24 h (key static per day) |
| Google Find My Device | service data 0xFEAA, frame 0x40/0x41 | frame 0x41 = separated (cleartext) | ~24 h once separated |
| Samsung Galaxy SmartTag | service data 0xFD5A | state byte (lost / overmature-lost) | ~24 h once overmature |
| Tile | service data 0xFEED / 0xFEEC / 0xFE84 | none — static MAC, always findable | indefinite (static MAC) |
| DULT (unified, emerging) | service data 0xFCB2 | near-owner bit (byte 14 LSB) | ~24 h separated |
Chipolo, Pebblebee, eufy, Motorola, etc. inherit the signature of whichever network (Apple or Google) their SKU joined — VIGIL detects the network.
VIGIL stores at most one sighting per tracker every 15 seconds, tagged with the
phone's current location fix when it has one, and judges each tracker's last 24
hours. Once VIGIL has seen it separated from its owner (a Tile always counts), a
tracker becomes SUSPICIOUS
with three spaced sightings and a peak that passed the RSSI gate, and ALERTING —
the only state that notifies — when it clears the full co-movement test:
VIGIL needs location on: without a current fix it can't count places or movement, so neither detector can alert.
Two trust signals suppress false alarms:
Tap any tracker for two ways to physically locate it:
0xAF command), Google Find My Device and DULT tags
(Chipolo, Pebblebee, eufy, Motorola), and Apple Find My accessories. Samsung
SmartTag and Tile expose no non-owner ring, so VIGIL points you to the
SmartThings / Tile app instead.Every shipping detector (AirGuard, iOS, Android's built-in) keys on device identity, so a key-rotating clone looks like a stream of one-off devices to every one of them. Positive Security's Find You (2022) holds ~2,000 Find My keys and sends one beacon per key, one beacon every 30 s, so a key recurs only every ~17 hours. An iPhone user carried it for five days, and neither their iPhone nor their roommate's raised an alert. Apple's Tracker Detect scan didn't show it, and AirGuard's manual scan listed it as a new device per beacon; AirGuard's background mode wasn't tested.
VIGIL's headline goal is to detect the attack, not the device: a rotating
clone is one physical radio that stays close and moves with you while its identity
churns thousands of times faster than any standards-compliant tracker is allowed
to. If it keeps advertising, that is an unbroken, close-range, co-moving RF
presence, whatever ID it shows. The full algorithm — a CUSUM churn
trigger, an identity-agnostic presence-track confirmer, and a co-movement gate,
plus the "identity-path × churn-path squeeze" meant to leave no safe rotation
rate — is designed in docs/detection-rotation-clone.md.
A first version is implemented in detect/PresenceEngine.kt and wired into the
scan service to raise a distinct rotating-tracker alert.
What it catches today, measured in an offline replay harness on synthetic
traces (app/src/test/kotlin/org/soulstone/vigil/eval/): a clone that switches
keys every 30 s and advertises every 2 s without pausing is confirmed in about 6
minutes of walking, with no alert in dense-urban, at-home or rush-hour-transit
ambient traces.
What it doesn't: the published Find You firmware. It sends each key in a
single beacon and then stays silent for 30 s, and any silence over 20 s ends the
clone detector's run, so every beacon starts again from zero; no key comes back
soon enough for the identity path either. Replayed with that timing, every beacon
heard, neither path alerts. The squeeze isn't closed by the shipped thresholds
either. A tracker that rotates every ~85 seconds to ~45 minutes evades both
detectors at the default sensitivity (to 30 min on High, 90 min on Low), and a
fast-rotating clone that cycles its radio on and off (30 s on, 30 s off in the
harness) is never detected, for the same 20-second reason. All of this is open
work, and none of these results has been checked against real RF yet — the
Flipper Zero emitter in flipper/vigil_emitter/ exists
to do that.
VIGIL reuses OVERWATCH's proven scanning stack and diverges where the temporal mission demands it (a persistent database instead of an in-memory store).
scan/TrackerSignatures.kt BLE hex signatures for every ecosystem
scan/TrackerParser.kt ScanResult -> TrackerObservation (five wire formats, incl. DULT)
scan/BleTrackerScanner.kt filtered BLE scan (screen-off capable) -> observations
service/ScanService.kt foreground service; geotags + persists + alerts
data/db/VigilDatabase.kt Room: trackers, sightings, alerts, baseline places
data/TrackerRepository.kt ingest + baseline + evaluate on every sighting
data/settings/Settings.kt preferences + a durable copy of the allowlist
detect/CoMovementEvaluator the temporal co-movement test + RSSI proximity gate
detect/PresenceEngine.kt identity-agnostic rotation-clone engine (v1)
detect/BaselineManager.kt learns anchor places -> auto-trusts static-ID tags seen there
ring/TrackerRinger.kt user-initiated GATT "Make it ring"
ring/RingReply.kt parses the tracker's reply to the ring command
data/location/LocationProvider.kt fused location (ported from OVERWATCH)
ui/ + MainActivity.kt Compose UI (Catppuccin Mocha)
A full technical write-up — the per-ecosystem detection methodology (how VIGIL tracks each tracker type) and the harder modeling engine for what AirGuard and the built-in detectors miss — is in paper/vigil.md.
Standard Android/Gradle. Run Gradle with JDK 17 or 21 (CI uses 17; Gradle 8.11
won't start on the newest JDKs) and an Android SDK: copy local.properties.example
to local.properties and set sdk.dir. A committed debug keystore signs CI and
local builds identically.
./gradlew :app:testDebugUnitTest # unit tests, including the offline detection eval
./gradlew :app:assembleDebug
# APK -> app/build/outputs/apk/debug/app-debug.apk
CI (.github/workflows/build.yml) runs the unit tests (including the offline
detection eval), then builds a debug APK on every push and attaches it to a GitHub
Release on v* tags.
Prototype. In place: BLE scan + filters, per-ecosystem parsing, Room temporal store, co-movement evaluator with RSSI gate, allowlist, learned baseline, the rotation-clone presence engine (v1, unit-tested), foreground service, Compose UI, "Make it ring" over GATT, and evidence export. Not yet: closing the squeeze gap and duty-cycle evasion described above, watching across days (the identity test looks back 24 h and the clone engine starts fresh each session), restarting after a reboot, field-tuning the clone engine on real captures, DULT get-identifier, and empirical validation. Before trusting the parser, capture real devices with nRF Connect — the SmartTag2 offsets are inferred from gen-1, and the Tile/AirTag reversing is a couple of years old.
Stands on the shoulders of AirGuard (TU Darmstadt / Seemoo-lab) and the SEEMOO Find My research, the IETF DULT working group, Adam Catley's AirTag teardown, and Positive Security's Find You clone research. VIGIL's aim is to go beyond AirGuard on the attacks it structurally cannot catch — see the design doc.
VIGIL is released under the BSD 3-Clause License. The web fonts in
docs/fonts/ are separately licensed under the SIL Open Font License 1.1; see
docs/fonts/OFL.txt.