跳转到内容

移动端触摸模拟与检测:手指为何比鼠标难几个数量级

行为建模那一篇,把鼠标轨迹和手指触摸并排讲了——运动控制律、1/f1/f 质地,两者通用。但那是一种为了讲原理而做的简化。这一篇要点破一个被严重低估、却决定成败的事实:

移动端手指触摸,和 PC 鼠标,根本不是一个量级的对象。 PC 鼠标是人机检测的浅水区;移动端触摸是深渊。要在移动端「像一根真手指」,难度比 PC「像一只真鼠标」高出几个数量级

这不是危言耸听,而是由物理管线决定的。而这件事的分量在于:如今搜索流量、以及一切「排名操控」类需求,主战场早已是移动端——移动端触摸这道门槛,才是真正的入场券。 绝大多数自动化,把 PC 的那套鼠标思路搬到移动端,等于裸奔


一、鼠标只有坐标,手指有一整个物理世界

Section titled “一、鼠标只有坐标,手指有一整个物理世界”

先把两者摊开对比。一个 PC 鼠标事件,本质上只携带 (x,y,t)(x, y, t)——坐标和一个时间。它没有压力、没有接触面积、没有方向,也没有任何底层采样管线。检测方能盯的,大约就是坐标轨迹(2 维)加事件间隔(1 维)。

一次移动端手指触摸则完全不同。它携带力、面、角,背后还拖着一整条采样与打包的物理管线:

同样一道滑动:鼠标 vs 手指PC 鼠标稀疏离散点 · 只有 (x, y, t)无压力 · 无面积 · 无方向 · 无采样管线检测维度 ≈ 3移动端手指IC 高频采样 + 力 / 面 / 角 + vsync 打包每次触摸拖着一整条物理管线检测维度 30+

单是这张对比就说明:把「让鼠标轨迹像人」的经验搬到移动端,只覆盖了移动端问题的十分之一都不到。剩下的九成,是 PC 世界里根本不存在的东西。下面逐层拆。


二、触摸的底层管线:从 IC 到 JS 的五级透传

Section titled “二、触摸的底层管线:从 IC 到 JS 的五级透传”

一次真实的手指触摸,在到达网页的 JavaScript 之前,要走过一条五级流水线,每一级都盖上一层难以伪造的物理印记

  1. 触控 IC 采样:屏幕的触控芯片以自己的固有频率(常见 120Hz、240Hz)连续采样,产出一串离散采样点,每个点带坐标、压力、接触面积。
  2. 内核 / EventHub:内核给每个采样点打上一个 CLOCK_MONOTONIC 时间戳(纳秒级、单调、系统级),并按一定规则把一小段时间内的多个采样点打包
  3. InputDispatcher:组装成 MotionEvent 对象——它不只含当前坐标,还挂着一串历史采样点(historical points)
  4. 浏览器内核(Blink):把这些量无损转换成 Web 世界的属性——压力、接触半径、旋转角。
  5. JS 层:网页通过 getCoalescedEvents() 等接口,读到这一整串子采样点。

PC 鼠标呢?它没有这条管线。mousemove 就是一个在系统事件层直接产生的离散事件,没有 IC 采样、没有打包、没有压力面积。这条管线的存在与否,就是移动端与 PC 的第一道鸿沟。


三、coalesced events:移动端触摸的「验伤指纹」

Section titled “三、coalesced events:移动端触摸的「验伤指纹」”

这条管线里,最锋利的一道检验,叫 coalesced events。它是 PC 上完全不存在的一道题,也是最多自动化「裸奔」暴露的地方。

机制:浏览器按 vsync(垂直同步,如 60fps ≈ 16.7ms)的节拍处理事件。但触控 IC 的采样比这快得多——一个 16.7ms 的 vsync 周期里,120Hz 的 IC 已经采了两三个点。为了不丢掉这些原始采样,浏览器把它们打包进同一个事件,网页调用 getCoalescedEvents() 就能取回这一串子采样点

于是有了这道致命的检验:

coalesced events:真机打包 vs 单点注入空壳真机vsync 帧内 IC 采多点 → 打包getCoalescedEvents() → 每帧返回 2~3 个子采样点 ✓注入每帧只有 1 个孤立点 → getCoalescedEvents() 空壳 ✗软件接口一次只能塞一条离散事件——没有打包,就没有 coalesced

真机物理触摸:IC 连续采样,每个 vsync 帧内自然攒下多个点,getCoalescedEvents() 返回一串子采样点。单点离散注入(通过软件接口一次塞入一条事件):每帧只有一个孤立点,根本没有打包发生,coalesced 数组是空的

检测方据此可以做几层判断:

  • 有没有 coalesced:极速滑动却 getCoalescedEvents().length ≤ 1,几乎坐实了单点注入。
  • 子采样点的 dt 对不对:真机 coalesced 内相邻点的间隔,应是 IC 采样周期 TicT_{ic} 的整数倍,叠加系统调度的高斯噪声:
ΔtkTic+N(0,σ2),k{1,2,}\Delta t \approx k\,T_{ic} + \mathcal{N}(0,\sigma^2),\qquad k \in \{1,2,\dots\}
  • dt 有没有小数:真机时间戳是内核纳秒级透传,dt 带小数尾巴;而粗糙注入常是整毫秒(8ms、16ms 的整数),一眼假。

这道题,PC 鼠标完全免修——鼠标没有 IC、没有打包、没有 coalesced 机制。移动端却必须答对。(怎么答对,是另一个话题,本篇只谈检测方如何出这道题、以及为什么它这么难。)


四、三个频率的纠缠:IC 采样率 × 屏幕刷新率 × vsync

Section titled “四、三个频率的纠缠:IC 采样率 × 屏幕刷新率 × vsync”

移动端触摸的时间轴,是三个独立频率互相纠缠的结果,这也是 PC 没有的复杂度:

  • 触控 IC 采样率:由触控芯片时钟决定(120/240Hz),走独立总线。
  • 屏幕刷新率:由显示驱动决定(60/90/120Hz)。
  • vsync / 合成器帧率:浏览器处理与打包事件的节拍。

三者解耦却纠缠。IC 采样率和刷新率没有硬连接,于是出现两种设备时序模式:IC 独立投递(采样节奏不随刷新率变)与 vsync 绑定(采样投递跟着刷新率走)。同一个品牌往往固定一种模式——这本身就是一个设备指纹,且必须和你声称的机型自洽(呼应跨层一致性)。

更要命的是渲染管线受限时的连锁反应:如果一个环境的刷新率被压得很低(vsync 周期变得很长),那么在一个漫长的 vsync 周期里,落在中间的那些 IC 采样点会被丢弃或覆盖,只留下寥寥一两个点派发给 JS。结果就是 coalesced 异常稀疏、dt 从本该的 8ms 变成 16ms、33ms 的诡异值。


五、力、面、角:手指的生物物理

Section titled “五、力、面、角:手指的生物物理”

管线之外,触摸还携带三个 PC 绝无的物理量,每一个都是「真手指」的证据:

  • 压力包络:真手指按压的力,随时间呈早峰单调衰减的非对称曲线(落屏瞬间最大、随即释放)——这在行为篇讲过。单点注入只能给每帧一个恒定压力值,造不出帧与帧之间连贯的包络形状。
  • 接触面积(半径):真手指在一次手势内接触半径基本稳定、却带细微帧间抖动,而不同手势间差异明显;且长轴短轴构成一个椭圆。恒定值或每帧独立乱跳,都不对。
  • 旋转角:椭圆接触的方向,有的设备报、有的不报——报不报本身是品牌特征。

关键在于,这些物理量因品牌而异:不同厂商的触控栈,有各自的压力模型、半径范围、旋不旋转。这构成一层品牌指纹,而它必须和设备声称的机型、以及上面那条 IC 时序模式全部自洽。你伪造出的组合,只要在「品牌的力学模式」上对不上「声称的机型」,就落成了浏览器指纹篇说的那种孤立点——每个字段看着合理,凑一起却是一台现实中不存在的设备。


现在可以把这笔账算清楚。检测一根手指是否真实,是一次多维联合检验

P(真手指)    P(力包络)P(半径分布)P(旋转模式)P(coalesced 结构)P(dt 纳秒性)P(三频率自洽)P(品牌指纹)P(\text{真手指}) \;\propto\; P(\text{力包络}) \cdot P(\text{半径分布}) \cdot P(\text{旋转模式}) \cdot P(\text{coalesced 结构}) \cdot P(\text{dt 纳秒性}) \cdot P(\text{三频率自洽}) \cdot P(\text{品牌指纹}) \cdots

PC 鼠标那边,能乘的项大约只有坐标轨迹和事件间隔——三项。移动端这边,是三十多项。哪怕伪造方在每一项上都做到 95% 逼真:

0.95300.00020.95^{30} \approx 0.0002

——万分之二的整体通过率。而这些维度,绝大多数落在伪造难度三级台阶最高一级「物理因果」:力包络由生物力学定、coalesced 由 IC 与 vsync 定、dt 由内核时钟定——它们不是改几个字段能补的,断一条链就穿

这就是为什么,把 PC 上那套「贝塞尔鼠标轨迹 + 匀速滑动」搬到移动端,在现代移动风控眼里近乎裸奔:它连 coalesced 这道题的题面都没读到,更别说三频率自洽、力面角连贯。它答的是 PC 的卷子,交到了移动端的考场。


七、弗兰肯斯坦与前沿阵地:为什么移动端的墙厚无数倍

Section titled “七、弗兰肯斯坦与前沿阵地:为什么移动端的墙厚无数倍”

前面算的是「维度多」。但更深的一层,是为什么移动端天生就给了防守方更多牌——这要从 PC 的本质说起。

PC 是一台弗兰肯斯坦。 它是用互不相干的零件拼起来的怪物:CPU 可以是任意架构、显卡可独可集、浏览器可以是任意内核甚至魔改版、插件随意增删、窗口能拉成任意尺寸;它没有触摸、没有传感器、没有一个出厂就绑定的统一设备身份。后果是——「什么样的组合,都可能是一台真 PC」。于是防守方缺少一条可依赖的基线:它没法断言「一台真 PC 就该长这样」。可校验的自洽约束少,能设计的风控逻辑就少,攻击方要满足的约束也随之少。这正是为什么 PC 上「补环境」相对可行——你要对齐的基线本就稀疏。

移动端恰恰相反:一部手机是一台出厂即锁死、高度集成的真实设备。 一个机型,对应一套确定且互相绑定的 SoC、GPU、屏幕、触控 IC、传感器、刷新率——这些维度天然互相印证:IC 的采样模式要配这颗 SoC、力包络要配这块屏的触控栈、传感器要配这个机型、刷新率要配这块屏。防守方因此握有一整套稠密、互锁的基线。

而这一切,最终指向一个商业事实:

对绝大多数流量入口——包括搜索引擎——PC 早已是剩余价值,移动端才是前沿阵地。 用户在哪,战场就在哪;而用户,早就在手机上。PC 端的流量是存量、是边缘、是收尾;移动端的流量是增量、是主战场、是未来。这意味着:一套只会在 PC 上「像人」的能力,握着的是一张过期的入场券——唯有跨过移动端触摸这道物理门槛,才算真正站上了今天的战场。


八、这对反爬与移动端意味着什么

Section titled “八、这对反爬与移动端意味着什么”

移动端是主战场,而移动端触摸是真正的门槛。 搜索流量与一切排名操控类需求,早已以移动端为主;而移动端触摸这条物理管线,是绝大多数自动化的葬身之地——不是因为它们不够努力,是因为它们带着 PC 的思路,去解一道多了几个数量级维度的题。

判别的锋刃在「联合」,不在任何单点。 让力像、让轨迹像、让 coalesced 有值——单独做到都不够;它们必须同时、且互相自洽地指向同一根真手指、在同一台真设备、跑在同一条健康的渲染管线上。这与全站反复出现的判断完全一致:边缘的合理是廉价的,联合的真实是昂贵的。

唯一能稳过这道门槛的,是整条物理管线的真实自洽——就像浏览器指纹篇的结论:可信不来自零件的拼装,而来自「源自真实、彼此本就自洽」的整体。能通过这三十多维联合检验的,只有真实,或与真实在物理上不可区分之物。至于如何抵达那种「不可区分」,是另一个远为艰深的工程问题——本篇只负责说清:这道门槛究竟有多高。


移动端手指触摸与 PC 鼠标不是一个量级的对象。鼠标只有 (x,y,t)(x,y,t)、检测维度约 3;手指背后是一整条 PC 不存在的物理管线——触控 IC 采样 → 内核纳秒打戳与 vsync 打包 → MotionEvent 历史点 → 浏览器无损转换 → JS getCoalescedEvents()——外加力(早峰衰减)、面(per-gesture 采样)、角(品牌相关)的生物物理,检测维度 30+。其中 coalesced events 是最锋利的一道题:真机物理触摸自然打包出多个子采样点,而单点离散注入的 coalesced 是空壳;再叠加 IC 采样率 × 刷新率 × vsync 的三频率纠缠(渲染一受限,触摸时序就穿帮),使伪造落进「物理因果」的最高一级。0.95300.95^{30}\approx 万分之二——把 PC 鼠标思路搬到移动端,等于用 PC 的卷子交移动端的考场。而这道题之所以难 PC 无数倍,根子在于:PC 是一台弗兰肯斯坦——零件随意拼凑、缺少跨维度的基线逻辑,防守方没几道题好出;移动端是出厂锁死的集成设备——维度互相绑定、互锁,防守方能设计的风控逻辑多了几个数量级,且道道联动。 再叠加商业现实——对包括搜索引擎在内的绝大多数流量入口,PC 早已是剩余价值,移动端才是前沿阵地——结论就此清晰:移动端触摸这道物理门槛,才是今天真正的入场券。