Skip to content

Snappy1

  • Home
  • Android
  • What
  • How
  • Is
  • Can
  • Does
  • Do
  • Why
  • Are
  • Who
  • Toggle search form

[FIXED] android – Reverse Engineering BLE device – Checksum?

Posted on November 11, 2022 By

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:

READ  [FIXED] android - what is device verity state
Powered by Inline Related Posts

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.

READ  [FIXED] android - Retrofit2 automatically removes the header with the content type
Powered by Inline Related Posts

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…

READ  [FIXED] android - Implementing long polling with retrofit and kotlin channels
Powered by Inline Related Posts

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.

Android Tags:android, bluetooth-lowenergy, checksum, crc

Post navigation

Previous Post: [FIXED] java – How can I solve this Android Studio Firebase problem?
Next Post: [FIXED] Android Java Publish result cast issue at MyCustomAdapter.java

Related Posts

[FIXED] java – Unable to retrieve RSSI values from BLE nordic thingy:52 Android
[FIXED] java – Android Espresso – How to check if string was abbreviated Android
[FIXED] android – daemon not running; starting now at tcp:5037 Android
[FIXED] firebase – E/FirebaseInstanceId: binding to the service failed. Flutter Android Android
[FIXED] android studio – How to solve sqlite_error Android
[FIXED] Error after converting Java to Kotlin in Android Android

Archives

  • April 2023
  • March 2023
  • February 2023
  • January 2023
  • December 2022
  • November 2022
  • October 2022
  • September 2022

Categories

  • ¿Cómo
  • ¿Cuál
  • ¿Cuándo
  • ¿Cuántas
  • ¿Cuánto
  • ¿Qué
  • Android
  • Are
  • At
  • C'est
  • Can
  • Comment
  • Did
  • Do
  • Does
  • Est-ce
  • Est-il
  • For
  • Has
  • Hat
  • How
  • In
  • Is
  • Ist
  • Kann
  • Où
  • Pourquoi
  • Quand
  • Quel
  • Quelle
  • Quelles
  • Quels
  • Qui
  • Should
  • Sind
  • Sollte
  • Uncategorized
  • Wann
  • Warum
  • Was
  • Welche
  • Welchen
  • Welcher
  • Welches
  • Were
  • What
  • What's
  • When
  • Where
  • Which
  • Who
  • Who's
  • Why
  • Wie
  • Will
  • Wird
  • Wo
  • Woher
  • you can create a selvedge edge: You can make the edges of garter stitch more smooth by slipping the first stitch of every row.2022-02-04
  • you really only need to know two patterns: garter stitch

Recent Posts

  • Can Vicks humidifier be used without filter?
  • What color is Spanish green?
  • How old is Jamie in The War That Saved My Life?
  • When should I use scalp massager for hair growth?
  • Can I put polyurethane over liming wax?

Recent Comments

No comments to show.

Copyright © 2023 Snappy1.

Powered by PressBook Grid Dark theme