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



我正在尝试对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.有四个,所以这是有道理的。但是,最后有四个字符,恕我直言,这是某种校验和,但我无法弄清楚是哪种。尝试了这个在线工具,但没有成功。

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

此外,以下是电机本身的更多值:

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

更新: 我用复仇来暴力破解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的代码返回错误的十六进制?

您可以对样本进行异或运算以减少变量的数量,因为这消除了任何初始值或最终异或(好像两者都为 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("matchn");

虽然这适用于这 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 %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);
}

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

最新更新