DA14531芯片固件逆向系列(3)- BLE收包流程分析及漏洞挖掘思路分享
字数 1628 2025-08-24 07:48:09
DA14531芯片固件逆向系列(3) - BLE收包流程分析及漏洞挖掘思路
1. 芯片基础信息
DA14531芯片使用Arm Cortex-M0+ CPU,其异常向量表位于0地址处。根据芯片手册:
- 地址空间[0, 0x4000000]是Remapped address space,基于SYS_CTRL_REG[REMAP_ADR0]的值重映射
- 当REMAP_ADR0=0x2时,SysRAM1(16kB: 0x07FC0000-0x07FC3FFF)被映射到0地址
- 开发者使用SDK编译的软件烧写在这个区域
2. 中断处理流程
2.1 关键中断
- IRQ #1: BLE_GEN_IRQn中断,与蓝牙收发包相关
- LE_RX_IRQn中断在每个数据包接收完毕后触发
- 处理函数:rwble_isr(位于异常向量表中)
2.2 rwble_isr函数分析
__BLEIRQ void rwble_isr(void){
while(1){
uint32_t irq_stat = ble_intstat_get();
if(irq_stat == 0) break;
// Rx中断处理
if(irq_stat & BLE_RXINTSTAT_BIT){
DBG_SWDIAG(BLE_ISR, RXINT, 1);
ble_intack_clear(BLE_RXINTSTAT_BIT);
dlg_rx_isr(); // 实际调用lld_evt_rx_isr
DBG_SWDIAG(BLE_ISR, RXINT, 0);
}
}
}
3. 收包处理流程
3.1 初始处理
lld_evt_rx_isr→lld_evt_rxlld_evt_rx触发#5号事件,回调函数为lld_evt_deffered_elt_handler
3.2 lld_evt_deffered_elt_handler
int lld_evt_deffered_elt_handler(int a1, int a2, int a3, int a4){
ke_event_clear(5u);
while(1){
// 取出packet结构
pkg_info = get_recv_pkg_info(&v11, &rx_cnt);
// 分配消息
msg_data = ke_msg_alloc(0x200, dest_id, 2u, 6u);
// 填充数据包信息
lld_data_rx_check((pkg_info_1 + 36), msg_data, rx_cnt);
// 发送消息处理
ke_msg_send(msg_data);
}
return pkg_info;
}
消息参数结构体:
struct lld_data_ind{
uint8_t rx_hdl; // 第一个RX buffer的句柄
uint8_t rx_cnt; // 接收的buffer数量
uint8_t tx_cnt; // 发送的数据buffer数量
uint8_t tx_cnt_cntl; // 发送的数据控制buffer数量
uint16_t evt_cnt; // 事件计数器
};
3.3 消息处理函数
msg_id为0x200的处理函数有两个:
llm_0x200_id_handler- 处理ADVERTISING CHANNEL PDUllc_0x200_id_handler- 处理DATA CHANNEL PDU
3.3.1 llm_0x200_id_handler
处理广播通道PDU,关键代码:
int llm_0x200_id_handler(int msg_id_1, lld_data_ind *param, int dest_id, int src_id){
rx_cnt = param->rx_cnt;
rx_hdl = param->rx_hdl;
while(1){
// 根据rx_hdl找到数据包描述符
rx_desc = (v26[4] + 10 * rx_hdl);
// 获取实际数据包地址
pkg_data = co_buf_rx_buffer_get(rx_desc);
// 获取设备地址
for(idx=0; idx<3; idx++){
device_addr[idx] = *(*ble_base_2[0] + idx + 0x115);
}
// 搜索匹配的数据包
while(rx_cnt && memcmp(device_addr, pkg_data_1, 6)){
co_buf_rx_free(rx_hdl);
rx_hdl = (rx_hdl + 1) & 7;
rx_desc = (v26[4] + 10 * rx_hdl);
rx_cnt = (rx_cnt - 1);
pkg_data_1 = co_buf_rx_buffer_get(rx_desc);
}
// 根据数据包类型处理
switch(rx_desc->rxheader & 0xF){ // llm_util_rxtype_getf
case 0: case 1: case 2: case 4: case 6:
llm_le_adv_report_ind(rx_desc); break;
case 3:
llm_le_scan_report_ind(rx_desc); break;
case 5:
llm_con_req_ind(rx_desc); break;
default: break;
}
}
}
数据包描述符结构:
struct co_buf_rx_desc{
uint16_t rxptr; // rx指针
uint16_t rxstatus; // 状态
uint16_t rxheader; // 头(包含长度和类型)
uint16_t rxchass; //
uint16_t rxdataptr; // 数据指针
};
rxheader字段:
- 高字节:数据包长度 (
BLE_RXADVLEN_MASK) - 低4位:数据包类型 (
BLE_RXTYPE_MASK)
3.3.2 llc_0x200_id_handler
处理数据通道PDU,关键代码:
int llc_0x200_id_handler(int msg_id, lld_data_ind *data, unsigned int dest_id, int src_id){
rx_cnt = data->rx_cnt;
first_rx_handle = data->rx_hdl;
task_index = dest_id >> 8;
llid = rx_desc->rxheader & 3; // LLID字段
if(llid == LLID_CONTINUE || llid == LLID_START){ // LL Data PDU
llc_data_rcv(task_index, first_rx_handle);
}else if(llid == LLID_CNTL){ // LL Control PDU
llc_cntl_rcv(task_index, first_rx_handle);
}
}
llc_data_rcv函数
void llc_data_rcv(uint16_t conhdl, uint8_t hdl){
idx = conhdl;
rxdesc = (off_7F047E4[0][4] + 10 * hdl);
// 分配消息
msg_data = ke_msg_alloc(0x100u, (conhdl << 8) + 1, (conhdl << 8) + 1, 8u);
// 填充消息数据
msg_data->task_index = idx;
pkg_length = HIBYTE(rxdesc->rxheader);
msg_data->pkg_length = pkg_length;
if((llc_env_tag_tbl[idx]->enc_state & 2) != 0){
msg_data->pkg_length = pkg_length - 4;
}
msg_data->llid = rxdesc->rxheader & 3;
msg_data->hdl = hdl;
ke_msg_send(msg_data);
}
llc_cntl_rcv函数
处理LL Control PDU:
int llc_cntl_rcv(int idx, int rx_hdl){
rxdesc = (off_7F0478C[4] + 10 * rx_hdl);
pkg_length = HIBYTE(rxdesc->rxheader);
// 获取opcode
opcode = *co_buf_rx_buffer_get(rxdesc);
// 根据opcode调用处理函数
tbl_index = 8 * opcode;
v9 = llc_handler_tbl;
msg_data = ke_msg_alloc(*(v7 + 4), dest_id, dest_id, *(v7 + 7));
pkg = co_buf_rx_buffer_get(rxdesc);
(*(v9 + tbl_index))(pkg, pkg_length, msg_data);
ke_msg_send(msg_data);
}
4. L2CAP协议处理
llc_data_rcv处理后通过hci_send_2_host让hci_acl_data_rx_handler处理L2CAP报文。
4.1 hci_acl_data_rx_handler
int hci_acl_data_rx_handler(int a1, llc_0x100_struct *msg_data, unsigned int dest_id){
// 获取数据包地址
data = co_buf_rx_buffer_get((*(off_7F14008 + 16) + 10 * msg_data->hdl));
// 解析L2CAP长度
data_length = (data[1] << 8) | *data;
// 分配内存
l2cc_pdu_recv = ke_msg_alloc(0xa01, ::dest_id, dest_id, data_length + 0x4C);
// 设置长度信息
l2cc_pdu_recv->rem_len = data_length + 4;
l2cc_pdu_data = &l2cc_pdu_recv_1->pdu;
p_rem_len = &l2cc_pdu_recv_1->rem_len;
p_offset = &l2cc_pdu_recv_1->offset;
pkg_length_from_hdr = msg_data->pkg_length;
offset = l2cc_pdu_recv_1->offset;
v18 = *p_rem_len;
// 拷贝L2CAP数据
if(offset + pkg_length_from_hdr > v18){
qmemcpy(&l2cc_pdu_data[offset], data_1, v18 - offset);
*p_offset = *p_rem_len;
task_l2cc_env->p_recv_ind->status = 52;
}else{
qmemcpy(&l2cc_pdu_data[offset], data_1, pkg_length_from_hdr);
*p_offset += pkg_length_from_hdr;
}
// 解析PDU
task_l2cc_env->p_recv_ind->status = l2cc_pdu_unpack(
&task_l2cc_env->p_recv_ind->pdu,
&task_l2cc_env->p_recv_ind->offset,
&task_l2cc_env->p_recv_ind->rem_len,
p_buffer, datac, BYTE1(dest_id), 2u);
}
L2CAP报文格式:
+-------------------+-------------------+-------------------+-------------------+
| Length | CID | 信息载荷(Information Payload) |
+-------------------+-------------------+-------------------+-------------------+
4.2 l2cc_pdu_unpack_func
uint8_t l2cc_pdu_unpack_func(struct l2cc_pdu *p_pdu, uint16_t *p_offset,
uint16_t *p_rem_len, const uint8_t *p_buffer,
uint16_t pkt_length, uint8_t conidx, uint8_t llid){
opcode = 0;
v31 = 0;
// 设置payload长度
p_pdu->payld_len = (p_buffer[1] << 8) | *p_buffer;
cid = (p_buffer[3] << 8) | p_buffer[2];
use_size = 4;
infomation_payload = p_buffer + 4;
switch(cid){
case 4:
if(opcode == 82) opcode = 20;
else if(opcode == 210) opcode = 21;
pkt_format = off_7F19B5C + 160; // l2cc_attribute_pkt_format_0
max_opcode = 31;
break;
// ...其他case...
}
v30 = *(pkt_format + opcode);
dst = &p_pdu->data.reject.pkt_id;
for(i = v30; ; i = ++v30){
v22 = *i;
if(!v22 || v8 || *p_rem_len < use_size) break;
if(v22 == 75){
qmemcpy(dst, infomation_payload, 0x10u);
use_size = (use_size + 16);
infomation_payload += 16;
dst += 16;
continue;
}
if(v22 != 97) goto LABEL_64;
v28 = (pkt_length - use_size);
dst = (2 * ((dst + 1) >> 1));
v31 = 1;
qmemcpy(dst, infomation_payload, v28);
use_size = (use_size + v28);
v21 = &dst[v28];
goto LABEL_67;
}
}
5. ATT协议处理
0xa01消息处理函数为sub_7F135F6和l2cc_pdu_recv_ind_handler,其中sub_7F135F6用于处理ATT报文。
5.1 attc_l2cc_pdu_recv_handler_func
int attc_l2cc_pdu_recv_handler_func(int code, l2cc_pdu_recv_ind *l2cc_pdu_recv){
v5 = l2cc_pdu_recv->pdu.data.code;
// 根据opcode查找处理函数
for(i = 0; i < 0xE; i = (i + 1)){
if(attc_handlers_0_0[i].code == v5){
func = attc_handlers_0_0[i].func;
}
}
if(func){
result = func(code, &l2cc_pdu_recv->pdu.data);
}
return result;
}
6. 漏洞挖掘思路
6.1 静态分析
跟踪外部数据流,关注:
- 长度字段校验
- 偏移字段校验
- 内存拷贝边界检查
- 资源分配、使用与释放配对
6.2 动态Fuzz
- 使用Unicorn模拟执行目标函数
- 使用AFL进行Fuzzing
- 使用蓝牙发包器进行黑盒Fuzzing
6.3 已知漏洞示例
6.3.1 llm_con_req_ind越界读
llm_con_req_ind函数未检查数据包长度,直接当作llm_pdu_con_req_rx结构体访问:
ar = data->latency;
if(ar > 500 || (v3 * 5 * (ar + 1) + 1) >> 1 > 10 * var ||
!data->chm.map[0] && !data->chm.map[1] &&
!data->chm.map[2] && !data->chm.map[3] &&
!(data->chm.map[4] << 27)){
return;
}
6.3.2 l2cc_pdu_unpack堆溢出
l2cc_pdu_unpack_func函数中:
- 当v22=75时,可能拷贝16字节导致越界
- 当v22=97时,可能一字节溢出
6.3.3 ATT报文处理越界读
sub_7F0FC44等ATT处理函数未充分检查长度:
if(*(data + 1) == 1) v4 = 2;
else v4 = 16;
idx = 0;
item_count = v4;
while(*(data + 2) > idx){
msg = ke_msg_alloc(dword_7F0FEBC - 21, v7, (code << 8) + 8, item_count + 4);
*msg = (*(data + idx + 5) << 8) | *(data + idx + 4);
*(msg + 2) = item_count;
idx_1 = (idx + 2);
qmemcpy((msg + 3), (data + idx_1 + 4), item_count);
idx = (idx_1 + item_count);
ke_msg_send(msg);
}
7. 总结
- 理解BLE协议栈各层处理流程是漏洞挖掘基础
- 重点关注数据包解析过程中的边界检查
- 结合静态分析和动态Fuzz提高漏洞发现效率
- 协议规范是逆向分析的重要参考