Crackle学习
crackle学习
GitHub - mikeryan/crackle: Crack and decrypt BLE encryption
crackle 是研究蓝牙安全的 mikeryan 大佬写的一款能够解密 BLE 传统配对(legacy pairing)后的流量包的工具,本文将通过 crackle 源码的阅读,学习 SMP 协议、学习如何解密传统配对的流量
通过 TK 计算 STK
这里用到了几个函数,copy_reverse 是用来逆序的
void copy_reverse(const u_char *bytes, uint8_t *dest, size_t len) {
unsigned i;
for (i = 0; i < len; ++i)
dest[i] = bytes[len - 1 - i];
}
计算 iv
void calc_iv(connection_state_t *state) {
assert(state != NULL);
copy_reverse(state->ivm, state->iv + 0, 4);
copy_reverse(state->ivs, state->iv + 4, 4);
}
iv 是在数据包中直接能找到的,在 LL_ENC_REQ 报文中是 ivm,意思是 master 的 iv
在 LL_ENC_RSP 报文中是 ivs,意思是 slave 的 iv
计算 iv 这里只需要把他们反转一下即可,因为在内存里放着的是逆序的,看一下 GDB 调试的
GDB 调试的方法,gdb crackle
然后 b 源码第几行
,比如b 608
下个断点,set args -i Legacy_pairing.pcapng
,然后点击运行即可
gdb-peda$ p &state->ivm
$1 = (uint8_t (*)[4]) 0x55555555f7c0
gdb-peda$ x/gx 0x55555555f7c0
0x55555555f7c0: 0x816400f1fda34aa8 //fda34aa8
gdb-peda$ p &state->ivs
$2 = (uint8_t (*)[4]) 0x55555555f7cc
gdb-peda$ x/gx 0x55555555f7cc
0x55555555f7cc: 0x00000000483dea50 //483dea50
gdb-peda$ p &state->iv
$3 = (uint8_t (*)[8]) 0x55555555f800
gdb-peda$ x/gx 0x55555555f800
0x55555555f800: 0x0000000000000000 //此时iv是空的
等执行完两个反转就得到了真实的 iv 值
gdb-peda$ x/gx 0x55555555f800
0x55555555f800: 0x50ea3d48a84aa3fd //50ea3d48 ivs a84aa3fd ivm
计算STK
STK 的生成方式在蓝牙的规范中用的是一个叫做 s1 的函数,实际上是 AES-128-CCM 加密
void calc_stk(connection_state_t *state, uint32_t numeric_key) {
uint8_t rand[16];
assert(state != NULL);
// calculate TK
numeric_key = htobe32(numeric_key);
memcpy(&state->tk[12], &numeric_key, 4);
// STK = s1(TK, Srand, Mrand) [pg 1971]
// concatenate the lower 8 octets of Srand and MRand
memcpy(rand + 0, state->srand + 8, 8);
memcpy(rand + 8, state->mrand + 8, 8);
aes_block(state->tk, rand, state->stk);
}
s1 这个函数需要 TK 的值和两个 random 的值,TK 需要转成小端序放在内存里,用 htobe32(numeric_key) 即可
gdb-peda$ p &numeric_key
$1 = (uint32_t *) 0x7fffffffe174
gdb-peda$ x/2gx 0x7fffffffe174
0x7fffffffe174: 0x5556072000031b16 0x5555524000005555 #1b160300
....执行后....
gdb-peda$ x/2gx 0x7fffffffe174
0x7fffffffe174: 0x55560720161b0300 0x5555524000005555 #00031b16
然后拷贝到 state->tk[12]
gdb-peda$ p &state->tk[12]
$2 = (uint8_t *) 0x5555555607dc ""
gdb-peda$ x/gx 0x5555555607dc
0x5555555607dc: 0x0000000000000000
....执行后....
gdb-peda$ x/gx 0x5555555607dc
0x5555555607dc: 0x00000000161b0300
两个 random 分别取高 8 字节拼接起来
看一下标准里咋说:For example if the 128-bit value r1 is 0x000F0E0D0C0B0A091122334455667788 then r1’is 0x1122334455667788. If the 128-bit value r2 is 0x010203040506070899AABBCCDDEEFF00 then r2’is 0x99AABBCCDDEEFF00。(r1 为 Srand,r2 为 Mrand,最后 r' = r1' || r2')
这明显是拿低位拼起来的啊,为啥代码这里是拿高位拼起来?还是说 wireshark 显示的是小端序?我理解不了了
gdb-peda$ p &state->mrand
$5 = (uint8_t (*)[16]) 0x55555555f76e
gdb-peda$ p &state->srand
$6 = (uint8_t (*)[16]) 0x55555555f77e
gdb-peda$ x/4gx 0x55555555f76e
0x55555555f76e: 0xec977c9fe63591dc 0x1a10975ba476bc02 //这是mrand
0x55555555f77e: 0xa20ea327c9897c01 0x575852f181e9db17 //这是srand
gdb-peda$ p &rand
$7 = (uint8_t (*)[16]) 0x7fffffffe160
gdb-peda$ x/2gx 0x7fffffffe160
0x7fffffffe160: 0x575852f181e9db17 0x1a10975ba476bc02 //这是拼起来后的rand
0x7fffffffe0e0: 0x9bf4b0e8f0e11f9a 0x62a06d79ae16425b //stk
0x7fffffffe0f0: 0x090a0b0c0d0e0f00 0x8877665544332211 //srand
0x7fffffffe100: 0x0807060504030201 0x00ffeeddccbbaa99 //mrand