USDT自动充值

菜宝钱包(caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。

前言

本文先容逆向DA14531芯片的固件,并先容一些辅助自动化剖析的剧本的实现。DA14531是Dialog公司研制的蓝牙芯片,用户可以在官网下载SDK来开发蓝牙的上层营业。

相关代码

https://github.com/hac425xxx/BLE-DA145XX/
ps: 文件所有发出后公然github项目

SDK环境搭建和IDA加载固件

下载SDK后,进入其中一个示例项目

DA145xx_SDK\6.0.14.1114\projects\target_apps\ble_examples\ble_app_profile\Keil_5

使用Keil打开,然后选择 Project->Manage->Component,设置目的为DA14531.

然后编译之后就会下面的目录天生编译好的固件

DA145xx_SDK\6.0.14.1114\projects\target_apps\ble_examples\ble_app_profile\Keil_5\out_DA14531\Objects

其中对照要害的几个文件有

ble_app_profile_531.axf: 工程文件编译为ELF文件
ble_app_profile_531.bin: 可以直接刷入系统RAM的二进制
ble_app_profile_531.hex: hex文件花样,相比.bin文件该文件中带有刷入的地址

其中 .axf 实在就是ELF文件,内里有符号信息和加载基地址等,.bin文件可以直接写入到芯片的牢固地址,查看芯片手册中的Memory Map部门

可以预测.bin文件的基地址为0x7fc0000,这点也可以通过用IDA剖析.axf 文件获得证实,下面为了简化剖析流程,直接用IDA剖析ble_app_profile_531.axf.

随便打开一些函数剖析,发现有的函数会直接将某些没有加载到内存的地址作为函数举行挪用,好比

int __fastcall gattc_write_req_ind_handler()
{
  v6 = MEMORY[0x7F22448](param->handle, att_idx); // 将 0x7F22448 作为函数举行挪用

查看芯片手册可以知道这块地址的形貌为

Boot/BLE ROM
Contains Boot ROM code and BLE protocol related code.

可以知道这块内存内里应该有一些协议栈底层的代码,而且翻看SDK的代码发现许多系统的函数都没有源码,在编译出来后的二进制内里也没有对应函数的实现,预测这部门函数的实现也在这个区域中。

最后让群友协助在开发板内里用jtag把这块内存dump了下来,然后咱们加载到IDA

File -> Load file -> Additional binary file

选择dump出来的二进制文件,然后设置加载地址为 0x7fc0000

加载完后,这些函数挪用就可以正常识别了。

加载函数符号

对部门函数剖析一段时间后,在搜索SDK的时刻,溘然发现了一个对照神奇的文件

da14531_symbols.txt

文件的部门内容如下

0x07f2270b T custs1_set_ccc_value
0x07f22823 T gattc_cmp_evt_handler
0x07f22837 T custs1_val_set_req_handler
0x07f22857 T custs1_val_ntf_req_handler
0x07f228b3 T custs1_val_ind_req_handler
0x07f2290f T custs1_att_info_rsp_handler
0x07f2294b T gattc_read_req_ind_handler
0x07f22b57 T gattc_att_info_req_ind_handler
0x07f22b99 T custs1_value_req_rsp_handler

看起来好像是一个列是符号内存地址,第三列是符号名,后面去网上搜索这个文件的作用,发现是keil支持的一种输入文件,用于在链接的时刻把符号引用替换为对应的内存地址地址,这样在固件运行时就可以正常挪用函数,这样也可以让一部门代码不开源。

最后写了个idapython剧本加载这个符号文件

import idaapi
import idc

fpath = "da14531_symbols.txt"

def define_func(addr, name):
    if addr & 1:
        addr -= 1
        idaapi.split_sreg_range(addr, idaapi.str2reg("T"), 1, idaapi.SR_user)
    else:
        idaapi.split_sreg_range(addr, idaapi.str2reg("T"), 0, idaapi.SR_user)

    if idaapi.create_insn(addr):
        idc.add_func(addr)
        idaapi.set_name(addr, name,idaapi.SN_FORCE)


def define_data(addr, name):
    idaapi.set_name(addr, name,idaapi.SN_FORCE)

with open(fpath, "r") as fp:
    for l in fp:
        try:
            addr, type, name = l.strip().split(" ")
            if addr.startswith(";"):
                continue
            addr = int(addr, 16)
            if type == "T":
                define_func(addr, name)
            else:
                define_data(addr, name)
        except:
            pass
  1. 主要逻辑就是一行一行的处置文件,抛弃 ; 开头的行
  2. 然后凭据第二列的值来举行对应的处置
  3. 若是是T示意这个符号是一个函数地址挪用define_func处置,否则就当做变量符号挪用define_data处置

主要提一下的就是在处置函数的时刻的代码

def define_func(addr, name):
    if addr & 1:
        addr -= 1
        idaapi.split_sreg_range(addr, idaapi.str2reg("T"), 1, idaapi.SR_user)
    else:
        idaapi.split_sreg_range(addr, idaapi.str2reg("T"), 0, idaapi.SR_user)

    if idaapi.create_insn(addr):
        idc.add_func(addr)
        idaapi.set_name(addr, name,idaapi.SN_FORCE)
  1. 首先需要凭据地址的更低位是否为1来判断是否为thumb指令,然后凭据情形设置idaapi.str2reg("T") 寄存器的值,IDA会凭据这个寄存器的值来判断后面反汇编指令时接纳的是thumb指令照样arm指令
  2. 然后挪用idaapi.create_insnida从函数地址处更先举行反汇编并确立指令
  3. 指令确立乐成之后就挪用idc.add_func确立一个函数并使用idaapi.set_name设置函数的名称

执行剧本后许多的系统函数都识别出来了。

操作系统义务识别

确立义务API剖析

剖析嵌入式系统,首先需要将系统中存在的task/历程识别出来,经由一番的资料查找和SDK学习,可以知道DA145x芯片中的操作系统为Riviera Waves实时系统,该系统使用用ke_task_create来确立一个义务,从SDK中可以获取函数的界说如下

/**
 ****************************************************************************************
 * @brief Create a task.
 *
 * @param[in]  task_type       Task type.
 * @param[in]  p_task_desc     Pointer to task descriptor.
 *
 * @return                     Status
 ****************************************************************************************
 */
uint8_t ke_task_create(uint8_t task_type, struct ke_task_desc const * p_task_desc);

可以看到函数两个参数,第一个示意义务的类型,第二个参数为ke_task_desc结构体指针,示意义务的形貌信息。

若是需要识别所有的义务,就可以通过查找 ke_task_create 的交织引用,然后获取函数挪用的两个参数即可拿到义务的类型和对应的义务形貌符地址,然后再剖析ke_task_desc就可以获取到每个义务的详细信息。

为了自动化的实现该目的,需要一个能在IDA中获取函数挪用参数值的剧本,下面首先剖析此剧本的实现

函数挪用参数识别剧本

剧本地址

https://github.com/hac425xxx/BLE-DA145XX/blob/main/argument_tracker.py

参数追踪主要在ArgumentTracker类中实现,剧本实现了两种参数识别的方式划分为基于汇编指令和模拟执行的函数挪用参数识别 基于IDA伪代码的函数挪用参数识别

下面划分对实在现举行先容

基于汇编指令和模拟执行的函数挪用参数识别

这种方式由 reobjc 剧本演变而来

,

Usdt第三方支付接口

菜宝钱包(caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。

,
https://github.com/duo-labs/idapython/blob/master/reobjc.py

功效实现于track_register函数,主要思绪是:

  1. 追踪存储函数参数的寄存器/内存地址的使用,做一个类似污点剖析的功效,直到找到最初赋值的位置(好比ldr, mov)
  2. 然后从赋值点更先使用unicorn模拟执行,一直执行到函数挪用的位置
  3. 然后从unicorn获取此时的对应寄存器和内存的值就可以获得详细的函数参数值

示例:

rom_ble:07F09CC0                 LDR             R1, =0x7F1F550
rom_ble:07F09CC2                 MOVS            R0, ,0  ; task_type
rom_ble:07F09CC4                 ADDS            R1, ,0x28 ; '(' ; p_task_desc
rom_ble:07F09CC6                 BL              ke_task_create

假设现在需要追踪参数二(即R1)的值,步骤如下:

  1. 首先从07F09CC6往前搜索R1的赋值点,发现 07F09CC4 这里是一条ADD指令不是最初赋值点,继续往上搜索
  2. 最后找到07F09CC0这里是LDR指令
  3. 然后使用unicron07F09CC0更先执行,一直执行到07F09CC6即可获取到在挪用ke_task_create参数二(即R1)的值

下面看看要害的代码

while curr_ea != idc.BADADDR:
            mnem = idc.print_insn_mnem(curr_ea).lower()
            dst = idc.print_operand(curr_ea, 0).lower()
            src = idc.print_operand(curr_ea, 1).lower()

            if dst == target and self.is_set_argument_instr(mnem):
                target = src
                target_value = src
                target_ea = curr_ea
                if target.startswith("="):
                    break

            if dst == target == "r0" and self.is_call_instr(mnem):
                previous_call = curr_ea
                break
            curr_ea = idc.prev_head(curr_ea-1, f_start)

主要就是不停挪用idc.prev_head往前剖析指令,然后对每条指令举行剖析,实现一个反向的污点跟踪,直到找到目的的赋值点为止,找到赋值点后就使用Unicorn去模拟执行

基于IDA伪代码的函数挪用参数识别

有的时刻基于汇编指令向后做跟踪会丢失部门信息,示例:

if(cond1)
{
    v4 = 0x101
}

if(cod2)
{
    v4 = 0x303;
}

if(cod4)
{
    v4 = 0x202;
}

some_func(v4 - 1, 0)

对于这样的代码若是直接使用第一种方式现实只会获得 v4 = 0x201,会遗漏两种可能值。

为了缓解这种情形,实现了一个基于IDA伪代码的参数值识别剧本,功效实现于decompile_tracer函数。

其主要思绪也是类似,首先定位需要获取的参数,然后提取参数字符串,划分跟踪参数的每个组成部门,找到赋值点,然后求出每个部门的值,从而获得参数的所有取值.

照样以上面的为例,假设需要获取参数1的值,处置流程如下

  1. 首先提取获得参数1的组成部门为 v4 和 1,1为常量,只需要追踪v4
  2. 然后往上追踪,找到v4的可能值为0x2020x3030x101
  3. 最后获得v4 - 1的所有可能值为0x2010x3020x100

义务自动化识别

首先找到ke_task_create的交织引用,然后行使ArgumentTracker中基于汇编的参数获取模式来提取参数的值

def dump_ke_task_create():
    retsult = {}
    logger = CustomLogger()
    m = CodeEmulator()
    at = ArgumentTracker()

    ke_task_create_addr = idaapi.get_name_ea(idaapi.BADADDR, "ke_task_create")
    for xref in XrefsTo(ke_task_create_addr, 0):
        frm_func = idc.get_func_name(xref.frm)
        ret = at.track_register(xref.frm, "r1")
        if ret.has_key("target_ea"):
            if m.emulate(ret['target_ea'], xref.frm):
                reg = m.mu.reg_read(UC_ARM_REG_R1)
                retsult[xref.frm] = reg

首先获取ke_task_create的地址,然后查找其交织引用

  1. 对于每个交织引用使用track_register来追踪r1寄存器(即参数二)
  2. ret['target_ea']示意赋值点,然后使用CodeEmulator从赋值点执行到函数挪用的位置(xref.frm
  3. 执行乐成后读取r1的值,即可获得义务形貌符的地址

拿到义务形貌符的地址后下面需要界说形貌符的类型,首先看看ke_task_desc的界说

/// Task descriptor grouping all information required by the kernel for the sche *** ng.
struct ke_task_desc
{
    /// Pointer to the state handler table (one element for each state).
    const struct ke_state_handler* state_handler;
    /// Pointer to the default state handler (element parsed after the current state).
    const struct ke_state_handler* default_handler;
    /// Pointer to the state table (one element for each instance).
    ke_state_t* state;
    /// Maximum number of states in the task.
    uint16_t state_max;
    /// Maximum index of supported instances of the task.
    uint16_t idx_max;
};

这里主要关注ke_state_handler,该结构中有一个msg_table,内里是一些函数指针和其对应的新闻id

/// Element of a message handler table.
struct ke_msg_handler
{
    /// Id of the handled message.
    ke_msg_id_t id;
    /// Pointer to the handler function for the msgid above.
    ke_msg_func_t func;
};

/// Element of a state handler table.
struct ke_state_handler
{
    /// Pointer to the message handler table of this state.
    const struct ke_msg_handler *msg_table;
    /// Number of messages handled in this state.
    uint16_t msg_cnt;
};

我们也就根据结构体界说使用响应的IDApython的接口即可(注重:使用idapython设置结构体前要确保对应的结构体已经导入到IDB中)

for k, v in retsult.items():
        frm_func = idc.get_func_name(k)
        task_desc_ea = v
        task_desc_name = "{}_task_desc".format(frm_func.split("_init")[0])
        define_ke_task_desc(task_desc_ea, task_desc_name)

        handler = idaapi.get_dword(task_desc_ea + 4)
        define_ke_state_handler(handler)

识别新闻和回调函数的交织引用

Riviera Waves系统中义务之间使用新闻来通报新闻,中止处置程序做了简朴处置后就会通过发送新闻交给对应的新闻处置函数举行处置,常用方式是使用ke_msg_alloc分配新闻,然后使用ke_msg_send将新闻发送出去。

ke_msg_alloc的界说如下

/**
 ****************************************************************************************
 * @brief Allocate memory for a message
 *
 * @param[in] id        Message identifier
 * @param[in] dest_id   Destination Task Identifier
 * @param[in] src_id    Source Task Identifier
 * @param[in] param_len Size of the message parameters to be allocated
 *
 */
void *ke_msg_alloc(ke_msg_id_t const id, ke_task_id_t const dest_id,
                   ke_task_id_t const src_id, uint16_t const param_len);

其中第一个参数为新闻ID,在系统中有许多新闻处置回调函数表,回调函数表大要结构都是由新闻ID和函数指针组成,在处置新闻发送出去后,系统会凭据新闻中的其他参数(好比dest_id)找到响应的回调函数表,然后凭据新闻ID去表中找到对应的回调函数,最后挪用回调函数处置新闻数据。

那我们就可以找到所有ke_msg_alloc的挪用点,然后提取出id,就可以知道每个函数使用了哪些新闻id,然后凭据新闻id去二进制内里搜索,找到新闻处置函数,最后将两者确立交织引用,这样在逆向剖析的时刻就很舒服了。

示例

rom_ble:07F17C4E                 LDR             R0, =0x805 ; id
rom_ble:07F17C50
rom_ble:07F17C50 loc_7F17C50                             ; DATA XREF: sub_7F06B94↑r
rom_ble:07F17C50                                         ; sub_7F0CE30↑r ...
rom_ble:07F17C50                 BL              ke_msg_alloc

确立完交织引用后在挪用ke_msg_alloc的位置,可以看的其事宜新闻的处置函数可能为sub_7F06B94sub_7F0CE30

下面先容凭据新闻ID搜索新闻处置函数的实现

def search_msg_handler(msg_id):

    ret = []

    data = " ".join(re.findall(".{2}", struct.pack("H", msg_id).encode("hex")))
    addr = 0x07F00000
    find_addr = idc.find_binary(addr, SEARCH_DOWN, data)

    while find_addr != idaapi.BADADDR:
        func_addr = idaapi.get_dword(find_addr + 4)
        if is_func_ea(func_addr):
            print "  msg_id 0x{:X} @ 0x{:X}, handler: 0x{:X}".format(msg_id, find_addr, func_addr)
            ret.append(func_addr)

        , custom_msg_handler
        func_addr = idaapi.get_dword(find_addr + 2)
        if is_func_ea(func_addr):
            print "  [custom_msg_handler] msg_id 0x{:X} @ 0x{:X}, handler: 0x{:X}".format(msg_id, find_addr, func_addr)
            ret.append(func_addr)

        find_addr = idc.find_binary(find_addr + 1, SEARCH_DOWN, data)

    return ret

经由逆向剖析,发现新闻处置函数和新闻id的关系主要有两种情形

  1. 新闻id起始地址 + 2的位置是函数地址
  2. 新闻id起始地址 + 4的位置是函数地址

两种情形划分对应custom_msg_handlerke_msg_handler两种界说新闻回调函数的结构体

/// Custom message handlers
struct custom_msg_handler 
{
    ke_task_id_t task_id;
    ke_msg_id_t id;
    ke_msg_func_t func;
};

/// Element of a message handler table.
struct ke_msg_handler
{
    ke_msg_id_t id;
    ke_msg_func_t func;
};

剧本也是这样的逻辑,划分实验这两个位置,若是是函数的话就认为是对应的回调函数,这样处置的坏处是没有思量新闻的其他参数,可能导致有的新闻处置函数对于某些场景现实是挪用不了的,然则照样会被我们的剧本确立交织引用,以是只能说是可能的新闻处置函数,不外这样也可以简化许多剖析流程了。

最后使用IDA函数设置交织引用即可

def add_ref(frm, to):
    idaapi.add_dref(frm, to, idaapi.dr_R)
    idaapi.add_dref(to, frm, idaapi.dr_R)

剧本使用方式

首先使用argument_tracker.py获取固件中每个函数的msg id的使用情形,然后将效果导出到文件中

https://github.com/hac425xxx/BLE-DA145XX/blob/main/argument_tracker.py,L610

然后使用search_msg_handler.py导入之前获取到的效果,并搜索新闻ID对应的回调函数,最后为两者确立交织引用。

https://github.com/hac425xxx/BLE-DA145XX/blob/main/search_msg_handler.py,L70

总结

本文先容更先剖析一个芯片的一些流程,先容一些辅助人工的剧本的实现原理。

Usdt官方交易所声明:该文看法仅代表作者自己,与本平台无关。转载请注明:收购usdt(www.caibao.it):DA14531芯片固件逆向系列(1)-固件加载和逆向剖析
发布评论

分享到:

usdt钱包(www.caibao.it):原创 C罗未婚妻手拎5只LV炫富!超短裙秀豆腐腿,3D身体媲美金卡戴珊
1 条回复
  1. AllbetGaming下载
    AllbetGaming下载
    (2021-03-27 00:01:20) 1#

    菜宝钱包(caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。脑洞大开的感觉

发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。