Loading a new file into IDA for the second time was easier. Again, I had to prepare it with
upx -d HIDApi.dll
and this time all functions were already nicely labeled for me. And again I spent a lot of time being stupid, starting here:
This is the ReadUSB function in HIDApi.dll, but it doesn't appear to be doing much more than calling ReadFile. Well, unless you notice the call to sub_10001320 in the bottom right box. And, yep, that function is important all right:
It seems to operate on 8 bytes, does a couple of xors, shifts, has magic constants and loops. About three hours of manual work leads to this equivalent Python function:
def decrypt(key, data):
cstate = [0x48, 0x74, 0x65, 0x6D, 0x70, 0x39, 0x39, 0x65]
shuffle = [2, 4, 0, 7, 1, 6, 5, 3]
phase1 = [0] * 8
for i, o in enumerate(shuffle):
phase1[o] = data[i]
phase2 = [0] * 8
for i in range(8):
phase2[i] = phase1[i] ^ key[i]
phase3 = [0] * 8
for i in range(8):
phase3[i] = ( (phase2[i] >> 3) | (phase2[ (i-1+8)%8 ] << 5) ) & 0xff
ctmp = [0] * 8
for i in range(8):
ctmp[i] = ( (cstate[i] >> 4) | (cstate[i]<<4) ) & 0xff
out = [0] * 8
for i in range(8):
out[i] = (0x100 + phase3[i] - ctmp[i]) & 0xff
return out
Yeah, it's a decryption function for a (bad) encryption system. It operates in several phases:
- In the first box, the input is taken from [eax+1] through [eax+8] and shuffled around. [eax+1] ends up at [eax+3], [eax+2] ends up at [eax+5], etc. Note that the indices in the Python version are zero-based.
- The second box iterates over a key stored at byte_1001A750 and XORs it into the data.
- The third box shifts the entire data by 3 bits to the right, using [esp+18h+arg_0] as a temporary store for wrapping around. [eax+8] becomes ([eax+8] >> 3) | ([eax+7] << 5); [eax+7] becomes ([eax+7] >> 3) | ([eax+6] <<5); etc.
- The fourth box does something with a buffer starting at [esp+18h+var_10], using esi as the loop index. At first I assumed that would be some kind of cipher state, that's why I called it cstate in the Python code. It was initialized with fixed values in the first box, and here, in the fourth box, it's nibble swapped and written into a buffer at [esp+18h+var_8], without changing the original state. I called that ctmp in the python version.
- The sixth box finally calculates the end result of the data in [eax+1] ff. by subtracting [esp+14h+var_8] ff. from it (using ecx as the loop index).
And now the satisfying part: Applying this function to some data I sniffed earlier yields:
41 00 00 41 0D 00 00 00 43 0C 9F EE 0D 00 00 00 42 12 87 DB 0D 00 00 00 6D 03 E2 52 0D 00 00 00 6E 4E C7 83 0D 00 00 00 71 03 39 AD 0D 00 00 00 50 03 38 8B 0D 00 00 00 57 22 C0 39 0D 00 00 00Yeah \o/
There's the 0D line terminator we were promised, the checksum matches, and among other there are 42 and 50 data types. 0x338 is decimal 824, which matches the CO₂ readout at the time the sample was taken. I'm reasonably sure that the temperature wasn't 12.87°C though.
Now all we have to do is get the device to send data without the Windows tool and figure out how to interpret the wealth of data we're receiving.
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.