DA14531芯片固件逆向系列(4)- L2CAP及ATT层收包再分析
字数 1000 2025-08-24 07:48:09
DA14531芯片固件逆向系列(4)- L2CAP及ATT层收包分析教学文档
1. 前言
本文档详细分析DA14531芯片固件中L2CAP层和ATT层的收包处理流程,重点解析ATT层报文解析过程,并介绍分析过程中发现的漏洞。通过本教程,读者将深入了解蓝牙协议栈中关键层的实现细节和安全问题。
2. L2CAP层收包处理回顾
2.1 主要处理函数
hci_acl_data_rx_handler: 处理L2CAP报文的主入口函数- 主要处理流程:
- 调用
l2cc_pdu_unpack检查L2CAP头部字段 - 检查Length和Channel ID合法性
- 将L2CAP报文拷贝到新分配的内存
- 根据数据包类型分发处理
- 调用
2.2 数据结构
struct l2cc_pdu_recv_ind {
uint8_t status; // 表示数据的大小
uint16_t rem_len; // 剩余长度
uint16_t offset; // 偏移量
struct l2cc_pdu pdu; // 指向具体的数据
};
struct l2cc_pdu {
uint16_t payld_len; // L2Cap Payload长度
uint16_t chan_id; // L2Cap Channel ID
union l2cc_pdu_data {
uint8_t code; // L2Cap packet code
// 各种L2CAP PDU结构体
struct l2cc_lecb_send_data_req send_lecb_data_req;
struct l2cc_reject reject;
struct l2cc_update_param_req update_req;
// ...其他PDU类型
} data;
};
3. ATT层报文解析
3.1 ATT PDU基本格式
ATT PDU由以下部分组成:
- 1字节的opcode
- 变长的数据部分
3.2 主要处理函数
int sub_7F135F6(int id, l2cc_pdu_recv_ind *l2cc_pdu_recv, unsigned int dest_task) {
ret = atts_l2cc_pdu_recv_handler(dest_task >> 8, l2cc_pdu_recv);
if (ret == 255) {
ret = attc_l2cc_pdu_recv_handler(dest_task >> 8, l2cc_pdu_recv);
}
// ...
}
处理流程:
- 首先尝试调用
atts_l2cc_pdu_recv_handler解析 - 如果返回255,则调用
attc_l2cc_pdu_recv_handler解析
3.3 ATT处理函数结构
struct att_handler_item {
unsigned __int8 code; // ATT PDU的opcode
unsigned __int8 d[3]; // 填充
dummy_func func; // 处理函数指针
};
处理函数查找逻辑:
for (i = 0; i < 0xE; i++) {
if (atts_handlers_0_0[i].code == code) {
func = atts_handlers_0_0[i].func;
}
}
if (func) {
result = (func)(dest_id, &l2cc_pdu_recv->pdu.data);
}
3.4 典型ATT PDU处理示例:Exchange MTU Response
PDU结构定义
struct l2cc_att_mtu_rsp {
uint8_t code; // Exchange MTU Response - 0x03
uint16_t mtu_size; // Server Rx MTU size
};
处理函数实现
int sub_7F0FC0C(int dest_id, l2cc_att_mtu_rsp *payload) {
mtu_size = gattm_get_max_mtu();
if (mtu_size >= payload->mtu_size) {
mtu_size = payload->mtu_size;
}
gattc_set_mtu(dest_id, mtu_size);
// ...其他处理
return 0;
}
4. 漏洞分析
4.1 l2cc_att_rd_by_type_rsp整数溢出导致堆溢出
漏洞代码
int __fastcall sub_7F0FDB4(int id, l2cc_att_rd_by_type_rsp *payload) {
// ...
if (gattc_get_mtu(id) - 2 > payload->each_len) {
msg = ke_msg_alloc(dword_7F102F0 + 4, v23, src_id, payload->each_len + 4);
sub_len = (payload->each_len - 2);
qmemcpy((msg + 6), &payload->data[2], sub_len); // 整数溢出
}
// ...
}
漏洞原理
- 当
each_len为1时,sub_len = each_len - 2会下溢变为0xFFFF - 导致
qmemcpy拷贝大量数据,造成堆溢出
4.2 ATT PDU处理函数多处越界读
示例1:sub_7F1015C
int __fastcall sub_7F1015C(int a1, l2cc_att_rd_rsp *payload) {
// ...
qmemcpy(&v7[5].next + 3, payload->value, 0x10u); // 直接拷贝0x10字节
// 没有检查payload实际长度,可能导致越界读
}
示例2:sub_7F11E24
void sub_7F11E24(int id, l2cc_att_rd_mult_req *payload) {
p_payload_length = (int)(payload - 1); // 获取&l2cc_pdu->payld_len
// 只检查p_payload_length是否为0
while (payload->nb_handles > v4 && *p_payload_length) {
data = (int)payload + 2 * v4;
// 可能访问越界
}
}
4.3 GATT写请求处理流程分析
关键函数调用链
sub_7F11F42 (处理0x52类型ATT PDU)
-> caller_gattc_write_req_ind
-> 发送GATTC_WRITE_REQ_IND(0xC15)消息
-> gattc_write_req_ind_handler (处理函数)
Write Command PDU结构
struct l2cc_att_wr_cmd {
uint8_t code; // Write Command - 0x52
uint16_t handle; // 要写入的属性句柄
uint16_t value_len; // 值长度
uint8_t value[]; // 要写入的值
};
5. 安全分析建议
- 长度校验:所有处理变长数据的函数都应严格校验输入长度
- 边界检查:数组/缓冲区访问前必须检查索引/偏移量是否有效
- 整数运算:特别注意减法、乘法等可能导致溢出的运算
- 消息处理:审计所有自定义的消息处理函数实现
- 协议合规:确保实现完全遵循BLE协议规范
6. 参考资源
- BLE空口包格式详解
- BLE Link layer协议解析
- Bluetooth Core Specification官方文档