
Specter — lecteur/balayeur passif de mouchards NFC à 13,56 MHz pour Flipper Zero. Détecteur EMF de contre-surveillance utilisant la puce NFC intégrée. Aucun matériel supplémentaire, n'émet jamais.
<!-- 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>
La forme d'onde n'est pas un graphique. Ce sont 128 colonnes de porteuse à 13,56 MHz que
Specter a enregistrées depuis un vrai lecteur en polling à 8 ms par colonne, relues
depuis la capture d'écran à côté. Son rapport cyclique mesure 28% là où le compteur propre
de l'appareil affichait 27%, et les deux chiffres figurent sur la bannière.
</sub>
</p>
<p align="center"><i>Balayez pour trouver les lecteurs que vous ne pouvez pas voir.</i></p>
<p align="center">
<a href="https://at0m-b0mb.github.io/Specter-FlipperZero/"><b>Site du projet</b></a>
·
<a href="https://github.com/at0m-b0mb/Specter-FlipperZero/releases/latest">Télécharger</a>
·
<a href="https://github.com/at0m-b0mb/specter-flipperzero/blob/main/docs/changelog.md">Journal des modifications</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> transforme votre Flipper Zero en <b>balayage anti-surveillance de poche</b> pour
les <b>lecteurs NFC actifs à 13,56 MHz</b>. Il <i>écoute passivement</i> le champ RF qu'un lecteur
sous tension émet en permanence — un skimmer de carte caché glissé dans un terminal de paiement,
un lecteur dissimulé derrière un panneau de porte, un enregistreur pirate scotché sous un bureau —
puis vous dit <b>où il se trouve</b>, <b>de quel type d'objet il s'agit</b>, <b>si la pièce est
propre</b>, et — laissé en veille — <b>le moment où l'un apparaît pendant votre absence</b>. Il
n'émet jamais.
</p>
<p align="center"><sub>Les lecteurs sont invisibles. Specter les rend visibles.</sub></p>
<p align="center">
<sub>
<b>Présenté dans</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>
---
## Sur le 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>Un balayage, du début à la fin : pièce calme → rapprochement → verrouillage → <b>ce que</b> c'est → le verdict de la pièce.</sub>
</p>
### Les écrans qu'aucun mode ne possède à lui seul
<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>
Les états calmes et l'intro — les quatre modes ci-dessous ont chacun leur propre
capture, donc voici celles qu'ils n'ont pas. <b>Chaque image
de ce README est une capture prise sur un vrai appareil</b>, réalisée via la
propre session RPC du Flipper par <code>tools_screenshot.py</code>. Il n'y a plus
de moteur de rendu de maquette dans ce projet — un dessin de l'interface est une seconde
implémentation de celle-ci, et il était en désaccord avec le firmware tout en paraissant
parfaitement convaincant (voir <a href="https://github.com/at0m-b0mb/specter-flipperzero/blob/main/docs/changelog.md">3.0.1</a>).
</sub>
</p>
---
## Les cinq modes
Specter, ce sont cinq outils autour d'un seul capteur. Chacun répond à une question
différente, donc le bon choix dépend de ce que vous cherchez réellement à découvrir :
| Mode | La question à laquelle il répond | À l'utiliser quand |
|---|---|---|
| **Sweep** | *Où est-ce ?* | Vous suspectez un appareil et voulez le localiser en vous déplaçant |
| **Fingerprint** | *De quel type d'objet s'agit-il ?* | Vous avez trouvé un émetteur et voulez savoir comment il se comporte |
| **Site Survey** | *Cette pièce est-elle propre ?* | Vous voulez un verdict unique pour tout un espace, mains libres |
| **Watch Mode** | *Un appareil est-il apparu pendant mon absence ?* | Vous laissez le Flipper quelque part pour monter la garde |
| **Logbook** | *Qu'ai-je trouvé, et quand ?* | Vous rédigez un rapport, ou comparez aujourd'hui avec la semaine dernière — filtrez par type de découverte |
---
### Sweep — *où est-ce ?*
<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">
**Ce qu'il fait.** Un mesureur de type EMF en direct. Tenez le Flipper à plat et déplacez-le
lentement au-dessus de l'objet que vous vérifiez — un terminal de paiement, un lecteur de porte,
le dessous d'une fente de distributeur, un colis, un bureau. L'aiguille suit la force du champ en
temps réel.
**Ce que vous voyez.**
- **Le cadran** — l'aiguille = la lecture en direct, le petit point = le *maintien de crête* (l'endroit
le plus fort que vous avez touché, pour comparer les positions).
- **`FIELD %`** — la même lecture sous forme de nombre, avec une **flèche de tendance ▲ / ▼**
qui vous indique si la dernière demi-seconde a rendu les choses *plus chaudes ou plus froides*. En
chasse, cette flèche compte plus que le nombre.
- **`PEAK %`** — la lecture la plus forte depuis l'armement, le nombre derrière le
point de maintien de crête.
- **Le repère sur le cadran** — où `READER` *commence* à votre sensibilité actuelle. Tout ce qui
le dépasse est ce que Specter considérera comme une détection, donc le cadran
répond à « qu'est-ce qui compte ? » au lieu de vous laisser deviner.
- **Bande inférieure** — jusqu'à ce que vous ayez trouvé votre premier lecteur, elle affiche les deux touches
que vous ne pouvez pas découvrir en appuyant au hasard (`LEFT=cal`, `hold OK=log`) ; ensuite,
votre sensibilité et une forme d'onde en direct. Elle bascule vers une barre d'alarme noire
**`● ACTIVE READER`** avec un mot de proximité dès qu'une porteuse est
détectée.
- **Proximité** — `FAINT → NEAR → CLOSE → STRONG → PEGGED`. `PEGGED` signifie que le mesureur est
*à fond* : vous êtes aussi proche que cette mesure peut le résoudre.
**Son.** Des clics geiger optionnels qui **s'accélèrent à mesure que vous vous rapprochez**, pour pouvoir
balayer avec le Flipper dans votre poche et chasser à l'oreille seule.
| Touche | Action |
|---|---|
| `OK` | Réinitialiser le maintien de crête et le compteur de contacts |
| `hold OK` | Enregistrer cette lecture dans le journal |
| `LEFT` | Calibrer sur le plancher de bruit de la pièce (3 s) |
| `UP` / `DOWN` | Ajuster la sensibilité sans quitter la chasse |
<br clear="right">
### Fingerprint — *de quel type d'objet s'agit-il ?*
<img src="https://raw.githubusercontent.com/at0m-b0mb/specter-flipperzero/main/screenshots/fingerprint_reader.png" width="42%" align="right" alt="Fingerprint — polling reader identified">
**Ce qu'il fait.** Une fois que Sweep a trouvé quelque chose, tenez le Flipper **immobile**
contre l'objet. Fingerprint cesse de mesurer la proximité et commence à **chronométrer les
fronts on/off de la porteuse**, ce qui distingue réellement un type d'émetteur d'un autre.
**Ce que vous voyez.** Un verdict, une barre de confiance, et les preuves derrière les deux :
| Classe | Ce que cela signifie en pratique |
|---|---|
| **CONTINUOUS** | La porteuse est maintenue en permanence |
| **POLLING** | Un cycle de polling fixe, cadencé par quartz — la période est affichée en ms |
| **INTERMITTENT** | En rafales mais irrégulier — souvent un téléphone ou un lecteur en cours d'utilisation |
En dessous se trouvent `PER` (période de polling), `BST` (largeur de rafale), `JIT` (gigue) et
`UP` (rapport cyclique réel), plus un **train d'impulsions d'analyseur logique** de la porteuse
brute — le verdict n'est donc jamais quelque chose que vous devez prendre sur parole. Un véritable
lecteur en polling apparaît comme une onde carrée impossible à confondre.
> Un `~` devant un chronométrage signifie qu'il est proche du plancher d'échantillonnage de 2 ms, et la
> confiance est réduite en conséquence.
| Touche | Action |
|---|---|
| `OK` | Enregistrer la découverte dans le journal |
| `hold OK` | Redémarrer la mesure |
<br clear="right">
### Site Survey — *cette pièce est-elle propre ?*
<img src="https://raw.githubusercontent.com/at0m-b0mb/specter-flipperzero/main/screenshots/survey_active.png" width="42%" align="right" alt="Site Survey — verdict">
**Ce qu'il fait.** Un balayage **chronométré** de tout un espace. Lancez-le, parcourez la pièce
normalement, et obtenez un verdict unique à la fin — sans surveiller l'aiguille.
**Ce que vous voyez.** Un compte à rebours et une barre de progression pendant l'exécution, puis une carte de verdict :
- **`CLEAN`** — rien n'a franchi le plancher de bruit
- **`TRACE`** — détections brèves ou faibles ; mérite une seconde passe plus lente
- **`ACTIVE READER`** — quelque chose était réellement actif et émettait
- **`TOO SHORT`** — vous l'avez arrêté avant qu'il ait gagné le droit de dire `CLEAN`
…avec `PEAK` / `AVG` du champ, le nombre de contacts, et `UP %` — **la proportion du relevé
pendant laquelle une porteuse était réellement active**, souvent le chiffre le plus révélateur des quatre.
Dure 30 s, 60 s ou 2 min, et enregistre automatiquement le résultat. La carte nomme
la durée sur laquelle il a été évalué, car un `CLEAN` de 30 secondes n'est pas la même
découverte qu'un de deux minutes — et une exécution écourtée à moins de 10 s rapporte
`TOO SHORT` au lieu de `CLEAN`. L'asymétrie est délibérée : **la présence est
une preuve, l'absence ne l'est pas.** Trouver quelque chose en une seconde est une vraie découverte, donc
`TRACE` et `ACTIVE READER` ne sont jamais retenus pour avoir été rapides ; ne rien trouver
en une seconde est une affirmation sur toute la pièce qu'une seconde ne peut pas étayer.
| Touche | Action |
|---|---|
| `OK` *(en cours)* | Arrêter maintenant et évaluer ce qui a été trouvé |
| `OK` *(sur la carte)* | Lancer un autre relevé |
<br clear="right">
### Watch Mode — *un appareil est-il apparu pendant mon absence ?*
<img src="https://raw.githubusercontent.com/at0m-b0mb/specter-flipperzero/main/screenshots/watch_reader.png" width="42%" align="right" alt="Watch Mode — reader present">
**Ce qu'il fait.** Monte la garde **indéfiniment**. Armez-le, posez le Flipper,
éloignez-vous. Là où Sweep est vous en chasse et Survey un test de durée fixe, Watch
attend simplement — pendant des minutes ou des heures.
**Ce que vous voyez.** Une grande horloge écoulée et `NO READER` tant que rien n'est là.
À l'instant où un lecteur apparaît, la bande s'inverse en **`ACTIVE READER`**, une barre de
force montre sa proximité, **l'écran se réveille**, et l'alarme retentit. La bande
est stable plutôt que clignotante — sur un écran 1 bit, un bloc inversé plein est
déjà la chose la plus visible, et les relevés qui changent réellement portent
le caractère vivant. Après son passage, l'écran conserve les preuves : `HITS` (combien de
contacts distincts), `PEAK`, `LAST` (depuis combien de temps remonte le plus récent) et
`UP` (durée totale pendant laquelle une porteuse était réellement active). L'horloge passe à `hh:mm`
au-delà de 99:59 plutôt que de se figer, donc une veille nocturne s'additionne toujours correctement.
Watch **ignore délibérément le mode furtif** — une garde sombre et silencieuse qui ne
vous dirait jamais qu'elle a vu quelque chose serait pire qu'inutile.
| Touche | Action |
|---|---|
| `OK` | Réarmer — efface l'horloge et les compteurs |
<br clear="right">
### Logbook — *qu'ai-je trouvé, et quand ?*
Les découvertes sont enregistrées **deux fois, à partir d'une seule action**, pour être lisibles sur l'appareil
*et* déjà être un tableur — aucune étape d'export à retenir :
<table>
<tr><th><code>logbook.txt</code> — groupé, pour la visionneuse sur l'appareil</th><th><code>logbook.csv</code> — une ligne plate par entrée</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>
Les entrées sont étiquetées `SWEEP`, `READER`, `SURVEY` ou `WATCH` et horodatées depuis
l'horloge du Flipper. Les deux fichiers résident dans `apps_data/specter/` sur la carte SD. Rien
ne quitte jamais l'appareil. Effacez-les depuis **Settings → Clear logbook** (confirmation
préalable — c'est la seule action irréversible que l'application puisse effectuer).
---
## Les éléments de support
### Auto-calibration
Appuyez sur **LEFT** sur l'écran Sweep et Specter écoute le **bruit de fond ambiant
pendant 3 secondes**, puis fixe le seuil de détection juste au-dessus de ce qu'il
a mesuré — enregistré comme votre sensibilité **Custom**. Chaque pièce a un plancher RF
différent ; cela s'accorde à celui dans lequel vous vous trouvez plutôt qu'à un nombre figé au
moment de la compilation. Tenez-vous dans un endroit calme lorsque vous le lancez.
### Mode furtif
Garde l'**écran et la LED éteints** pendant toute la balayage, afin que le Flipper ne brille pas
alors que c'est vous qui observez. Le son et la vibration continuent de fonctionner — le
but n'est pas de désactiver le retour d'information avec lequel vous balayez. Quitter un écran furtif
rallume l'affichage, donc `BACK` vous ramène toujours à un menu éclairé.
### Lire le compteur
**Pourquoi un lecteur que vous touchez n'émet pas 100 % du temps.** Le détecteur
mesure une seule chose physique : la fraction du temps où une porteuse 13,56 MHz est active.
Mais les lecteurs **interrogent** — une courte salve, un repos, une autre salve. Un terminal typique
ne rayonne que **20 à 35 % du temps**, donc le rapport cyclique brut *sature* autour de 30 %
quelle que soit votre proximité.
Afficher ce nombre brut sur la jauge faisait qu'une détection parfaite ressemblait à un tiers
d'une détection. Le compteur est plutôt mis à l'échelle par rapport à cette bande d'interrogation réelle :
| Rapport cyclique brut de la porteuse | Compteur | Proximité |
|---|---|---|
| 3 % (bruit ambiant) | 10 % | `FAINT` |
| 12 % | 40 % | `NEAR` |
| 20 % | 67 % | `CLOSE` |
| 28 % | 93 % | `STRONG` |
| ≥30 % *(posé sur un lecteur)* | **100 %** | `PEGGED` |
Le **rapport cyclique brut n'est jamais perdu** : le plancher de bruit, l'auto-calibration et le
`UP` de l'écran Fingerprint fonctionnent tous encore en rapport cyclique réel, car ceux-ci
décrivent le *signal*, pas votre distance par rapport à lui. Vous voulez le nombre littéral ?
**Settings → Meter scale → Duty %**.
### Tout persiste
La sensibilité, la durée de balayage, le son, la vibration, la LED, le mode furtif, la journalisation et le mode du compteur sont
**enregistrés sur la carte SD** dès que vous les modifiez. Un kit de balayage qui oublie sa
configuration est pire que pas de persistance du tout.
---
## Comment ça fonctionne
Chaque lecteur NFC sous tension **interroge continuellement l'air avec une porteuse 13,56 MHz**, en attendant qu'une carte
se réveille. Vous ne pouvez pas la voir, mais la puce NFC du Flipper le peut : le **ST25R3916** dispose d'un
**détecteur de champ externe** matériel (le même circuit qui permet au Flipper d'émuler une carte et de savoir quand un lecteur
lui parle). Specter met la puce en mode détection seule et **échantillonne ce bit « champ présent ? »
environ 500 fois par seconde** — sans jamais activer sa propre porteuse.
Ce seul bit, échantillonné assez rapidement, porte deux signaux indépendants :```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
```
**Combien → force.** Sur une courte fenêtre, Specter mesure *quelle fraction du temps* un porteur est
présent et la lisse en **FIELD %**. Un lecteur situé juste au-dessus du Flipper fait culminer le mètre ;
un lecteur plus faible ou plus éloigné le fait légèrement bouger. Cela pilote l'aiguille, le mot de proximité et le taux de clics.
**Quel rythme → identité.** Séparément, chaque transition de ce bit est chronométrée. Un lecteur qui se réveille pendant
20 ms toutes les 200 ms produit un motif de rafale/espace avec presque aucune gigue, car sa boucle de scrutation est
pilotée par une machine à états cadencée par cristal. Le mouvement de la main et le bruit RF ne sont pas aussi stables. Cette
différence est ce qui sépare **POLLING** de **INTERMITTENT**.
### Les couches de décision sont testées sur un vrai ordinateur
Les endroits où Specter transforme des nombres en une *affirmation* — « ceci est un lecteur en scrutation », « cette pièce est
propre », « voici ce que l'aiguille devrait indiquer » — sont du C pur sans dépendances matérielles, et ils sont
fixés par des tests hôtes plutôt que découverts sur l'appareil :```bash
make -C test # 527 checks: classifier, verdict, meter scaling, presence,
# cadence, log filtering, log wrapping
```
---
## Installation
Pas de devboard, pas de firmware à flasher — c'est un simple `.fap`.
**Option A — `.fap` précompilé (le plus simple)**
1. Récupérez le bon `.fap` depuis la page [**Releases**](https://github.com/at0m-b0mb/Specter-FlipperZero/releases) :
| Votre firmware | Fichier |
|---|---|
| **Officiel / stock** | `specter.fap` |
| **Unleashed, RogueMaster, Momentum** ou tout ce qui est plus récent | `specter-fw-dev.fap` |
> Si le Flipper affiche **`APP:87 < FW:88 — This app might not work`**, vous avez le
> mauvais fichier — prenez l'autre. La version d'API de l'application est figée au moment de la compilation,
> donc une seule build ne peut pas satisfaire les deux lignées de firmware. Elle fonctionne généralement quand même si vous
> appuyez sur Continue, mais la build correspondante ne le demandera pas.
2. Ouvrez **qFlipper**, glissez le fichier dans `SD Card / apps / NFC /`.
3. Sur le Flipper : **Apps → NFC → Specter**.
**Option B — compilez-le vous-même avec [`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
```
Le `.fap` atterrit dans `dist/specter.fap` ; `ufbt launch` le copie dans `apps/NFC/` et le lance pour vous.
> Les icônes dans `icons/` et la bannière dans `images/` sont générées — régénérez-les avec
> `python3 tools_gen_icons.py` et `python3 tools_gen_banner.py` (nécessite `pillow`).
> Tout ce qui se trouve dans `screenshots/` est capturé depuis un appareil réel avec
> `python3 tools_screenshot.py` (nécessite `pyserial` et un Flipper sur USB).
---
## Utilisation
**Un balayage, de bout en bout :**
1. Lancez **Specter**. Sur l'écran **Sweep**, appuyez sur **LEFT** et restez immobile pendant 3 secondes pour
calibrer sur le bruit de fond de la pièce.
2. Tenez le Flipper à plat et déplacez-le **lentement** sur ce que vous vérifiez — un terminal de carte, un
lecteur de porte, le dessous d'une fente de distributeur, un colis, un bureau.
- **Forme d'onde calme / plate** → aucun lecteur actif à portée.
- **Aiguille qui monte, clics qui s'accélèrent** → vous approchez d'un émetteur. Continuez vers le pic.
- **`● ACTIVE READER` + bordure d'alarme** → un lecteur actif est juste ici.
3. Vous avez trouvé quelque chose ? Revenez en arrière et ouvrez **Fingerprint**. Tenez le Flipper immobile contre l'appareil et laissez la cadence se stabiliser — quelques secondes suffisent généralement. Appuyez sur **OK** pour enregistrer la trouvaille.
4. Vous nettoyez plutôt toute une pièce ? Ouvrez **Site Survey**, parcourez l'espace jusqu'à ce que la barre se remplisse, et lisez le verdict.
5. Vous quittez la zone ? Laissez le Flipper en **Watch Mode** et il montera la garde, se réveillant et sonnant si un lecteur apparaît pendant votre absence.
6. Consultez le **Logbook** pour tout ce que vous avez enregistré, horodaté — ou récupérez `logbook.csv` depuis la carte dans un tableur.
> Balayez d'abord un lecteur que vous savez fiable (votre propre téléphone en NFC, ou un terminal sans contact en lequel vous avez confiance) pour voir à quoi ressemble un champ fort et légitime sur le compteur — et ce que donne son empreinte. Ensuite, partez en chasse.
### Commandes
| Écran | Touche | Action |
|---|---|---|
| **Sweep** | `OK` | Réinitialiser le pic / les contacts |
| | `hold OK` | Enregistrer la lecture actuelle |
| | `LEFT` | Calibrer le bruit de fond (3 s) |
| **Fingerprint** | `OK` | Sauvegarder la trouvaille dans le logbook |
| | `hold OK` | Redémarrer la mesure |
| **Site Survey** | `OK` | Relancer le relevé |
| **Watch Mode** | `OK` | Réarmer (réinitialiser le compteur et l'horloge) |
| *n'importe où* | `BACK` | Remonter d'un niveau |
---
## Limites honnêtes
- **13,56 MHz (HF) uniquement.** Specter détecte la **bande NFC** — celle utilisée par les skimmers de paiement sans contact, la plupart des lecteurs d'accès modernes, les lecteurs de transport et d'hôtel. Il **ne peut pas** voir les lecteurs **125 kHz (LF)** (anciens panneaux de porte HID Prox / EM4100) ; le chemin LF du Flipper n'a pas de bit équivalent de détection de champ.
- **Il détecte la *porteuse* d'un lecteur, pas ce qu'il lit.** Specter vous dit *qu'un lecteur actif est ici, à peu près à quelle distance, et sur quel rythme il interroge* — il ne décode, n'identifie ni ne capture rien de ce que fait le lecteur.
- **La force est relative, non calibrée.** `FIELD %` et les mots de proximité sont un guide comparatif « plus chaud / plus froid » pour le balayage, pas une distance mesurée en cm. C'est une lecture *mise à l'échelle* du rapport cyclique de la porteuse par rapport à une bande d'interrogation typique (voir [Lecture du compteur](#-reading-the-meter--fixed-in-23)) ; un lecteur qui interroge anormalement peu lira plus bas à la même distance, et un lecteur en mode onde continue sature de plus loin.
- **Le compteur sature.** `PEGGED` signifie que la porteuse est au maximum de ce que ce lecteur maintient jamais — au-delà, se rapprocher ne peut réellement pas produire un nombre plus grand. Le Flipper vibre une fois quand cela se produit, pour que vous sachiez qu'il faut arrêter de bouger même quand vous ne voyez pas l'écran. Utilisez le maintien `PEAK` pour comparer les positions à la place.
- **La cadence se résout à ~2 ms.** C'est la période d'échantillonnage. Les timings proches de cette valeur sont affichés avec un **`~`** et la confiance est réduite en conséquence — Specter préfère signaler son propre plancher de résolution plutôt que de citer un nombre d'apparence précise qu'il ne peut pas assumer.
- **`CLEAN` signifie propre à la sensibilité que vous avez choisie.** Un skimmer dormant qui ne se réveille qu'à un vrai tap, ou un skimmer blindé, reste invisible à *n'importe quel* seuil. L'absence de lecture n'est pas une garantie d'absence.
- **Une seule radio à la fois.** Specter prend le contrôle de la puce NFC pendant le balayage, donc fermez d'abord toute autre application NFC (il affichera *NFC unavailable* si autre chose occupe la radio).
---
## Légal & éthique
Specter est un outil **défensif, en écoute seule** — il **n'émet jamais**, n'alimente jamais un champ, ne touche jamais le lecteur. Utilisez-le pour balayer **votre propre** zone de POS, porte, bureau ou effets personnels, ou du matériel que vous êtes **explicitement autorisé** à évaluer. Vous êtes responsable de la façon dont vous l'utilisez. Renseignez-vous sur les lois locales.
---
## Feuille de route
- [x] Persister sensibilité / son / vibration / LED à travers les redémarrages
- [x] « Logbook » des détections avec horodatages sur la carte SD
- [x] Balayage en arrière-plan avec l'écran éteint — *mode furtif*
- [x] Seuil ajustable pour un contrôle de portée plus fin — *auto-calibration + sensibilité personnalisée*
- [x] Empreinte d'un émetteur par sa cadence d'interrogation
- [x] Relevé de site chronométré avec verdict de pièce
- [x] Export du logbook en CSV pour reporting — *écrit en direct à côté du .txt*
- [x] Mode veille de longue durée sans surveillance avec alarme de réveil à la détection
- [x] Faire utiliser au compteur toute sa plage face aux vrais lecteurs à interrogation
- [x] Flèche de tendance plus chaud / plus froid pendant la chasse
- [ ] Étudier une détection de lecteur à base de bobine LF (125 kHz) comme mode séparé
- [ ] Filtrage du logbook par type sur l'appareil
---
## Structure du projet```
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
```
---
## Image de marque
**BACKLIGHT.** Composé en **Didot** pour l'identité, **Futura** pour la voix, **Andale Mono**
pour chaque valeur mesurée. L'orange est `#FE8A2C` — *échantillonné* à partir d'une capture
qFlipper de cette application en cours d'exécution, c'est donc le rétroéclairage propre du
Flipper plutôt qu'une couleur choisie pour lui ressembler. Chaque capture dans `screenshots/`
comporte exactement deux couleurs, `(254,138,44)` et `(0,0,0)` ; c'est ce que le matériel émet.
Il n'est jamais utilisé comme couleur de marque. **La seule braise sur n'importe quel asset est
la lumière que l'appareil a réellement émise** — des pixels à l'intérieur d'une vraie capture,
collés tels quels à une échelle entière. L'illustration est dessinée dans la même teinte, quinze
degrés plus froide. Une seule rampe, lue par les deux extrémités : **plus d'encre signifie plus
de signal sur papier, plus de lumière signifie plus de signal sur noir** — car le papier est
l'endroit où un balayage se termine (Specter écrit un journal horodaté que vous pouvez confier à
quelqu'un) et le noir est l'endroit où cela se produit (le mode furtif force l'extinction du
rétroéclairage).
L'accent ne dessine qu'une seule chose et rien d'autre : **la porteuse mesurée.** Il n'est jamais
une règle, un cadre, une marge ou un mot — il n'y a aucun mot doré nulle part dans le système, et
le moteur de rendu lève une exception si l'on tente d'en créer un. Ainsi la bannière porte
exactement un objet de la couleur d'accent, et cet objet est une mesure.
La forme d'onde représente 128 colonnes de porteuse que Specter a enregistrées depuis un vrai
lecteur de sondage à 8 ms par colonne, extraites de la capture d'écran à côté. Le segment plat à
gauche est le tampon de trace avant qu'il ne se remplisse, et non un silence que quelqu'un aurait
mesuré. Son rapport cyclique indique **28%** là où le compteur propre de l'appareil affichait
**27%**, et les deux nombres figurent sur la bannière. La marque est un cycle de sondage à **30%**
de rapport cyclique — les 12 ms sur 40 ms de ce lecteur, et aussi `SPECTER_FULL_SCALE_DUTY`, le
rapport cyclique auquel le compteur affiche 100%. Un seul nombre, deux raisons indépendantes
d'être celui-là.
`#FE8A2C` est la seule couleur ici que nous n'avons pas choisie.
| Asset | Taille | Où il va |
|---|---|---|
| `images/banner.png` | 2560×800 | En-tête du README, fond papier (2× pour rester net sur retina) |
| `images/banner-dark.png` | 2560×800 | Le même dessin sur noir véritable, servi par `<picture>` aux lecteurs en mode sombre |
| `images/social-preview.png` | **1280×640** | **Settings → Social preview** — la taille recommandée par GitHub (min 640×320, limite 1 Mo) |
| `images/mark.png` | 512×512 | Logo carré : publications sociales, diapositives, favicon. **Pas un avatar de dépôt** — GitHub affiche la photo de profil du *propriétaire* à côté d'un dépôt, il n'y a donc pas d'avatar par dépôt à définir |
| `images/mark-180/64/32/16.png` | comme nommé | 180 et 64 réduits ; **32 et 16 sont dessinés nativement** pour que rien ne s'anti-alias en bouillie |
| `images/demo.gif` | 384×192 | Un parcours scripté à travers chaque mode, **enregistré depuis l'appareil** — `tools_screenshot.py --tour-gif` |
| `images/splash.gif` | 384×192 | L'intro de démarrage, enregistrée à partir de la requête de lancement — `--splash` |
| `images/screens.png` | — | Planche contact des captures, fond clair, reconstruite par `--sheet` |
| `images/screens-dark.png` | — | La même planche sur noir véritable, servie par `<picture>` aux lecteurs en mode sombre |
| `screenshots/*.png` | 512×256 | **Chaque écran, capturé depuis un vrai appareil** via RPC. Exactement deux couleurs : celles du panneau lui-même. |```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
```
> L'aperçu social est le seul élément que GitHub ne peut pas récupérer depuis le dépôt — il doit être téléversé une fois manuellement dans **Settings → Social preview**. C'est ce qui s'affiche lorsque le dépôt est partagé sur Slack, X, LinkedIn ou Discord.
---
## Crédits
- Développé par **[at0m-b0mb](https://github.com/at0m-b0mb)**.
- Fait partie d'une famille d'outils de sécurité pour Flipper : **[Argus](https://github.com/at0m-b0mb/Argus-FlipperZero)** (watchdog Wi-Fi deauth / evil-twin), **[GhostTag](https://github.com/at0m-b0mb/GhostTag-FlipperZero)** (chasseur anti-pistage BLE) et **[Cerberus](https://github.com/at0m-b0mb/flipper-cerberus)** (watchdog RF Sub-GHz).
- Propulsé par le [firmware Flipper Zero](https://github.com/flipperdevices/flipperzero-firmware) NFC HAL (détection de champ `furi_hal_nfc`) + [ufbt](https://github.com/flipperdevices/flipperzero-ufbt).
## Licence
[MIT](https://github.com/at0m-b0mb/specter-flipperzero/blob/main/LICENSE) © 2026 at0m-b0mb