USB 描述符、VID/PID,以及描述符克隆是什么意思
USB HID 描述符里包含什么、哪些字段能识别一台设备、为什么会有兼容鼠标列表,以及为什么通用描述符作为身份比克隆描述符更弱。
简短回答
USB HID 描述符是设备插入时对自身的描述:厂商 ID、产品 ID、字符串和报告格式。描述符克隆是指输入设备不再呈现自己的通用身份,而是呈现接在它上面的那只真鼠标的身份。
本页内容10
什么是 USB 描述符?
任何 USB 设备插入时,主机都会要求它描述自己,在它回答之前什么都不会发生。设备会回复一组描述符。设备描述符说明它是谁生产的、是什么型号;配置描述符说明它有几个接口。接口描述符和端点描述符说明每个接口是做什么的、希望多久被轮询一次。凡是键盘、鼠标或手柄,还会发送一个 HID 报告描述符,描述它将要发送的消息的精确字节布局。这个交互过程叫做枚举,在几毫秒内完成,发生在加载任何驱动之前。操作系统之后对这台设备的所有认知,都来自这几毫秒。你的输入设备也要回答同样的问题,而它可能答得好,也可能答得很糟。
哪些描述符字段能识别一台设备
这里列的不是完整的 USB 规范,但主机上的软件无需特殊权限就能读取下面列出的每一个字段。
| 字段 | 包含什么 | 暴露什么 |
|---|---|---|
| idVendor (VID) | 由 USB 标准组织分配给公司的 16 位编号 | 设备是谁生产的。机器上本来就接着某知名厂商的鼠标,出现这家厂商的 VID 毫不起眼;出现开发板厂商的 VID,就值得追问了 |
| idProduct (PID) | 由厂商分配给具体型号的 16 位编号 | 是哪个型号。VID 和 PID 合起来就是设备的公开身份,驱动匹配、遥测和设备清点工具都以这一对编号为依据 |
| iManufacturer 和 iProduct | 指向字符串描述符的索引,读回来就是文本 | 设备管理器中显示的可读名称。这是最常被遗忘的字段,而当它写着任何鼠标都不会写的内容时,也是最容易露馅的地方 |
| iSerialNumber | 指向序列号字符串的可选索引 | 每台设备独有的标识。有些鼠标有,很多鼠标没有。Windows 会用它判断换了端口重新连接的设备是不是同一台 |
| bcdDevice | 由厂商定义的设备修订号 | 固件修订版本。真实产品会随版本发布更新这个值。一个在全世界所有设备上都从不变化的值,本身就是一种特征 |
| bcdUSB | 设备声称实现的 USB 规范版本 | 它自称属于哪一代 USB 规范,以及它的全速或高速行为是否与它的其他声明一致 |
| bDeviceClass 和 bInterfaceClass | 类别代码。类别 3 即 HID | 表明这是一个人机接口设备,而不是存储、音频设备或串口适配器 |
| 接口数量 | 一台物理设备暴露了多少个功能 | 这是一只鼠标,还是一个由鼠标加串口再加其他东西组成的复合设备。一只普通鼠标通常不会同时是三样东西 |
| 端点上的 bInterval | 设备向主机请求的轮询间隔 | 它声称想要的回报率。描述符请求的轮询间隔并不短,实际回报却快得多,这样的设备就是自相矛盾 |
| HID 报告描述符 | 用途页和用途、坐标轴字段位宽、按键数量、滚轮,以及任何厂商自定义集合 | 设备的形态。按键数量、移动坐标轴是 8 位还是 16 位、有没有横向滚轮,以及是否存在该型号本应有、实际却没有的厂商专属数据块 |
这些字段没有一个是保密的,读取它们也都不需要驱动。主机上的任何进程都可以枚举接在上面的设备,并读取其中的每一个字段。
警告
为什么只改 VID 和 PID 不够
大家往往把厂商 ID 和产品 ID 当作全部身份,它们也确实是任何人最先检查的东西,但它们只是众多数值中的两个。设想一台设备带着某知名鼠标厂商的 VID 和 PID,产品字符串却仍然写着某款微控制器开发板的名字。它的报告描述符声明的按键数量与该型号不符,端点请求的轮询间隔也是该型号从来不用的。这比完全不改还要糟,因为抄了一半的身份自相矛盾,而检测要找的恰恰就是这种不一致。如果你要改 ID,就把与之配套的所有东西都改掉。
为什么会有兼容鼠标列表
老一代透传固件不会解析所接鼠标的描述符,而是内置写死的配置文件:一张手工编写的、针对指定型号的报告布局表。如果你插的鼠标在列表里,固件就知道如何解码它的报告、如何复制它的身份。如果插的是别的鼠标,要么毫无反应,要么移动乱套。兼容鼠标这个问题就是这么来的,在那一代固件上,这是买鼠标之前必须核实的实际限制。新一代固件会在枚举过程中从设备读取布局并自动适配,MAKCM 自动解析 HID 报告描述符就是最典型的例子。这就是为什么在当前的设备上这些列表已经不再重要,不过你仍然应该确认自己的固件属于哪一代,而不是靠猜。
描述符克隆是怎么实现的
描述符克隆分两步完成。输入设备先在主机端枚举真鼠标,读取那只鼠标对自己的描述,然后在设备端把同样的回答呈现给游戏主机。因此,游戏主机看到的身份属于一只真实存在、而且确实插着的鼠标,只不过隔了一层。完整克隆会覆盖 ID、字符串、修订号、接口布局和报告描述符,这些回答彼此一致,因为它们都来自同一台真实设备。部分克隆只覆盖 ID,却把字符串或报告描述符留成透传设备自己的,这就又回到了不一致的问题上。所以,对任何固件真正有用的问题不是它能不能伪装 VID,而是它复制了描述符的多少内容。
提示
默认身份、通用身份与克隆身份对比
默认身份,就是透传板如实报出自己是什么。以不做任何修改的 Teensy 为例,这意味着一台本来没有任何理由接开发板的机器上,出现了开发板的厂商 ID 和产品字符串。通用身份,是一只编造出来但看着足够真实的鼠标:一只符合 HID 类规范的鼠标,配上中性的字符串。单独看毫不起眼,但它对不上任何在售产品,而且所有运行这款固件的人都是同一个身份。克隆身份,是你手上那只真鼠标的描述符。它前后一致,对得上一款真实存在的产品,而且因为每个人的鼠标不同,不同用户之间的身份也各不相同。这是三种不同的处境,并且有高下之分。但无论哪一种都不能保证防住任何东西,因为这个排序衡量的是你呈现出多少异常,而不是安全性。
如何自查你的设备
在游戏主机上用 USB 设备查看工具来做这件事,这样你看到的输入设备就和游戏主机看到的一样。只需要五分钟,得到的信息比任何论坛帖子都多。
- 产品字符串和制造商字符串读起来像一只鼠标,而不像微控制器或开发板
- 厂商 ID 和产品 ID 是配套的,并且对得上一款真实存在的产品
- 报告描述符中的按键数量和滚轮与所声称的型号一致
- 设备只呈现一个 HID 鼠标接口,而不是鼠标加串口,再加一个你从来不用的键盘
- 请求的轮询间隔是所声称型号可能使用的数值
- 序列号字符串的有无与所声称的型号一致
- 设备管理器中没有出现任何在普通游戏电脑上不该看到的名称
警告
克隆描述符掩盖不了什么
完美克隆的描述符能让设备在枚举阶段毫不起眼,但对设备之后注入的移动的时序和形态毫无作用。分析恰恰发生在后面这一层,这也是大家真正担心的那一层。无论描述符怎么写,瞬间吸附的移动都会被看出来,过于平滑的移动也一样。每次甩枪都是完全相同的加速曲线,或者修正目标位置的速度比人手还快,也是同样的道理。描述符方面的工作消除的是一类容易被抓到的信号。这项工作必须做,但单靠它不够,把它当成足够了,正是那些配置极好的人因为操作像软件而被抓的原因。
描述符常见问题
游戏能读取我鼠标的描述符吗?
机器上的进程无需特殊权限就能枚举已连接的 USB 设备并读取其描述符,拥有内核权限的反作弊软件当然更能做到。某款游戏是否会这么做、拿到结果后又会做什么,除了游戏厂商自己,谁都无法如实告诉你。
如果我克隆了真鼠标的描述符,会不会出现两台一模一样的设备?
不会,因为你的真鼠标接在输入设备的主机端,而不是接在游戏主机上,所以游戏主机始终只看到一台带有这个身份的设备。如果你把真鼠标也直接插到游戏主机上,就恰恰制造出了你想要避免的重复设备。
克隆描述符违法吗?
呈现其他公司的厂商 ID,属于滥用分配给该公司的标识符,同时也涉及游戏的服务条款问题。我们不是律师,以上不构成法律建议。不过,技术上是可以做到的。
修改描述符会影响性能吗?
报告描述符会。如果你声明的是 16 位而不是 8 位的相对坐标轴,较大的位移量就能放进一个报告里,而不会被截断拆分。大幅甩枪时移动是平滑还是有台阶感,差别就在这里;而身份字段对性能完全没有影响。
我的辅助有一个伪装描述符(spoof descriptor)开关,打开就够了吗?
打开比不打开好,但单靠它不是完整的答案。先弄清楚它到底改了什么:只改了 ID,还是连字符串和报告描述符一起改了。然后在游戏主机上用设备查看工具核查结果,不要只相信复选框上的标签。
应该用一只便宜鼠标来当克隆源吗?
用你实际在玩的那只鼠标。克隆的意义就在于身份对得上一台你很可能拥有并在使用的设备,而你的真鼠标就是你手里最可信的那一台。
资料来源
默认身份、通用身份和克隆身份的优劣排序是我们自己的判断,自查清单也一样,来自实测。
- 01Microsoft Learn, USB device descriptors
表中的 idVendor、idProduct、字符串和类别字段,以及 Windows 如何根据它们进行匹配
learn.microsoft.com
- 02
- 03
继续阅读
仍然没有解决?在 Discord 提交工单
全部指南