Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save kelvie/0b4edb12b90f6a1933c57c7f07625d70 to your computer and use it in GitHub Desktop.
Save kelvie/0b4edb12b90f6a1933c57c7f07625d70 to your computer and use it in GitHub Desktop.
gigabyte sidekick reversing notes

OSD sidekick reversing

Summary

It uses the HID device:

0bda:1100 Realtek Semiconductor Corp. HID Device

Send SET_REPORT frame that looks like:

Setup Data
    bmRequestType: 0x21
    bRequest: SET_REPORT (0x09)
    wValue: 0x0200
        ReportID: 0
        ReportType: Output (2)
    wIndex: 0
    wLength: 192
    bRequest: 9
    wValue: 0x0200
    wIndex: 0 (0x0000)
    wLength: 192
    Data Fragment: <see below

Data fragment:

0000   40 c6 00 00 00 00 20 00 6e 00 80 00 00 00 00 00   @..... .n.......
0010   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0020   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0030   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0040   51 8X XX XX 00 XX XX 00 00 00 00 00 00 00 00 00   Q....1..........
0050   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0060   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0070   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0080   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0090   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00a0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00b0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................

0x40 to 0x42 seem to be:

Addrbyte
0x40Always 0x51
0x410x81 + length
0x4201 for read, 03 for write, 00 for ???

Byte 0x41 is simply 0x81 plus the length

byte 0x43controlbyte 0x45 (value)byte 0x46
0x10brightnessvalue (1-100)doesn’t appear to matter
0x12contrastvalue (1-100)
0x87sharpnessvalue (0-10)

Looks like the checksum might be dependent on the control, but it also looks like it’s not checked by the monitor, so it can be set to zero. For the 0x1X commands it appears to be:

uint8_t checksum(uint8_t *msg, uint8_t len) {

    uint8_t checksum_lower = 0x6;

    for (int i=0; i < len; i++) {
        checksum_lower ^= msg[i] & 0xf;
    }
    checksum_lower &= 0xf;

    // Add higher bits and return. Undefined when len == 0.
    return checksum + 0xf0 & (msg[len-1] ^ 0xc0);

}

Dissection

Host sends 3 SET_REPORTs per setting it wants to change, followed by one GET_REPORT.

Set reports look like:

Setup Data
    bmRequestType: 0x21
    bRequest: SET_REPORT (0x09)
    wValue: 0x0200
        ReportID: 0
        ReportType: Output (2)
    wIndex: 0
    wLength: 192
    bRequest: 9
    wValue: 0x0200
    wIndex: 0 (0x0000)
    wLength: 192
    Data Fragment: <see below

The differentiator in the SET reports seems to be in frame[0x65], or ~usb.data_fragment[0x41] it’s 83, 84, or 00.

The 84 packet, then the 83 packet, then the 00.

84 Packet (just usb.data_fragment):

0000   40 c6 00 00 00 00 20 00 6e 00 80 00 00 00 00 00   @..... .n.......
0010   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0020   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0030   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0040   51 84 03 12 00 31 f5 00 00 00 00 00 00 00 00 00   Q....1..........
0050   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0060   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0070   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0080   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0090   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00a0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00b0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................

83 packet:

0000   40 c6 00 00 00 00 24 00 6e 00 80 00 00 00 00 00   @.....$.n.......
0010   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0020   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0030   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0040   51 83 01 e0 2c 1f 00 00 00 00 00 00 00 00 00 00   Q...,...........
0050   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0060   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0070   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0080   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0090   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00a0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00b0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................

00 packet:

0000   40 d6 51 00 00 00 24 00 6e 01 80 00 00 00 00 00   @.Q...$.n.......
0010   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0020   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0030   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0040   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0050   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0060   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0070   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0080   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0090   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00a0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00b0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................

GET_REPORT (on report ID 0) is then sent, and this is returned (full usb frame)

0000   1c 00 10 d0 4a 4b 0a dd ff ff 00 00 00 00 08 00   ....JK..........
0010   01 01 00 07 00 80 02 c0 00 00 00 03 6e 88 02 00   ............n...
0020   e0 2c 00 09 00 00 71 00 00 00 00 00 00 00 00 00   .,....q.........
0030   00 00 00 00 00 00 00 00 00 00 00 00 6e 88 02 00   ............n...
0040   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0050   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0060   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0070   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0080   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0090   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00a0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00b0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00c0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00d0   00 00 00 00 00 00 00 00 00 00 00 00               ............

The GET_REPORT response doesn’t appear to change, nor do (most of) the 83, or the 00 SET_REPORT packets.

Let’s look at what changes in the 84’s then, and look at the bytes that changed (the only line that changes ever is the one starting at usb.data_fragment[0x40], and tshark can’t slice arrays so we use cut – 0x40 × 2 bytes per hex string + 1 is 129.

0x84 packet

tshark -r gigabyte-osd-sidekick-change-settings.pcapng -Y 'usbhid.setup.bRequest == 0x09 && frame[0x65] == 0x84' -e 'usb.data_fragment' -Tfields | cut -c 129-144 | sed 's/.\{2\}/& /g'

If I remember right, I changed the brightness, then contrast, then brightness again, then sharpness:

51 84 03 10 00 48 8e 00
51 84 03 10 00 40 86 00
51 84 03 10 00 3e f8 00
51 84 03 10 00 3d fb 00
51 84 03 10 00 3c fa 00
51 84 03 10 00 3b fd 00
51 84 03 10 00 3a fc 00
51 84 03 10 00 39 ff 00
51 84 03 10 00 37 f1 00
51 84 03 10 00 36 f0 00
51 84 03 10 00 35 f3 00
51 84 03 10 00 34 f2 00
51 84 03 10 00 33 f5 00
51 84 03 10 00 32 f4 00
51 84 03 10 00 31 f7 00
51 84 03 10 00 32 f4 00
51 84 03 10 00 34 f2 00
51 84 03 10 00 33 f5 00
51 84 03 10 00 32 f4 00
51 84 03 10 00 33 f5 00
51 84 03 10 00 36 f0 00
51 84 03 10 00 40 86 00
51 84 03 10 00 44 82 00
51 84 03 10 00 47 81 00
51 84 03 10 00 48 8e 00
51 84 03 10 00 49 8f 00
51 84 03 10 00 4c 8a 00
51 84 03 10 00 4b 8d 00
51 84 03 10 00 4a 8c 00
51 84 03 12 00 31 f5 00
51 84 03 12 00 2c e8 00
51 84 03 12 00 2d e9 00
51 84 03 12 00 2f eb 00
51 84 03 12 00 30 f4 00
51 84 03 12 00 31 f5 00
51 84 03 12 00 33 f7 00
51 84 03 12 00 34 f0 00
51 84 03 12 00 33 f7 00
51 84 03 12 00 32 f6 00
51 84 03 10 00 4b 8d 00
51 84 03 10 00 4d 8b 00
51 84 03 10 00 4e 88 00
51 84 03 10 00 50 96 00
51 84 03 10 00 4f 89 00
51 84 03 10 00 43 85 00
51 84 03 10 00 41 87 00
51 84 03 10 00 42 84 00
51 84 03 10 00 47 81 00
51 84 03 10 00 46 80 00
51 84 03 10 00 43 85 00
51 84 03 10 00 42 84 00
51 84 03 10 00 40 86 00
51 84 03 10 00 3e f8 00
51 84 03 10 00 3d fb 00
51 84 03 10 00 3c fa 00
51 84 03 87 00 06 57 00
51 84 03 87 00 07 56 00
51 84 03 87 00 08 59 00
51 84 03 87 00 09 58 00
51 84 03 87 00 08 59 00
51 84 03 87 00 05 54 00

0x43 seems to be:

byte 0x43controlbyte 0x45 (value)byte 0x46
0x10brightnessvalue (1-100)checksum?
0x12contrastvalue (1-100)
0x87sharpnessvalue (0-10)

It looks like the values for command 0x12 are always 2 higher than for 0x10, so there’s some binary addition here presumably.

Clue is probably:

51 84 03 10 00 39 ff 00
51 84 03 10 00 40 86 00
51 84 03 10 00 41 87 00

So it overflows from 0xff to 0x86

It gets weird when the command is 0x87 though:

51 84 03 87 00 05 54 00
51 84 03 87 00 06 57 00
51 84 03 87 00 07 56 00
51 84 03 87 00 08 59 00
51 84 03 87 00 09 58 00

why tf does it go +3, -1, +3, -1, whereas for other commands, it’s -3, +1: twos complement?

CMD VAL CHK
10  31   f7
10  32   f4
10  33   f5
10  34   f2
10  35   f3
10  36   f0
10  37   f1
10  38   fe?? (didn't capture this one)
10  39   ff
10  3a   fc
10  3b   fd
10  3c   fa
10  3d   fb
10  3e   f8
10  40   86
10  41   87
10  42   84
10  43   85
10  44   82
10  46   80
10  47   81
10  48   8e
10  49   8f
10  4a   8c
10  4b   8d
10  4c   8a
10  4d   8b
10  4e   88
10  4f   89
10  50   96

12  2c   e8
12  2d   e9
12  2f   eb
12  30   f4
12  31   f5
12  32   f6
12  33   f7
12  34   f0

87  05   54
87  06   57
87  07   56
87  08   59
87  09   58

On futher inspection, it looks like the lower 4 and upper 4 bits of each byte are checksummed separately. I can use a lookup table for each byte, after adding up the values.

So for just the lower bits

CMDVALCHK
bytelowerlower
1024
1204
8754

So for 10 and 12 (lower bits):

(cmd + val) & 0xfbinarychksum & 0xfbinary
0000060110
1000170111
2001040100
3001150101
4010020010
5010130011
6011000000
7011110001
81000e1110
91001f1111
a1010c1100
b1011d1101
c1100a1010
d1101b1011
e111081000
f111191001

Jesus, so it’s just swapping of the middle 2 bits, so, xor the lower bits with 0x6.

Let’s do higher bits, seems to only depend on cmd?

(cmd & 0xf0) >> 4binarychksum >> 4binary
30011f1111
4010081000
5010191001

Looks like a xor of 0xc?

chksum = (cmd + val) & 0xf ^ 0x6 + val & 0xf0 ^ 0xc0

Only confusing part is whent he CMD is 87, it seems to follow a different algorithm, now I can suspect two’s complement – this might be a negative number. 0x87 is -121 as a int8_t, or someone wasn’t mindful of using signed integers when writing this checksum function.

cmdvalue (low)chksum(low)
1000 011101010100
1000 011101100111
1000 011101110110
1000 011110001001
1000 011110011000

Uh so just flip the last bit (xor 1)… or is it xor 0111 and THEN xor 0110. xor on all bytes on the lower 4 bits seems to be how this works across the board for the lower 4 bytes of the checksum.

It seems like the lower byte is actually just the xor of all the lower 4 bits of every byte in the message with 0x6 (the message being the part at offset 0x43 of the data payload).

Higher bytes seems to still just be dependent on the upper 4 bits of the last byte.

0x83 packet

UPDATE: This doesn’t seem to matter. I can just send the 0x84 packet and the settings work.

Same tshark command as above, but with 0x83 (starts at usb.data_fragment[0x40]):

51 83 01 e0 5e 6d 00 00
51 83 01 e0 6b 58 00 00
51 83 01 e0 6c 5f 00 00
51 83 01 e0 2c 1f 00 00
51 83 01 e0 50 63 00 00
51 83 01 e0 51 62 00 00
51 83 01 e0 53 60 00 00
51 83 01 e0 2c 1f 00 00
51 83 01 e0 2c 1f 00 00
51 83 01 e0 2c 1f 00 00
51 83 01 e0 2c 1f 00 00
51 83 01 e0 2c 1f 00 00
51 83 01 e0 2c 1f 00 00
51 83 01 e0 53 60 00 00
51 83 01 e0 2c 1f 00 00
51 83 01 e0 2c 1f 00 00
51 83 01 e0 6a 59 00 00
51 83 01 e0 69 5a 00 00
51 83 01 e0 69 5a 00 00
51 83 01 e0 50 63 00 00
51 83 01 e0 51 62 00 00
51 83 01 e0 51 62 00 00
51 83 01 e0 51 62 00 00
51 83 01 e0 51 62 00 00
51 83 01 e0 51 62 00 00
51 83 01 e0 51 62 00 00
51 83 01 e0 51 62 00 00
51 83 01 e0 23 10 00 00
51 83 01 e0 50 63 00 00
51 83 01 e0 51 62 00 00
51 83 01 e0 23 10 00 00
51 83 01 e0 2c 1f 00 00
51 83 01 e0 2c 1f 00 00
51 83 01 e0 2c 1f 00 00
51 83 01 e0 2c 1f 00 00
51 83 01 e0 2c 1f 00 00
51 83 01 e0 2c 1f 00 00
51 83 01 e0 2c 1f 00 00
51 83 01 e0 2c 1f 00 00
51 83 01 e0 2c 1f 00 00
51 83 01 e0 2c 1f 00 00
51 83 01 e0 2c 1f 00 00
51 83 01 e0 2c 1f 00 00
51 83 01 e0 2c 1f 00 00
51 83 01 e0 2c 1f 00 00
51 83 01 e0 2c 1f 00 00
51 83 01 e0 2c 1f 00 00

0x44 - 0x45 are the only bytes that change, but it sure does like to be 2c 1f though.

Wonder what this means… probably need to check which commands these correspond to.

0x85 command

some commands use 0x85, like this low blue light command:

0000   40 c6 00 00 00 00 20 00 6e 00 80 00 00 00 00 00   @..... .n.......
0010   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0020   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0030   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0040   51 85 03 e0 0b 00 04 38 00 00 00 00 00 00 00 00   Q......8........
0050   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0060   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0070   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0080   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0090   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00a0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00b0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................

the ~e0~ pattern is the interesting one, according to
https://gist.github.com/wadimw/4ac972d07ed1f3b6f22a101375ecac41, ~e0 69 00 XX~
is supposed to do a KVM switch, so let's test it.

Wonder if the checksum is the same first. Well, it looks like checksum doesn't matter.

OK, looks like we got new values
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment