【软件逆向-外星人笔记本键盘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
      | importloggingimporttimeimportusbfromusb importUSBErrorclassAlienwareUSBDriver:    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    defacquire(self):        """ Acquire control of the USB controller."""        ifself._control_taken:            return        self._device =usb.core.find(idVendor=AlienwareUSBDriver.VENDOR_ID, idProduct=AlienwareUSBDriver.PRODUCT_ID)        ifself._device isNone:            logging.error("ERROR: No AlienFX USB controller found; tried VID {}, PID {}"                          .format(AlienwareUSBDriver.VENDOR_ID, AlienwareUSBDriver.PRODUCT_ID))        try:            self._device.set_configuration()        exceptUSBError as exc:            logging.error("Cant set configuration. Error : {}".format(exc.strerror))        try:            usb.util.claim_interface(self._device, 0)        exceptUSBError 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)))    defrelease(self):        ifnotself._control_taken:            return        try:            usb.util.release_interface(self._device, 0)        exceptUSBError as exc:            logging.error("Cant release interface. Error : {}".format(exc.strerror))        try:            self._device.attach_kernel_driver(0)        exceptUSBError 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)))    defwrite_packet(self, pkt):        ifnotself._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)))            iflen(pkt) !=num_bytes_sent:                logging.error("writePacket: intended to write {} of {} bytes but wrote {} bytes"                              .format(pkt, len(pkt), num_bytes_sent))            returnnum_bytes_sent        exceptUSBError as exc:            logging.error("writePacket: {}".format(exc)) | 
其中设备信息可以在wireshark中查看
 
| 
      1
      
      2
      | VENDOR_ID =0xd62PRODUCT_ID =0xc2b0 | 
在使用 device.ctrl_transfer 发送数据时需要指定 bmRequestType, bRequest, wValue, wIndex,这些信息也可以在wireshark中查看
 
| 
      1
      
      2
      
      3
      
      4
      | OUT_BM_REQUEST_TYPE =0x21OUT_B_REQUEST =0x09OUT_W_VALUE =0x3ccOUT_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    }    defget_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))        returnout    chars =list(keymap.keys())    a =0    a_color =0    b =0    b_color =0    fori, k inenumerate(chars):        key =keymap[k]        a =key        a_color =0xff0000        b =0        b_color =0        ifi > 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
      | cc8c0500010101010101010101010101010101010101010101010101010101010101010101010101000000000100010101010101010101010101010100000000cc8c0600000101010101010101010101010101000000000000010001010101010101010101000100000000000101000101010001000100010100010000000000 | 
开启波动灯光的命令,格式如下:
| 
      1
      | cc800305000001010101000000000000050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | 
在分析过程中,也是花费了不少时间, 主要是 alineware command center 在全键盘设置成一个颜色时,会发送很多数据包,而且每个键对应的颜色值还不一样,我以为有特殊的算法
比如我设置成绿色时,发送的数据包如下:
 
他会为每个键随机生成颜色相近的值, 这些值同样是以十六进制表示的RGB颜色代码。我们可以解析这些代码来看看这些颜色是否相似。
以下是解析的颜色及其RGB值:
从这些解析的值可以看出,这些颜色大多是绿色调,但是其中有不同的亮度和饱和度。例如,00 FF 4A是一个明亮的绿色,而00 8E 29是一个相对较深的绿色。
总的来说,这些颜色都是绿色调,并且大部分的颜色是相似的。不过,其中的一些颜色(如00 FF 4A)会显得明显更亮和饱和。所以,这些颜色大部分是相似的。
更多【软件逆向-外星人笔记本键盘USB协议逆向】相关视频教程:www.yxfzedu.com