DA14531芯片固件逆向系列(3)- BLE收包流程分析及漏洞挖掘思路分享


文章首發於

https://xz.aliyun.com/t/9194

前言

本文介紹定位和分析DA14531收包流程的方法,並提供簡單的BLE協議漏洞挖掘思路。

定位收包函數

通過查看DA14531的芯片手冊,我們知道這個芯片使用的CPU是 Arm Cortex-M0+,我們知道M0的異常向量表位於0地址處,所以我們去看看DA14531手冊的memory map 一節中是怎么定義0這個地址的。

1611668769467.png

可以看到地址空間 [0, 0x4000000] 的描述如下

Remapped address space based on SYS_CTRL_REG[REMAP_ADR0].

看意思感覺是0地址這塊區域會根據SYS_CTRL_REG[REMAP_ADR0]的值被重新映射。

在手冊中搜索SYS_CTRL_REG,可以找到REMAP_ADR0的定義

1611668912556.png

可以看到REMAP_ADR0的取值不同會把不同的區域重映射到0地址處,這里以值為 0x2 為例,此時會把RAM (SysRAM1)這塊區域映射到0地址,查看memory map可以知道這款區域的詳細信息

SysRAM1 (16 kB): 0x07FC0000 to 0x07FC3FFF

所以0x07FC0000開頭就存放着異常向量表, 實際上開發者使用DA14531芯片的SDK編譯出來的軟件就是燒寫在這個區域,自己編譯一個軟件,然后加載到IDA既可拿到異常向量表的信息

1611669271841.png

找到異常向量表后,可以去翻翻手冊,看看DA14531使用的中斷的類型和描述

1611669383450.png

可以看的 IRQ #1BLE_GEN_IRQn中斷感覺和藍牙收發包相關,其中LE_RX_IRQn中斷在每個數據包接收完畢后觸發。

然后我們去異常向量表中找到 IRQ #1 的處理函數,由於M0芯片的內置異常為16個,所以 IRQ #1 的處理函數為rwble_isr

收包流程分析

rwble_isr函數入口會對40000xxx的地址進行訪問

void rwble_isr()
{
  if ( unk_40000200 << 31 )
  {
    unk_40000200 |= 2u;
  }
  if ( (unk_40000200 & 0x400000) != 0 )
  {
    unk_40000200 |= 0x400000u;

可以在SDK中搜索這些地址,拿到地址的信息,比如0x40000200地址處是藍牙的控制寄存器

#define BLE_CNTL2_REG                        (0x40000200) /* BLE Control Register 2 */

逆向了一小會后發現rwble_isr函數的源碼在SDK中,可以直接查看,和收包相關的代碼如下

__BLEIRQ void rwble_isr(void)
{
    // Loop until no more interrupts have to be handled
    while (1)
    {
        // Check BLE interrupt status and call the appropriate handlers
        uint32_t irq_stat = ble_intstat_get();
        if (irq_stat == 0)
            break;
        // Rx interrupt
        if (irq_stat & BLE_RXINTSTAT_BIT)
        {
            DBG_SWDIAG(BLE_ISR, RXINT, 1);

            ble_intack_clear(BLE_RXINTSTAT_BIT);

            dlg_rx_isr();

            DBG_SWDIAG(BLE_ISR, RXINT, 0);
        }

函數首先調用ble_intstat_get 讀取中斷狀態,然后根據irq_stat中的二進制位判斷中斷的類型(感覺和Linux的共享IRQ機制類似),如果有BLE_RXINTSTAT_BIT表示是收包中斷,會調用 dlg_rx_isr 處理收包事件。

dlg_rx_isr函數實際調用的是lld_evt_rx_isrlld_evt_rx_isr 會進入 lld_evt_rx 函數

void __fastcall lld_evt_rx(lld_evt_tag *elt)
{
  v1 = elt + 1;
  v2 = *(82 * LOWORD(elt[1].anchor_point.basetime_cnt) + *off_7F09820 + 0x114) >> 12;// ble_cntl_get
  add_evt_deferred_tag(elt, 0, v2);
  HIBYTE(v1->tx_prog.maxcnt) += v2;
  ke_event_set(5u);
}

主要就是觸發 #5 號事件去完成具體的收包過程,該事件的回調函數在lld_evt_init中注冊

int __fastcall lld_evt_init(int a1, int a2, int a3, int a4)
{
	...................
	...................
	return ke_event_callback_set(5u, lld_evt_deffered_elt_handler);
}

因此會進入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);

        // 0x200 的回調函數 0x7F0581B, 0x7F0CB37
        msg_data = ke_msg_alloc(0x200, dest_id, 2u, 6u);
        
        // 填充msg_data里面的數據包相關信息
        lld_data_rx_check((pkg_info_1 + 36), msg_data, rx_cnt);
        
        // 消息回調函數處理數據
        ke_msg_send(msg_data);                    
    }

    return pkg_info;
}

首先清除#5號事件,然后從全局鏈表中取出一個包的信息,然后發送一個 msg_id0x200 消息,消息的param部分由 lld_data_rx_check 填充,param的結構體定義如下

struct lld_data_ind
{
    /// Handle of the first RX buffer
    uint8_t rx_hdl;
    /// Number of received buffers
    uint8_t rx_cnt;
    /// Number of transmitted data buffers
    uint8_t tx_cnt;
    /// Number of transmitted data control buffers
    uint8_t tx_cnt_cntl;
    /// Event counter
    uint16_t evt_cnt;
};

可以看到 rx_hdlrx_cnt 用於表示收到的數據包的信息,msg_id0x200的處理函數有兩個

0x7F0581B  llc_0x200_id_handler
0x7F0CB37  llm_0x200_id_handler

llm_0x200_id_handler

該函數用於處理 ADVERTISING CHANNEL PDU, 經過簡化的關鍵代碼如下

int __fastcall 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);
    idx = 0;
    pkg_data_1 = pkg_data;

    // 從全局變量里面取 6 個字節的保存到 device_addr
    do
    {
        device_addr[idx] = *(*ble_base_2[0] + idx + 0x115);
        idx = (idx + 1);
    }
    while ( idx < 3 );

    // 搜索和 device_addr 匹配的數據包
    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;
    }
  }

代碼邏輯

  1. 首先從param里面取出rx_cnt和rx_hdl,然后根據rx_hdl得到對應數據包描述符rx_desc
  2. 然后會去全局變量里面拷貝6個字節保存到device_addr,然后會從rx_hdl開始遍歷rx_cnt個數據包,直到找到包頭6個字節和device_addr相同的數據包為止。
  3. 然后通過rxheader中的數據包類型來進行調用相應的函數進行進一步的處理

rx_desc的結構定義如下

struct co_buf_rx_desc
{
    /// rx pointer
    uint16_t rxptr;
    /// status
    uint16_t rxstatus;
    /// rx header
    uint16_t rxheader;
    /// rx chass
    uint16_t rxchass;
    /// rx data pointer
    uint16_t rxdataptr;
};

其中rxdataptr指向存放藍牙數據的位置,需要通過co_buf_rx_buffer_get將其轉換為實際的內存地址

uint8_t *__fastcall co_buf_rx_buffer_get(struct co_buf_rx_desc *rx_desc)
{
  return (rx_desc->rxdataptr + *ble_base_1);
}

rxheader中包含了該數據包的類型、長度等信息,在SDK中搜索rxheader的引用,可以找到如下函數來推測rxheader的含義

#define BLE_RXADVLEN_MASK   ((uint16_t)0x0000FF00)
#define BLE_RXADVLEN_LSB    8

#define BLE_RXTYPE_MASK     ((uint16_t)0x0000000F)
#define BLE_RXTYPE_LSB      0

// 返回 rxdesc 數據包的長度
uint8_t llm_util_rxlen_getf(struct co_buf_rx_desc *rxdesc)
{
    uint16_t localVal =  rxdesc->rxheader;
    return ((localVal & BLE_RXADVLEN_MASK) >> BLE_RXADVLEN_LSB);
}

// 返回數據包的類型
uint16_t llm_util_rxtype_getf(struct co_buf_rx_desc *rxdesc)
{
    uint16_t localVal =  rxdesc->rxheader;
    return ((localVal & BLE_RXTYPE_MASK) >> BLE_RXTYPE_LSB);
}

通過分析這些函數的含義和對rxheader的使用可以知道rxheader中一些字段的含義

高字節表示數據包的長度
低4位表示數據包的類型

PS:后面分析其他的部分時,反推得到rxheader其實就是 ADV PDU Header, 其格式如下

adv-header.png

通過分析llm_con_req_ind和 BLE 5.0 的協議規范,可以知道 pkg_data 指向的是Uncoded PHY 空口包的 PDU 部分.

air-packet-struct.png

翻看了2.3 ADVERTISING CHANNEL PDU中的所有PDU類型,除了采用Common Extended Advertising Payload Format格式的PDU外,其他的PDU的頭6個字節都是AdvA,表示發送廣播的設備地址。因此該函數開頭就是在根據數據包的AdvA來查找到對應設備發送的數據包。

llc_0x200_id_handler

從上一節的分析我們知道llm_0x200_id_handler用於處理ADVERTISING CHANNEL PDU, 根據協議的規范,llc_0x200_id_handler函數應該就是用於處理DATA CHANNEL PDU報文,協議規范定義如下

data-channel-pdu.png

可以看的PDU由2字節的headerpayload組成,其中header的結構定義如下:

data-channel-pdu-header.png

下面分析和數據包相關的代碼

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;               // Handle of the first RX buffer
    task_index = dest_id >> 8;

    llid = rx_desc->rxheader & 3;             
    // LL Data PDU
    if ( llid == LLID_CONTINUE || llid == LLID_START )
    {
        llc_data_rcv(task_index, first_rx_handle);
    }
    else
    {
        // LL Control PDU
        if ( llid != LLID_CNTL ) 
        {
            goto LABEL_58;
        }
        llc_cntl_rcv(task_index, first_rx_handle);
    }

主要是根據rxheaderLLID字段來判斷數據包的類型,如果是LL Data PDU就調用llc_data_rcv處理,如果是控制PDU就使用llc_cntl_rcv處理。

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;
  llc_env_tag_tbl = off_7F047E8;
  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);
}
  1. 首先獲取到數據包的描述符rxdesc
  2. 申請一個消息id為0x100的消息,並將數據包的長度、hdl等信息填入消息數據中
  3. 最后將消息發送出去

該消息的處理函數為 sub_7F05D200x07F05D20),主要就是修改消息的id(0x806)和dest_id,然后使用hci_send_2_host讓消息處理函數去進一步處理

int __fastcall sub_7F05D20(int a1, llc_0x100_struct *msg_data, unsigned int a3)
{
 
    msg_hdr = CONTAINING_RECORD(msg_data, ke_msg, param);
    msg_hdr->id = dw_0x806;
    msg_hdr->dest_id = v4;
    hci_send_2_host(msg_data);                  // 0x7F13D97,hci_acl_data_rx_handler

0x806的消息處理函數為hci_acl_data_rx_handler,該函數處理的數據就是l2cap的協議數據了。

llc_cntl_rcv

該函數用於處理LL Control PDU,其BLE規范定義如下

ll-control-pdu.png

主要就是一個字節的opcode和ctrdata,函數主要代碼如下

int __fastcall 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);
    v8 = msg_data;

    ke_msg_send(v8);

主要就是獲取數據包的opcode,然后根據opcode找到對應的函數對數據包進行處理

回調函數表的結構如下

1612358563014.png

L2CAP協議報文處理

通過上一節的分析,可以知道llc_data_rcv在對數據進行簡單的處理后,就通過hci_send_2_host函數讓hci_acl_data_rx_handler去處理L2CAP協議層的報文。

函數的關鍵代碼

int __fastcall 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 的length
    data_length = (data[1] << 8) | *data;    
   
    // 根據 l2cap 的length分配內存
    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的數據到新分配的l2cc_pdu_recv

    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;
    }


    // 把p_buffer里面的pdu解析到 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);

上述代碼流程如下

  1. 首先根據 msg_data->hdl拿到L2CAP數據的起始地址,保存到data變量。
  2. 然后解析L2CAP的length字段,即data的頭兩個字節,結果保存在data_length。
  3. 根據data_length分配消息數據l2cc_pdu_recv,然后會把data的數據拷貝到l2cc_pdu_recv->pdu。
  4. 調用l2cc_pdu_unpack解析L2CAP數據中的information payload,並將解析后的結果保存到l2cc_pdu_recv->pdu。
  5. 最后會調用ke_msg_send將l2cc_pdu_recv消息發送出去,讓對應消息處理函數進行下面的處理。

L2CAP的報文格式如下:

1612537466445.png

在逆向過程中對着協議規范可以簡化逆向的流程。

0xa01的消息處理函數為sub_7F135F6l2cc_pdu_recv_ind_handler,其中sub_7F135F6 位於gattc_default_state 中,表示這個函數是 gattc 這個任務的其中一個處理函數。

BLE-ARCH.png

根據BLE的協議棧結構,我們可以知道sub_7F135F6應該是用於處理 ATT 報文。

漏洞挖掘思路與示例

理清楚數據流動后,就可以開展漏洞挖掘了,漏洞挖掘手段主要就是源碼審計和Fuzz測試。

如果是靜態源碼審計就是跟蹤外部的數據流,分析程序在處理數據時是否存在問題,比如長度沒做校驗等,關注的問題主要有數據中長度字段、偏移字段的校驗,內存拷貝是否存在越界、資源的分配、使用於釋放是否配對等。

如果是要做Fuzz測試的話,就需要識別出處理數據的函數,對其進行適配,比如使用Unicorn將其模擬執行起來,然后使用AFL對其進行Fuzzing.或者可以采用一些藍牙的發包器,自己寫Fuzzer或者采用Peach等工具進行黑盒的Fuzzing.

llm_con_req_ind越界讀漏洞

前面我們分析到llm_0x200_id_handler函數主要用於處理 ADVERTISING CHANNEL PDU,函數首先根據數據包的AdvA找到需要處理的數據包,然后如果數據包類型是LL_CONNECT_REQ就會進入llm_con_req_ind進行處理

      switch ( rx_desc->rxheader & 0xF )
      {
        case LL_CONNECT_REQ:
            llm_con_req_ind(rx_desc);

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;

如果數據包實際長度小於結構體的大小就會導致越界讀。

l2cc_pdu_unpack堆溢出漏洞

hci_acl_data_rx_handler函數里面會調用l2cc_pdu_unpack來對L2CAP報文的infomation_payload部分進行解析

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

該函數實際調用l2cc_pdu_unpack_func

uint8_t __fastcall 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;
  p_pdu->payld_len = (p_buffer[1] << 8) | *p_buffer;// 設置p_pdu->payld_len為 l2cap hdr里面的長度
  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;
      .......
      .......

  }

函數首先根據數據中的cid字段來獲取pkt_format,比如當 cid 為4時,pkt_format為l2cc_attribute_pkt_format,max_opcode31,然后函數會根據pkt_formatopcode的值來決定解析infomation_payload的方式.

  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;
  }

use_size 表示已經解析的數據長度, p_rem_len 表示p_pdup_buffer的大小.

當 v22 為 75 時, 會直接從infomation_payload拷貝 0x10 字節的數據到p_pdu里面, 如果此時p_pdu和infomation_payload剩余字節數小於0x10就會導致越界讀寫。

當 v22 為 97 時,會計算infomation_payload剩余大小v28,然后會對 dst + 1,最后把v28字節數據拷貝到dst ,一字節溢出

該函數中的其他分支也有類似的問題,不過由於hci_acl_data_rx_handler函數在給l2cc_pdu_recv分配內存時多分配了0x4C字節,且l2cc_pdu_unpack_func里面的越界大小最大也只有0x10,實際越界寫也無法利用。

l2cc_pdu_recv = ke_msg_alloc(dword_7F14010 - 1, ::dest_id, dest_id, data_length + 0x4C);/

處理ATT報文時多處越界讀可能導致DOS

sub_7F135F6用於處理ATT報文,該函數會調用attc_l2cc_pdu_recv_handler進行處理

int attc_l2cc_pdu_recv_handler_func(int code, l2cc_pdu_recv_ind *l2cc_pdu_recv)
{
  v5 = l2cc_pdu_recv->pdu.data.code;
  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;
}

函數首先會根據pdu的code從attc_handlers_0_0里面找到對應的處理函數,然后調用處理函數進行處理,attc_handlers函數表如下

rom_ble:07F1FD64 attc_handlers_0 att_handler_item <3, 0, sub_7F0FC0C+1>
rom_ble:07F1FD64                 att_handler_item <5, 0, sub_7F0FC44+1>
rom_ble:07F1FD64                 att_handler_item <7, 0, sub_7F0FCFE+1>
rom_ble:07F1FD64                 att_handler_item <9, 0, sub_7F0FDB4+1>
rom_ble:07F1FD64                 att_handler_item <0x11, 0, sub_7F10054+1>
rom_ble:07F1FD64                 att_handler_item <0xB, 0, sub_7F1015C+1>
rom_ble:07F1FD64                 att_handler_item <0xD, 0, sub_7F1015C+1>
rom_ble:07F1FD64                 att_handler_item <0xF, 0, sub_7F10266+1>
rom_ble:07F1FD64                 att_handler_item <0x13, 0, sub_7F1032C+1>
rom_ble:07F1FD64                 att_handler_item <0x17, 0, sub_7F1034C+1>
rom_ble:07F1FD64                 att_handler_item <0x19, 0, sub_7F103AE+1>
rom_ble:07F1FD64                 att_handler_item <1, 0, sub_7F1045C+1>
rom_ble:07F1FD64                 att_handler_item <0x1D, 0, sub_7F103E6+1>
rom_ble:07F1FD64                 att_handler_item <0x1B, 0, sub_7F103E6+1>

這里面的回調函數有一些共性問題,這里以sub_7F0FC44為例

int __fastcall sub_7F0FC44(int code, int data)
{
  
  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);
  }

函數首先根據 data + 1處的一個字節來決定 item_count 的值 (2 或者 16),然后會從 data + 2 開始取出2字節作為循環的邊界,不斷的從 data 拷貝數據,並發送消息,這個過程沒有檢查數據長度,所以最多可以拷貝 0xffff * 16 字節。

總結

本文主要介紹了DA14531芯片BLE底層協議棧(LL層和L2CAP)收包處理,並提供挖掘BLE協議棧漏洞的思路,其實和普通漏洞挖掘沒有太大區別,關鍵是定位污點數據,然后就是常規的源碼分析和Fuzz測試技術的運用。

參考鏈接

https://blog.csdn.net/zhoutaopower/article/details/95104632


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM