首页
论坛
课程
招聘
[逆向分析] [原创]尝试去抖音ollvm混淆
2021-2-8 17:54 9112

[逆向分析] [原创]尝试去抖音ollvm混淆

2021-2-8 17:54
9112

当时用angr和unicorn跑脚本都有点问题,就直接ida下断点去追踪了。

用angr模块打印出来所有的真实调用块,然后在ida用idc脚本下断点,ida tracing跟踪,会记录每个断点执行过的地址,如下图:

然后处理下这些地址,生成一个数组,直接用脚本patch。

处理方式就是脚本把地址减去基地址,10414C0D8块 跳到104152DF8块  104152DF8块跳到104150F18块

 


contentlist = list(content.split('\t'))

#print(contentlist)

for x in range(len(contentlist) - 1) :

    if len(contentlist[x+1] ) > 1: 

         

        beforeaddr = int(contentlist[x],16) - 0x130000

        nextaddr = int(contentlist[x+1],16) - 0x130000

 

        if flow[beforeaddr] != None:

            if nextaddr not in flow[beforeaddr]:

                flow[beforeaddr].append(nextaddr)        

        else:

            flow[beforeaddr].append(nextaddr)

    

#        print('flow:%s' % flow)


#print('%s' % flow)

print('=======flow:%d=====' % len(flow))

for k, v in flow.items():

    print('%#x: ' % k, [hex(child) for child in v])





遍历地址,保存cset的跳转条件


    flow = symbol_debug()

    


    print('flow:%d' % len(flow))     

    print('************************flow******************************')

 


            


    for k, v in flow.items():

        print('%#x: ' % k, [hex(child) for child in v])



    # state_list = {}


    for parent, childs in flow.items():

        if len(childs) > 1:    

            print('parent11:%s' % parent)

            print('childs11:%s, %d' % (childs, len(childs)))       

            relevant = None

            for relevant_node in relevants_without_retn:

                if relevant_node.addr == parent:

                    relevant = relevant_node

            block = project.factory.block(parent, size=relevant.size)

            for ins in block.capstone.insns:

                if ins.insn.mnemonic.startswith('csel') or ins.insn.mnemonic.startswith('cset'):

                    if relevant not in patch_instrs:

                        patch_instrs[relevant] = ins

                        print('patch_instrs[relevant]=11=:%s' % relevant)


            #cset在block里面没有的话,就在next block里找

            if relevant not in patch_instrs:

                print('patch_instrs[relevant]==none:%x' % relevant.addr)

                if len(list(supergraph.successors(relevant))) == 1:

                    successorsNode = list(supergraph.successors(relevant))[0]

                    block = project.factory.block(successorsNode.addr, size=successorsNode.size)

                    for ins in block.capstone.insns:

                        if ins.insn.mnemonic.startswith('csel') or ins.insn.mnemonic.startswith('cset'):

                            if relevant not in patch_instrs:

                                print('patch_instrs[relevant]=22=:%s' % relevant)

                                patch_instrs[relevant] = ins

            if relevant not in patch_instrs:

                print('has_branches but not csel:%x' % relevant.addr)


    print('patch_instrs:%s' % patch_instrs)

然后就直接修改跳转地址


    print('************************patch*****************************')

    with open(filename, 'rb') as origin:

        # Attention: can't transform to str by calling decode() directly. so use bytearray instead.

        origin_data = bytearray(origin.read())

        origin_data_len = len(origin_data)


    recovery_file = filename + '_recovered'

    if nopCode == 1:

        recovery_file = filename + '_recovered_nop'

     

    recovery = open(recovery_file, 'wb')


    # patch irrelevant blocks

    if nopCode == 1:

        print('nop_nodes:%d' % len(nop_nodes))

        print('nop_nodes11:%s' % nop_nodes)

        for nop_node in nop_nodes:

            fill_nop(origin_data, nop_node.addr-base_addr,nop_node.size, project.arch)



     # remove unnecessary control flows

    for parent, childs in flow.items():


              

        relevant = None

        for relevant_node in relevants_without_retn:

            if relevant_node.addr == parent:

                relevant = relevant_node


        if len(childs) == 1:

            print('parent22:%s' % hex(int(parent)))

            print('childs22:%s, %d' % (hex(int(childs[0])), len(childs)))

            parent_block = project.factory.block(relevant.addr, size=relevant.size)

            last_instr = parent_block.capstone.insns[-1]

            file_offset = last_instr.address - base_addr

            # patch the last instruction to jmp

            

            if project.arch.name in ARCH_ARM64:

                # FIXME: For aarch64/arm64, the last instruction of prologue seems useful in some cases, so patch the next instruction instead.

                if relevant.addr == start:

                    # file_offset += 4


                    patch_value = ins_b_jmp_hex_arm64(last_instr.address, childs[0], 'b')

                    patch_value = asm_no_branch(last_instr.address, childs[0])

                else:


                    patch_value = ins_b_jmp_hex_arm64(last_instr.address, childs[0], 'b')

                    patch_value = asm_no_branch(last_instr.address, childs[0])

                if project.arch.memory_endness == "Iend_BE":

                    patch_value = patch_value[::-1]

            patch_instruction(origin_data, file_offset, patch_value)

        else:

            

            # print('patch_instrs222:%s' % patch_instrs)

            print('parent33:%s' % hex(int(parent)))

            print('childs33:%s,%s %d' % (hex(int(childs[0])), hex(int(childs[1])), len(childs)))

            if relevant in patch_instrs:

                instr = patch_instrs[relevant]

                # print('patch_instrs111222:%s' % instr)

                parent_block = project.factory.block(relevant.addr, size=relevant.size)

                last_instr = parent_block.capstone.insns[-2]

                file_offset = last_instr.address - base_addr

                # patch instructions starting from `cmovx` to the end of block

                fill_nop(origin_data, file_offset, relevant.addr +

                         relevant.size - base_addr - file_offset, project.arch)

                

                if project.arch.name in ARCH_ARM64:

                    # patch the cset.xx instruction to bx instruction

                    bx_cond = instr.op_str.split(',')[-1].strip()

                    patch_value = ins_b_jmp_hex_arm64(last_instr.address, childs[0], bx_cond.lower())

                    # patch_value = asm_has_branch(instr.address, childs[0], bx_cond.lower())

                    if project.arch.memory_endness == 'Iend_BE':

                        patch_value = patch_value[::-1]

                    patch_instruction(origin_data, file_offset, patch_value)


                    file_offset += 4

                    # patch the next instruction to b instruction

                    # print('childs[1]:%x' % childs[1])

                    


                    patch_value = ins_b_jmp_hex_arm64(last_instr.address+4, childs[1], 'b')

                    # patch_value = asm_no_branch(instr.address+4, childs[1])

                    print('patch_value11:%s'%patch_value)

     

                    if project.arch.memory_endness == 'Iend_BE':

                        patch_value = patch_value[::-1]

                    patch_instruction(origin_data, file_offset, patch_value)


    assert len(origin_data) == origin_data_len, "Error: size of data changed!!!"

    recovery.write(origin_data)

    recovery.close()

    print('Successful! The recovered file: %s' % recovery_file)

还原后,直接f5


还原前:


ps:

以上还原还要修复条件跳转的,可以用符号执行判断,后面没时间我就没弄了

因为没有具体还原出算法,也只是一种还原思路而已,可能这个还原还是有问题的,望赐教。


[公告] 2021 KCTF 春季赛 防守方征题火热进行中!

最后于 2021-2-14 00:18 被花少年编辑 ,原因:
收藏
点赞2
打赏
分享
最新回复 (3)
雪    币: 4434
活跃值: 活跃值 (1133)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
尐进 活跃值 2021-3-8 11:13
2
0
mark 不错
雪    币: 199
活跃值: 活跃值 (93)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
孤月独明 活跃值 2021-3-8 11:54
3
0
用angr模块打印出来所有的真实调用块 楼主这块是怎么弄的呢? IDA调试时候引入angr打印?还是写py静态分析so打印呢(这个在操作时候发现对libcms.so进行生产CFG时总是出错,是angr对安卓arm支持的不友好导致的么?)求救
雪    币: 15
活跃值: 活跃值 (230)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
花少年 活跃值 2021-3-16 13:54
4
0
孤月独明 用angr模块打印出来所有的真实调用块 楼主这块是怎么弄的呢? IDA调试时候引入angr打印?还是写py静态分析so打印呢(这个在操作时候发现对libcms.so进行生产CFG时总是出错,是angr ...
用angr模块打印出来所有的真实调用块,是用angr模块提取,当时好像是用签名来区分的,首先区分出哪些是真实块,哪些是虚拟块。有的ollvm是直接主分发器调用虚拟块,虚拟块分叉调用虚拟块,最后才调用真实块,然后真实块处理跳转或者单独一个处理(控制分支)块,然后跳到子分发器,最后再跳回主分发器,有的ollvm是没有子分发器,多个真实块跳到一个cset 然后直接回去主分发器。
游客
登录 | 注册 方可回帖
返回