Reverse Engineering Govee Smart Lights
Getting started
First we need to enable bluetooth logging on the smartphone, for me it was in
Settings > Developer options > Enable Bluetooth HCI snoop log
Now we activate bluetooth and use the govee app. I also started a screen recording to make debugging easier later on!
Exporting the log
Now to find out where the log is generated we can use adb:
adb shell cat /etc/bluetooth/bt_stack.conf
and search for BtSnoopFileName=/data/log/bt/btsnoop_hci.log
which reveils the folder, but its not generated as a single file, there are multiple logs suffixed with timestamps, so we simply download the whole folder
adb pull /data/log/bt
Now we load the hci log into Wireshark and filter for the device to make things easier:
We can now see the data of the packets but to make debugging easier I exported the packets as json
because that way I can build a little "log debugger" tool that helps me to see which packets are sent at what time in the screen recording:
First contact
Before we start looking at the actual data transfer we need to find out some BLE (bluetooth low energy) ids. I'm using nodejs with @abandonware/noble
.
We simply start scanning for bluetooth devices and it shows up:
Now we connect to the device and ask for the available services and characteristics. The trick is to wait a at least 100 ms after connecting, otherwise we receive an empty array.
We receive a whole lot of services and characteristics:
service 1800
characteristic 2a00 (Device Name)
characteristic 2a01 (Appearance)
characteristic 2a04 (Peripheral Preferred Connection Parameters)
characteristic 2ac9 (null)
service 1801
characteristic 2a05 (Service Changed)
service 000102030405060708090a0b0c0d1910
characteristic 000102030405060708090a0b0c0d2b10
characteristic 000102030405060708090a0b0c0d2b11
service 02f0000000000000000000000000fe00
characteristic 02f0000000000000000000000000ff03
characteristic 02f0000000000000000000000000ff02
characteristic 02f0000000000000000000000000ff01
characteristic 02f0000000000000000000000000ff00
If we take a look at a write request we know which service and characteristics are the ones we need:
service 000102030405060708090a0b0c0d1910
write characteristic: 000102030405060708090a0b0c0d2b11
read characteristic: 000102030405060708090a0b0c0d2b10
Analyzing the traffic
The first thing that is quite noticeable are lots packages which are send at a constant interval:
HUAWEI Mate 20 lite -> Govee_H6054_1146
0200001b0017000400521400aa33000000000000000000000000000000000099
Govee_H6054_1146 -> HUAWEI Mate 20 lite
0200201b00170004001b1000aa33000000000000000000000000000000000099
This is a keepalive loop, but to make the log less confusing we hide them for now.
Next noticeable pattern is that package data always starts with OxAA
, 0x33
or 0xA3
Let's take a look at the packet that is send when we turn on the lights:
3333110000000000000000000000000000000001
33 -> the packet type "power"
33 -> the command "power"
1 -> turn on light nr. 1
1 -> turn on light nr. 2
Packets are always padded to a total length of 20 bytes, where the last byte is a checksum. By taking a few different packages and trying around a bit I found out that the checksum is calculated by taking all bytes and performing an XOR operation:
function hexify(x) {
let toReturn = x.toString(16)
return toReturn.length < 2 ? '0' + toReturn : toReturn
}
function prepareMsg(bytes) {
while (bytes.length < 19) bytes.push(0);
let checksum = Number(bytes[0]);
bytes.slice(1).forEach(byte => {
checksum ^= Number(byte);
});
return Buffer.from([...bytes, checksum].map(hexify).join(""), "hex");
}
Packet types
By looking at the video and the corresponding packets I could extract the following packet types:
It is available as npm package: