DA14531芯片固件逆向系列(2)- 操作系统底层机制分析
字数 2537 2025-08-24 07:48:09

DA14531芯片固件逆向分析:操作系统底层机制详解

概述

DA145x软件平台采用了由Riviera Waves许可的小型高效实时内核,该内核提供了以下核心功能:

  • 任务创建和状态转换
  • 任务之间的消息交换
  • 计时器管理
  • 动态内存分配
  • BLE事件的调度和处理

基础数据结构

co_list链表实现

co_list是Riviera Waves系统中广泛使用的单向链表实现,用于存放各种数据如消息数据。

核心数据结构

// 链表中的节点
struct co_list_hdr {
    /// 指向下一个链表节点
    struct co_list_hdr *next;
};

/// 链表头的结构
struct co_list {
    // 链表头节点
    struct co_list_hdr *first;
    // 链表尾节点
    struct co_list_hdr *last;
    // 链表中的节点个数
    uint32_t cnt;
    // 链表中最多节点数
    uint32_t maxcnt;
    // 链表中最少节点数
    uint32_t mincnt;
};

链表操作函数

  1. 链表初始化 - co_list_init

    void __fastcall co_list_init(struct co_list *list) {
        list->first = 0;
        list->last = 0;
        list->cnt = 0;
        list->maxcnt = 0;
        list->mincnt = -1;
    }
    
  2. 插入节点

    • co_list_push_back - 将节点插入到链表尾部
    • co_list_push_front - 将节点插入到链表头部
  3. 节点出链表 - co_list_pop_front

    struct co_list_hdr *__fastcall co_list_pop_front(struct co_list *list) {
        struct co_list_hdr *item = list->first;
        if (list->first) {
            list->first = item->next;
            if (!item->next) {
                list->last = 0;
            }
            list->cnt--;
            // 更新mincnt等统计信息
        }
        return item;
    }
    
  4. 取出节点 - co_list_extract

    • 从指定节点开始取出指定数量的节点
    • 注意:如果nb_following超过链表长度会导致空指针问题
  5. 查找节点 - co_list_find

    • 遍历链表查找指定节点
  6. 链表合并 - co_list_merge

    void __fastcall co_list_merge(struct co_list *list1, struct co_list *list2) {
        list1->last->next = list2->first;
        list1->last = list2->last;
        list2->first = 0;
        list1->cnt += list2->cnt;
        list2->cnt = 0;
    }
    

事件调度机制

Riviera Waves实现了事件调度机制,任务可以通知特定的事件处理函数进行事务处理。

事件管理结构

struct ke_event_table_struct {
    int pending_event_bits;  // 事件状态位
    int callback_list[6];    // 事件处理函数数组
};

关键API

  1. 事件初始化 - ke_event_init

    void ke_event_init() {
        memset(p_ke_event_table, 0, sizeof(ke_event_table_struct));
    }
    
  2. 注册事件处理函数 - ke_event_callback_set

    uint8_t __fastcall ke_event_callback_set(uint8_t event_type, void (*p_callback)(void)) {
        if (event_type < 6) {
            p_ke_event_table->callback_list[event_type] = p_callback;
            return 0;
        }
        return 3; // 错误码
    }
    
  3. 事件调度 - ke_event_schedule

    • 检查所有事件状态,调用pending状态的事件处理函数
    • 不会自动清除事件状态位,需在回调中调用ke_event_clear
  4. 设置/清除事件状态

    • ke_event_set - 设置事件状态位为1
    • ke_event_clear - 设置事件状态位为0

系统注册的事件处理函数

地址 事件号 回调函数地址 回调函数名称
0x7F08BB2 0x5 0x7F08A6E lld_evt_deffered_elt_handler
0x7F09CCE 0x0 0x7F02744 llm_encryption_done
0x7F0E5C2 0x3 0x7F0E58E event_3_callback_func
0x7F0E956 0x4 0x7F0E87C event_4_callback_func
0x7F1CDEC 0x1 0x7F1CCDE event_1_callback_func
0x7F1D06C 0x2 0x7F1CFFA event_2_callback_func

任务管理机制

任务ID组成

typedef uint16_t ke_task_id_t;

// 从类型和索引构建任务ID
#define KE_BUILD_ID(type, index) ((ke_task_id_t)(((index) << 8)|(type)))

// 从任务ID获取类型
#define KE_TYPE_GET(ke_task_id) ((ke_task_id) & 0xFF)

// 从任务ID获取索引
#define KE_IDX_GET(ke_task_id) (((ke_task_id) >> 8) & 0xFF)

任务描述符

struct ke_task_desc {
    /// 状态处理函数表(每个状态一个元素)
    const struct ke_state_handler* state_handler;
    /// 默认状态处理函数
    const struct ke_state_handler* default_handler;
    /// 状态表(每个实例一个元素)
    ke_state_t* state;
    /// 任务中状态的最大数量
    uint16_t state_max;
    /// 任务支持实例的最大索引
    uint16_t idx_max;
};

任务操作

  1. 创建任务 - ke_task_create

    uint8_t __fastcall ke_task_create(uint8_t task_type, const struct ke_task_desc *p_task_desc) {
        if (task_type < 26) {
            if (!p_task_desc_table_0[task_type]) {
                p_task_desc_table_0[task_type] = p_task_desc;
                return 0;
            }
            return 4; // 错误码
        }
        return 3; // 错误码
    }
    
  2. 设置任务状态 - ke_state_set

    void __fastcall ke_state_set(const ke_task_id_t id, const ke_state_t state_id) {
        int state_idx = HIBYTE(id);
        if (id < 0x1Au) {
            ke_task_desc *task = p_task_desc_table_0[id];
            if (task->idx_max > state_idx) {
                task->state[state_idx] = state_id;
                notify_handle_saved_msg(id); // 通知内核处理保存的消息
            }
        }
    }
    

系统任务列表

初始化函数 地址 任务结构地址
llc_init 0x7F02CBE 0x7F1F1E8
lld_init 0x7F06E1E 0x7F1F540
llm_init 0x7F09CC6 0x7F1F578
gtl_init_func 0x7F0E322 0x7F1F7F0
gattc_init 0x7F125BE 0x7F1FE44
gattm_init 0x7F13824 0x7F1FF40
l2cc_init 0x7F13B7A 0x7F1FFE0
gapc_init 0x7F1567C 0x7F2004C
gapm_init 0x7F176D4 0x7F201B4

消息调度机制

消息结构

struct ke_msg {
    struct co_list_hdr hdr;  // 链表头
    uint32_t saved;          // 保存标志
    ke_msg_id_t id;          // 消息ID
    ke_task_id_t dest_id;    // 目标任务ID
    ke_task_id_t src_id;     | 源任务ID
    uint16_t param_len;      // 参数长度
    uint32_t param[1];       // 参数数据
};

消息操作API

  1. 申请消息 - ke_msg_alloc

    void *__fastcall ke_msg_alloc(const ke_msg_id_t id, const ke_task_id_t dest_id, 
                                 const ke_task_id_t src_id, const uint16_t param_len) {
        ke_msg *msg = ke_malloc(param_len + 16, 2);
        msg->hdr.next = -1;
        msg->saved = 0;
        msg->id = id;
        msg->dest_id = dest_id;
        msg->src_id = src_id;
        msg->param_len = param_len;
        memset(msg->param, 0, param_len);
        return msg->param;
    }
    
  2. 释放消息 - ke_msg_free

    int __fastcall ke_msg_free(int a1) {
        return ke_free(a1);
    }
    
    • 注意:ke_msg_alloc返回的是param指针,而ke_msg_free需要ke_msg*指针
  3. 发送消息 - ke_msg_send

    bool __fastcall ke_msg_send(int param) {
        ke_msg *msg_hdr = (param - 16);
        // 关闭中断
        co_list_push_back(&p_ke_env->queue_sent, &msg_hdr->hdr);
        // 恢复中断
        return ke_event_set(1u); // 触发事件1
    }
    

内核环境结构

struct ke_env_tag {
    /// 已发送但尚未传递给接收者的消息队列
    struct co_list queue_sent;
    /// 已传递但尚未被接收者消费的消息队列
    struct co_list queue_saved;
    /// 定时器队列
    struct co_list queue_timer;
    
    #if (KE_MEM_RW)
    /// 堆内存管理相关字段
    struct mblock_free * heap[KE_MEM_BLOCK_MAX];
    uint16_t heap_size[KE_MEM_BLOCK_MAX];
    #if (KE_PROFILING)
    uint16_t heap_used[KE_MEM_BLOCK_MAX];
    uint32_t max_heap_used;
    #endif
    #endif
};

消息处理流程

  1. 事件1的回调函数event_1_callback_func处理消息:

    • queue_sent取出消息
    • 先在custom_msg_handlers中搜索处理函数
    • 找不到则调用get_msg_handler根据消息ID和目标ID搜索
    • 调用找到的处理函数
    • 处理成功(返回0)则释放消息,返回2则保存到queue_saved
  2. 关键处理逻辑:

    msg_handle_result = msg_handle_func(msg->id, msg->param, msg->dest_id, msg->src_id);
    if (msg_handle_result) {
        if (msg_handle_result == 2) {
            msg->saved = 1;
            co_list_push_back(&p_ke_env_->queue_saved, &msg->hdr);
        }
    } else {
        ke_msg_free(msg);
    }
    

总结

Riviera Waves内核通过精心设计的数据结构和机制实现了高效的实时任务调度:

  1. 链表管理co_list提供了高效的内存管理基础
  2. 事件驱动:6个事件槽位支持灵活的事件响应机制
  3. 任务管理:26个任务槽位,支持状态机和消息处理
  4. 消息传递:完善的发送、保存和处理机制
  5. 内存管理:与消息系统紧密集成的动态内存分配

理解这些底层机制对于DA14531芯片的固件逆向分析和漏洞挖掘至关重要。

DA14531芯片固件逆向分析:操作系统底层机制详解 概述 DA145x软件平台采用了由Riviera Waves许可的小型高效实时内核,该内核提供了以下核心功能: 任务创建和状态转换 任务之间的消息交换 计时器管理 动态内存分配 BLE事件的调度和处理 基础数据结构 co_ list链表实现 co_list 是Riviera Waves系统中广泛使用的单向链表实现,用于存放各种数据如消息数据。 核心数据结构 链表操作函数 链表初始化 - co_list_init 插入节点 co_list_push_back - 将节点插入到链表尾部 co_list_push_front - 将节点插入到链表头部 节点出链表 - co_list_pop_front 取出节点 - co_list_extract 从指定节点开始取出指定数量的节点 注意:如果nb_ following超过链表长度会导致空指针问题 查找节点 - co_list_find 遍历链表查找指定节点 链表合并 - co_list_merge 事件调度机制 Riviera Waves实现了事件调度机制,任务可以通知特定的事件处理函数进行事务处理。 事件管理结构 关键API 事件初始化 - ke_event_init 注册事件处理函数 - ke_event_callback_set 事件调度 - ke_event_schedule 检查所有事件状态,调用pending状态的事件处理函数 不会自动清除事件状态位,需在回调中调用 ke_event_clear 设置/清除事件状态 ke_event_set - 设置事件状态位为1 ke_event_clear - 设置事件状态位为0 系统注册的事件处理函数 | 地址 | 事件号 | 回调函数地址 | 回调函数名称 | |------------|--------|--------------------|-------------------------------| | 0x7F08BB2 | 0x5 | 0x7F08A6E | lld_ evt_ deffered_ elt_ handler | | 0x7F09CCE | 0x0 | 0x7F02744 | llm_ encryption_ done | | 0x7F0E5C2 | 0x3 | 0x7F0E58E | event_ 3_ callback_ func | | 0x7F0E956 | 0x4 | 0x7F0E87C | event_ 4_ callback_ func | | 0x7F1CDEC | 0x1 | 0x7F1CCDE | event_ 1_ callback_ func | | 0x7F1D06C | 0x2 | 0x7F1CFFA | event_ 2_ callback_ func | 任务管理机制 任务ID组成 任务描述符 任务操作 创建任务 - ke_task_create 设置任务状态 - ke_state_set 系统任务列表 | 初始化函数 | 地址 | 任务结构地址 | |------------------|------------|---------------| | llc_ init | 0x7F02CBE | 0x7F1F1E8 | | lld_ init | 0x7F06E1E | 0x7F1F540 | | llm_ init | 0x7F09CC6 | 0x7F1F578 | | gtl_ init_ func | 0x7F0E322 | 0x7F1F7F0 | | gattc_ init | 0x7F125BE | 0x7F1FE44 | | gattm_ init | 0x7F13824 | 0x7F1FF40 | | l2cc_ init | 0x7F13B7A | 0x7F1FFE0 | | gapc_ init | 0x7F1567C | 0x7F2004C | | gapm_ init | 0x7F176D4 | 0x7F201B4 | 消息调度机制 消息结构 消息操作API 申请消息 - ke_msg_alloc 释放消息 - ke_msg_free 注意: ke_msg_alloc 返回的是 param 指针,而 ke_msg_free 需要 ke_msg* 指针 发送消息 - ke_msg_send 内核环境结构 消息处理流程 事件1的回调函数 event_1_callback_func 处理消息: 从 queue_sent 取出消息 先在 custom_msg_handlers 中搜索处理函数 找不到则调用 get_msg_handler 根据消息ID和目标ID搜索 调用找到的处理函数 处理成功(返回0)则释放消息,返回2则保存到 queue_saved 关键处理逻辑: 总结 Riviera Waves内核通过精心设计的数据结构和机制实现了高效的实时任务调度: 链表管理 : co_list 提供了高效的内存管理基础 事件驱动 :6个事件槽位支持灵活的事件响应机制 任务管理 :26个任务槽位,支持状态机和消息处理 消息传递 :完善的发送、保存和处理机制 内存管理 :与消息系统紧密集成的动态内存分配 理解这些底层机制对于DA14531芯片的固件逆向分析和漏洞挖掘至关重要。