├── README.md ├── cfg.py └── libvdog.so /README.md: -------------------------------------------------------------------------------- 1 | ## Introduction 2 | this is arm64 deollvm based on @wumingxia. 3 | 4 | added fix instructions.(Only test "JNI_OnLoad") 5 | 6 | [more informations](https://bbs.pediy.com/thread-252321.htm) 7 | 8 | ## Thanks 9 | 10 | @wumingxia -------------------------------------------------------------------------------- /cfg.py: -------------------------------------------------------------------------------- 1 | #coding=utf-8 2 | 3 | from capstone import * 4 | from capstone.arm64 import * 5 | from graphviz import Digraph 6 | from unicorn import * 7 | from unicorn.arm64_const import * 8 | from keystone import * 9 | 10 | 11 | def reg_ctou(regname):# 12 | # This function covert capstone reg name to unicorn reg const. 13 | type1 = regname[0] 14 | if type1 == 'w' or type1 =='x': 15 | idx = int(regname[1:]) 16 | if type1 == 'w': 17 | return idx + UC_ARM64_REG_W0 18 | else: 19 | if idx == 29: 20 | return 1 21 | elif idx == 30: 22 | return 2 23 | else: 24 | return idx + UC_ARM64_REG_X0 25 | elif regname=='sp': 26 | return 4 27 | return None 28 | 29 | def asm_no_branch(ori,dist): 30 | ks = Ks(KS_ARCH_ARM64, KS_MODE_LITTLE_ENDIAN) 31 | print ("b #0x%x" % dist) 32 | ins, count = ks.asm(("b #0x%x" % dist),ori) 33 | return ins 34 | 35 | def asm_has_branch(ori,dist1,dist2,cond): 36 | ks = Ks(KS_ARCH_ARM64, KS_MODE_LITTLE_ENDIAN) 37 | if ori == 0xb73b4: 38 | pass 39 | print "b%s #0x%x;b #0x%x" % (cond,dist1,dist2) 40 | ins, count = ks.asm("b%s #0x%x;b #0x%x" % (cond,dist1,dist2),ori) 41 | return ins 42 | 43 | # callback for tracing instructions 44 | def hook_code(uc, address, size, user_data): 45 | global real_blocks 46 | global csel_addrs 47 | global list_trace 48 | global startaddr 49 | global debugflag 50 | global isSucess 51 | global distAddr 52 | global branch_control 53 | global list_blocks 54 | ban_ins = ["bl"] 55 | 56 | if isSucess: 57 | mu.emu_stop() 58 | #raw_input() 59 | return 60 | 61 | if address > end: 62 | uc.emu_stop() 63 | return 64 | 65 | for ins in md.disasm(bin1[address:address+size],address): 66 | print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size)) 67 | print(">>> 0x%x:\t%s\t%s" % (ins.address, ins.mnemonic, ins.op_str)) 68 | print 69 | 70 | if address in real_blocks: 71 | if list_trace.has_key(address): 72 | print "sssssss" 73 | ch = raw_input("This maybe a fake block. codesign:%s " % get_code_sign(list_blocks[address])) 74 | 75 | uc.emu_stop() 76 | else: 77 | list_trace[address] = 1 78 | 79 | if address in real_blocks and address != startaddr: 80 | isSucess = True 81 | distAddr = address 82 | print 'find:%x' % address 83 | uc.emu_stop() 84 | return 85 | 86 | #是否跳过指令 87 | flag_pass = False 88 | for b in ban_ins: 89 | if ins.mnemonic.find(b) != -1: 90 | flag_pass = True 91 | break 92 | 93 | #只允许对栈的操作 94 | if ins.op_str.find('[') != -1: 95 | if ins.op_str.find('[sp') == -1: 96 | flag_pass = True 97 | for op in ins.operands: 98 | if op.type == ARM64_OP_MEM: 99 | addr = 0 100 | if op.value.mem.base != 0: 101 | addr += mu.reg_read(reg_ctou(ins.reg_name(op.value.mem.base))) 102 | elif op.value.index != 0: 103 | addr += mu.reg_read(reg_ctou(ins.reg_name(op.value.mem.index))) 104 | elif op.value.disp != 0: 105 | addr += op.value.disp 106 | if addr >= 0x80000000 and addr < 0x80000000 + 0x10000 * 8: 107 | flag_pass = False 108 | if flag_pass: 109 | print("will pass 0x%x:\t%s\t%s" %(ins.address, ins.mnemonic, ins.op_str)) 110 | uc.reg_write(UC_ARM64_REG_PC, address + size) 111 | return 112 | 113 | 114 | #breaks 0x31300 115 | if address in [ 0xB72EC ] or debugflag: 116 | debugflag = True 117 | print("0x%x:\t%s\t%s" % (ins.address, ins.mnemonic, ins.op_str)) 118 | while True: 119 | c = raw_input('>') 120 | if c == '': 121 | break 122 | if c == 's': 123 | uc.emu_stop() 124 | return 125 | if c == 'r': 126 | debugflag = False 127 | break 128 | if c[0] == '!': 129 | reg = reg_ctou(c[1:]) 130 | print "%s=%x (%d)" % (c[1:], mu.reg_read(reg),mu.reg_read(reg)) 131 | continue 132 | 133 | if ins.mnemonic == 'ret': 134 | uc.reg_write(UC_ARM64_REG_PC, 0) 135 | isSucess = False 136 | print "ret ins.." 137 | mu.emu_stop() 138 | 139 | #ollvm branch 140 | if ins.mnemonic == 'csel': 141 | print("csel 0x%x:\t%s\t%s" %(ins.address, ins.mnemonic, ins.op_str)) 142 | regs = [reg_ctou(x) for x in ins.op_str.split(', ')] 143 | assert len(regs) == 4 144 | v1 = uc.reg_read(regs[1]) 145 | v2 = uc.reg_read(regs[2]) 146 | if branch_control == 1: 147 | uc.reg_write(regs[0],v1) 148 | else: 149 | uc.reg_write(regs[0],v2) 150 | uc.reg_write(UC_ARM64_REG_PC, address + size) 151 | 152 | # callback for memory exception 153 | def hook_mem_access(uc,type,address,size,value,userdata): 154 | pc = uc.reg_read(UC_ARM64_REG_PC) 155 | print 'pc:%x type:%d addr:%x size:%x' % (pc,type,address,size) 156 | #uc.emu_stop() 157 | return False 158 | 159 | 160 | def get_code_sign(block): 161 | insn = block['capstone'] 162 | sign = '' 163 | for i in insn: 164 | sign += i.mnemonic 165 | for op in i.operands: 166 | sign += str(op.type) 167 | return sign 168 | 169 | def get_context(): 170 | global mu 171 | regs = [] 172 | for i in range(31): 173 | idx = UC_ARM64_REG_X0 + i 174 | regs.append(mu.reg_read(idx)) 175 | regs.append(mu.reg_read(UC_ARM64_REG_SP)) 176 | return regs 177 | 178 | def set_context(regs): 179 | global mu 180 | if regs == None: 181 | return 182 | for i in range(31): 183 | idx = UC_ARM64_REG_X0 + i 184 | mu.reg_write(idx,regs[i]) 185 | mu.reg_write(UC_ARM64_REG_SP,regs[31]) 186 | 187 | def find_path(start_addr,branch = None): 188 | global real_blocks 189 | global bin1 190 | global mu 191 | global list_trace 192 | global startaddr 193 | global distAddr 194 | global isSucess 195 | global branch_control 196 | try: 197 | list_trace = {} 198 | startaddr = start_addr 199 | isSucess = False 200 | distAddr = 0 201 | branch_control = branch 202 | mu.emu_start(start_addr,0x10000) 203 | print "emu end.." 204 | 205 | except UcError as e: 206 | pc = mu.reg_read(UC_ARM64_REG_PC) 207 | print ("111 pc:%x" % pc) 208 | if pc != 0: 209 | #mu.reg_write(UC_ARM64_REG_PC, pc + 4) 210 | return find_path(pc + 4,branch) 211 | else: 212 | print("ERROR: %s pc:%x" % (e,pc)) 213 | if isSucess: 214 | return distAddr 215 | return None 216 | 217 | ssign = {u'b2', 218 | u'cmp11b.ne2', 219 | u'cmp11mov11b.ne2', 220 | u'movz12movk12b2', 221 | u'movz12movk12cmp11b.ne2', 222 | u'movz12movk12cmp11mov11b.ne2', 223 | 'movz12movk12cmp11mov11movz12movk12movz12movk12b.ne2', 224 | u'movz12movk12cmp11movz12movk12b.eq2', 225 | 'movz12movk12cmp11movz12movk12b.ne2', 226 | 'movz12movk12movz12movk12b2', 227 | 'movz12movk12cmp11movz12movk12movz12movk12movz12movk12b.eq2', 228 | 'movz12movk12movz12movk12cmp11b.eq2', 229 | 'movz12movk12movz12movk12cmp11movz12movk12b.eq2', 230 | 'movz12movk12cmp11b.eq2', 231 | 'ldr13b2', 232 | 'mov11movz12movk12cmp11movz12movk12b.eq2' 233 | } 234 | ssign2 = set() 235 | def is_real_blocks(ins): 236 | sign = get_code_sign(ins) 237 | if sign in ssign: 238 | return False 239 | if sign.endswith('movk12movz12movk12b.ne2'): 240 | return False 241 | 242 | for insn in item['capstone']: 243 | #print insn.mnemonic 244 | if insn.mnemonic not in ['movz','movk','cmp','b.eq','b.ne']: 245 | return True 246 | ssign2.add(sign) 247 | return False 248 | 249 | def draw(): 250 | queue = [] 251 | dot = Digraph(comment='The Round Table') 252 | dot.attr('node', shape='box') 253 | queue.append(offset) 254 | check = {} 255 | while len(queue) > 0: 256 | pc = queue.pop() 257 | if check.has_key(pc): 258 | continue 259 | check[pc] = 1 260 | dot.node(hex(pc),list_blocks[pc]['ins']) 261 | for i in flow[pc]: 262 | if i != None: 263 | dot.edge(hex(pc), hex(i), constraint='true') 264 | queue.append(i) 265 | dot.render('test-output/round-table.gv', view=True) 266 | 267 | def fix(bin): 268 | queue.append(offset) 269 | check = {} 270 | while len(queue) > 0: 271 | pc = queue.pop() 272 | if check.has_key(pc): 273 | continue 274 | check[pc] = 1 275 | 276 | if(len(flow[pc]) == 2): 277 | insn = list_blocks[pc]["capstone"][-2] 278 | patch_offset = list_blocks[pc]["eaddress"] - 4 279 | if insn.mnemonic == 'csel': 280 | opcode = asm_has_branch(patch_offset, flow[pc][0], flow[pc][1], insn.op_str[-2:]) 281 | op_str = "".join([ chr(i) for i in opcode ]) 282 | bin = bin[:patch_offset] + op_str + bin[patch_offset+8:] 283 | else: 284 | print "error !!!!!!" 285 | raw_input() 286 | 287 | if(len(flow[pc]) == 1): 288 | patch_offset = list_blocks[pc]["eaddress"] 289 | opcode = asm_no_branch(patch_offset, flow[pc][0]) 290 | op_str = "".join([ chr(i) for i in opcode ]) 291 | bin = bin[:patch_offset] + op_str + bin[patch_offset+4:] 292 | 293 | if(len(flow[pc]) == 0): 294 | #ret block 295 | continue 296 | 297 | for i in flow[pc]: 298 | if i != None: 299 | queue.append(i) 300 | 301 | with open("libvdog.new.so","wb") as fp: 302 | fp.write(bin) 303 | 304 | def print_real_blocks(): 305 | print '######################### real blocks ###########################' 306 | cnt = 0 307 | for i in real_blocks: 308 | item = list_blocks[i] 309 | print item['ins'] 310 | print 311 | if cnt == 50: 312 | c = raw_input() 313 | if c == 'c': 314 | return 315 | cnt += 1 316 | 317 | 318 | offset = 0x70438 #function start 319 | end = 0x7170C #function end 320 | 321 | bin1 = open('libvdog.so','rb').read() 322 | md = Cs(CS_ARCH_ARM64,CS_MODE_ARM) 323 | md.detail = True #enable detail analyise 324 | 325 | list_blocks = {} 326 | block_item = {} 327 | processors = {} 328 | dead_loop = [] 329 | real_blocks = [] 330 | csel_list_cond = {} 331 | 332 | 333 | isNew = True 334 | insStr = '' 335 | for i in md.disasm(bin1[offset:end],offset): 336 | insStr += "0x%x:\t%s\t%s\n" %(i.address, i.mnemonic, i.op_str) 337 | if isNew: 338 | isNew = False 339 | block_item = {} 340 | block_item["saddress"] = i.address 341 | block_item['capstone'] = [] 342 | 343 | block_item['capstone'].append(i) 344 | block_item['flag'] = False 345 | 346 | if len(i.groups) > 0 or i.mnemonic == 'ret': 347 | isNew = True 348 | block_item["eaddress"] = i.address 349 | block_item['ins'] = insStr 350 | 351 | for op in i.operands: 352 | if op.type == ARM64_OP_IMM: 353 | block_item["naddress"] = op.value.imm 354 | 355 | if op.value.imm == i.address: 356 | print "dead loop:%x" % i.address 357 | dead_loop.append(i.address) 358 | 359 | if not processors.has_key(op.value.imm): 360 | processors[op.value.imm] = 1 361 | else: 362 | processors[op.value.imm] += 1 363 | if not block_item.has_key("naddress"): 364 | block_item['naddress'] = None 365 | list_blocks[block_item["saddress"]] = block_item 366 | 367 | insStr = '' 368 | 369 | #delete dead loop 370 | for dead in dead_loop: 371 | if processors.has_key(dead): 372 | del(processors[dead]) 373 | 374 | for b in list_blocks: 375 | if processors.has_key(list_blocks[b]['naddress']): 376 | if processors[list_blocks[b]['naddress']] > 1: 377 | real_blocks.append(list_blocks[b]['saddress']) 378 | 379 | fake_blocks = [] 380 | for i in real_blocks: 381 | item = list_blocks[i] 382 | if not is_real_blocks(item): 383 | print '## fake block ###' 384 | print item['ins'] 385 | fake_blocks.append(i) 386 | 387 | for x in fake_blocks: 388 | real_blocks.remove(x) 389 | 390 | for i in list_blocks: 391 | if list_blocks[i]['ins'].find('ret') != -1: 392 | print 'ret block:%x' % i 393 | real_blocks.append(i) 394 | 395 | print '######################### real blocks ###########################' 396 | print [hex(x) for x in real_blocks] 397 | 398 | #Initialize emulator in ARM64 mode 399 | mu = Uc(UC_ARCH_ARM64, UC_MODE_ARM) 400 | 401 | #init stack 402 | mu.mem_map(0x80000000,0x10000 * 8) 403 | 404 | # map 4MB memory for this emulation 405 | mu.mem_map(0, 4 * 1024 * 1024) 406 | 407 | # write machine code to be emulated to memory 408 | mu.mem_write(0, bin1) 409 | mu.reg_write(UC_ARM64_REG_SP, 0x80000000 + 0x10000 * 6) 410 | mu.hook_add(UC_HOOK_CODE, hook_code) 411 | mu.hook_add(UC_HOOK_MEM_UNMAPPED, hook_mem_access) 412 | 413 | list_trace = {} 414 | debugflag = False 415 | 416 | if offset in real_blocks: 417 | real_blocks.remove(offset) 418 | 419 | queue = [(offset,None)] 420 | flow = {} 421 | 422 | while len(queue) != 0: 423 | env = queue.pop() 424 | pc = env[0] 425 | set_context(env[1]) 426 | item = list_blocks[pc] 427 | if pc in flow: 428 | #print "???" 429 | continue 430 | flow[pc] = [] 431 | 432 | #代码块中有ollvm生成的分支 433 | if item['ins'].find('csel') != -1: 434 | #raw_input() 435 | ctx = get_context() 436 | p1 = find_path(pc,0) 437 | if p1 != None: 438 | queue.append((p1,get_context())) 439 | flow[pc].append(p1) 440 | 441 | set_context(ctx) 442 | p2 = find_path(pc,1) 443 | 444 | if p1 == p2: 445 | p2 = None 446 | 447 | if p2 != None: 448 | queue.append((p2,get_context())) 449 | flow[pc].append(p2) 450 | else: 451 | p = find_path(pc) 452 | if p != None: 453 | queue.append((p,get_context())) 454 | flow[pc].append(p) 455 | draw() 456 | fix(bin1) 457 | 458 | -------------------------------------------------------------------------------- /libvdog.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeT1t/deollvm/bd09e6c9e38461f9d0768a781893a3b2543b7ce3/libvdog.so --------------------------------------------------------------------------------