
Specter — passiver 13,56-MHz-NFC-Leser/Skimmer-Bug-Sweep für Flipper Zero. Gegenüberwachungs-EMF-Meter unter Verwendung des integrierten NFC-Chips. Keine zusätzliche Hardware, sendet niemals.
<!-- banner: paper by default, true black when the reader's GitHub is dark -->
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="images/banner-dark.png">
<img src="https://assets.kitploit.com/production/public/readmes/42070/74bc06f0a6a1b0731b4768b4256f662b34a582f5b9701a9bce44cb1b10bedd0d.png" alt="Specter — NFC reader and skimmer bug-sweep for Flipper Zero" width="100%">
</picture>
</p>
<p align="center">
<sub>
Die Wellenform ist keine Grafik. Sie besteht aus 128 Spalten eines 13,56 MHz-Trägers, den
Specter von einem echten Polling-Reader mit 8 ms pro Spalte aufgezeichnet und aus dem
danebenstehenden Screenshot wieder ausgelesen hat. Ihr Tastverhältnis misst 28 %, wo der
eigene Zähler des Geräts 27 % ausgab, und beide Zahlen stehen auf dem Banner.
</sub>
</p>
<p align="center"><i>Durchsuche den Raum nach den Readern, die du nicht sehen kannst.</i></p>
<p align="center">
<a href="https://at0m-b0mb.github.io/Specter-FlipperZero/"><b>Projektseite</b></a>
·
<a href="https://github.com/at0m-b0mb/Specter-FlipperZero/releases/latest">Download</a>
·
<a href="https://github.com/at0m-b0mb/specter-flipperzero/blob/main/docs/changelog.md">Changelog</a>
</p>
<!-- live badges: these track the repo, so the README never goes stale -->
<p align="center">
<a href="https://github.com/at0m-b0mb/Specter-FlipperZero/releases/latest"><img src="https://img.shields.io/github/v/release/at0m-b0mb/Specter-FlipperZero?style=flat-square&color=8A6714&labelColor=000000" alt="Latest release"></a>
<a href="https://github.com/at0m-b0mb/Specter-FlipperZero/releases"><img src="https://img.shields.io/github/downloads/at0m-b0mb/Specter-FlipperZero/total?style=flat-square&color=8A6714&labelColor=000000&label=downloads" alt="Downloads"></a>
<a href="https://github.com/at0m-b0mb/Specter-FlipperZero/stargazers"><img src="https://img.shields.io/github/stars/at0m-b0mb/Specter-FlipperZero?style=flat-square&color=8A6714&labelColor=000000" alt="Stars"></a>
<a href="https://github.com/at0m-b0mb/Specter-FlipperZero/actions/workflows/build.yml"><img src="https://img.shields.io/github/actions/workflow/status/at0m-b0mb/Specter-FlipperZero/build.yml?branch=main&style=flat-square&labelColor=000000&color=8A6714&label=build" alt="Build"></a>
</p>
<p align="center">
<img src="https://img.shields.io/badge/platform-Flipper%20Zero-8A6714?style=flat-square&logo=flipper&logoColor=FE8A2C&labelColor=000000" alt="Flipper Zero">
<img src="https://img.shields.io/badge/radio-13.56%20MHz%20NFC%20(onboard)-8A6714?style=flat-square&labelColor=000000" alt="Onboard NFC">
<img src="https://img.shields.io/badge/extra%20hardware-none-8A6714?style=flat-square&labelColor=000000" alt="No extra hardware">
<img src="https://img.shields.io/badge/transmits-never-8A6714?style=flat-square&labelColor=000000" alt="Listen-only">
<img src="https://img.shields.io/badge/license-MIT-8A6714?style=flat-square&labelColor=000000" alt="MIT">
</p>
<p align="center">
<b>Specter</b> verwandelt deinen Flipper Zero in einen Taschen-<b>Bug-Sweep zur
Gegenüberwachung</b> für <b>aktive 13,56-MHz-NFC-Reader</b>. Es <i>lauscht passiv</i> auf das
HF-Feld, das ein eingeschalteter Reader ständig abstrahlt — ein versteckter Karten-Skimmer,
der in ein Zahlungsterminal geschmuggelt wurde, ein verdeckter Reader hinter einer
Türverkleidung, ein heimlicher Logger, der unter einen Schreibtisch geklebt wurde — und sagt
dir dann, <b>wo er ist</b>, <b>um welche Art von Gerät es sich handelt</b>, <b>ob der Raum
sauber ist</b> und — wenn du es auf Wache lässt — <b>in dem Moment, in dem einer auftaucht,
während du weg bist</b>. Es sendet niemals.
</p>
<p align="center"><sub>Die Reader sind unsichtbar. Specter macht sie sichtbar.</sub></p>
<p align="center">
<sub>
<b>Bekannt aus</b><br>
<a href="https://www.helpnetsecurity.com/2026/07/29/specter-flipper-zero-skimmer-detector/">Help Net Security</a>
·
<a href="https://cybersecuritynews.com/specter-with-flipper-zero/">Cyber Security News</a>
</sub>
</p>
---
## Auf dem Flipper
<p align="center">
<img src="https://raw.githubusercontent.com/at0m-b0mb/specter-flipperzero/main/images/demo.gif" width="70%" alt="Specter in use: sweeping a quiet room, closing in on a reader, locking on at PEGGED, fingerprinting its polling cadence, then a site-survey verdict">
</p>
<p align="center">
<sub>Ein Sweep, von Anfang bis Ende: ruhiger Raum → näher kommen → eingerastet → <b>was</b> es ist → das Urteil des Raums.</sub>
</p>
### Die Bildschirme, die kein einzelner Modus besitzt
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="images/screens-dark.png">
<img src="https://raw.githubusercontent.com/at0m-b0mb/specter-flipperzero/main/images/screens.png" width="100%" alt="The boot intro, main menu, Sweep listening, Fingerprint with nothing there, a Site Survey walking, Watch with a contact logged, the Logbook and Settings — all captured from a real Flipper Zero">
</picture>
</p>
<p align="center">
<sub>
Die ruhigen Zustände und das Intro — die vier Modi unten bringen jeweils ihre eigene
Aufnahme mit, dies sind also die, die sie nicht abdecken. <b>Jedes Bild
in diesem README ist eine Aufnahme von einem echten Gerät</b>, aufgenommen über die
eigene RPC-Session des Flippers mit <code>tools_screenshot.py</code>. Es gibt in diesem
Projekt keinen Mockup-Renderer mehr — eine Zeichnung der UI ist eine zweite
Implementierung davon, und sie widersprach der Firmware, während sie vollkommen
überzeugend aussah (siehe <a href="https://github.com/at0m-b0mb/specter-flipperzero/blob/main/docs/changelog.md">3.0.1</a>).
</sub>
</p>
---
## Die fünf Modi
Specter sind fünf Werkzeuge rund um einen Sensor. Jedes beantwortet eine andere Frage, der
richtige hängt also davon ab, was du tatsächlich herausfinden willst:
| Modus | Die Frage, die er beantwortet | Verwende ihn, wenn |
|---|---|---|
| **Sweep** | *Wo ist es?* | Du ein Gerät vermutest und es durch Herumgehen lokalisieren willst |
| **Fingerprint** | *Um welche Art von Gerät handelt es sich?* | Du einen Emitter gefunden hast und wissen willst, wie er sich verhält |
| **Site Survey** | *Ist dieser Raum sauber?* | Du ein einziges Urteil für einen ganzen Raum willst, freihändig |
| **Watch Mode** | *Ist einer aufgetaucht, während ich weg war?* | Du den Flipper irgendwo zurücklässt, damit er Wache hält |
| **Logbook** | *Was habe ich gefunden, und wann?* | Du es aufschreibst oder heute mit letzter Woche vergleichst — filtere nach Fundtyp |
---
### Sweep — *wo ist es?*
<img src="https://raw.githubusercontent.com/at0m-b0mb/specter-flipperzero/main/screenshots/sweep_reader.png" width="42%" align="right" alt="Sweep — reader found, meter pegged">
**Was es tut.** Ein Live-Messgerät im EMF-Stil. Halte den Flipper flach und bewege ihn langsam
über die Sache, die du prüfst — ein Kartenterminal, einen Türleser, die Unterseite
einer ATM-Lippe, ein Paket, einen Schreibtisch. Die Nadel folgt der Feldstärke in Echtzeit.
**Was du siehst.**
- **Die Skala** — Nadel = Live-Messwert, der kleine Punkt = *Peak-Hold* (die stärkste
Stelle, die du berührt hast, damit du Positionen vergleichen kannst).
- **`FIELD %`** — derselbe Messwert als Zahl, mit einem **▲ / ▼ Trendpfeil**, der
dir sagt, ob die letzte halbe Sekunde die Sache *wärmer oder kälter* gemacht hat. Wenn
du jagst, zählt dieser Pfeil mehr als die Zahl.
- **`PEAK %`** — der stärkste Messwert seit dem Scharfschalten, die Zahl hinter dem
Peak-Hold-Punkt.
- **Die Markierung über der Skala** — wo `READER` bei deiner aktuellen
Empfindlichkeit *beginnt*. Alles dahinter ist das, was Specter als Treffer bezeichnet, die Skala
beantwortet also „was zählt?", statt dich raten zu lassen.
- **Untere Leiste** — bis du deinen ersten Reader gefunden hast, trägt sie die beiden Tasten,
die du nicht durch Drücken entdecken kannst (`LEFT=cal`, `hold OK=log`); danach
deine Empfindlichkeit und eine Live-Wellenform. Sie wechselt zu einer schwarzen
**`● ACTIVE READER`**-Alarmleiste mit einem Näherungswort, sobald ein Träger
erkannt wird.
- **Näherung** — `FAINT → NEAR → CLOSE → STRONG → PEGGED`. `PEGGED` bedeutet, das Messgerät ist
*am Anschlag*: du bist so nah, wie diese Messung auflösen kann.
**Ton.** Optionale Geiger-Klicks, die **schneller werden, je näher du kommst**, sodass du
mit dem Flipper in der Tasche sweepen und allein nach Gehör jagen kannst.
| Taste | Aktion |
|---|---|
| `OK` | Peak-Hold und Kontaktzähler zurücksetzen |
| `hold OK` | Diesen Messwert im Logbuch speichern |
| `LEFT` | Auf den Grundrauschpegel des Raums kalibrieren (3 s) |
| `UP` / `DOWN` | Empfindlichkeit schrittweise ändern, ohne die Jagd zu verlassen |
<br clear="right">
### Fingerprint — *um welche Art von Gerät handelt es sich?*
<img src="https://raw.githubusercontent.com/at0m-b0mb/specter-flipperzero/main/screenshots/fingerprint_reader.png" width="42%" align="right" alt="Fingerprint — polling reader identified">
**Was es tut.** Sobald Sweep etwas gefunden hat, halte den Flipper **still**
dagegen. Fingerprint hört auf, die Nähe zu messen, und beginnt, **die Ein-/Aus-Flanken
des Trägers zu messen** — genau das unterscheidet eine Art von
Emitter von einer anderen.
**Was du siehst.** Ein Urteil, einen Konfidenzbalken und die Belege für beides:
| Klasse | Was das in der Praxis bedeutet |
|---|---|
| **CONTINUOUS** | Der Träger wird dauerhaft aufrechterhalten |
| **POLLING** | Ein fester, quarzgetakteter Poll-Zyklus — die Periode wird in ms angezeigt |
| **INTERMITTENT** | Stoßweise, aber unregelmäßig — oft ein Telefon oder ein Reader in Benutzung |
Darunter stehen `PER` (Poll-Periode), `BST` (Burst-Breite), `JIT` (Jitter) und
`UP` (echter Duty-Cycle), plus ein **Logikanalysator-Pulszug** des rohen
Trägers — das Urteil ist also nie etwas, das du einfach glauben musst. Ein echter
Polling-Reader erscheint als unverkennbare Rechteckwelle.
> Eine `~` vor einer Zeitangabe bedeutet, dass sie nahe an der 2-ms-Abtastgrenze liegt, und die
> Konfidenz wird entsprechend abgewertet.
| Taste | Aktion |
|---|---|
| `OK` | Den Fund im Logbuch speichern |
| `hold OK` | Die Messung neu starten |
<br clear="right">
### Site Survey — *ist dieser Raum sauber?*
<img src="https://raw.githubusercontent.com/at0m-b0mb/specter-flipperzero/main/screenshots/survey_active.png" width="42%" align="right" alt="Site Survey — verdict">
**Was es tut.** Ein **zeitlich begrenzter** Sweep eines ganzen Raums. Starte ihn, gehe den Raum
normal ab und erhalte am Ende ein einziges Urteil — kein Nadelbeobachten.
**Was du siehst.** Ein Countdown und ein Fortschrittsbalken während des Laufs, dann eine Urteilskarte:
- **`CLEAN`** — nichts hat den Grundrauschpegel überschritten
- **`TRACE`** — kurze oder schwache Treffer; ein langsamerer zweiter Durchgang lohnt sich
- **`ACTIVE READER`** — etwas war tatsächlich an und hat gesendet
- **`TOO SHORT`** — du hast es abgebrochen, bevor es sich das Recht verdient hatte, `CLEAN` zu sagen
…mit `PEAK` / `AVG`-Feld, Kontaktanzahl und `UP %` — **wie viel des Surveys
ein Träger tatsächlich an war**, oft die aussagekräftigste der vier Zahlen.
Läuft 30 s, 60 s oder 2 min und protokolliert das Ergebnis automatisch. Die Karte nennt
die Länge, über die bewertet wurde, denn ein 30-sekündiges `CLEAN` ist kein
anderer Fund als ein zwei Minuten langes — und ein Lauf, der kürzer als 10 s abgeschnitten wird,
meldet `TOO SHORT` statt `CLEAN`. Die Asymmetrie ist beabsichtigt: **Anwesenheit ist
Beweis, Abwesenheit nicht.** Etwas in einer Sekunde zu finden ist ein echter Fund, also
werden `TRACE` und `ACTIVE READER` nie zurückgehalten, weil sie schnell waren; nichts
in einer Sekunde zu finden ist eine Aussage über den ganzen Raum, die eine Sekunde nicht
stützen kann.
| Taste | Aktion |
|---|---|
| `OK` *(laufend)* | Jetzt stoppen und bewerten, was da ist |
| `OK` *(auf der Karte)* | Einen weiteren Survey ausführen |
<br clear="right">
### Watch Mode — *ist einer aufgetaucht, während ich weg war?*
<img src="https://raw.githubusercontent.com/at0m-b0mb/specter-flipperzero/main/screenshots/watch_reader.png" width="42%" align="right" alt="Watch Mode — reader present">
**Was es tut.** Hält **unbegrenzt** Wache. Scharf schalten, den Flipper absetzen,
weggehen. Wo Sweep du beim Jagen bist und Survey ein Test mit fester Länge ist, wartet Watch
einfach — minuten- oder stundenlang.
**Was du siehst.** Eine große verstrichene Uhr und `NO READER`, solange nichts da ist.
In dem Moment, in dem ein Reader auftaucht, invertiert das Band zu **`ACTIVE READER`**, ein
Stärkebalken zeigt, wie nah er ist, der **Bildschirm wacht auf** und der Alarm ertönt. Das Band
ist stabil statt blinkend — auf einem 1-Bit-Bildschirm ist ein solider invertierter Block
ohnehin schon das Lauteste darauf, und die Anzeigen, die sich wirklich ändern, tragen
die Lebendigkeit. Nachdem er vorbei ist, behält der Bildschirm die Belege: `HITS` (wie viele
einzelne Kontakte), `PEAK`, `LAST` (wie lange der jüngste her ist) und
`UP` (Gesamtzeit, in der ein Träger tatsächlich an war). Die Uhr läuft über 99:59 hinaus auf `hh:mm`
weiter, statt einzufrieren, sodass eine Übernachtwache weiterhin summiert.
Watch **ignoriert bewusst den Stealth-Modus** — eine dunkle, stille Wache, die dir nie
sagt, dass sie etwas gesehen hat, wäre schlimmer als nutzlos.
| Taste | Aktion |
|---|---|
| `OK` | Neu scharf schalten — löscht die Uhr und die Zähler |
<br clear="right">
### Logbook — *was habe ich gefunden, und wann?*
Funde werden **zweimal aus einer Aktion** gespeichert, sodass sie auf dem Gerät lesbar
*und* bereits eine Tabellenkalkulation sind — kein Export-Schritt, an den man denken muss:
<table>
<tr><th><code>logbook.txt</code> — gruppiert, für den On-Device-Viewer</th><th><code>logbook.csv</code> — eine flache Zeile pro Eintrag</th></tr>
<tr><td>
```
2026-07-18 14:35:11
SURVEY 60s ACTIVE max74…
2026-07-18 14:32:07
READER POLLING 204ms…
```
</td><td>
```
timestamp,type,detail
2026-07-18 14:35:11,SURVEY,60s ACTIVE …
2026-07-18 14:32:07,READER,POLLING 204ms …
```
</td></tr>
</table>
Einträge werden mit `SWEEP`, `READER`, `SURVEY` oder `WATCH` getaggt und mit dem
Zeitstempel der Flipper-Uhr versehen. Beide Dateien liegen in `apps_data/specter/` auf der SD-Karte. Nichts
verlässt jemals das Gerät. Löschen Sie sie über **Einstellungen → Logbuch löschen** (vorher bestätigen — es ist die einzige irreversible Aktion der App).
---
## Die unterstützenden Funktionen
### Auto-Kalibrierung
Drücken Sie **LEFT** auf dem Sweep-Bildschirm und Specter lauscht **3 Sekunden lang auf den
Umgebungsgeräuschpegel** und setzt dann die Erkennungsschwelle knapp über den gemessenen
Wert — gespeichert als Ihre **Custom**-Empfindlichkeit. Jeder Raum hat einen anderen RF-
Grundpegel; dies stimmt auf den Raum ab, in dem Sie gerade stehen, statt auf einen zur
Build-Zeit fest eingebauten Wert. Stehen Sie an einem ruhigen Ort, wenn Sie dies ausführen.
### Stealth-Modus
Hält **Bildschirm und LED dunkel** während des gesamten Sweeps, damit der Flipper nicht leuchtet,
während Sie derjenige sind, der sucht. Ton und Vibration funktionieren weiter — der
Sinn ist nicht, das Feedback abzuschalten, mit dem Sie sweepen. Das Verlassen eines Stealth-Bildschirms
schaltet das Display wieder ein, sodass `BACK` Sie immer auf einem beleuchteten Menü landen lässt.
### Den Meter ablesen
**Warum ein Reader, den Sie berühren, nicht 100 % der Zeit sendet.** Der Detektor
misst eine physikalische Sache: welchen Bruchteil der Zeit ein 13,56-MHz-Träger aktiv ist.
Aber Reader **pollen** — ein kurzer Burst, ein Schlaf, ein weiterer Burst. Ein typisches Terminal
strahlt nur **20–35 % der Zeit** ab, sodass der rohe Duty-Cycle um etwa 30 %
*sättigt*, egal wie nah Sie kommen.
Diesen rohen Wert auf der Anzeige darzustellen, ließ eine perfekte Erkennung wie ein Drittel
einer Erkennung aussehen. Der Meter ist stattdessen gegen dieses reale Polling-Band skaliert:
| Roher Träger-Duty | Meter | Nähe |
|---|---|---|
| 3 % (Raumrauschen) | 10 % | `FAINT` |
| 12 % | 40 % | `NEAR` |
| 20 % | 67 % | `CLOSE` |
| 28 % | 93 % | `STRONG` |
| ≥30 % *(auf einem Reader aufliegend)* | **100 %** | `PEGGED` |
Der **rohe Duty geht nie verloren**: der Grundpegel, die Auto-Kalibrierung und das
`UP` des Fingerprint-Bildschirms funktionieren weiterhin im echten Duty-Cycle, weil diese
das *Signal* beschreiben, nicht Ihre Entfernung davon. Möchten Sie die wörtliche Zahl?
**Einstellungen → Meter-Skala → Duty %**.
### Alles bleibt erhalten
Empfindlichkeit, Survey-Länge, Ton, Vibration, LED, Stealth, Logging und Meter-Modus werden
**auf der SD-Karte gespeichert**, sobald Sie sie ändern. Ein Sweep-Kit, das seine
Konfiguration vergisst, ist schlimmer als gar keine Persistenz.
---
## Wie es funktioniert
Jeder eingeschaltete NFC-Reader **pingt kontinuierlich mit einem 13,56-MHz-Träger in die Luft**, wartet darauf, dass eine Karte
aufwacht. Sie können es nicht sehen, aber der NFC-Chip des Flippers kann es: der **ST25R3916** hat einen Hardware-
**Fremdfeld-Detektor** (dieselbe Schaltung, die den Flipper eine Karte emulieren und erkennen lässt, wann ein Reader
mit ihm spricht). Specter parkt den Chip im Detect-Only-Modus und **sampelt dieses „Feld vorhanden?"-Bit
~500 Mal pro Sekunde** — ohne jemals seinen eigenen Träger einzuschalten.
Dieses einzelne Bit trägt, schnell genug gesampelt, zwei unabhängige Signale:
```mermaid
flowchart LR
R["Hidden 13.56 MHz reader / skimmer<br/>(constantly polling its field)"] -- "RF carrier" --> A
subgraph FLIP["Flipper Zero — Specter.fap"]
A["ST25R3916 NFC chip<br/>external-field detector"] --> S["Sampler thread<br/>~500 samples/s"]
S --> M["<b>How much?</b><br/>carrier duty-cycle<br/>peak · average · contacts"]
S --> C["<b>What rhythm?</b><br/>burst / gap edge timing<br/>period · jitter"]
M --> G["EMF gauge · waveform<br/>geiger clicks · LED · vibe"]
M --> V["Survey verdict<br/>CLEAN / TRACE / ACTIVE"]
C --> K["Emitter class<br/>CONTINUOUS / POLLING /<br/>INTERMITTENT + pulse train"]
V --> L["SD logbook"]
K --> L
end
```
**Wie viel → Stärke.** Über ein kurzes Zeitfenster misst Specter, *welchen Bruchteil der Zeit* ein Träger
vorhanden ist, und glättet diesen Wert zum **FELD %**. Ein Leser, der direkt auf dem Flipper sitzt, treibt das Messgerät auf Anschlag;
ein schwächerer oder weiter entfernter bewegt es nur leicht. Das steuert die Nadel, das Näherungswort und die Klickrate.
**Welcher Rhythmus → Identität.** Separat wird jeder Übergang dieses Bits zeitlich erfasst. Ein Leser, der alle
200 ms für 20 ms aufwacht, erzeugt ein Burst-/Lückenmuster mit nahezu keinem Jitter, weil seine Polling-Schleife von einer
quarzgesteuerten Zustandsmaschine angetrieben wird. Handbewegung und HF-Rauschen sind nicht so gleichmäßig. Dieser
Unterschied ist es, was **POLLING** von **INTERMITTENT** trennt.
### Die Entscheidungsebenen werden auf einem echten Computer getestet
Die Stellen, an denen Specter Zahlen in eine *Behauptung* verwandelt — „das ist ein Polling-Leser", „dieser Raum ist
sauber", „das ist es, was die Nadel anzeigen sollte" — sind reines C ohne Hardware-Abhängigkeiten, und sie werden
durch Host-Tests festgenagelt statt auf dem Gerät entdeckt:
```bash
make -C test # 527 checks: classifier, verdict, meter scaling, presence,
# cadence, log filtering, log wrapping
```
---
## Installation
Kein Devboard, keine Firmware zum Flashen — es ist eine einzige `.fap`.
**Option A — vorgefertigte `.fap` (am einfachsten)**
1. Lade die passende `.fap` von der [**Releases**](https://github.com/at0m-b0mb/Specter-FlipperZero/releases)-Seite:
| Deine Firmware | Datei |
|---|---|
| **Offiziell / Stock** | `specter.fap` |
| **Unleashed, RogueMaster, Momentum** oder neuer | `specter-fw-dev.fap` |
> Wenn der Flipper **`APP:87 < FW:88 — This app might not work`** anzeigt, hast du die
> falsche — nimm die andere Datei. Die API-Version der App ist beim Kompilieren festgelegt,
> daher kann ein einzelner Build nicht beide Firmware-Linien bedienen. Sie läuft normalerweise trotzdem, wenn du
> auf Continue drückst, aber der passende Build fragt nicht nach.
2. Öffne **qFlipper**, ziehe die Datei auf `SD Card / apps / NFC /`.
3. Auf dem Flipper: **Apps → NFC → Specter**.
**Option B — selbst bauen mit [`ufbt`](https://github.com/flipperdevices/flipperzero-ufbt)**
```bash
# one-time
python3 -m pip install --upgrade ufbt
# from the repo root, with your Flipper plugged in over USB:
ufbt # build specter.fap into ./dist
ufbt launch # build, upload to the Flipper and open it
make -C test # run the host tests for the pure logic
```
Die `.fap` landet in `dist/specter.fap`; `ufbt launch` kopiert sie nach `apps/NFC/` und startet sie für dich.
> Icons in `icons/` und das Banner in `images/` werden generiert — neu erzeugen mit
> `python3 tools_gen_icons.py` und `python3 tools_gen_banner.py` (benötigt `pillow`).
> Alles in `screenshots/` wird von einem echten Gerät mit
> `python3 tools_screenshot.py` aufgenommen (benötigt `pyserial` und einen Flipper per USB).
---
## Verwendung
**Ein Sweep, von Anfang bis Ende:**
1. Starte **Specter**. Auf dem **Sweep**-Bildschirm drücke **LEFT** und halte 3 Sekunden still, um
auf den Grundrauschpegel des Raums zu kalibrieren.
2. Halte den Flipper flach und bewege ihn **langsam** über die Sache, die du prüfst — ein Kartenterminal, ein
Türleser, die Unterseite einer ATM-Lippe, ein Paket, einen Schreibtisch.
- **Ruhige / flache Wellenform** → kein aktiver Leser in Reichweite.
- **Nadel steigt, Klicks werden schneller** → du näherst dich einem Emitter. Weiter auf den Peak zu.
- **`● ACTIVE READER` + Alarmrahmen** → ein aktiver Leser ist genau hier.
3. Etwas gefunden? Geh zurück und öffne **Fingerprint**. Halte den Flipper still dagegen und lass die
Kadenz sich einpendeln — ein paar Sekunden reichen normalerweise. Drücke **OK**, um den Fund zu speichern.
4. Stattdessen einen ganzen Raum abklären? Öffne **Site Survey**, geh den Raum ab, bis sich der Balken füllt, und lies
das Urteil.
5. Verlässt du den Bereich? Versetze den Flipper in den **Watch Mode** und er hält Wache, wacht auf und schlägt Alarm,
wenn ein Leser auftaucht, während du weg bist.
6. Prüfe das **Logbook** auf alles, was du gespeichert hast, mit Zeitstempel — oder zieh `logbook.csv` von der Karte
in eine Tabellenkalkulation.
> Sweepe zuerst einen bekannt guten Leser (dein eigenes Handy mit NFC oder ein kontaktloses Terminal, dem du vertraust), um
> zu sehen, wie ein starkes, legitimes Feld auf dem Messgerät aussieht — und wie sein Fingerprint sich liest. Dann
> geh auf die Jagd.
### Steuerung
| Bildschirm | Taste | Aktion |
|---|---|---|
| **Sweep** | `OK` | Peak / Kontakte zurücksetzen |
| | `hold OK` | Aktuelle Messung protokollieren |
| | `LEFT` | Grundrauschen kalibrieren (3 s) |
| **Fingerprint** | `OK` | Den Fund im Logbook speichern |
| | `hold OK` | Die Messung neu starten |
| **Site Survey** | `OK` | Die Survey erneut ausführen |
| **Watch Mode** | `OK` | Neu scharf schalten (Zähler und Uhr zurücksetzen) |
| *überall* | `BACK` | Eine Ebene nach oben |
---
## Ehrliche Einschränkungen
- **Nur 13,56 MHz (HF).** Specter erfasst das **NFC-Band** — das, das von kontaktlosen Zahlungsskimmern,
den meisten modernen Zutrittslesern, Transit- und Hotel-Lesern verwendet wird. Es **kann keine** **125 kHz (LF)**-Leser
sehen (ältere HID Prox / EM4100-Türpanels); der LF-Pfad des Flippers hat kein äquivalentes Feld-Erkennungsbit.
- **Es erfasst den *Träger* eines Lesers, nicht das, was er liest.** Specter sagt dir, *dass ein aktiver Leser hier ist,
ungefähr wie nah, und in welchem Rhythmus er pollt* — es dekodiert, identifiziert oder erfasst nichts,
was der Leser tut.
- **Die Stärke ist relativ, nicht kalibriert.** `FIELD %` und die Näherungswörter sind ein vergleichender „wärmer /
kälter"-Leitfaden zum Sweepen, keine gemessene Entfernung in cm. Es ist eine *skalierte* Messung des Träger-
Duty-Cycles gegen ein typisches Polling-Band (siehe [Reading the meter](#-reading-the-meter--fixed-in-23));
ein Leser, der ungewöhnlich spärlich pollt, liest bei gleicher Entfernung niedriger, und einer im Dauerstrich-
Modus schlägt schon aus größerer Entfernung an.
- **Das Messgerät läuft voll.** `PEGGED` bedeutet, der Träger ist so stark, wie dieser Leser ihn überhaupt hält — darüber
hinaus kann ein Näherkommen wirklich keine größere Zahl erzeugen. Der Flipper summt einmal, wenn das passiert,
damit du weißt, dass du aufhören sollst, dich zu bewegen, auch wenn du den Bildschirm nicht sehen kannst. Nutze die `PEAK`-Haltefunktion, um
stattdessen Positionen zu vergleichen.
- **Die Kadenz löst auf ~2 ms auf.** Das ist die Abtastperiode. Zeitwerte in der Nähe davon werden mit einer
**`~`** angezeigt und die Konfidenz wird entsprechend abgewertet — Specter markiert lieber seine eigene Auflösungs-
grenze, als eine präzise aussehende Zahl zu nennen, hinter der es nicht stehen kann.
- **`CLEAN` bedeutet sauber bei der Sensitivität, die du gewählt hast.** Ein ruhender Skimmer, der nur bei einem echten Tap aufwacht,
oder einer, der abgeschirmt ist, bleibt bei *jedem* Schwellenwert unsichtbar. Das Fehlen einer Messung ist keine Garantie für
Abwesenheit.
- **Ein Funkgerät zur Zeit.** Specter übernimmt den NFC-Chip während des Sweepens, also schließe zuerst jede andere NFC-App
(es wird *NFC unavailable* anzeigen, wenn etwas anderes das Funkgerät belegt).
---
## Rechtliches & Ethik
Specter ist ein **defensives, nur zuhörendes** Werkzeug — es **sendet nie**, erzeugt nie ein Feld, berührt nie
den Leser. Nutze es, um **deinen eigenen** POS-Bereich, deine Tür, deinen Schreibtisch oder deine Habseligkeiten zu sweepen, oder Hardware, für deren Bewertung du **ausdrücklich
autorisiert** bist. Du bist dafür verantwortlich, wie du es verwendest. Kenne deine lokalen Gesetze.
---
## Roadmap
- [x] Sensitivität / Ton / Vibration / LED über Neustarts hinweg beibehalten
- [x] „Logbook" der Erkennungen mit Zeitstempeln auf der SD-Karte
- [x] Hintergrund-Sweep bei ausgeschaltetem Bildschirm — *Stealth-Modus*
- [x] Einstellbarer Schwellenwert für feinere Bereichskontrolle — *Auto-Kalibrierung + Custom-Sensitivität*
- [x] Einen Emitter anhand seiner Polling-Kadenz fingerprinten
- [x] Zeitgesteuerte Site Survey mit Raumurteil
- [x] Das Logbook als CSV für die Berichterstattung exportieren — *live neben der .txt geschrieben*
- [x] Langlaufender unbeaufsichtigter Watch-Modus mit Wake-on-Detection-Alarm
- [x] Das Messgerät seinen vollen Bereich gegen echte Polling-Leser nutzen lassen
- [x] Wärmer/kälter-Trendpfeil während der Jagd
- [ ] Eine LF-(125 kHz)-spulenbasierte Lesererkennung als separater Modus untersuchen
- [ ] Geräteinterne Logbook-Filterung nach Typ
---
## Projektstruktur
```
Specter-FlipperZero/
├── application.fam # Flipper app manifest (category: NFC)
├── specter.c / specter_i.h # app entry, wiring, alert feedback, stealth
├── helpers/
│ ├── field_detector.{c,h} # worker thread: samples the field-present bit,
│ │ # duty-cycle + edge timing + calibration
│ ├── emitter_classify.{c,h} # pure: cadence -> CONTINUOUS/POLLING/INTERMITTENT
│ ├── survey_verdict.{c,h} # pure: survey stats -> CLEAN/TRACE/ACTIVE
│ ├── field_scale.{c,h} # pure: raw carrier duty -> full-scale meter
│ ├── present_hold.h # pure: debounce presence across poll gaps
│ ├── cadence.h # pure: burst/gap/period timing with liveness
│ ├── ema.h # pure: the strength smoother
│ ├── log_filter.h # pure: keep only one kind of finding
│ ├── log_wrap.h # pure: wrap an entry at words, not mid-word
│ ├── specter_settings.{c,h} # persisted settings (saved_struct)
│ └── specter_log.{c,h} # SD logbook, RTC-stamped .txt + live .csv
├── views/
│ ├── view_chrome.h # the header, presence dot and error screen
│ │ # every measurement view shares
│ ├── sweep_view.{c,h} # the EMF gauge / waveform / alarm screen
│ ├── fingerprint_view.{c,h} # classification card + pulse-train trace
│ ├── survey_view.{c,h} # progress + verdict card
│ └── watch_view.{c,h} # unattended guard: clock + count + alarm
├── scenes/ # scene-manager navigation
├── test/ # host tests for the pure decision layers
├── icons/ # 1-bit Flipper icons (generated)
├── images/ # banner, mark and social card (generated), plus
│ # device_*.png: real captures off a Flipper
├── screenshots/ # the six captures the Apps Catalog listing uses
├── docs/catalog/ # the prepared catalog manifest + how to submit
├── tools_check_layout.py # static overlap + clearance checker (CI-gated)
└── tools_gen_*.py # regenerate icons / mockups / banner / GIF
```
---
## Branding
**BACKLIGHT.** Für die Identität **Didot**, für die Stimme **Futura**, für jeden
gemessenen Wert **Andale Mono**. Das Orange ist `#FE8A2C` — *entnommen* aus einer
qFlipper-Aufnahme dieser laufenden App, es ist also das eigene Backlight des
Flippers und keine Farbe, die gewählt wurde, um ihm zu ähneln. Jede Aufnahme in
`screenshots/` besteht aus genau zwei Farben, `(254,138,44)` und `(0,0,0)`; das
ist es, was die Hardware ausgibt.
Sie wird niemals als Markenfarbe verwendet. **Die einzige Glut auf jedem Asset
ist Licht, das das Gerät tatsächlich ausgestrahlt hat** — Pixel innerhalb einer
echten Aufnahme, unverändert in ganzzahligem Maßstab eingefügt. Artwork wird im
selben Farbton fünfzehn Grad kühler gezeichnet. Eine Rampe, von entgegengesetzten
Enden gelesen: **mehr Tinte bedeutet mehr Signal auf Papier, mehr Licht bedeutet
mehr Signal auf Schwarz** — denn Papier ist dort, wo ein Sweep endet (Specter
schreibt ein mit Zeitstempel versehenes Logbuch, das man jemandem aushändigen
kann) und Schwarz ist dort, wo er stattfindet (der Stealth-Modus erzwingt das
Abschalten des Backlights).
Der Akzent zeichnet eine Sache und nichts anderes: **gemessener Träger.** Er ist
niemals eine Linie, ein Rahmen, ein Rand oder ein Wort — es gibt nirgendwo im
System goldene Wörter, und der Renderer wirft einen Fehler, wenn eines versucht
wird. Das Banner trägt also genau ein akzentfarbenes Objekt, und dieses Objekt
ist eine Messung.
Die Wellenform besteht aus 128 Spalten des Trägers, die Specter von einem echten
Polling-Reader mit 8 ms pro Spalte aufgezeichnet hat, zurückgewonnen aus dem
danebenliegenden Screenshot. Der flache Verlauf links ist der Trace-Puffer,
bevor er sich gefüllt hat, nicht Stille, die jemand gemessen hätte. Ihr
Tastverhältnis liest **28%**, wo der eigene Zähler des Geräts **27%** ausgegeben
hat, und beide Zahlen stehen auf dem Banner. Die Marke ist ein Poll-Zyklus bei
**30%** Tastverhältnis — die 12 ms von 40 ms dieses Readers, und zugleich
`SPECTER_FULL_SCALE_DUTY`, das Tastverhältnis, bei dem das Messgerät 100% liest.
Eine Zahl, zwei unabhängige Gründe, sie zu sein.
`#FE8A2C` ist die einzige Farbe hier, die wir nicht gewählt haben.
| Asset | Größe | Wohin es gehört |
|---|---|---|
| `images/banner.png` | 2560×800 | README-Header, Papierhintergrund (2×, damit es auf Retina scharf bleibt) |
| `images/banner-dark.png` | 2560×800 | Dieselbe Zeichnung auf echtem Schwarz, per `<picture>` an Dark-Mode-Leser ausgeliefert |
| `images/social-preview.png` | **1280×640** | **Settings → Social preview** — GitHubs empfohlene Größe (min. 640×320, 1 MB Obergrenze) |
| `images/mark.png` | 512×512 | Quadratische Logo-Marke: Social-Posts, Slides, Favicon. **Kein Repo-Avatar** — GitHub zeigt das *Profilbild des Eigentümers* neben einem Repo, es gibt also keinen pro-Repo-Avatar zum Festlegen |
| `images/mark-180/64/32/16.png` | wie benannt | 180 und 64 herunterskaliert; **32 und 16 sind nativ gezeichnet**, damit nichts zu Brei anti-aliasiert wird |
| `images/demo.gif` | 384×192 | Ein geskripteter Durchlauf durch jeden Modus, **vom Gerät aufgezeichnet** — `tools_screenshot.py --tour-gif` |
| `images/splash.gif` | 384×192 | Das Boot-Intro, aufgezeichnet ab der Startanforderung — `--splash` |
| `images/screens.png` | — | Kontaktabzug der Aufnahmen, heller Hintergrund, neu erzeugt durch `--sheet` |
| `images/screens-dark.png` | — | Derselbe Abzug auf echtem Schwarz, per `<picture>` an Dark-Mode-Leser ausgeliefert |
| `screenshots/*.png` | 512×256 | **Jeder Bildschirm, von einem echten Gerät aufgenommen** über RPC. Genau zwei Farben: die des Panels selbst. |
```bash
python3 tools_brand_data.py # the facts the branding is allowed to state
python3 tools_gen_banner.py # banner (light + dark), social preview, logo mark
python3 tools_gen_icons.py # 1-bit Flipper icons
# these need a Flipper on USB (pip install pyserial)
python3 tools_screenshot.py --all # walk every screen and capture it
python3 tools_screenshot.py --reader # the shots that need a live reader
python3 tools_screenshot.py --splash # the boot intro
python3 tools_screenshot.py --tour-gif # one GIF of the app being used
```
> Die soziale Vorschau ist das einzige Asset, das GitHub nicht aus dem Repo übernehmen kann — sie muss einmal manuell unter **Settings → Social preview** hochgeladen werden. Sie wird angezeigt, wenn das Repo auf Slack, X, LinkedIn oder Discord geteilt wird.
---
## Credits
- Entwickelt von **[at0m-b0mb](https://github.com/at0m-b0mb)**.
- Teil einer Flipper-Sicherheitstool-Familie: **[Argus](https://github.com/at0m-b0mb/Argus-FlipperZero)** (Wi-Fi-Deauth-/Evil-Twin-Watchdog), **[GhostTag](https://github.com/at0m-b0mb/GhostTag-FlipperZero)** (BLE-Anti-Stalking-Jäger) und **[Cerberus](https://github.com/at0m-b0mb/flipper-cerberus)** (Sub-GHz-RF-Watchdog).
- Basiert auf der [Flipper Zero Firmware](https://github.com/flipperdevices/flipperzero-firmware) NFC HAL (`furi_hal_nfc` Felddetektion) + [ufbt](https://github.com/flipperdevices/flipperzero-ufbt).
## Lizenz
[MIT](https://github.com/at0m-b0mb/specter-flipperzero/blob/main/LICENSE) © 2026 at0m-b0mb