Understanding the Xbox 360 Wired Controller’s USB Data
Dave Madison | March 17, 2019
原文:Understanding the Xbox 360 Wired Controller's USB Data - Parts Not Included
译者注:本文通过描述符获取和USB抓包的方式,分析了Xbox控制器的各类USB描述符、接口和端点配置以及数据包格式,为自制Xbox控制器提供了重要指导。在本文的帮助下,译者已成功在STM32和CH549单片机上实现了Xbox控制器。基本步骤可以总结为:①在一个HID例程基础上,删除原有所有描述符,放入下文中的设备、配置、字符串描述符。②在USB中断处理中,根据主机的需求正确地发出这些描述符让主机获取到(涉及USB基础,不详述)。③按描述符里的值正确配置每个端点的大小,写好端点的发送和接收。④Xbox主要使用端点1,按下文数据格式发送数据包即可。
正文:
我最近的一个项目是试图修改一些微控制器使之作为XInput设备工作,以模仿Xbox控制器。这个过程的第一步是提取并分解设备的“USB描述符”。这些描述符是一个标准化报告的层次结构,描述了设备的功能,包括谁制造的、支持什么版本的USB、如何供电等等。通过将Xbox控制器的描述符复制到我自己的设备上,我可以让计算机相信我的设备也是Xbox控制器,并且会像Xbox控制器一样运行,因此可以使用Xbox控制器的驱动程序轻松地与游戏对接。
但是,我不想只是把描述符从一个地方复制粘贴到另一个地方,我想试着准确地了解幕后发生了什么。我想了解这些描述符中的信息是如何转化为设备行为的特征的。
Methodology 方法论
我使用的是一个真正的Microsoft Xbox 360有线控制器,连接到我的Windows 10。为了探查USB数据,我使用了带有USBPcap插件的Wireshark 2.6.4版本。插入控制器后,Windows将请求设备和配置描述符,控制器将以对应内容响应。Wireshark同时提供USB数据包的原始数据和使用其内置的分析器得出的对数值的分析分类。我已经使用有关USB标准的公开信息尽我所能验证了这些分析分类。
您可以使用许多其他工具来读取这些描述符,无论是从主机(PC)还是使用串联在USB线上的分析仪。这对我来说很简单,也很有效。如果在实际的Xbox 360主机上使用USB分析仪会更好,但不幸的是,我手头没有这两样东西。值得庆幸的是,上述方法已经告诉了我所需要知道的一切,以使我能模拟控制数据端点。
如果您不熟悉USB通信那么其中一些信息可能有点难以理解。有很多网站帮助我理解了这些描述符,包括USB Made Simple、USB in a NutShell,当然还有USB公开标准本身——可在USB.org上获得。
也有一些人在网上发布了关于Xbox 360控制器USB如何工作的信息,比如Zach Littell,他为Xinput的Teensy实现。通过互联网档案馆,Free60维基(现已失效)中也有一些信息可用。这些都是有用的资源,尽管它们都没有彻底分解描述符的含义。
The Device Descriptor 设备描述符
最上层的描述符是设备描述符。这如同一幅总览全局的画面——它告诉主机在与设备通信时使用什么USB标准,识别是谁制造的,是什么产品,以及告诉接下来的配置描述符的数量。
以下是Wireshark报告的有线控制器的完整设备描述符:
bLength: 18
bDescriptorType: 0x01 (DEVICE)
bcdUSB: 0x0200
bDeviceClass: 0xFF (Vendor Specific)
bDeviceSubClass: 0xFF
bDeviceProtocol: 0xFF
bMaxPacketSize0: 8
idVendor: 0x045E (Microsoft Corp.)
idProduct: 0x028E (Xbox360 Controller)
bcdDevice: 0x0114
iManufacturer: 1
iProduct: 2
iSerialNumber: 3
bNumConfigurations: 1
Raw Hex: 0x12 0x01 0x00 0x02 0xff 0xff 0xff 0x08 0x5e 0x04 0x8e 0x02 0x14 0x01 0x01 0x02 0x03 0x01
设备描述符(bDescriptorType为0x01)的长度为18字节(bLength)。它告诉我们控制器使用USB 2.0(bcdUSB),并且端点0(设备控制端点)的最大数据包大小为8字节(bMaxPacketSize0)。
设备的USB设备类、子类和设备协议都设置为0xFF(bDeviceClass、bDeviceSubClass和bDeviceProtocol)。这表明这些是特定于供应商的,不遵循任何现有的USB设备标准。
供应商ID(VID)将控制器标识为由Microsoft Corporation(idVendor)制造,产品ID(PID)将其标识为Xbox 360控制器(idProduct)。此特定控制器的硬件版本为0x0114(bcdedit设备)。这是供应商定义的版本号,编码为二进制编码的十进制(bcd)。在8421编码的情况下,它可转换为十进制数字“1”、“1”和“4”,这可能意味着在语义版本控制中,此控制器的版本为1.1.4。
描述制造商、产品和序列号的字符串分别位于索引1、2和3中(iManufacturer、iProduct和iSerialNumber)。最后,该设备只有一个配置(bNumConfigurations)。
Configuration Descriptor 配置描述符
接下来是配置描述符。对于每种配置,它说明了设备是如何供电的以及它有多少接口。这可以被解释为下文中的接口和端点描述符的“头”。
根据上文的设备描述符,有线Xbox 360控制器只有一个配置:
bLength: 9
bDescriptorType: 0x02 (CONFIGURATION)
wTotalLength: 153
bNumInterfaces: 4
bConfigurationValue: 1
iConfiguration: 0
bmAttributes: 0b10100000 (NOT SELF-POWERED, REMOTE-WAKEUP)
bMaxPower: 250 (500 mA)
Raw Hex: 0x09 0x02 0x99 0x00 0x04 0x01 0x00 0xa0 0xfa
配置描述符(bDescriptorType为0x02)的长度为9字节(bLength)。以下所有接口和端点描述符的总长度(包括配置描述符本身的9个字节)为153个字节(wTotalLength)。该设备总共包含4个接口(bNumInterfaces),用于选择此特定配置的值为1(bConfigurationValue)。从可用的字符串描述符中,没有任何字符串描述此配置(iConfiguration为0)。
设备属性(bmAttributes)是按比特标志位描述设备功能的。比特0-4被保留,并且在描述符中为0。位5为“1”,表示控制器支持远程唤醒(可以将主机从待机状态唤醒)。位6为“0”表示设备未自供电,位7为“1”表示设备由总线供电。
由于设备属性表示控制器由总线供电,bMaxPower变量以2 mA为单位告诉主机控制器可以消耗的功率。在描述符中的情况下,为500毫安。
Interface Descriptors 接口描述符
紧接在配置描述符之后的是接口描述符。接口描述符将较低级别的端点分组成了为设备提供单个功能的逻辑组们。
根据配置描述符,有线Xbox 360控制器有4个接口。
Interface 0: Control Data 接口0:控制数据
bLength: 9
bDescriptorType: 0x04 (INTERFACE)
bInterfaceNumber: 0
bAlternateSetting: 0
bNumEndpoints: 2
bInterfaceClass: 0xFF (Vendor Specific)
bInterfaceSubClass: 0x5D
bInterfaceProtocol: 0x01
iInterface: 0
Raw Hex: 0x09 0x04 0x00 0x00 0x02 0xff 0x5d 0x01 0x00
第一个接口描述符(bDescriptorType为0x04)的长度为9字节(bLength)。这是第一个接口(bInterface Number,索引为0)。它包含两个端点(bNumEndpoints),并且没有描述它的字符串描述符(iInterface索引为0)。
所有四个接口都使用特定于供应商的接口类(bInterface class)。这使得无法推断接口子类或协议的任何特定内容,因为它们是专有的。USB规范还允许设置(bAlternateSetting)将接口描述符标记为备用。四个控制器的接口都没有使用此功能。
从测试来看,该接口似乎用于发送/接收控制数据。
Unknown Descriptor (If0) 未知描述符(If0)
紧接在接口描述符之后的是类型为0x21的附加未知描述符。这个bDescriptorType ID没有列在我能找到的任何公共USB标准中,我使用的USB分析程序似乎也不知道。我的最佳猜测是,这是某种特定于供应商的描述符,它包含了关于接口布局的更具体的细节,很像HID用途页(usage page)。
所有四个接口都有一个未知描述符,使用类型0x21或类型0x41(接口3)。
bLength: 17
bDescriptorType: 0x21 (UNKNOWN)
???: 0x00, 0x01, 0x01
???: 0x25
bEndpointAddress: 0x81 IN Endpoint: 1
bMaxDataSize: 20
???: 0x00, 0x00, 0x00, 0x00, 0x13
bEndpointAddress: 0x01 OUT Endpoint: 1
bMaxDataSize: 8
???: 0x00, 0x00
Raw Hex: 0x11 0x21 0x00 0x01 0x01 0x25 0x81 0x14 0x00 0x00 0x00 0x00 0x13 0x01 0x08 0x00 0x00
前两个字节由USB标准定义,必须指描述符的长度(bLength)和描述符类型(bDescriptorType)。除此之外,描述符其余部分的属性是随便的猜测。
我确信这里包括了接口的端点编号及其预期的数据包大小。0x81是从控制器发送到主机的控制数据的端点,通常为20字节长(0x14,端点后的下一个数字)。0x01是从主机接收的控制数据的端点,当接手柄震动数据时,长度可达8字节(0x08,也是端点后的下一个数字)。端点编号至少可以通过修改端点描述符本身中的bEndpointAddresss值来确认——如果这些未知描述符里的值不跟着也更改,Windows驱动程序将无法正常通信。
除此之外,bDescriptorType之后的前三个字节似乎在未知描述符之间进行了标准化:0x00、0x01、0x01。我不知道这些值指的是什么,或者其他???符号代指的值是什么。希望有更多USB知识的人能够填补我的空缺。
Endpoint 1 IN: Control Data Send 输入端点1:控制数据发送
下一个描述符是第一个端点描述符。端点描述符描述总线的最低级别部分,并帮助主机确定带宽需求。
bLength: 7
bDescriptorType: 0x05 (ENDPOINT)
bEndpointAddress: 0x81 IN Endpoint: 1
bmAttributes: 0x03
wMaxPacketSize: 32
bInterval: 4
Raw Hex: 0x07 0x05 0x81 0x03 0x20 0x00 0x04
第一个端点描述符(bDescriptorType为0x05)的长度为7字节(bLength)。字段bEndpointAddress指定端点编号和端点方向。位0-3描述端点编号(0x#1=1),位7描述方向(0x8#=IN,从主机的角度来看)。它在一次数据更新中能够发送的最大字节数为32(wMaxPacketSize)。
此端点使用中断传输类型(bmAttributes的位0和位1均设置为高)。对于中断传输类型,bInterval设置指定USB“帧”中的轮询间隔。对于低速和全速设备,每个“帧”为1毫秒,这意味着该端点每4毫秒(即250赫兹)轮询一次新数据。
请注意,尽管这是一个“in”端点,但它被命名为控制数据发送,因为“in”和“out”术语指的是从主机的视角来看待连接。在观察端点方向时,请记住这一点。
这是用于将控制层面的信息从控制器发送到计算机的端点。
Endpoint 1 OUT: Control Data Receive 输出端点1:控制数据接收
bLength: 7
bDescriptorType: 0x05 (ENDPOINT)
bEndpointAddress: 0x01 OUT Endpoint: 1
bmAttributes: 0x03
wMaxPacketSize: 32
bInterval: 8
Raw Hex: 0x07 0x05 0x01 0x03 0x20 0x00 0x08
第二个端点描述符与第一个端点描述符几乎相同,只是它是OUT端点(从主机接收数据),并且以第一个端点一半的速度更新(b间隔为8而不是4,或者说第二个端点是125 Hz而第一个是250 Hz)。
这是用于接收从计算机发送到控制器的控制数据信息的端点,包括LED模式和震动电机数据。
Interface 1: Headset (and Expansion Port?) 接口1:耳机(和扩展端口?)
bLength: 9
bDescriptorType: 0x04 (INTERFACE)
bInterfaceNumber: 1
bAlternateSetting: 0
bNumEndpoints: 4
bInterfaceClass: 0xFF (Vendor Specific)
bInterfaceSubClass: 0x5D
bInterfaceProtocol: 0x03
iInterface: 0
Raw Hex: 0x09 0x04 0x01 0x00 0x04 0xff 0x5d 0x03 0x00
接口1与接口0非常相似,只是它有4个端点而不是2个(bNumEndpoints),并且使用0x03而不是0x01作为bInterface Protocol(特定于供应商,未知)。
从测试来看,这个接口似乎可以处理控制器的耳机。由于它们是物理连接的,我还假设这可以处理控制器底部的扩展端口。
Unknown Descriptor (If1) 未知描述符(If1)
bLength: 27
bDescriptorType: 0x21 (UNKNOWN)
???: 0x00, 0x01, 0x01
???: 0x01
bEndpointAddress: 0x82 IN Endpoint: 2
bMaxDataSize: 64
???: 0x01
bEndpointAddress: 0x02 OUT Endpoint: 2
bMaxdataSize: 32
???: 0x16
bEndpointAddress: 0x83 IN Endpoint: 3
bMaxDataSize: 0
???: 0x00, 0x00, 0x00, 0x00, 0x00, 0x16
bEndpointAddress: 0x03 OUT Endpoint: 3
bMaxDataSize: 0
???: 0x00, 0x00, 0x00, 0x00, 0x00
Raw Hex: 0x1b 0x21 0x00 0x01 0x01 0x01 0x82 0x40 0x01 0x02 0x20 0x16 0x83 0x00 0x00 0x00 0x00 0x00 0x00 0x16 0x03 0x00 0x00 0x00 0x00 0x00 0x00
这是另一个未知的描述符,这个明显更长,包含了更多有趣的未知细节。如果我对端点地址和数据包长度的假设成立,那么这似乎表明这对#3端点不需要任何数据。多么奇怪…
Endpoint 2 IN: Microphone Data Send 输入端点2:麦克风数据发送
bLength: 7
bDescriptorType: 0x05 (ENDPOINT)
bEndpointAddress: 0x82 IN Endpoint: 2
bmAttributes: 0x03
wMaxPacketSize: 32
bInterval: 2
Raw Hex: 0x07 0x05 0x82 0x03 0x20 0x00 0x02
与接口0的端点类似,接口1的第一个端点使用中断传输类型,每2毫秒最多接收32个字节。它是地址为2的IN端点。
这是用于将麦克风数据从控制器的耳机端口发送到主机的端点。
Endpoint 2 OUT: Headset Audio Receive 输出端点2:耳机音频接收
bLength: 7
bDescriptorType: 0x05 (ENDPOINT)
bEndpointAddress: 0x02 OUT Endpoint: 2
bmAttributes: 0x03
wMaxPacketSize: 32
bInterval: 4
Raw Hex: 0x07 0x05 0x02 0x03 0x20 0x00 0x04
这个OUT端点几乎与它的姐妹“IN”端点相同,只是它以后者一半的速率运行(bInterval为4而不是2)。
这是用于将音频数据从主机推送到控制器的单声道耳机输出的端点。
Endpoint 3 IN: Unknown, Send 输入端点3:未知,发送
bLength: 7
bDescriptorType: 0x05 (ENDPOINT)
bEndpointAddress: 0x83 IN Endpoint: 3
bmAttributes: 0x03
wMaxPacketSize: 32
bInterval: 64
Raw Hex: 0x07 0x05 0x83 0x03 0x20 0x00 0x40
这个IN端点类似于其他两个音频端点,但它定义了一个明显更长的64帧的bInterval间隔。
由于它与耳机音频端点捆绑在一起,因此该端点可能用于控制器底部的4针扩展端口。我试着将Xbox 360 chatpad连接到控制器的扩展端口,但没有看到通过USB报告的任何数据。也许聊天板需要以某种方式初始化,然而我没有真正的Xbox 360可以测试。
Endpoint 3 OUT: Unknown, Receive 输出端点3:未知,接收
bLength: 7
bDescriptorType: 0x05 (ENDPOINT)
bEndpointAddress: 0x03 OUT Endpoint: 3
bmAttributes: 0x03
wMaxPacketSize: 32
bInterval: 16
Raw Hex: 0x07 0x05 0x03 0x03 0x20 0x00 0x10
这个OUT端点类似于上面的其他三个端点,但定义了16帧的较短的bInterval。
我再次假设这个端点用于耳机插孔旁边的4针扩展端口,尽管除了初始化之外,我没有从PC上看到此端点上有任何数据。
Interface 2: Unknown 接口2:未知
bLength: 9
bDescriptorType: 0x04 (INTERFACE)
bInterfaceNumber: 2
bAlternateSetting: 0
bNumEndpoints: 1
bInterfaceClass: 0xFF (Vendor Specific)
bInterfaceSubClass: 0x5D
bInterfaceProtocol: 0x02
iInterface: 0
Raw Hex: 0x09 0x04 0x02 0x00 0x01 0xff 0x5d 0x02 0x00
接口2与接口1和0非常相似,只是它只有一个端点(bNumEndpoints),并且使用0x02作为bInterfaceProtocol(特定于供应商,未知),而不是0x01(If0)或0x02(If1)。
Unknown Descriptor (If2) 未知描述符(If2)
bLength: 9
bDescriptorType: 0x21 (UNKNOWN)
???: 0x00, 0x01, 0x01
???: 0x22
bEndpointAddress: 0x84 IN Endpoint: 4
bMaxDataSize: 7
???: 0x00
Raw Hex: 0x09 0x21 0x00 0x01 0x01 0x22 0x84 0x07 0x00
还有另一个未知描述符与未知接口相关联。与接口1中更复杂的未知描述符相比,尽管同样神秘,但要解包的内容要少得多。
Endpoint 4 IN: Unknown, Send 输入端点4:未知,发送
bLength: 7
bDescriptorType: 0x05 (ENDPOINT)
bEndpointAddress: 0x84 IN Endpoint: 4
bmAttributes: 0x03
wMaxPacketSize: 32
bInterval: 16
Raw Hex: 0x07 0x05 0x84 0x03 0x20 0x00 0x10
这是配置描述符定义的最后一个端点。它是又一个中断类型的数据输出(IN)端点,使用16毫秒(bInterval)的间隔。在我的测试中,Windows驱动程序从不轮询这个端点的数据,所以我不知道它的用途。
Interface 3: Security Method 接口3:安全措施
bLength: 9
bDescriptorType: 0x04 (INTERFACE)
bInterfaceNumber: 3
bAlternateSetting: 0
bNumEndpoints: 0
bInterfaceClass: 0xFF (Vendor Specific)
bInterfaceSubClass: 0xFD
bInterfaceProtocol: 0x13
iInterface: 4
Raw Hex: 0x09 0x04 0x03 0x00 0x00 0xff 0xfd 0x13 0x04
最后一个接口没有与其关联的端点,但有一个与其关联的字符串描述符(iInterface)。索引4处的字符串描述符为:
Xbox Security Method 3, Version 1.00, © 2005 Microsoft Corporation. All rights reserved.
这表明,尽管它没有关联的端点,但该接口用于控制器安全措施,确保只有正版或授权的控制器才能连接到Xbox 360主机。从我的测试来看,这种安全方法似乎没有在Windows 10驱动程序上实现。
Unknown Descriptor (If3) 未知描述符(If3)
bLength: 6
bDescriptorType: 0x41 (UNKNOWN)
???: 0x00, 0x01, 0x01
???: 0x03
Raw Hex: 0x06 0x41 0x00 0x01 0x01 0x03
最后则是最后一个未知的描述符。这与其他未知项的bDescriptorType不同(此处是0x41而非0x21)。由于此接口没有端点,因此此未知描述符没有bEndpointAddress字段。开头的典型的3字节就在那里,还有一个额外的“0x03”字节。我仍然不知道这些字节的意义是什么。
这个未知描述符标志了配置描述符的结束,其总共153个字节。
String Descriptors 字符串描述符
最后但同样重要的是字符串描述符。这些是可选的、人类可读的信息,可以通过索引链接到设备信息、配置描述符或接口描述符。我使用这个工具获得了字符串描述符,该工具也用于仔细检查其他描述符。
Index | LANGID | String |
0x01 | 0x0000 | "©Microsoft Corporation" |
0x02 | 0x0000 | "Controller" |
0x03 | 0x0000 | "08FEC93" |
0x04 | 0x0000 | "Xbox Security Method 3, Version 1.00, © 2005 Microsoft Corporation. All rights reserved." |
根据Xbox 360有线控制器的设备描述符,制造商由字符串0x01(iManufacturer)提供,产品由字符串0x02(iProduct)提供,序列号由字符串0x03(iSerialNumber)提供。第四个描述符用于描述接口3(iInterface)。
我假设每个控制器的序列号字符串不同,尽管我手头只有一个控制器所以我无法比较它们。
The Complete Descriptor 完整的描述符
希望这能很好地分析控制器的USB描述符及其含义。由于我的最终目标是重复使用这些描述符,为我自己的嵌入式项目添加XInput支持,所以我在C语言数组中需要这些描述符,以便在微控制器的USB栈中使用。以下是控制器设备和配置描述符的完整数组,并附有注释:
// Xbox 360 Wired Controller USB Descriptors
// Assembled by David Madison
// www.partsnotincluded.com
const byte DeviceDescriptor[] = {
0x12, // bLength
0x01, // bDescriptorType
0x00, 0x02, // bcdUSB (2.0)
0xFF, // bDeviceClass
0xFF, // bDeviceSubClass
0xFF, // bDeviceProtocol
0x08, // bMaxPacketSize0
0x5E, 0x04, // idEVendor (Microsoft Corp.)
0x8E, 0x02, // idProduct (Xbox360 Controller)
0x14, 0x01, // bcdDevice
0x01, // iManufacturer
0x02, // iProduct
0x03, // iSerialNumber
0x01, // bNumConfigurations
};
const byte ConfigurationDescriptor[] = {
// Configuration Descriptor
0x09, // bLength
0x02, // bDescriptorType (CONFIGURATION)
0x99, 0x00, // wTotalLength (153)
0x04, // bNumInterfaces
0x01, // bConfigurationValue
0x00, // iConfiguration
0xA0, // bmAttributes
0xFA, // bMaxPower
/* ---------------------------------------------------- */
// Interface 0: Control Data
0x09, // bLength
0x04, // bDescriptorType (INTERFACE)
0x00, // bInterfaceNumber
0x00, // bAlternateSetting
0x02, // bNumEndpoints
0xFF, // bInterfaceClass
0x5D, // bInterfaceSubClass
0x01, // bInterfaceProtocol
0x00, // iInterface
// Unknown Descriptor (If0)
0x11, // bLength
0x21, // bDescriptorType
0x00, 0x01, 0x01, 0x25, // ???
0x81, // bEndpointAddress (IN, 1)
0x14, // bMaxDataSize
0x00, 0x00, 0x00, 0x00, 0x13, // ???
0x01, // bEndpointAddress (OUT, 1)
0x08, // bMaxDataSize
0x00, 0x00, // ???
// Endpoint 1: Control Surface Send
0x07, // bLength
0x05, // bDescriptorType (ENDPOINT)
0x81, // bEndpointAddress (IN, 1)
0x03, // bmAttributes
0x20, 0x00, // wMaxPacketSize
0x04, // bInterval
// Endpoint 1: Control Surface Receive
0x07, // bLength
0x05, // bDescriptorType (ENDPOINT)
0x01, // bEndpointAddress (OUT, 1)
0x03, // bmAttributes
0x20, 0x00, // wMaxPacketSize
0x08, // bInterval
/* ---------------------------------------------------- */
// Interface 1: Headset (and Expansion Port?)
0x09, // bLength
0x04, // bDescriptorType (INTERFACE)
0x01, // bInterfaceNumber
0x00, // bAlternateSetting
0x04, // bNumEndpoints
0xFF, // bInterfaceClass
0x5D, // bInterfaceSubClass
0x03, // bInterfaceProtocol
0x00, // iInterface
// Unknown Descriptor (If1)
0x1B, // bLength
0x21, // bDescriptorType
0x00, 0x01, 0x01, 0x01, // ???
0x82, // bEndpointAddress (IN, 2)
0x40, // bMaxDataSize
0x01, // ???
0x02, // bEndpointAddress (OUT, 2)
0x20, // bMaxDataSize
0x16, // ???
0x83, // bEndpointAddress (IN, 3)
0x00, // bMaxDataSize
0x00, 0x00, 0x00, 0x00, 0x00, 0x16, // ???
0x03, // bEndpointAddress (OUT, 3)
0x00, // bMaxDataSize
0x00, 0x00, 0x00, 0x00, 0x00, // ???
// Endpoint 2: Microphone Data Send
0x07, // bLength
0x05, // bDescriptorType (ENDPOINT)
0x82, // bEndpointAddress (IN, 2)
0x03, // bmAttributes
0x20, 0x00, // wMaxPacketSize
0x02, // bInterval
// Endpoint 2: Headset Audio Receive
0x07, // bLength
0x05, // bDescriptorType (ENDPOINT)
0x02, // bEndpointAddress (OUT, 2)
0x03, // bmAttributes
0x20, 0x00, // wMaxPacketSize
0x04, // bInterval
// Endpoint 3: Unknown, Send
0x07, // bLength
0x05, // bDescriptorType (ENDPOINT)
0x83, // bEndpointAddress (IN, 3)
0x03, // bmAttributes
0x20, 0x00, // wMaxPacketSize
0x40, // bInterval
// Endpoint 3: Unknown, Receive
0x07, // bLength
0x05, // bDescriptorType (ENDPOINT)
0x03, // bEndpointAddress (OUT, 3)
0x03, // bmAttributes
0x20, 0x00, // wMaxPacketSize
0x10, // bInterval
/* ---------------------------------------------------- */
// Interface 2: Unknown
0x09, // bLength
0x04, // bDescriptorType (INTERFACE)
0x02, // bInterfaceNumber
0x00, // bAlternateSetting
0x01, // bNumEndpoints
0xFF, // bInterfaceClass
0x5D, // bInterfaceSubClass
0x02, // bInterfaceProtocol
0x00, // iInterface
// Unknown Descriptor (If2)
0x09, // bLength
0x21, // bDescriptorType
0x00, 0x01, 0x01, 0x22, // ???
0x84, // bEndpointAddress (IN, 4)
0x07, // bMaxDataSize
0x00, // ???
// Endpoint 4: Unknown, Send
0x07, // bLength
0x05, // bDescriptorType (ENDPOINT)
0x84, // bEndpointAddress (IN, 4)
0x03, // bmAttributes
0x20, 0x00, // wMaxPacketSize
0x10, // bInterval
/* ---------------------------------------------------- */
// Interface 3: Security Method
0x09, // bLength
0x04, // bDescriptorType (INTERFACE)
0x03, // bInterfaceNumber
0x00, // bAlternateSetting
0x00, // bNumEndpoints
0xFF, // bInterfaceClass
0xFD, // bInterfaceSubClass
0x13, // bInterfaceProtocol
0x04, // iInterface
// Unknown Descriptor (If3)
0x06, // bLength
0x41, // bDescriptorType
0x00, 0x01, 0x01, 0x03, // ???
};
Control Surface Data Format 控制层面的数据格式
结束对设备、配置和字符串描述符的分析后,我还需要最后一步才能说“完成了分析”,这一步是弄清楚控制层面的信息是以怎样的格式进行USB传输的。这很容易通过操纵设备的控制并将其与此时Wireshark捕获的USB数据流进行对比来确定。以下是分析:
Control Surfaces (In) 控制层面(输入)
来自控制器的每个数据更新都是从接口0上的端点1 IN(0x81)发送的20字节数据包。字节0是消息类型(0x00),字节1是以字节为单位的长度(0x14=20)。打包的控制层面数据存储在数据包中部的12个字节中(2-13)。最后6个字节,即14–19,是未使用的(均为0x00)。
Buttons 按钮
字节2和3包含位数组中控制器数字按钮的按压状态,其中按下“1”,释放“0”。这包括所有8个表面按钮、2个操纵杆按钮、中央“Xbox”按钮和方向键。
从低位到高位,字节2映射到方向键(上、下、左、右)、开始、后退、L3和R3按钮。字节3的前三位映射到左肩键(LB)、右肩键(RB)和“Xbox”按钮。位4-7分别映射到A、B、X和Y按钮。位3未使用(为0)。
Triggers 扳机
字节4和5包含表示左右模拟扳机状态的8位无符号整数(依次地),其中值“0”被释放,值“255”被完全按下。
Joysticks 摇杆
字节6-13包含双模拟操纵杆的位置,为16位有符号整数。这些以小端模式存储(低字节优先),X轴在Y之前,东北方向为正。
左操纵杆位置存储在字节6、7、8和9中,而右操纵杆位置则存储在字节10、11、12和13中。
Rumble and LED Data (Out) 震动和LED数据(输出)
对控制器的用户反馈功能的数据更新在端点1 OUT(0x01)上发送,即在接口0上。这些更新遵循与发送数据相同的格式,其中字节0是“类型”标识符,字节1是数据包长度(以字节为单位)。
Rumble (0x00) 震动(类型0x00)
类型字节“0x00”表示震动数据包。控制器包含两个震动电机:一个大重量在左握把,一个小重量在右握把。这两个电机的值都在一个数据包中更新。
震动值是表示电机速度的8位无符号整数,其中“0”表示关闭,“255”表示最大速度。左电机的震动值存储在下标3中,而右电机的震动值存储在下标4中。
此数据包通常为8字节长,其中字节2、5、6和7未使用(为0x00)。
LEDs (0x01) LED(类型0x01)
类型字节“0x01”表示LED数据包。控制器的中心环周围有四个绿色LED,用于显示分配的玩家编号或其他状态信息。此数据包选择控制器显示的LED动画。有关LED动画及其时间安排的更多信息,请参阅这一文章。
LED动画的标识符存储在数据包下标2中。此数据包通常为3字节长,所有字节都已使用。
Conclusion 结论
呜呼!好吧,这是一篇冗长、详细、枯燥的技术帖子……希望我已经解释得足够好了。我知道我当然对Xbox 360控制器的USB功能的工作有了更好的了解,我希望你也能了解。
然而,这并不是一个完整的分析。这个控制器还有很多谜团需要解决,尤其是用于通过操纵台(接口3)进行身份验证的安全方法。已经有人在逆向工程方面取得了一些成功,甚至有一个成功的中间人攻击(他甚至识别了安全命令!)——尽管到目前为止,我不相信有人能够(公开)破坏它。
下一个挑战是应用这些知识使得一个具有USB功能的微控制器能模拟Xbox控制器。敬请期待!