So, looking at the transmitted data wasn't that successful in figuring out the protocol. My normal approach now would be to 'spoof' the other side of the communication channel and see how the device under test changes behaviour. In this case I would've written a short piece of Python to send out this initial SET_REPORT packet and then vary contents and look at how the device responds differently. But that's some boring work I've done several times before with other devices, so I'd wanted to try something new and this would be the perfect chance. I've never reverse-engineered anything by looking at a disassembly, so let's dive in!
The friendly people at Hex-Rays offer a freeware version of IDA Pro 5 for non-commercial use. It's not the latest and greatest, but should suffice. I installed it and loaded the ZG.exe file with full analysis turned on. This is the result:
That doesn't seem right. Scrolling through only shows a lot of data, but no functions. And the strings window has none of the user interface strings we were seeing when running the program. I've also tried some online disassembler services, and one of them gave a crucial hint: It identified the program as packed with UPX. IDA seems to have noticed too (there's this "UPX1" prefix to the left), but wasn't doing anything about it. Or maybe I didn't know to tell it to do something about it.
As the wikipedia article tells us, UPX is available for Linux and comes with a decompressor, so we can use
upx -d ZG.exeand try IDA again, this time around analysis takes much longer:
We know that the program somehow gets data from the USB and then decodes this into CO₂ readings to display. So the obvious point of attack would seem to be to find the place where it reads from USB and then follow to where the data is processed to get an insight into how the packets are being decoded.
Doing a search for "read" in the names window at first doesn't yield a lot of useful things. There's a "aTusbthread0", but I couldn't make sense of what it did. There also are a couple of "TComm::Read…", which seem to point to an entire include serial communications library. But then finally:
After being stupid for a couple hours I decided that apparently it would "LoadLibrary" the HIDApi.dll and then call "GetProcAddress" for each of "FindUSB", "WriteUSB", and "ReadUSB" and store the resulting value (likely a pointer to the respective functions) into memory. To better keep track I renamed the memory locations: dword_4FCADC became HID_FindUSB, dword_4FCAE0 became HID_WriteUSB, and dword_4FCAE4 became HID_ReadUSB. And, bonus, the cross-reference information shows that HID_ReadUSB is only used twice: Once here to write it, and a second time, in a call:
I'm skipping a couple of steps here: For a time I was distracted by byte_4FCAAE which seems to decide whether a read or write on the USB will happen. I also tried out the integrated debugger and was single stepping the program from the return of the HID_ReadUSB call on, and then found:
Verification in the debugger shows that, yes, the output data of HID_ReadUSB really contains the protocol as parsed here. So whatever has been done to the USB traffic must have happened earlier, in the HIDApi.dll. That will be what we load into IDA next.
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.