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:
Addr | byte |
---|---|
0x40 | Always 0x51 |
0x41 | 0x81 + length |
0x42 | 01 for read, 03 for write, 00 for ??? |
Byte 0x41
is simply 0x81
plus the length
byte 0x43 | control | byte 0x45 (value) | byte 0x46 |
---|---|---|---|
0x10 | brightness | value (1-100) | doesn’t appear to matter |
0x12 | contrast | value (1-100) | |
0x87 | sharpness | value (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);
}
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.
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 0x43 | control | byte 0x45 (value) | byte 0x46 |
---|---|---|---|
0x10 | brightness | value (1-100) | checksum? |
0x12 | contrast | value (1-100) | |
0x87 | sharpness | value (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
CMD | VAL | CHK |
byte | lower | lower |
---|---|---|
10 | 2 | 4 |
12 | 0 | 4 |
87 | 5 | 4 |
So for 10 and 12 (lower bits):
(cmd + val) & 0xf | binary | chksum & 0xf | binary |
---|---|---|---|
0 | 0000 | 6 | 0110 |
1 | 0001 | 7 | 0111 |
2 | 0010 | 4 | 0100 |
3 | 0011 | 5 | 0101 |
4 | 0100 | 2 | 0010 |
5 | 0101 | 3 | 0011 |
6 | 0110 | 0 | 0000 |
7 | 0111 | 1 | 0001 |
8 | 1000 | e | 1110 |
9 | 1001 | f | 1111 |
a | 1010 | c | 1100 |
b | 1011 | d | 1101 |
c | 1100 | a | 1010 |
d | 1101 | b | 1011 |
e | 1110 | 8 | 1000 |
f | 1111 | 9 | 1001 |
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) >> 4 | binary | chksum >> 4 | binary |
---|---|---|---|
3 | 0011 | f | 1111 |
4 | 0100 | 8 | 1000 |
5 | 0101 | 9 | 1001 |
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.
cmd | value (low) | chksum(low) |
---|---|---|
1000 0111 | 0101 | 0100 |
1000 0111 | 0110 | 0111 |
1000 0111 | 0111 | 0110 |
1000 0111 | 1000 | 1001 |
1000 0111 | 1001 | 1000 |
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.
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.
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