前言
我朋友一台 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值:
- 00 3F 11 - RGB(0, 63, 17)
- 00 FF 4A - RGB(0, 255, 74)
- 00 8E 29 - RGB(0, 142, 41)
- 00 91 2A - RGB(0, 145, 42)
- 00 A3 2F - RGB(0, 163, 47)
- 00 9E 2D - RGB(0, 158, 45)
- 00 9B 2D - RGB(0, 155, 45)
- 00 99 2C - RGB(0, 153, 44)
- 00 A0 2E - RGB(0, 160, 46)
- 00 91 2A - RGB(0, 145, 42)
- 00 8E 29 - RGB(0, 142, 41)
- 00 9B 2D - RGB(0, 155, 45)
- 00 CC 3B - RGB(0, 204, 59)
- 00 8E 29 - RGB(0, 142, 41)
- 00 FF 4A - RGB(0, 255, 74)
从这些解析的值可以看出,这些颜色大多是绿色调,但是其中有不同的亮度和饱和度。例如,00 FF 4A是一个明亮的绿色,而00 8E 29是一个相对较深的绿色。
总的来说,这些颜色都是绿色调,并且大部分的颜色是相似的。不过,其中的一些颜色(如00 FF 4A)会显得明显更亮和饱和。所以,这些颜色大部分是相似的。