Solution 1 :
You can XOR pairs of the the samples to reduce the number of variables, since this eliminates any initial value or final xor (as if both were 0), and a search only needs to look for the polynomial and if it’s non-reflected (left shifting) or reflected (right shifting). For CRC16, that’s only 64k loops for left shift and 64k loops for right shift. It’s possible to get multiple polynomials that appear to work, so more samples are needed to confirm.
* AF: a55a030232200001 00 03 bd03
* TF: a55a030232200001 00 02 9c13
0000000000000000 00 01 2110
* HF: a55a030232200001 00 01 ff23
* LK: a55a030232200001 00 00 de33
01 2110
At first I assumed the last 2 bytes were swapped, so the 01 2110 would be 01 10 21, a common left shifting (not reflected) CRC, but I didn’t get it to work, due to a mistake on my part (I had the size wrong on one of the checks).
I then assumed that the last 2 bytes were big endian, 0x2110, and for a left shifting CRC on a single data byte of 0x01, the CRC is the same as the polynomial. However the least significant bit (bit 0) of the polynomial needs to be 1 and bit 0 of 0x2110 is 0, so I tried a right shifting CRC.
Doing a brute force search for a reflected polynomial that results in a CRC of 0x2110 for a single data byte = 0x01, there are 3 cases, but only one of them has bit 15 == 1, 0xEBB2, so that is the assumed polynomial.
Then doing a brute force search for the initial value and final xor = 0x0000 or 0xffff, to match all 4 examples, initial value = 0xa6ab, final xor = 0x0000.
Example bit level code:
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
#define POLY (0xebb2) /* polynomial */
#define INIT (0xa6ab) /* initial value */
#define FXOR (0x0000) /* final xor == xor out */
uint16_t crc16(uint8_t * bfr, size_t size)
{
uint16_t crc = INIT;
int i;
while(size--){
crc ^= *bfr++;
for(i = 0; i < 8; i++)
/* assumes two's complement */
crc = (crc>>1)^((0-(crc&1))&POLY);
}
return(crc^FXOR);
}
The resulting CRC will match the 4 examples, if the CRC is compared with swapped bytes:
crc = crc16(bfr, 10);
if((bfr[10] == crc>>8) && (bfr[11] == crc&0xff))
printf("matchn");
Although this works for these 4 cases, I couldn’t be sure that this is the actual CRC being used without more cases.
I then recommended swapping the last 2 bytes of the 12 and 16 byte messages, and using reveng. This would suggest that left shifting and POLY of 0x1021 was a possible polynmial, so I repeated my test and ended up getting the same result as reveng:
For the left shifting CRC, the code is different:
#define POLY (0x1021) /* polynomial */
#define INIT (0xa55a) /* initial value */
#define FXOR (0x0000) /* final xor == xor out */
uint16_t crc16(uint8_t * bfr, size_t size)
{
uint16_t crc = INIT;
int i;
while(size--){
crc ^= ((uint16_t)(*bfr++))<<8;
for(i = 0; i < 8; i++)
/* assumes two's complement */
crc = (crc<<1)^((0-(crc>>15))&POLY);
}
return(crc^FXOR);
}
This is the brute force search code I used. It could take up to 4 billion loops, but that only takes a few minutes so I didn’t bother optimizing it. It could be optimized using a table lookup based on the polynomial, or using assembly code with SSSE3 xmm registers (about 8 times as fast as table lookup).
#include <stdio.h>
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
#define POLY (0x1021) /* polynomial */
static uint16_t INIT; /* initial value */
static uint16_t FXOR; /* final xor == xor out */
/* left shifting crc using POLY == 0x1021 */
uint16_t crc16(uint8_t * bfr, size_t size)
{
uint16_t crc = INIT;
int i;
while(size--){
crc ^= (uint16_t)(*bfr++)<<8;
for(i = 0; i < 8; i++)
/* assumes two's complement */
crc = (crc<<1)^((0-(crc>>15))&POLY);
}
return(crc^FXOR);
}
/* assume last 2 bytes are swapped versus nomral CRC */
int main(int argc, char**argv)
{
uint16_t crc;
uint8_t bfr0[] = {0xa5,0x5a,0x03,0x02,0x32,0x20,0x00,0x01,0x00,0x00,0xde,0x33};
uint8_t bfr1[] = {0xa5,0x5a,0x03,0x02,0x32,0x20,0x00,0x01,0x00,0x01,0xff,0x23};
uint8_t bfr2[] = {0xa5,0x5a,0x03,0x02,0x32,0x20,0x00,0x01,0x00,0x02,0x9c,0x13};
uint8_t bfr3[] = {0xa5,0x5a,0x03,0x02,0x32,0x20,0x00,0x01,0x00,0x03,0xbd,0x03};
uint8_t bfr4[] = {0xa5,0x5a,0x03,0x00,0x0e,0x00,0x00,0x05,0x00,0x00,0x96,0xff,0x00,0x00,0xa9,0x6b};
uint8_t bfr5[] = {0xa5,0x5a,0x03,0x00,0x0e,0x00,0x00,0x05,0x00,0x00,0x6f,0x00,0x00,0x00,0xf0,0xc8};
uint8_t bfr6[] = {0xa5,0x5a,0x03,0x00,0x0e,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0xff,0x00,0x33,0x46};
uint8_t bfr7[] = {0xa5,0x5a,0x03,0x00,0x0e,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x01,0xff,0x0d,0x68};
FXOR = 0;
do{
INIT = 0;
do{
crc = crc16(bfr0, 10);
if(crc != 0x33de)
continue;
crc = crc16(bfr1, 10);
if(crc != 0x23ff)
continue;
crc = crc16(bfr2, 10);
if(crc != 0x139c)
continue;
crc = crc16(bfr3, 10);
if(crc != 0x03bd)
continue;
crc = crc16(bfr4, 14);
if(crc != 0x6ba9)
continue;
crc = crc16(bfr5, 14);
if(crc != 0xc8f0)
continue;
crc = crc16(bfr6, 14);
if(crc != 0x4633)
continue;
crc = crc16(bfr7, 14);
if(crc != 0x680d)
continue;
goto match0;
}while(++INIT != 0);
}while(++FXOR != 0);
match0:
printf("%04x %04xn", INIT, FXOR);
crc = crc16(bfr0, 10);
printf("%04xn", crc);
crc = crc16(bfr1, 10);
printf("%04xn", crc);
crc = crc16(bfr2, 10);
printf("%04xn", crc);
crc = crc16(bfr3, 10);
printf("%04xn", crc);
crc = crc16(bfr4, 14);
printf("%04xn", crc);
crc = crc16(bfr5, 14);
printf("%04xn", crc);
crc = crc16(bfr6, 14);
printf("%04xn", crc);
crc = crc16(bfr7, 14);
printf("%04xn", crc);
return(0);
}
Assuming left shifting POLY == 0x1021, the code determined that INIT == 0xa55a and FXOR == 0x0000 worked for the 8 cases I tested. Since XFOR == 0x0000, it only had to run 64k loops.
Problem :
I’m trying to reverse-engineer a BLE device (Gimbal). I’m already successful at replicating exact commands after sniffing btsnoop_hci.log
, and here’s a few of them:
* AF: a55a030232200001 00 03 bd03
* TF: a55a030232200001 00 02 9c13
* HF: a55a030232200001 00 01 ff23
* LK: a55a030232200001 00 00 de33
These commands change the operating mode, which I’m guessing is encoded in HEX from 01
to 03
. There is four of them, so it makes sense. BUT, there are four chars at the end, which IMHO is some kind of checksum, but I cant figure out what kind. Tried this online tool but no success there.
Why do I need to know how to calculate checksum? Because I want to control the motors too from an analog joystick and I cant just copy paste thousands of values and map them.
Also, here’re more values for the motors themselves:
(Speed 1) (Dir 1) (Speed 2) (Dir 2) (CRC???)
a55a03000e0000050000 6f 00 00 00 f0c8 (Goes Left)
a55a03000e0000050000 ff 00 00 00 6f0e (Goes Left Fast)
a55a03000e0000050000 96 ff 00 00 a96b (Goes Right)
a55a03000e0000050000 01 ff 00 00 1bfc (Goes Right Fast)
a55a03000e0000050000 00 00 01 ff 0d68 (Goes Up)
a55a03000e0000050000 00 00 ff 00 3346 (Goes Down)
Update:
I used reveng to brute-force the POLY & INIT:
Step 1:
Ran the command (Last two bytes in motor commands are reversed):
reveng -w 16 -s a55a03000e00000500006f000000c8f0 a55a03000e0000050000ff0000000e6f a55a03000e000005000096ff00006ba9 a55a03000e000005000001ff0000fc1b
Step 1 – Result:
width=16 poly=0x1021 init=0xa55a refin=false refout=false xorout=0x0000 check=0x0459 residue=0x0000 name=(none)
Step 2:
Ran the command (Last two bytes in mode commands are reversed):
reveng -w 16 -s a55a030232200001000303bd a55a0302322000010002139c a55a030232200001000123ff a55a030232200001000033de
Step 2 – Result:
width=16 poly=0x1021 init=0xa55a refin=false refout=false xorout=0x0000 check=0x0459 residue=0x0000 name=(none)
width=16 poly=0x4dd7 init=0xd565 refin=true refout=true xorout=0x0000 check=0x39bd residue=0x0000 name=(none)
So, it’s quite apparent that poly (0x1021) & init (0xa55a) matches within different types of messages.
However, if I use function:
#define POLY (0x1021)
#define INIT (0xa55a)
uint16_t crc16(uint8_t * bfr, size_t size)
{
uint16_t crc = INIT;
int i;
while(size--){
crc ^= *bfr++;
for(i = 0; i < 8; i++)
/* assumes two's complement */
crc = (crc>>1)^((0-(crc&1))&POLY);
}
return(crc);
}
The CRC values still dont match original ones. There’s something in my knowledge I’m missing.
Example:
uint8_t bfr[] = { 0xa5, 0x5a, 0x03, 0x02, 0x32, 0x20, 0x00, 0x01, 0x00, 0x02 };
uint16_t crc = crc16(bfr, 10);
Should result in 139c
(swapped from original), but instead I get: 1fb1
This is the actual value (a55a030232200001 00 02 9c13
) that the poly & init was generated from…
Update:
Re-Checking all the 14-byte (motor) values (last bytes are swapped) just to be sure:
a55a03000e00000500006f000000c8f0
a55a03000e0000050000ff0000000e6f
a55a03000e000005000096ff00006ba9
a55a03000e000005000001ff0000fc1b
a55a03000e0000050000000001ff680d
a55a03000e00000500000000ff004633
Commands called:
reveng -w 16 -s a55a03000e00000500006f000000c8f0 a55a03000e0000050000ff0000000e6f a55a03000e000005000096ff00006ba9 a55a03000e000005000001ff0000fc1b a55a03000e0000050000000001ff680d a55a03000e00000500000000ff004633
reveng -w 16 -s a55a030232200001000303bd a55a0302322000010002139c a55a03000e00000500006f000000c8f0 a55a03000e0000050000ff0000000e6f
Result:
width=16 poly=0x1021 init=0xa55a refin=false refout=false xorout=0x0000 check=0x0459 residue=0x0000 name=(none)
And accordingly (with two 10 byte and two 14 byte values):
width=16 poly=0x1021 init=0xa55a refin=false refout=false xorout=0x0000 check=0x0459 residue=0x0000 name=(none)
width=16 poly=0x1021 init=0x5545 refin=false refout=false xorout=0xf01f check=0x0459 residue=0xf01f name=(none)
Poly & Init must be right. But, when generating the CRC of the first one:
// a5 5a 03 00 0e 00 00 05 00 00 6f 00 00 00 f0c8 (swapped c8f0)
uint8_t bfr[] = { 0xa5, 0x5a, 0x03, 0x00, 0x0e, 0x00, 0x00, 0x05, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00 };
uint16_t crc = crc16(bfr, 14);
I get the output of Hex: 532
. I dont get it. How come, the code, I generated the Poly & Init from, returns the wrong hex?
Comments
Comment posted by Dainius Vaičiulis
Maybe there is something wrong with the function itself, or in the way I’m calling it. Can`t figure out what that is…
Comment posted by Dainius Vaičiulis
14 byte examples have been rechecked, also the 10 byte / 14 byte have been put to reveng at the same time. How come if I’m using the same code (I’ve put to reveng) in the crc function can’t return a correct crc…?
Comment posted by rcgldr
@DainiusVaičiulis – looks like the most common CRC is poly=0x1021 init=0xa55a refin=false refout=false xorout=0x0000. Somehow I missed that in my brute force search attempt.
Comment posted by Dainius Vaičiulis
Your update solved it! It was left-shifting CRC! Thank you!!!
Comment posted by rcgldr
@DainiusVaičiulis – I had messed up on my original seach for left shifting 0x1021, as I got the buffer size wrong. I fixed that while you were doing reveng. The results were the same. For the POLY search, being able to XOR two same size buffer eliminates INIT and XFOR (as if both are 0x0000), only requiring 64k loop to find potential values for POLY. I could have checked 0x1021 first, since that is common, but 64k loops goes pretty fast.