外星人笔记本键盘USB协议逆向

前言

我朋友一台 dell g16 购买时直接安装了linux系统,但是linux上没有官方的键盘控制中心,所以无法控制键盘灯光,于是我就想着能不能逆向一下键盘的协议,然后自己写一个控制键盘灯光的程序。我自己的外星人笔记本是m16,所以我就先从m16开始逆向。

USB协议分析

通过 chatgpt 得知,AlienFX设备通常通过USB接口连接到计算机。键盘的灯光控制是通过HID (人机接口设备) 协议进行的。当你使用AlienFX软件时,这些程序会发送特定的命令到键盘,告诉它如何设置灯光效果。

现在wireshark已经支持HID协议的解析,所以我们可以直接使用wireshark来分析USB协议。在安装wireshark是需要勾选安装USBPcap

打开wireshark,选择USBPcap1

设置要捕获的usb设备,然后点击start

打开 alienware command center,设置键盘灯光,在灯光效果除设置颜色为红色,然后点击应用。

然后我们就可以在wireshark中看到usb协议的数据包了, 我们可以看到有两个数据包,一个是发送数据包,一个是接收数据包。可以通过设置过滤器来过滤掉接收数据包,只看发送数据包,过滤设置为 usb.src == "host"

发现仅仅是改了一个按键的颜色,就发送了很多数据包,而且每个数据包的长度都不一样,这是因为每个数据包都是一个命令,而且每个命令的长度都不一样,所以我们需要找到每个命令的格式,然后才能解析出每个命令的含义。

验证数据包

这个时候我们需要写一个测试程序,来分析哪一个包让键盘改变了颜色,然后再分析这个包的格式。这里我们使用python将数据重发到usb设备,然后观察键盘的变化。

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | import logging import time import usb from usb ``import USBError class AlienwareUSBDriver: ``VENDOR_ID ``= 0xd62 ``PRODUCT_ID ``= 0xc2b0 ``SEND_BM_REQUEST_TYPE ``= 0x21 ``SEND_B_REQUEST ``= 0x09 ``SEND_W_VALUE ``= 0x3cc ``SEND_W_INDEX ``= 0x0 ``PACKET_LENGTH ``= 63 ``def __init__(``self``): ``self``._control_taken ``= False ``self``._device ``= None ``def acquire(``self``): ``""" Acquire control of the USB controller.""" ``if self``._control_taken: ``return ``self``._device ``= usb.core.find(idVendor``=``AlienwareUSBDriver.VENDOR_ID, idProduct``=``AlienwareUSBDriver.PRODUCT_ID) ``if self``._device ``is None``: ``logging.error(``"ERROR: No AlienFX USB controller found; tried VID {}, PID {}" ``.``format``(AlienwareUSBDriver.VENDOR_ID, AlienwareUSBDriver.PRODUCT_ID)) ``try``: ``self``._device.set_configuration() ``except USBError as exc: ``logging.error(``"Cant set configuration. Error : {}"``.``format``(exc.strerror)) ``try``: ``usb.util.claim_interface(``self``._device, ``0``) ``except USBError as exc: ``logging.error(``"Cant claim interface. Error : {}"``.``format``(exc.strerror)) ``self``._control_taken ``= True ``logging.debug(``"USB device acquired, VID={}, PID={}"``.``format``(``hex``(AlienwareUSBDriver.VENDOR_ID), ``hex``(AlienwareUSBDriver.PRODUCT_ID))) ``def release(``self``): ``if not self``._control_taken: ``return ``try``: ``usb.util.release_interface(``self``._device, ``0``) ``except USBError as exc: ``logging.error(``"Cant release interface. Error : {}"``.``format``(exc.strerror)) ``try``: ``self``._device.attach_kernel_driver(``0``) ``except USBError as exc: ``logging.error(``"Cant re-attach. Error : {}"``.``format``(exc.strerror)) ``self``._control_taken ``= False ``logging.debug(``"USB device released, VID={}, PID={}"``.``format``(``hex``(AlienwareUSBDriver.VENDOR_ID), ``hex``(AlienwareUSBDriver.PRODUCT_ID))) ``def write_packet(``self``, pkt): ``if not self``._control_taken: ``return ``try``: ``num_bytes_sent ``= self``._device.ctrl_transfer( ``self``.SEND_BM_REQUEST_TYPE, ``self``.SEND_B_REQUEST, ``self``.SEND_W_VALUE, ``self``.SEND_W_INDEX, ``pkt, ``0``) ``logging.debug(``"wrote: {}, {} bytes"``.``format``(pkt, ``len``(pkt))) ``if len``(pkt) !``= num_bytes_sent: ``logging.error(``"writePacket: intended to write {} of {} bytes but wrote {} bytes" ``.``format``(pkt, ``len``(pkt), num_bytes_sent)) ``return num_bytes_sent ``except USBError as exc: ``logging.error(``"writePacket: {}"``.``format``(exc)) |

其中设备信息可以在wireshark中查看

|-----|---------------------------------------------------|
| 1 2 | VENDOR_ID ``= 0xd62 PRODUCT_ID ``= 0xc2b0 |

在使用 device.ctrl_transfer 发送数据时需要指定 bmRequestType, bRequest, wValue, wIndex,这些信息也可以在wireshark中查看

|---------|---------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 | OUT_BM_REQUEST_TYPE ``= 0x21 OUT_B_REQUEST ``= 0x09 OUT_W_VALUE ``= 0x3cc OUT_W_INDEX ``= 0x0 |

尝试将wireshark中的数据包发送到键盘,通过测试发现,其中一条数据包发送后,Q键的灯光才会改变

|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 | if __name__ ``=``= '__main__'``: ``device ``= AlienwareUSBDriver() ``device.acquire() ``data ``= bytes.fromhex(``'cc8c020073072f46121278b56519a6f9661799e568127ab7691aaaff6a2aaaff6c2aaaff6e137fbf7019a6f9711aaaff8608334b8708334b8808334b2bfc0000'``) ``device.write_packet(data) |

数据包格式分析

通过分析得知,每次改变颜色,awcc 会通过CC 8C 02 00 命令把所有按键的颜色发送一遍 ,经过多次测试,发现每个按键的颜色都是由三个字节表示,分别是 R G B,所以我们可以通过改变这三个字节来改变按键的颜色。包格式如下:

经过多次尝试后,将整个键盘的对应序号,得到如下表格

Key Code Key Code Key Code Key Code
esc 1 f4 5 u 0x31 lshift 0x52
f1 2 f5 6 i 0x32 z 0x54
f2 3 f6 7 o 0x33 x 0x55
f3 4 f7 8 p 0x34 c 0x56
f8 9 3 0x18 [ 0x35 v 0x57
f9 0xa 4 0x19 ] 0x36 b 0x58
f10 0xb 5 0x1a \ 0x38 n 0x59
f11 0xc 6 0x1b a 0x3f m 0x5a
f12 0xd 7 0x1c s 0x40 , 0x5b
home 0xe 8 0x1d d 0x41 . 0x5c
end 0xf 9 0x1e f 0x42 / 0x5d
del 0x10 0 0x1f g 0x43 rshift 0x5f
` 0x15 - 0x20 h 0x44 up 0x73
1 0x16 = 0x21 j 0x45 lctrl 0x65
2 0x17 back 0x24 k 0x46 fn 0x66
tab 0x29 caps 0x3e l 0x47 lwin 0x68
q 0x2b enter 0x4b ; 0x48 lalt 0x69
w 0x2c space 0x6a ' 0x49 ralt 0x70
e 0x2d rwin 0x6e right 0x88 rctrl 0x71
r 0x2e ralt 0x70 down 0x87 left 0x86
t 0x2f microphone 0x14 voice0 0x11 voice+ 0x13
y 0x30 voice- 0x12 voice+ 0x13 voice- 0x12

可以用一个例子来验证上面的keymap是否正确

|-------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | if __name__ ``=``= '__main__'``: ``device ``= AlienwareUSBDriver() ``device.acquire() ``keymap ``= { ``'esc'``: ``1``, ``'f1'``: ``2``, ``'f2'``: ``3``, ``'f3'``: ``4``, ``'f4'``: ``5``, ``'f5'``: ``6``, ``'f6'``: ``7``, ``'f7'``: ``8``, ``'f8'``: ``9``, ``'f9'``: ``0xa``, ``'f10'``: ``0xb``, ``'f11'``: ``0xc``, ``'f12'``: ``0xd``, ``'home'``: ``0xe``, ``'end'``: ``0xf``, ``'del'``: ``0x10``, ```''```: 0x15, '1': 0x16, '2': 0x17, '3': 0x18, '4': 0x19, '5': 0x1a, '6': 0x1b, '7': 0x1c, '8': 0x1d, '9': 0x1e, '0': 0x1f, '-': 0x20, '=': 0x21, 'back': 0x24, 'microphone': 0x14, 'tab': 0x29, 'q': 0x2b, 'w': 0x2c, 'e': 0x2d, 'r': 0x2e, 't': 0x2f, 'y': 0x30, 'u': 0x31, 'i': 0x32, 'o': 0x33, 'p': 0x34, '[': 0x35, ']': 0x36, '\\': 0x38, 'voice0': 0x11, 'caps': 0x3e, 'a': 0x3f, 's': 0x40, 'd': 0x41, 'f': 0x42, 'g': 0x43, 'h': 0x44, 'j': 0x45, 'k': 0x46, 'l': 0x47, ';': 0x48, '\'': 0x49, 'enter': 0x4b, 'voice+': 0x13, 'lshift': 0x52, 'z': 0x54, 'x': 0x55, 'c': 0x56, 'v': 0x57, 'b': 0x58, 'n': 0x59, 'm': 0x5a, ',': 0x5b, '.': 0x5c, '/': 0x5d, 'rshift': 0x5f, 'up': 0x73, 'voice-': 0x12, 'lctrl': 0x65, 'fn': 0x66, 'lwin': 0x68, 'lalt': 0x69, 'space': 0x6a, 'ralt': 0x70, 'rwin': 0x6e, 'rctrl': 0x71, 'left': 0x86, 'down': 0x87, 'right': 0x88` ` }` ` def` `get_key_bytes(a, b, a_color, b_color):` ` header =` `bytes.fromhex('cc8c0200')` ` a_bytes =` `(a << 24` `| a_color).to_bytes(4, byteorder='big')` ` b_bytes =` `(b << 24` `| b_color).to_bytes(4, byteorder='big')` ` data =` `header +` `a_bytes +` `b_bytes` ` out =` `data +` `bytes(64` `-` `len(data))` ` return` `out` ` chars =` `list(keymap.keys())` ` a =` `0` ` a_color =` `0` ` b =` `0` ` b_color =` `0` ` for` `i, k in` `enumerate(chars):` ` key =` `keymap[k]` ` a =` `key` ` a_color =` `0xff0000` ` b =` `0` ` b_color =` `0` ` if` `i > 0:` ` b =` `keymap[chars[i -` `1]]` ` b_color =` `0x00ff00` ` device.write_packet(get_key_bytes(a, b, a_color, b_color))` ` time.sleep(0.3)` ` a_color =` `0x00ff00` ` b_color =` `0x00ff00` ` ``device.write_packet(get_key_bytes(a, b, a_color, b_color))` |

效果如下:

这样我们就可以根据需要,来动态设置每个按键的颜色了。

其他命令

其中还有清除灯光的命令,格式如下:

|---|------------------------------------------------------------------------------------------------------------------------------------|
| 1 | cc8c1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 |

关闭波动灯光的命令,格式如下:

|-----|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 | cc8c0500010101010101010101010101010101010101010101010101010101010101010101010101000000000100010101010101010101010101010100000000 cc8c0600000101010101010101010101010101000000000000010001010101010101010101000100000000000101000101010001000100010100010000000000 |

开启波动灯光的命令,格式如下:

|---|------------------------------------------------------------------------------------------------------------------------------------|
| 1 | cc800305000001010101000000000000050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 |

最后

在分析过程中,也是花费了不少时间, 主要是 alineware command center 在全键盘设置成一个颜色时,会发送很多数据包,而且每个键对应的颜色值还不一样,我以为有特殊的算法

比如我设置成绿色时,发送的数据包如下:

他会为每个键随机生成颜色相近的值, 这些值同样是以十六进制表示的RGB颜色代码。我们可以解析这些代码来看看这些颜色是否相似。

以下是解析的颜色及其RGB值:

  1. 00 3F 11 - RGB(0, 63, 17)
  2. 00 FF 4A - RGB(0, 255, 74)
  3. 00 8E 29 - RGB(0, 142, 41)
  4. 00 91 2A - RGB(0, 145, 42)
  5. 00 A3 2F - RGB(0, 163, 47)
  6. 00 9E 2D - RGB(0, 158, 45)
  7. 00 9B 2D - RGB(0, 155, 45)
  8. 00 99 2C - RGB(0, 153, 44)
  9. 00 A0 2E - RGB(0, 160, 46)
  10. 00 91 2A - RGB(0, 145, 42)
  11. 00 8E 29 - RGB(0, 142, 41)
  12. 00 9B 2D - RGB(0, 155, 45)
  13. 00 CC 3B - RGB(0, 204, 59)
  14. 00 8E 29 - RGB(0, 142, 41)
  15. 00 FF 4A - RGB(0, 255, 74)

从这些解析的值可以看出,这些颜色大多是绿色调,但是其中有不同的亮度和饱和度。例如,00 FF 4A是一个明亮的绿色,而00 8E 29是一个相对较深的绿色。

总的来说,这些颜色都是绿色调,并且大部分的颜色是相似的。不过,其中的一些颜色(如00 FF 4A)会显得明显更亮和饱和。所以,这些颜色大部分是相似的。

相关推荐
三佛科技-187366133978 小时前
分享机械键盘MCU解决方案
单片机·嵌入式硬件·计算机外设
TESmart碲视8 小时前
Mac 真正多显示器支持:TESmart USB-C KVM(搭载 DisplayLink 技术)如何实现
macos·计算机外设·电脑
error:(8 小时前
蓝牙鼠标频繁卡顿?一招解决 Win10/11 的 USB 省电机制干扰问题
计算机外设
智慧地球(AI·Earth)18 小时前
给AI配一台手机+电脑?智谱AutoGLM上线!
人工智能·智能手机·电脑
C116111 天前
电脑 hdmi 没有声音问题解决
电脑
桃杬1 天前
lvgl修改输入设备驱动使其支持鼠标右键、双击、滑轮...
计算机外设
绀目澄清2 天前
unity UGUI 鼠标画线
unity·计算机外设·游戏引擎
沙尘暴炒饭2 天前
前端vue使用canvas封装图片标注功能,鼠标画矩形框,标注文字 包含下载标注之后的图片
前端·vue.js·计算机外设
星空的资源小屋2 天前
Digital Clock 4,一款免费的个性化桌面数字时钟
stm32·单片机·嵌入式硬件·电脑·excel
sukalot3 天前
window显示驱动开发—头戴式和专用显示器
计算机外设