【软件逆向-外星人笔记本键盘USB协议逆向】此文章归类为:软件逆向。
我朋友一台 dell g16 购买时直接安装了linux系统,但是linux上没有官方的键盘控制中心,所以无法控制键盘灯光,于是我就想着能不能逆向一下键盘的协议,然后自己写一个控制键盘灯光的程序。我自己的外星人笔记本是m16,所以我就先从m16开始逆向。
通过 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 FF 4A是一个明亮的绿色,而00 8E 29是一个相对较深的绿色。
总的来说,这些颜色都是绿色调,并且大部分的颜色是相似的。不过,其中的一些颜色(如00 FF 4A)会显得明显更亮和饱和。所以,这些颜色大部分是相似的。
更多【软件逆向-外星人笔记本键盘USB协议逆向】相关视频教程:www.yxfzedu.com