threathunterX/gesture-fingerprint
GitHub: threathunterX/gesture-fingerprint
基于九维行为特征的移动端手势指纹识别引擎,通过端侧触摸埋点数据判定操作来源并区分具体作弊工具类型。
Stars: 0 | Forks: 0
# gesture-fingerprint
**中文** · [English](./README.en.md)
从手机触摸埋点数据里,判定一次点击 / 滑动来自 **人手、软件脚本,还是某一种作弊硬件**。
不只是"人机识别"——它会告诉你是**哪一种**机器。

## 它长什么样
**输入**:App 触摸埋点 CSV
**输出**:
# 未知点击/滑动行为识别报告
- 手势总数:50
- 预测类别数:1
## 总体预测分布
| 预测类别 | 数量 | 占比 |
|--------------------|-----|---------|
| 自动化脚本-屏幕录制 | 50 | 100.00% |
## 分组正式判定
| 分组 | 样本数 | 最低样本量 | 状态 | 最终判断 | 组内主类别占比 |
|--------------------|-------|----------|-------|---------------------|-----------|
| gesture_type=SWIPE | 50 | 30 | 可判定 | 自动化脚本-屏幕录制 | 100.00% |
每条判定都带证据,不是黑箱打分:
| 实际设备 | 判定 | 证据 |
|---|---|---|
| 机械臂 | 机械臂 | 存在明显接触面积,进入真实接触 / 物理触控分支;接触面积中等、压力偏低、速度波动偏高,符合机械臂 / 物理工具特征 |
| 真人 | 真人 | 存在明显接触面积;接触面积较大、压力更自然且存在压力波动,符合真人触控;滑动速度较高且速度波动处于自然范围 |
| 屏幕录制脚本 | 自动化脚本-屏幕录制 | 接触面积接近 0 且平均压力接近 1,符合注入 / 非真实接触类基础特征;逐点压力波动接近 0;路径冗余率落在录制轨迹常见区间 |
对应的原始特征值:
| 实际设备 | 接触面积 (px²) | 平均压力 | 速度波动系数 |
|---|---:|---:|---:|
| 真人 | 21252 | 0.419 | 0.669 |
| 机械臂 | 8057 | 0.155 | 0.617 |
| 屏幕录制脚本 | 0 | 1.000 | 0.002 |
| 鼠标点击器 | 0 | 0.941 | 0.354 |
## 能识别这 10 类
| | |
|---|---|
| **真人** | 对照基准 |
| **软件类** | 代码开发脚本 · 屏幕录制脚本 · HID |
| **硬件类** | 电容点击器 · 鼠标点击器 · 机械臂 · 自动滑屏点赞器 · 刷屏器一 · 刷屏器二 |
**每一类工具的轨迹签名都不一样**——这也是"能分辨出是哪一种机器"的直观来源:

从左上到右下依次是:真人、机械臂、鼠标点击器、代码开发脚本、屏幕录制脚本、自动滑屏点赞器、刷屏器一、刷屏器二。同样是 250 次滑动——
- **真人**散成一片扇形,起点终点各不相同,还带着自然的弯曲
- **机械臂**分布最接近真人,但明显更集中、更有结构(它是最难分辨的一类)
- **代码脚本**是一根根笔直的竖线,钉在固定的几个 X 坐标上
- **屏幕录制**只剩两条折线——250 次操作,回放的是同一段录制
- **刷屏器**和**自动滑屏点赞器**收缩成一小束,物理电机只会走那一条道
## 规则从哪来
全部结论来自实测。

实验录像截帧。左:机械臂用摄像头看屏幕、机械爪物理点击;中:电容点击器的硅胶头吸在屏幕上;右:鼠标点击器通过蓝牙 HID 驱动。
我们采购了 **8 类市售作弊硬件**(总计 1863.6 元,从 61.9 元的电容点击器到 1535 元的机械臂),外加自建的软件脚本对照组;在 3 部手机、不同系统版本上,让真人和每一类工具做同样的点击、滑动、长按;覆盖平放 / 竖放 / 手持坐着 / 手持行走 / 地铁等多种手机状态;每类每场景重复 300–500 次;采集端每十几毫秒记录一组数据。
**累计有效样本 7.5 万+ 条手势。**
然后从 9 个维度逐项比对:
| 维度 | 区分力 | 主要结论 |
|---|---|---|
| 滑动速度波动系数 | 很高 | 脚本类接近 0;真人约 0.51;机械臂约 0.65;刷屏器一最高约 1.09 |
| 按压接触面积 | 很高 | 软件注入 / HID / 鼠标点击器大多为 0;真人最大;机械臂、刷屏器、电容点击器为非零中高面积 |
| 按压力度 | 高 | 脚本 / HID / 自动滑屏点赞器几乎恒为 1;真人、机械臂、刷屏器、电容点击器处于更低且互不相同的区间 |
| 压力波动系数 | 高 | 脚本 / HID 几乎为 0;真人约 0.23 |
| 滑动平均速度 | 中高 | 自动滑屏点赞器最快,真人较快,鼠标点击器最慢 |
| 滑动路径冗余率 | 中 | 代码脚本几乎直线;真人实际划过的路比两点直线多约 4.8% |
| 滑动平均曲率 | 中 | 代码脚本、电容点击器接近 0;机械臂、真人、刷屏器更高 |
| 线性加速度合量 † | 中 | 反映手机是否被真实手持移动,手持行走显著高于平放 |
| 陀螺仪合量 † | 中 | 同上,平放 / 竖放接近 0 |
以"压力波动"这一维为例,9 类工具的分布长这样:

真人(n=19,354)是一条自然的钟形分布,P50 ≈ 0.23;
代码脚本、屏幕录制、自动滑屏点赞器**几乎 100% 落在 0**——每一次按压的力度分毫不差;
鼠标点击器则堆在 1.4142 这个古怪的固定值上。
**机器最大的破绽不是做得差,恰恰是做得太完美。**
其余 8 个维度的完整图表见 [docs/02 · 九维度分析](./docs/02-九维度分析.md)。
完整实验数据与逐维度分析见 [docs/](./docs)。
## 快速开始
python3 -m venv .venv && source .venv/bin/activate
pip install -e .
gesture-classify \
--summary-csv 你的数据.csv \
--trajectory-csv 你的数据_trajectory.csv \
--output-dir ./out
纯 Python 标准库实现,**零第三方依赖**,Python ≥ 3.9。
不安装也能直接跑:
PYTHONPATH=src python3 -m gesture_behavior_classifier.cli \
--summary-csv 你的数据.csv --trajectory-csv 你的数据_trajectory.csv --output-dir ./out
## 输入数据
需要两个 CSV:
**手势级汇总**(一行一次手势)
`gesture_index` `gesture_type` `start_x/y` `end_x/y` `duration_ms` `avg_pressure` `touch_major/minor` `avg_curvature` `linear_x/y/z` `gyro_x/y/z` …
**轨迹点**(一行一个采样点,强烈建议提供)
`gesture_index` `point_index` `x` `y` `pressure` `touch_major` `touch_minor` `vx_px_s` `vy_px_s` `curvature`
压力波动、接触面积、速度波动、路径冗余率都依赖逐点数据,只给汇总 CSV 会损失大部分判别力。
这些字段长什么样,看采集时的实时读数最直观——下面是**鼠标点击器**点一下屏幕,手机记下的原始信号:

注意 **`接触面积: 0.0 × 0.0 px`** ——它根本没碰到屏幕,事件是从蓝牙 HID 通道注入的。
这就是判定的第一层分水岭:**有没有真实的物理接触**,装不出来。
至于同一张图里的 `压力: avg=0.500`,它是第一轮观察到的模式;
第二轮换批次实测却是 0.94——**这个不一致后来成了我们最主要的误判来源**,
也是"阈值必须按机型标定"最直接的例证,详见 [docs/04 · 验证与局限](./docs/04-验证与局限.md)。
## 输出
| 文件 | 内容 |
|---|---|
| `识别报告.md` | 面向阅读的汇总报告 |
| `分组正式判定.csv` | **正式结论**:满足最低样本量后的批次判断 |
| `手势识别结果.csv` | 每条手势的预测类别、置信度、命中规则证据、各类别得分 |
| `手势特征明细.csv` | 每条手势算出的全部 9 维特征 |
| `批次识别汇总.csv` | 按动作类型和手机状态的预测分布 |
## 盲测结果
用 5 种典型方式各采一批**未参与规则制定**的新数据,喂给引擎做盲判。
样本量很小(每组 50–66 条),下表是原始计数而非统计意义上的准确率估计:
| 实际设备 | 判对 / 总数 | 主要错误 |
|---|---:|---|
| 真人-食指滑动 | 50 / 50 | — |
| 屏幕录制脚本 | 50 / 50 | — |
| 真人-拇指滑动 | 53 / 54 | 1 条输出未知 |
| 鼠标点击器 | 39 / 50 | 11 条误判为 HID |
| 机械臂 | 48 / 66 | 18 条输出未知 |
**没有出现跨大类的错误**——真人从未被判成机器,机器也从未被判成真人。
所有错误都发生在同一大类内部,或退化成"未知"。这是目前最值得信赖的一点。
弱的两行我们照样放出来,归因分析见 [docs/04 · 验证与局限](./docs/04-验证与局限.md)。
欢迎带着你自己的数据来提 issue 或 PR。
## 已知边界
请务必先读这一节——它决定了这个项目能不能用在你的场景里。
- **不是实时拦截器。** SWIPE 需 ≥30 条、TAP 需 ≥50 条才输出正式结论,低于门槛只给参考值。做的是**账号 / 会话级定性**,拦不住单次请求。合适的位置是跟在现有拦截链路后面做定性和溯源,不是替代它。
- **需要端侧埋点。** 压力、接触面积、逐点轨迹只有 App 内能采到,网关侧、服务端日志里没有这些字段。
- **阈值需按机型标定。** 接触面积、速度阈值是绝对值,与屏幕 DPI、触控驱动、ROM 强相关。当前阈值基于我们的实验设备,**接入新机型时这是必须先做的一步**——阈值集中在一个 JSON 里,标定不需要改代码。
- **传感器两维需要上下文。** 线性加速度 / 陀螺仪的规则依赖「手机状态」标注,埋点未提供时按 7 维判定。
- **规则引擎,不是训练出来的模型。** 每条判定可解释、可审计、可以直接拿去出报告;代价是它不会自己适应新设备。
- **`confidence` 不是概率。** 它是规则得分的单调变换,只适合同一批数据内部排序,不能当作"有 99% 的把握"来读。
## 典型用法
- **摸底** —— 采一批真实流量,看你的 App 上机器操作占多少、分别是什么工具
- **打标** —— 为你已有的风控模型产出带工具类型的黑样本,解决标注样本稀缺的问题
- **分级** —— 工具成本可以作为判断对手投入强度的参考:61 元的电容点击器和 1535 元的机械臂,背后的组织化程度不在一个量级。具体怎么处置由你的风控策略决定
## 调整阈值
所有阈值集中在 [`default_rules.json`](./src/gesture_behavior_classifier/config/default_rules.json),改 JSON 即可,不需要动代码:
gesture-classify --config 我的阈值.json ...
也可以调整分组口径和样本量门槛:
# 默认只按 gesture_type 分组(适合无上下文的线上数据)
gesture-classify --group-by gesture_type,phone_status ...
# 更保守的样本量要求
gesture-classify --min-samples 100 ...
## 项目结构
src/gesture_behavior_classifier/
├── features.py # 从原始 CSV 计算 9 维特征
├── rules.py # 规则打分与单条手势判定
├── aggregate.py # 批次聚合与最低样本量门槛
├── report.py # Markdown 报告生成
├── cli.py # 命令行入口
└── config/
└── default_rules.json # 全部阈值
docs/ # 完整实验数据与逐维度分析
example_outputs/ # 一次真实运行的完整输出样例
人工测试数据/ # 5 组盲测数据,可直接复现 README 里的准确率
tests/
## 数据说明
`人工测试数据/` 里包含 **104 条真人手势**(54 条拇指滑动 + 50 条食指滑动),
以及各类工具产生的对照数据。关于其中的真人数据:
- **来源**:本项目内部测试人员在实验环境下录制,参与者事先知悉数据将随项目公开并表示同意。
- **内容**:仅包含触摸坐标、压力、接触面积、时间戳与设备传感器读数。
**不含**姓名、设备标识、账号、位置、通讯录或任何可定位到个人的字段。
- **用途**:仅供复现本项目实验与开展防御性研究。
触摸动力学在学理上属于行为生物特征。虽然从数十条滑动曲线反向识别到具体个人在实践中不可行,
我们仍然把来源和边界写在这里——**发布生物特征相关数据集却不说明来源,是不应该的**。
如果你基于本项目采集自己的数据,请自行评估适用的个人信息保护义务。
## 采集 App 说明
Releases 页面的 `gesture-collector-v2.apk` 是我们做实验时用的 Android 采集端。
SHA-256 f3e6a1940a16641a431b0654bf2cf36a02db31babdf752fa07696d122dbf91f2
大小 11,543,975 bytes
**它采集什么**:当前应用内的触摸事件(坐标、压力、接触面积、时间戳)与设备传感器读数
(加速度计、陀螺仪、重力、磁力计),写入本机 CSV 文件。**不联网、不上传、不读取其他应用的数据。**
**使用建议**:
- 这是**实验室工具**,用于在专用测试机上复现实验,不建议装在日常使用的手机上
- 安装前请核对上面的 SHA-256;不匹配就不要装
- 只从本仓库的 Releases 页面获取,不要从第三方镜像下载
想改造成自己的埋点,直接照着 [docs/01 · 实验方法](./docs/01-实验方法.md) 里的字段规范做即可,不必依赖这个 APK。
## 我们为什么做这个
抢票、抢购、直播刷量、游戏挂机——跟你抢的很多根本不是人,而是一批几十块钱就能买到、专门用来模拟真人操作手机的设备。
这些设备能被识别出来。我们把方法和数据都公开,是希望更多平台能把它们挡在门外。
**平台每挡住一台机器,真人就多拿回一点本该属于自己的东西。**
## License
[Apache-2.0](./LICENSE)
## 免责声明
- 本项目仅用于**防御性**的安全研究与风控能力建设,请勿用于任何非法用途。
- 本项目**不提供任何作弊设备的购买渠道、货源信息或使用教程**。文档中列出设备价格与实现原理,
目的是说明检测方法的实验依据和对手的成本结构。
- 文中提及的设备名称、协议名称及商标归各自权利人所有,本项目与其均无关联。
- 判定结果是基于统计特征的推断,可能出现误判。**是否据此对用户采取处置措施,
以及由此产生的后果,由使用方自行判断和承担。**
- 按 Apache-2.0 约定,本项目按"现状"提供,不附带任何明示或默示的担保。
标签:Apex, 人机识别, 反作弊, 文档结构分析, 机器学习, 移动开发, 逆向工具, 风控系统