<!-- 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>
这段波形不是图形。它是 128 列 13.56 MHz 载波,Specter 以每列 8 ms
从一台真实的轮询读取器上录制,并从旁边的截图中读回。其占空比测得为 28%,
而设备自身的计数器显示为 27%,两个数字都印在横幅上。
</sub>
</p>
<p align="center"><i>扫描你看不见的读取器。</i></p>
<p align="center">
<a href="https://at0m-b0mb.github.io/Specter-FlipperZero/"><b>项目主页</b></a>
·
<a href="https://github.com/at0m-b0mb/Specter-FlipperZero/releases/latest">下载</a>
·
<a href="https://github.com/at0m-b0mb/specter-flipperzero/blob/main/docs/changelog.md">更新日志</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> 将你的 Flipper Zero 变成一台针对<b>活跃 13.56 MHz NFC 读取器</b>的
口袋级<b>反监视窃听器扫描仪</b>。它<i>被动监听</i>已开机读取器持续发射的射频场——
藏匿在支付终端里的隐蔽读卡器、门板后面的秘密读取器、贴在桌子底下的流氓记录器——
然后告诉你<b>它在哪里</b>、<b>它是什么类型的东西</b>、<b>房间是否干净</b>,
以及——在持续值守时——<b>你不在时它出现的那一刻</b>。它从不发射信号。
</p>
<p align="center"><sub>读取器是隐形的。Specter 让它们显形。</sub></p>
<p align="center">
<sub>
<b>媒体报道</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>
---
## 在 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>一次完整的扫描:安静的房间 → 逐渐接近 → 锁定 → 它<b>是什么</b> → 房间的判定。</sub>
</p>
### 不属于任何单一模式的屏幕
<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>
安静状态和启动画面——下面四种模式各自带有自己的截图,所以这些是它们没有的。
<b>本 README 中的每一张图片都是真实设备的截图</b>,通过
<code>tools_screenshot.py</code> 经由 Flipper 自身的 RPC 会话截取。
本项目已不再有任何模拟渲染器——绘制 UI 等于对它的第二套实现,
而它在看起来完全可信的同时却与固件不一致(见 <a href="https://github.com/at0m-b0mb/specter-flipperzero/blob/main/docs/changelog.md">3.0.1</a>)。
</sub>
</p>
---
## 五种模式
Specter 是围绕一个传感器构建的五种工具。每一种回答不同的问题,
所以正确的选择取决于你实际想查明什么:
| 模式 | 它回答的问题 | 何时使用 |
|---|---|---|
| **Sweep** | *它在哪里?* | 你怀疑有设备,想通过移动来精确定位 |
| **Fingerprint** | *它是什么类型的东西?* | 你已找到发射源,想知道它的行为方式 |
| **Site Survey** | *这个房间干净吗?* | 你想要对整个空间给出一个免手操作的判定 |
| **Watch Mode** | *我不在时它出现了吗?* | 你要把 Flipper 留在某处站岗 |
| **Logbook** | *我发现了什么,何时发现的?* | 你要撰写报告,或将今天与上周对比——按发现类型筛选 |
---
### Sweep — *它在哪里?*
<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">
**它的作用。** 一个实时 EMF 风格的仪表。将 Flipper 平放,在你检查的东西上方缓慢移动——
读卡终端、门禁读取器、ATM 插卡口下方、包裹、桌子。指针实时跟随场强。
**你会看到。**
- **表盘**——指针 = 实时读数,小圆点 = *峰值保持*(你触碰过的最强位置,
这样你可以比较不同位置)。
- **`FIELD %`**——同一读数以数字显示,带有 **▲ / ▼ 趋势箭头**,
告诉你过去半秒是*变热还是变冷*。搜寻时,这个箭头比数字更重要。
- **`PEAK %`**——自你启动以来的最强读数,即峰值保持圆点背后的数字。
- **表盘上的标记**——在你当前灵敏度下 `READER` *开始*的位置。越过它的一切
都是 Specter 会判定为命中的,所以表盘回答了"什么才算数?",而不是让你去猜。
- **底部条**——在你找到第一个读取器之前,它显示两个你无法通过乱按发现的按键
(`LEFT=cal`、`hold OK=log`);之后显示你的灵敏度和实时波形。检测到载波的那一刻,
它会切换为黑色 **`● ACTIVE READER`** 警报条,并附带一个接近度词语。
- **接近度**——`FAINT → NEAR → CLOSE → STRONG → PEGGED`。`PEGGED` 意味着仪表
*已顶格*:你已接近到该测量能分辨的极限。
**声音。** 可选的盖革咔嗒声,**越靠近速度越快**,这样你可以把 Flipper 放在口袋里
仅凭听觉进行扫描。
| 按键 | 操作 |
|---|---|
| `OK` | 重置峰值保持和接触计数 |
| `hold OK` | 将此读数保存到日志簿 |
| `LEFT` | 校准到房间的噪声底(3 秒) |
| `UP` / `DOWN` | 在不中断搜寻的情况下调整灵敏度 |
<br clear="right">
### Fingerprint — *它是什么类型的东西?*
<img src="https://raw.githubusercontent.com/at0m-b0mb/specter-flipperzero/main/screenshots/fingerprint_reader.png" width="42%" align="right" alt="Fingerprint — polling reader identified">
**它的作用。** 一旦 Sweep 找到了东西,将 Flipper **静止**贴住它。Fingerprint
停止测量接近度,转而**对载波的通断边沿计时**,这才是真正区分一种发射源
与另一种发射源的关键。
**你会看到。** 一个判定、一个置信度条,以及两者背后的证据:
| 类别 | 实际含义 |
|---|---|
| **CONTINUOUS** | 载波持续保持开启 |
| **POLLING** | 固定的、晶振定时的轮询周期——周期以 ms 显示 |
| **INTERMITTENT** | 突发但不规则——通常是手机或正在使用中的读取器 |
下方是 `PER`(轮询周期)、`BST`(突发宽度)、`JIT`(抖动)和
`UP`(真实占空比),外加原始载波的**逻辑分析仪脉冲序列**——
所以判定绝不是你必须盲目相信的东西。真正的轮询读取器会显示为
unmistakable 的方波。
> 计时前的 `~` 表示它接近 2 ms 采样下限,置信度会相应打折。
| 按键 | 操作 |
|---|---|
| `OK` | 将发现保存到日志簿 |
| `hold OK` | 重新开始测量 |
<br clear="right">
### Site Survey — *这个房间干净吗?*
<img src="https://raw.githubusercontent.com/at0m-b0mb/specter-flipperzero/main/screenshots/survey_active.png" width="42%" align="right" alt="Site Survey — verdict">
**它的作用。** 对整个空间进行**定时**扫描。启动它,正常走过房间,
最后得到一个单一判定——无需盯着指针。
**你会看到。** 运行时显示倒计时和进度条,然后是一张判定卡:
- **`CLEAN`**——没有任何东西越过噪声底
- **`TRACE`**——短暂或微弱的命中;值得再慢速走一遍
- **`ACTIVE READER`**——确实有东西开启并发射
- **`TOO SHORT`**——你在它获得说 `CLEAN` 的资格之前就停止了它
……附带 `PEAK` / `AVG` 场强、接触计数和 `UP %`——**载波在整个扫描中实际开启的时间占比**,
这往往是四个数字中最能说明问题的。运行 30 秒、60 秒或 2 分钟,并自动记录结果。
判定卡会标明评级所依据的时长,因为 30 秒的 `CLEAN` 与两分钟的 `CLEAN`
不是同一种发现——而短于 10 秒的运行会报告 `TOO SHORT` 而非 `CLEAN`。
这种不对称是刻意的:**存在即证明,不存在则不然。** 一秒内发现东西是真实的发现,
所以 `TRACE` 和 `ACTIVE READER` 绝不会因为太快而被扣留;一秒内什么都没发现
则是关于整个房间的断言,而一秒无法支撑这一断言。
| 按键 | 操作 |
|---|---|
| `OK` *(运行中)* | 立即停止并对其已有结果评级 |
| `OK` *(在判定卡上)* | 再运行一次扫描 |
<br clear="right">
### Watch Mode — *我不在时它出现了吗?*
<img src="https://raw.githubusercontent.com/at0m-b0mb/specter-flipperzero/main/screenshots/watch_reader.png" width="42%" align="right" alt="Watch Mode — reader present">
**它的作用。** **无限期**站岗。启动它,放下 Flipper,走开。Sweep 是你在搜寻,
Survey 是固定时长的测试,而 Watch 只是等待——几分钟或几小时。
**你会看到。** 当没有任何东西时,显示一个大号已用时钟和 `NO READER`。
读取器出现的那一刻,条带反转为 **`ACTIVE READER`**,强度条显示它有多近,
**屏幕唤醒**,警报响起。条带是稳定的而非闪烁——在 1 位屏幕上,
一个实心反色块已经是上面最响亮的东西,而真正变化的读数承载着活跃感。
它经过之后,屏幕保留证据:`HITS`(多少次独立接触)、`PEAK`、`LAST`
(最近一次是多久以前)和 `UP`(载波实际开启的总时间)。时钟在超过 99:59 后
滚动为 `hh:mm` 而非冻结,所以整夜值守仍能累加。
Watch **刻意忽略隐身模式**——一个黑暗、沉默、从不告诉你它看到了什么的守卫
比无用更糟。
| 按键 | 操作 |
|---|---|
| `OK` | 重新启动——清除时钟和计数器 |
<br clear="right">
### Logbook — *我发现了什么,何时发现的?*
发现会**通过一次操作保存两次**,所以它们既能在设备上阅读,
*又*已经是电子表格——无需记住导出步骤:
<table>
<tr><th><code>logbook.txt</code> — 分组,供设备内查看器使用</th><th><code>logbook.csv</code> — 每条记录一行扁平数据</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>
条目会打上 `SWEEP`、`READER`、`SURVEY` 或 `WATCH` 标签,并按照 Flipper
的时钟打上时间戳。两个文件都位于 SD 卡上的 `apps_data/specter/` 中。任何内容
都不会离开设备。可从 **Settings → Clear logbook** 清除它们(会先要求确认——这是该应用唯一
不可逆的操作)。
---
## 辅助功能
### 自动校准
在 Sweep 屏幕上按 **LEFT**,Specter 会**聆听环境噪声底
3 秒**,然后将检测阈值设置为略高于它所测量到的值——保存为你的 **Custom** 灵敏度。每个房间的射频
底噪都不同;这会根据你所在的房间进行调校,而不是使用构建时写死的
数值。运行校准时要站在安静的地方。
### 隐身模式
在整个扫描过程中保持**屏幕和 LED 熄灭**,这样当你是那个在观察的人时,Flipper 不会发光。
声音和振动继续工作——重点不是禁用你用来扫描的反馈。退出隐身屏幕会重新点亮
显示屏,因此 `BACK` 总是会带你回到亮起的菜单。
### 读取仪表
**为什么你正在接触的读卡器不会一直 100% 发射。** 检测器只测量一个物理量:13.56 MHz
载波处于开启状态的时间占比。
但读卡器会**轮询**——短暂爆发、休眠、再次爆发。典型的终端
只有**20–35% 的时间**在辐射,因此无论你靠得多近,原始占空比都会在 30% 左右
*饱和*。
在仪表上显示那个原始数字,会让一次完美检测看起来只有三分之一。
仪表改为基于真实的轮询区间进行缩放:
| 原始载波占空比 | 仪表 | 接近程度 |
|---|---|---|
| 3%(房间噪声) | 10% | `FAINT` |
| 12% | 40% | `NEAR` |
| 20% | 67% | `CLOSE` |
| 28% | 93% | `STRONG` |
| ≥30% *(贴在读卡器上)* | **100%** | `PEGGED` |
**原始占空比永远不会丢失**:噪声底、自动校准以及
Fingerprint 屏幕的 `UP` 仍然使用真实占空比工作,因为它们描述的是*信号*,而不是你与它的距离。想要字面
数字?**Settings → Meter scale → Duty %**。
### 一切都会持久保存
灵敏度、扫描时长、声音、振动、LED、隐身、日志记录和仪表模式都会在你更改它们的
那一刻**保存到 SD 卡**。一个会忘记自身设置的扫描套件比完全没有持久化还糟。
---
## 工作原理
每个已通电的 NFC 读卡器都会持续**以 13.56 MHz 载波向空中发送 ping**,等待卡片
唤醒。你看不见它,但 Flipper 的 NFC 芯片可以:**ST25R3916** 具有硬件
**外部场检测器**(与让 Flipper 模拟卡片并知道读卡器
正在与它通信的是同一电路)。Specter 让芯片处于仅检测模式,并**每秒约 500 次采样那个“场是否存在?”位**
——而从不开启自己的载波。
这一个位,只要采样足够快,就携带两个独立信号:
```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
```
**强度 → 信号强度。** 在一个短窗口内,Specter 测量载波出现的*时间占比*,并将其平滑为 **FIELD %**。紧贴在 Flipper 上的读卡器会让仪表满格;较弱或较远的读卡器则只会轻微推动它。这驱动了指针、接近度文字和点击速率。
**节奏 → 身份识别。** 另外,该比特的每次跳变都会被计时。一个每 200 ms 唤醒 20 ms 的读卡器会产生几乎无抖动的突发/间隔模式,因为其轮询循环由晶振定时的状态机驱动。手部移动和射频噪声不会那么稳定。正是这种差异将 **POLLING** 与 **INTERMITTENT** 区分开来。
### 决策层在真实计算机上经过测试
Specter 将数字转化为*断言*的地方——“这是一个轮询读卡器”、“这个房间是干净的”、“这是指针应该显示的读数”——是纯 C 代码,不依赖硬件,并且它们是通过主机测试来确定的,而不是在设备上发现的:
```bash
make -C test # 527 checks: classifier, verdict, meter scaling, presence,
# cadence, log filtering, log wrapping
```
---
## 安装
无需开发板,无需刷写固件——它只是一个 `.fap` 文件。
**方式 A — 预编译的 `.fap`(最简单)**
1. 从 [**Releases**](https://github.com/at0m-b0mb/Specter-FlipperZero/releases) 页面获取对应的 `.fap` 文件:
| 你的固件 | 文件 |
|---|---|
| **官方 / 原版** | `specter.fap` |
| **Unleashed、RogueMaster、Momentum** 或任何更新的版本 | `specter-fw-dev.fap` |
> 如果 Flipper 提示 **`APP:87 < FW:88 — This app might not work`**,说明你选错了
> 文件——请换另一个。应用的 API 版本在编译时即已固定,
> 因此单个构建无法同时满足两条固件线。通常按下 Continue 后仍可运行,
> 但匹配的构建版本不会弹出此提示。
2. 打开 **qFlipper**,将文件拖放到 `SD Card / apps / NFC /`。
3. 在 Flipper 上:**Apps → NFC → Specter**。
**方式 B — 使用 [`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
```
`.fap` 会生成到 `dist/specter.fap`;`ufbt launch` 会把它复制到 `apps/NFC/` 并为你启动。
> `icons/` 中的图标和 `images/` 中的横幅是生成的——用
> `python3 tools_gen_icons.py` 和 `python3 tools_gen_banner.py` 重新生成(需要 `pillow`)。
> `screenshots/` 中的所有内容都是用
> `python3 tools_screenshot.py` 从真实设备上截取的(需要 `pyserial` 和通过 USB 连接的 Flipper)。
---
## 使用它
**一次完整的扫描:**
1. 启动 **Specter**。在 **Sweep** 屏幕上,按 **LEFT** 并保持静止 3 秒,以
校准到房间的噪声底。
2. 将 Flipper 平放,并**缓慢**地在你正在检查的东西上移动——一台刷卡终端、一个
门禁读卡器、ATM 插卡口的下方、一个包裹、一张桌子。
- **安静 / 平坦的波形** → 范围内没有活动读卡器。
- **指针攀升,咔哒声加快** → 你正在接近一个发射源。继续朝峰值前进。
- **`● ACTIVE READER` + 警报边框** → 这里就有一个活动读卡器。
3. 发现了什么?退出并打开 **Fingerprint**。将 Flipper 静止贴住它,让
节奏稳定下来——通常几秒钟就足够了。按 **OK** 保存该发现。
4. 而是要清理整个房间?打开 **Site Survey**,在空间中走动直到进度条填满,然后读取
判定结果。
5. 要离开该区域?把 Flipper 置于 **Watch Mode**,它会站岗放哨,在你离开期间
如果有读卡器出现,它会唤醒并发出警报。
6. 查看 **Logbook**,了解你保存的所有内容,带时间戳——或者把 `logbook.csv` 从卡中
导出到电子表格。
> 先扫描一个已知良好的读卡器(你自己的手机做 NFC,或者一个你信任的非接触式终端),
> 看看强而合法的场在仪表上是什么样子——以及它的指纹读数是什么。然后
> 再去搜寻。
### 控制
| 屏幕 | 按键 | 操作 |
|---|---|---|
| **Sweep** | `OK` | 重置峰值 / 接触次数 |
| | `hold OK` | 记录当前读数 |
| | `LEFT` | 校准噪声底(3 秒) |
| **Fingerprint** | `OK` | 将发现保存到日志簿 |
| | `hold OK` | 重新开始测量 |
| **Site Survey** | `OK` | 再次运行勘测 |
| **Watch Mode** | `OK` | 重新布防(清除计数和时钟) |
| *任意位置* | `BACK` | 返回上一级 |
---
## 诚实的局限性
- **仅限 13.56 MHz(HF)。** Specter 感知的是 **NFC 频段**——非接触式支付侧录器、
大多数现代门禁读卡器、交通和酒店读卡器所使用的频段。它**无法**看到 **125 kHz(LF)** 读卡器
(较旧的 HID Prox / EM4100 门禁面板);Flipper 的 LF 路径没有等效的场检测位。
- **它感知的是读卡器的*载波*,而不是它读取的内容。** Specter 告诉你*这里有一个活动读卡器,
大致有多近,以及它以什么节奏轮询*——它不会解码、识别或捕获读卡器所做的任何事情。
- **强度是相对的,不是校准过的。** `FIELD %` 和接近程度词语是用于扫描的比较性“更热 /
更冷”指南,而不是以厘米为单位的测量距离。它是对载波
占空比相对于典型轮询频段的*缩放*读数(参见 [读取仪表](#-reading-the-meter--fixed-in-23));
一个轮询异常稀疏的读卡器在相同距离下会读数更低,而一个处于连续波
模式的读卡器会从更远处就顶格。
- **仪表会到顶。** `PEGGED` 意味着载波已经达到这个读卡器所能保持的最高水平——超过
那一点,再靠近也确实无法产生更大的数字。发生这种情况时 Flipper 会振动一次,
这样即使你看不到屏幕,也知道该停止移动。改用 `PEAK` 保持来比较
位置。
- **节奏分辨率约为 2 ms。** 这就是采样周期。任何接近它的时间都会显示为
带 **`~`** 的值,并且置信度会相应打折——Specter 宁愿标出自己的分辨率
下限,也不愿报出一个它无法支撑的看似精确的数字。
- **`CLEAN` 意味着在你选择的灵敏度下干净。** 一个只在真正触碰时才唤醒的休眠侧录器,
或者一个被屏蔽的侧录器,在*任何*阈值下都不可见。没有读数并不保证
不存在。
- **一次只能用一个无线电。** Specter 在扫描时会接管 NFC 芯片,所以先关闭任何其他 NFC 应用
(如果有其他东西占用无线电,它会显示 *NFC unavailable*)。
---
## 法律与伦理
Specter 是一个**防御性、仅监听**的工具——它**从不发射**,从不给场供电,从不接触
读卡器。用它来扫描**你自己的** POS 区域、门、桌子或随身物品,或者你**明确
获授权**评估的硬件。你要为自己的使用方式负责。了解你当地的法律。
---
## 路线图
- [x] 在重启后保持灵敏度 / 声音 / 振动 / LED 设置
- [x] 带时间戳的检测“日志簿”保存到 SD 卡
- [x] 屏幕关闭时的后台扫描——*隐身模式*
- [x] 可调阈值以实现更精细的范围控制——*自动校准 + 自定义灵敏度*
- [x] 通过发射源的轮询节奏对其进行指纹识别
- [x] 带房间判定结果的定时现场勘测
- [x] 将日志簿导出为 CSV 以便报告——*与 .txt 同步实时写入*
- [x] 长时间无人值守的监视模式,带检测唤醒警报
- [x] 让仪表在面对真实轮询读卡器时使用其完整量程
- [x] 搜寻时的更热/更冷趋势箭头
- [ ] 研究基于 LF(125 kHz)线圈的读卡器感知作为单独模式
- [ ] 在设备上按类型过滤日志簿
---
## 项目结构
```
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
```
---
## 品牌
**BACKLIGHT。** 标识使用 **Didot**,语气使用 **Futura**,每一个测量值使用 **Andale Mono**。橙色是 `#FE8A2C` —— *取样*自本应用运行时的一次 qFlipper 捕获,因此它是 Flipper 自身的背光,而非为模仿它而挑选的颜色。`screenshots/` 中的每一次捕获都恰好是两种颜色,`(254,138,44)` 和 `(0,0,0)`;这就是硬件所发出的。
它从不被用作品牌色。**任何素材上唯一的余烬都是设备实际发出的光** —— 真实捕获中的像素,以整数倍缩放原样粘贴。图稿以同一色相绘制,但冷十五度。一条色阶,从两端读取:**纸上墨越多意味着信号越强,黑底上光越多意味着信号越强** —— 因为纸是扫描结束之处(Specter 写下一本带时间戳的日志,你可以交给别人),而黑是它发生之处(隐身模式强制关闭背光)。
强调色只画一样东西,别无其他:**测得的载波。** 它从不是一条线、一个边框、一个边距或一个词 —— 整个系统中没有任何金色文字,若有人尝试,渲染器会报错。因此横幅恰好携带一个强调色对象,而那个对象是一次测量。
波形是 128 列载波,Specter 以每列 8 ms 从一台真实的轮询读取器上记录,再从旁边的截图中刮取回来。左侧的平坦段是填充前的跟踪缓冲区,而非任何人测量到的静默。其占空比读数为 **28%**,而设备自身的计数器打印为 **27%**,两个数字都在横幅上。该标记是一个轮询周期,占空比 **30%** —— 那台读取器的 40 ms 中的 12 ms,同时也是 `SPECTER_FULL_SCALE_DUTY`,即仪表读数为 100% 时的占空比。一个数字,两个独立的理由让它成为它。
`#FE8A2C` 是这里唯一一个不是我们选择的颜色。
| 素材 | 尺寸 | 用途 |
|---|---|---|
| `images/banner.png` | 2560×800 | README 页眉,纸底(2× 以便在视网膜屏上保持清晰) |
| `images/banner-dark.png` | 2560×800 | 同一图稿在纯黑上,由 `<picture>` 提供给深色模式读者 |
| `images/social-preview.png` | **1280×640** | **Settings → Social preview** —— GitHub 推荐尺寸(最小 640×320,上限 1 MB) |
| `images/mark.png` | 512×512 | 方形标志:社交帖子、幻灯片、favicon。**不是仓库头像** —— GitHub 在仓库旁显示的是*所有者*的个人资料图片,因此没有可设置的按仓库头像 |
| `images/mark-180/64/32/16.png` | 如命名 | 180 和 64 为降采样;**32 和 16 为原生绘制**,因此不会有任何东西被抗锯齿糊成一团 |
| `images/demo.gif` | 384×192 | 脚本化遍历每一种模式,**从设备上录制** —— `tools_screenshot.py --tour-gif` |
| `images/splash.gif` | 384×192 | 启动介绍,从启动请求开始录制 —— `--splash` |
| `images/screens.png` | — | 捕获的缩略图总览,浅色底,由 `--sheet` 重建 |
| `images/screens-dark.png` | — | 同一总览在纯黑上,由 `<picture>` 提供给深色模式读者 |
| `screenshots/*.png` | 512×256 | **每一个屏幕,均通过 RPC 从真实设备捕获**。恰好两种颜色:面板自身的颜色。 |
```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
```
> 社交预览是 GitHub 无法从仓库中自动获取的一项资源——必须在 **Settings → Social preview** 中手动上传一次。当仓库在 Slack、X、LinkedIn 或 Discord 上分享时,显示的就是它。
---
## 致谢
- 由 **[at0m-b0mb](https://github.com/at0m-b0mb)** 构建。
- 属于 Flipper 安全工具系列的一部分:**[Argus](https://github.com/at0m-b0mb/Argus-FlipperZero)**(Wi-Fi 去认证 / 邪恶双胞胎监控)、**[GhostTag](https://github.com/at0m-b0mb/GhostTag-FlipperZero)**(BLE 反跟踪猎手)和 **[Cerberus](https://github.com/at0m-b0mb/flipper-cerberus)**(Sub-GHz 射频监控)。
- 由 [Flipper Zero 固件](https://github.com/flipperdevices/flipperzero-firmware) NFC HAL(`furi_hal_nfc` 场检测)+ [ufbt](https://github.com/flipperdevices/flipperzero-ufbt) 提供支持。
## 许可证
[MIT](https://github.com/at0m-b0mb/specter-flipperzero/blob/main/LICENSE) © 2026 at0m-b0mb