利用简单的unicron去除ollvm控制流程平坦化

    技术2022-07-15  39

    一.脚本如下

    #-*- coding=utf-8 -*- from unicorn import * from unicorn import arm_const from unicorn.arm_const import * from capstone import * from capstone.arm import * from keystone import * def hook_code(uc, address, size, user_data): global FindFlag global Finding global JMP_PC ban_ins = ["bl"] #print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size)) for ins in md.disasm(bin1[address:address+size],address): print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size)) print(">>> 0x%x:\t%s\t%s" % (ins.address, ins.mnemonic, ins.op_str)) print("") if FindFlag: if JMP_PC[Finding] == 0: print("jmp is %x"%(ins.address)) JMP_PC[Finding]=ins.address Finding=0 FindFlag=0 else: input('Something is error') flag_pass = False for b in ban_ins: if ins.mnemonic.find(b) != -1: flag_pass = True break if ins.op_str=='#0x3f4':#this is jmp 0x3f4 0x3f4 is switch case print("jmp switch") Finding=ins.address JMP_PC[Finding]=0 if ins.address==0x3F0:#first blood print("jmp switch") Finding=ins.address JMP_PC[Finding] = 0 if Finding and ins.address==0x414: print("ready to jmp") FindFlag=1 if flag_pass: print("will pass 0x%x:\t%s\t%s" % (ins.address, ins.mnemonic, ins.op_str)) uc.reg_write(UC_ARM_REG_PC, address + size) return def hook_mem_access(uc,type,address,size,value,userdata): pc = uc.reg_read(UC_ARM_REG_PC) print('pc:%x type:%d addr:%x size:%x' % (pc,type,address,size)) #uc.emu_stop() return False def hook_error(uc,type,address,size,value,userdata): pc = uc.reg_read(UC_ARM_REG_PC) print('pc:%x type:%d addr:%x size:%x' % (pc,type,address,size)) #uc.emu_stop() return False FindFlag=0 Finding=0 JMP_PC={} md = Cs(CS_ARCH_ARM,CS_MODE_ARM) md.detail = True #enable detail analyise offset = 0x3D4 #function start end = 0x600 #function end f=open('C:/Users/wei/Desktop/KanXueCTF/unicorn/test_arm','rb') bin1 = f.read() f.close() mu = Uc(UC_ARCH_ARM, UC_MODE_ARM) #init stack mu.mem_map(0x80000000,0x10000 * 8) mu.mem_map(0, 4 * 1024 * 1024) mu.mem_write(0,bin1) mu.reg_write(UC_ARM_REG_SP, 0x80000000 + 0x10000 * 6) mu.reg_write(UC_ARM_REG_LR, end)#retn addr mu.hook_add(UC_HOOK_CODE, hook_code) mu.hook_add(UC_HOOK_MEM_UNMAPPED,hook_mem_access) #mu.hook_add(UC_ERR_HOOK,hook_error) mu.emu_start(offset,end) print(JMP_PC) ks = Ks(KS_ARCH_ARM, KS_MODE_ARM) patch_file=open('C:/Users/wei/Desktop/KanXueCTF/unicorn/patch_test_arm','wb') bin_w=bytearray(bin1) for key,value in JMP_PC.items(): jmp_addr=value addr=key print('{key}:{value}'.format(key=hex(addr), value=hex(jmp_addr))) r=ks.asm('b 0x%x'%(jmp_addr),addr)[0] print(list(map(hex,r))) for i in range(len(r)): bin_w[addr+i]=r[i] patch_file.write(bin_w) patch_file.close()

    下面重点讲一下脚本的作用

    核心就是,记录一下进入switch之前的分支和switch之后的分支

    像那些bl,blx之类的都跳过

    二.代码注释

    def hook_code(uc, address, size, user_data): global FindFlag #是否查找完毕 global Finding #是否正在查找 global JMP_PC #记录跳前和跳后的 ban_ins = ["bl"] #print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size)) for ins in md.disasm(bin1[address:address+size],address): print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size)) print(">>> 0x%x:\t%s\t%s" % (ins.address, ins.mnemonic, ins.op_str)) print("") if FindFlag: #发现结束的标志 if JMP_PC[Finding] == 0: print("jmp is %x"%(ins.address)) JMP_PC[Finding]=ins.address Finding=0 FindFlag=0 else: input('Something is error') flag_pass = False #这一段是跳过 bl blr for b in ban_ins: if ins.mnemonic.find(b) != -1: flag_pass = True break if ins.op_str=='#0x3f4':#这一段是 跳入swtich print("jmp switch") Finding=ins.address #finding 不为0 开始了 JMP_PC[Finding]=0 if ins.address==0x3F0:#这是第一次进入分支但是不是用的 b print("jmp switch") Finding=ins.address JMP_PC[Finding] = 0 if Finding and ins.address==0x414: #结束查找了 print("ready to jmp") FindFlag=1 #结束的标志 下一条指令就是了 if flag_pass: print("will pass 0x%x:\t%s\t%s" % (ins.address, ins.mnemonic, ins.op_str)) uc.reg_write(UC_ARM_REG_PC, address + size) return

    这个使用具体实例patch的程序来自于

    https://github.com/0x1shyboy1/uncorn-patch-easyollvm/blob/master/test_arm

    patch前后代码对比

    patch后

    Processed: 0.011, SQL: 9