逆向工程BLE设备-校验和?

时间:2020-06-08 00:12:50

标签: android bluetooth-lowenergy checksum crc

我正在尝试对BLE设备(云台)进行反向工程。在嗅探btsnoop_hci.log之后,我已经成功地复制了确切的命令,下面是其中的一些:

* AF: a55a030232200001 00 03 bd03
* TF: a55a030232200001 00 02 9c13
* HF: a55a030232200001 00 01 ff23
* LK: a55a030232200001 00 00 de33

这些命令更改了操作模式,我猜它以十六进制格式从01编码为03。其中有四个,因此很有意义。但是,最后有四个字符,恕我直言,这是某种校验和,但我不知道是哪种类型。尝试过此online tool,但没有成功。

为什么我需要知道如何计算校验和?因为我也想通过模拟操纵杆控制电机,所以我不能只复制粘贴数千个值并映射它们。

此外,这是电动机本身的更多值:

                      (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)

更新: 我用reveng暴力破解了POLY&INIT:

步骤1: 跑命令(电机命令中的最后两个字节相反):

reveng -w 16 -s a55a03000e00000500006f000000c8f0 a55a03000e0000050000ff0000000e6f a55a03000e000005000096ff00006ba9 a55a03000e000005000001ff0000fc1b

第1步-结果:

width=16  poly=0x1021  init=0xa55a  refin=false  refout=false  xorout=0x0000  check=0x0459  residue=0x0000  name=(none)

步骤2: 跑命令(模式命令中的最后两个字节相反):

reveng -w 16 -s a55a030232200001000303bd a55a0302322000010002139c a55a030232200001000123ff a55a030232200001000033de

第2步-结果:

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)

因此,很明显,poly (0x1021)和init (0xa55a) 匹配在不同类型的消息中。

但是,如果我使用函数:

#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);
}

CRC值仍与原始值不匹配。据我所知,我有些失踪了。

示例:

uint8_t bfr[] = { 0xa5, 0x5a, 0x03, 0x02, 0x32, 0x20, 0x00, 0x01, 0x00, 0x02 };
uint16_t crc = crc16(bfr, 10);

应该导致139c(从原始交换),但我得到:1fb1 这是从...生成poly和init的实际值(a55a030232200001 00 02 9c13

更新: 重新检查所有14字节(电动机)的值(交换最后的字节),以确保:

a55a03000e00000500006f000000c8f0
a55a03000e0000050000ff0000000e6f
a55a03000e000005000096ff00006ba9
a55a03000e000005000001ff0000fc1b
a55a03000e0000050000000001ff680d
a55a03000e00000500000000ff004633

命令称为:

reveng -w 16 -s a55a03000e00000500006f000000c8f0 a55a03000e0000050000ff0000000e6f a55a03000e000005000096ff00006ba9 a55a03000e000005000001ff0000fc1b a55a03000e0000050000000001ff680d a55a03000e00000500000000ff004633

reveng -w 16 -s a55a030232200001000303bd a55a0302322000010002139c a55a03000e00000500006f000000c8f0 a55a03000e0000050000ff0000000e6f

结果:

width=16  poly=0x1021  init=0xa55a  refin=false  refout=false  xorout=0x0000  check=0x0459  residue=0x0000  name=(none)

相应地(具有两个10字节和两个14字节的值):

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必须正确。但是,当生成第一个的CRC时:

//                 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);

我得到十六进制的输出:532。我不明白。我从中生成Poly&Init的代码为什么返回错误的十六进制?

1 个答案:

答案 0 :(得分:2)

您可以对样本进行XOR,以减少变量的数量,因为这消除了任何初始值或最终xor(就像两个都是0),并且搜索仅需要查找多项式,并且它不是非整数即可。反射(左移)或反射(右移)。对于CRC16,左移仅64k循环,右移仅64k循环。可能会获得多个看起来有效的多项式,因此需要更多样本进行确认。

* 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

起初,我假设交换了最后2个字节,所以01 2110将是01 10 21,这是常见的左移CRC(未反映),但是由于我的一个错误,我没有使它起作用。部分(其中一张支票的尺寸不正确)。

然后,我假定最后2个字节是大端字节0x2110,对于单个数据字节0x01上的左移CRC,CRC与多项式相同。但是,多项式的最低有效位(位0)需要为1,0x2110的位0为0,因此我尝试了右移CRC。

对一个反映多项式进行蛮力搜索,对于单个数据字节= 0x01,CRC的值为0x2110,这有3种情况,但只有一种情况的位15 == 1,0xEBB2,因此假定多项式。

然后用蛮力搜索初始值和最终xor = 0x0000或0xffff,以匹配所有4个示例,即初始值= 0xa6ab,最终xor = 0x0000。

位级别代码示例:

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);
}

如果将CRC与交换的字节进行比较,则生成的CRC将与这4个示例匹配:

crc = crc16(bfr, 10);
if((bfr[10] == crc>>8) && (bfr[11] == crc&0xff))
    printf("match\n");

尽管这对这4种情况都有效,但我不能确定这是否是在没​​有更多情况下使用的实际CRC。


然后,我建议交换12和16字节消息的最后2个字节,并使用reveng。这表明左移和0x1021的POLY可能是多项式,因此我重复测试并最终得到与reveng相同的结果:

对于左移CRC,代码是不同的:

#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);
}

这是我使用的蛮力搜索代码。它最多可能需要40亿次循环,但这只需要几分钟,因此我不必费心对其进行优化。可以使用基于多项式的表查找或使用带有SSSE3 xmm寄存器的汇编代码(约为表查找的8倍)来对它进行优化。

#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 %04x\n", INIT, FXOR);
    crc = crc16(bfr0, 10);
    printf("%04x\n", crc);
    crc = crc16(bfr1, 10);
    printf("%04x\n", crc);
    crc = crc16(bfr2, 10);
    printf("%04x\n", crc);
    crc = crc16(bfr3, 10);
    printf("%04x\n", crc);
    crc = crc16(bfr4, 14);
    printf("%04x\n", crc);
    crc = crc16(bfr5, 14);
    printf("%04x\n", crc);
    crc = crc16(bfr6, 14);
    printf("%04x\n", crc);
    crc = crc16(bfr7, 14);
    printf("%04x\n", crc);
    return(0);
}

假设左移POLY == 0x1021,代码确定INIT == 0xa55a和FXOR == 0x0000适用于我测试的8种情况。由于XFOR == 0x0000,它只需要运行64k循环。