Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What checksum algorithm do to these packets use?

I'm working on building a python library for manipulating the lighting and programmability features of my cheap Chinese iGK64 mechanical keyboard, because the Windows driver app doesn't work on Linux.

I've run the manufacturer's driver app in a Windows VM and captured USB packets for analysis. Over the last couple of days in my free time I've been breaking down the contents of these packets to determine what the different parts are, and what they do.

So far I've determined these facts:

  • Each packet sent to and received from the keyboard is 64 bytes.
  • The first byte is some kind of destination designator. I am referring to it as the "register id", probably more accurately would be "page id". This field is 1 byte long.
  • The second byte is an "instruction". Eg, 0x02 for "write", 0x01 for "read", I've also seen 0x09 (I think is "execute"), and 0x00 (I think is noop or ping). This field is 1 byte long.
  • The next part is the "address", it is a 16-bit unsigned integer instructing where the read or write takes place. This field is 2 bytes long, little-endian.
  • Next is the payload-length. A 16-bit unsigned integer instructing how many bytes to read or write. This field is 2 bytes long, little-endian.
  • Before the payload itself is the checksum. A 16-bit value, I know little about it. Field is 2 bytes long and I'm assuming little-endian.
  • The payload is last. Its between zero and 56 bytes in length, but is padded out with zeros so the total packet size is 64 bits.
  • All together looks like reg:{} instr: {} addr: {} len: {} checksum: {} payload: {}

Here's an example of a packet:

Raw:

0x220200003800E670FFFFFFFFFFFFFFFF010000020200000204000002080000021000000220000002FFFFFFFFFFFFFFFF00040002000500020006000200070002

Deconstructed:

reg: 0x22 instr: 0x02 addr: 0x0000 len: 56 (0x3800) sum: 0xE670
payload: 0xFFFFFFFFFFFFFFFF010000020200000204000002080000021000000220000002FFFFFFFFFFFFFFFF00040002000500020006000200070002

I'm stuck at determining the algorithm used to calculate the Checksum. I've tried some basic xoring sequences, and some add/substract methods, but none are correct.

Hes an example of two nearly identical packets, both to same register, and payload length of zero, Only difference is the instruction and addr. But see checksums are different.

Raw1:

0x210201000000B63D0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Deconstructed1:

reg: 0x21 instr: 0x02 addr: 0x0100 len: 00 (0x0000) sum: 0xB63D
payload: 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Raw2:

0x21000000000092610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Deconstructed2:

reg: 0x21 instr: 0x00 addr: 0x0000 len: 00 (0x0000) sum: 0x9261
payload: 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

In this case, this is a communication from the host to the peripheral (write tr register 0x21, addr 0x100, zero payload), then from the peripheral to the host (register 0x21 "ack").

I'm pretty sure every property of the packet is used in the calculation of the checksum, including reg id, instr, addr, len, and the whole payload.

Here's some more examples which might help shed light on how the checksum is calculated:

Raw3 (this is a PING, or "alive" packet sent several times per second between host and peripheral):

0x0C0000000000A70D0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Deconstructed3:

reg: 0x0C instr: 0x00 addr: 0x0000 len: 00 (0x0000) sum: 0xA70D
payload: 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Raw4 (one with a payload of all 0xFF):

0x220288013800BC74FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

Deconstructed4:

reg: 0x22 instr: 0x02 addr: 0x8801 len: 56 (0x3800) sum: 0xBC74 
payload 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

I have a couple of larger raw communication dumps, but they are probably not useful for determining the checksum algorithm anymore than the examples provided here.

Any help would be greatly appreciated!

like image 637
Ashley Sommer Avatar asked Oct 16 '22 09:10

Ashley Sommer


1 Answers

To help others, I added here how Ashley came up with the correct checksum for the raw data

2202880138000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

Came up with 0x74BC

2202000038000000FFFFFFFFFFFFFFFF010000020200000204000002080000021000000220000002FFFFFFFFFFFFFFFF00040002000500020006000200070002

Came up with 0x70E6 on CRC-CCITT (0xFFFF)   0x70E6

on site https://www.lammertbies.nl/comm/info/crc-calculation.html

To get the correct checksum in a raw data packet, just remove the checksum from the raw data packet and enter it into the crc-calculation site to find the checksum type.

Here is code to determine the checksum, courtesy falsetru of stackoverflow: ( you need to (pip) or pip3 install crc16)

import binascii
import crc16

def crccitt(hex_string):
    byte_seq = binascii.unhexlify(hex_string)
    crc = crc16.crc16xmodem(byte_seq, 0xffff)
    return '{:04X}'.format(crc & 0xffff)

#In [387]: #crccitt('21020100000000000000000000000000000000000000000000000000000000000#000000000000000000000000000000000000000000000000000000000000000')                                                       
#Out[387]: '3DB6'
like image 119
oppressionslayer Avatar answered Oct 19 '22 23:10

oppressionslayer