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 初始处理

  1. lld_evt_rx_isrlld_evt_rx
  2. lld_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的处理函数有两个:

  1. llm_0x200_id_handler - 处理ADVERTISING CHANNEL PDU
  2. llc_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_hosthci_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_7F135F6l2cc_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 静态分析

跟踪外部数据流,关注:

  1. 长度字段校验
  2. 偏移字段校验
  3. 内存拷贝边界检查
  4. 资源分配、使用与释放配对

6.2 动态Fuzz

  1. 使用Unicorn模拟执行目标函数
  2. 使用AFL进行Fuzzing
  3. 使用蓝牙发包器进行黑盒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. 总结

  1. 理解BLE协议栈各层处理流程是漏洞挖掘基础
  2. 重点关注数据包解析过程中的边界检查
  3. 结合静态分析和动态Fuzz提高漏洞发现效率
  4. 协议规范是逆向分析的重要参考
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函数分析 3. 收包处理流程 3.1 初始处理 lld_evt_rx_isr → lld_evt_rx lld_evt_rx 触发#5号事件,回调函数为 lld_evt_deffered_elt_handler 3.2 lld_ evt_ deffered_ elt_ handler 消息参数结构体: 3.3 消息处理函数 msg_ id为0x200的处理函数有两个: llm_0x200_id_handler - 处理ADVERTISING CHANNEL PDU llc_0x200_id_handler - 处理DATA CHANNEL PDU 3.3.1 llm_ 0x200_ id_ handler 处理广播通道PDU,关键代码: 数据包描述符结构: rxheader字段: 高字节:数据包长度 ( BLE_RXADVLEN_MASK ) 低4位:数据包类型 ( BLE_RXTYPE_MASK ) 3.3.2 llc_ 0x200_ id_ handler 处理数据通道PDU,关键代码: llc_ data_ rcv函数 llc_ cntl_ rcv函数 处理LL Control PDU: 4. L2CAP协议处理 llc_data_rcv 处理后通过 hci_send_2_host 让 hci_acl_data_rx_handler 处理L2CAP报文。 4.1 hci_ acl_ data_ rx_ handler L2CAP报文格式: 4.2 l2cc_ pdu_ unpack_ func 5. ATT协议处理 0xa01消息处理函数为 sub_7F135F6 和 l2cc_pdu_recv_ind_handler ,其中 sub_7F135F6 用于处理ATT报文。 5.1 attc_ l2cc_ pdu_ recv_ handler_ func 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 结构体访问: 6.3.2 l2cc_ pdu_ unpack堆溢出 l2cc_pdu_unpack_func 函数中: 当v22=75时,可能拷贝16字节导致越界 当v22=97时,可能一字节溢出 6.3.3 ATT报文处理越界读 sub_7F0FC44 等ATT处理函数未充分检查长度: 7. 总结 理解BLE协议栈各层处理流程是漏洞挖掘基础 重点关注数据包解析过程中的边界检查 结合静态分析和动态Fuzz提高漏洞发现效率 协议规范是逆向分析的重要参考