def patch_32_esp(addr, sub_value, is_sub_esp): count = idc.ItemSize(addr) #get value v = idc.GetOperandValue(addr, 1) #print(hex(v)) if v == -1: print('get value error') return if count == 3: #.text:0804867C 83 EC 18 sub esp, 18h off = 0xff - v if sub_value < off: idc.PatchByte(addr + 2, v + sub_value) else: idc.PatchByte(addr + 2, 0xff) idc.MakeCode(addr) else: #.text:0804875B 81 EC 30 02 00 00 sub esp, 230h idc.PatchDword(addr + 2, v + sub_value) idc.MakeCode(addr) if is_sub_esp != 0: print('patch [sub esp, %s] ok, addr: %s' % (hex(v), hex(addr))) else: print('patch [add esp, %s] ok, addr: %s' % (hex(v), hex(addr))) return
def patch_64_rsp(addr, sub_value, is_sub_rsp): count = idc.ItemSize(addr) #get value v = idc.GetOperandValue(addr, 1) #print(hex(v)) if v == -1: print('get value error') return if count == 4: #.text:000055BBF4127FD9 48 83 EC 10 sub rsp, 10h off = 0xff - v if sub_value < off: idc.PatchByte(addr + 3, v + sub_value) else: idc.PatchByte(addr + 3, 0xff) idc.MakeCode(addr) else: #.text:00007EFEA44A5310 48 81 EC 20 01 00 00 sub rsp, 120h idc.PatchDword(addr + 3, v + sub_value) idc.MakeCode(addr) if is_sub_rsp != 0: print('patch [sub rsp, %s] ok, addr: %s' % (hex(v), hex(addr))) else: print('patch [add rsp, %s] ok, addr: %s' % (hex(v), hex(addr))) return
def FixFmulY(head, y, bFun): # fix y global g_off_y for off in g_off_y: if bFun: idc.PatchByte(head + off + 7, y) else: idc.PatchByte(head + off, y) return
def patch(self, patch, fill_nop=True): """Change the content of object by `patch` if fill_nop is True and size(patch) < size(object): add some 0x90 """ print("PATCH ASKED at <{0}| size {1}> with {2}".format( self.addr, self.size, patch)) nop = 0x90 #<- need to adapt to other platform if self.size < len(patch): raise ValueError("Patch if too big for {0}".format(self)) if self.size != len(patch) and not fill_nop: pass # raise Value("Patch is too small for {0} and no fill_patch (better idea than raise ?)".format(self)) # Not patching other bytes seems cool ? full_patch = list(patch) + [nop] * (self.size - len(patch)) for addr, byte in zip(range(self.addr, self.addr + self.size), full_patch): if isinstance(byte, str): byte = ord(byte) if idc.Byte(addr) == byte: print("NOPATCH BYTE : SAME VALUE") continue if not idc.PatchByte(addr, byte): print("PATCH addr {0} with byte {1} failed".format( hex(addr), hex(byte)))
def patchBinary(start, data, size=-1): if size < 0: size = len(data) else: size = min(len(data), size) for i in range(size): idc.PatchByte(start + i, ord(data[i]))
def resolve_loops(): PATTERNS = ["81 FB ?? ?? ?? ?? 75"] count_patched = 0 count_not_patched = 0 for pattern in PATTERNS: ea = 0 while ea != BADADDR: ''' pattern: 81 FB ?? ?? ?? ?? 75 .text:00406AA0 01 C7 add edi, eax .text:00406AA2 66 41 inc cx .text:00406AA4 43 inc ebx .text:00406AA5 81 FB A6 01 00 00 cmp ebx, 1A6h .text:00406AAB 75 F3 jnz short loc_406AA0 patched: .text:00406AA0 01 C7 add edi, eax .text:00406AA2 66 41 inc cx .text:00406AA4 43 inc ebx .text:00406AA5 90 nop .text:00406AA6 90 nop .text:00406AA7 90 nop .text:00406AA8 90 nop .text:00406AA9 90 nop .text:00406AAA 90 nop .text:00406AAB 90 nop .text:00406AAC 90 nop ''' ea = idc.FindBinary(ea, SEARCH_NEXT | SEARCH_DOWN | SEARCH_CASE, pattern) if ea_in_bounds(ea): # Patch CMP and conditional jmp instructions in order to remove the loop idc.PatchByte(ea + 0, 0x90) idc.PatchByte(ea + 1, 0x90) idc.PatchByte(ea + 2, 0x90) idc.PatchByte(ea + 3, 0x90) idc.PatchByte(ea + 4, 0x90) idc.PatchByte(ea + 5, 0x90) idc.PatchByte(ea + 6, 0x90) idc.PatchByte(ea + 7, 0x90) idc.MakeCode(ea) count_patched += 1 print "\tPatched resolve_loops: {0}".format(count_patched) print "\tNot Patched resolve_loops: {0}".format(count_not_patched)
def copy(fl, ea=None): if ea is None: ea = idc.ScreenEA() fl = open(fl, 'rb') ctn = fl.read() fl.close() for i, c in enumerate(ctn): idc.PatchByte(ea + i, ord(c))
def mem_hook(unicornObject, accessType, memAccessAddress, memAccessSize, memValue, userData): #if accessType == UC.UC_MEM_READ: # print("Read: ", hex(memAccessAddress), memAccessSize, hex(memValue)) if accessType == UC.UC_MEM_WRITE: #print("Write: ", hex(memAccessAddress), memAccessSize, hex(memValue)) if memAccessSize == 1: idc.PatchByte(memAccessAddress, memValue) elif memAccessSize == 2: idc.PatchWord(memAccessAddress, memValue) elif memAccessSize == 4: idc.PatchDword(memAccessAddress, memValue)
def patch_call_free(addr): count = idc.ItemSize(addr) #.text:000011E1 E8 5A FE FF FF call _free #.text:000055BBF4127EFF E8 5C F9 FF FF call _free for i in range(count): idc.PatchByte(addr + i, 0x90) #patch to NOP instr idc.MakeCode(addr) print('patch [call _free] ok, addr: %s' % hex(addr)) return
def PatchCall(addr): ''' To improve IDA's analysis, the patch converts call abuse into push and jmp instructions. The patched data is saved as a comment. ''' if idc.Byte(addr) == 0xE8: logger = logging.getLogger(__name__) logger.info("Patching push as call @ %x" % addr) data_size = idc.Dword(addr + 1) if (data_size) >= 0xfe: logger.info("Can't patch: 0x%x" % addr) return fname = idc.GetFunctionName(addr) target = idc.GetOperandValue(addr, 0) data_addr = addr + idautils.DecodeInstruction(addr).size orig_data = idc.Word(data_addr) orig_asm = idc.GetDisasm(addr) idc.MakeUnknown(addr, target - addr, idaapi.DOUNK_EXPAND | idaapi.DOUNK_DELNAMES) idc.PatchByte(addr, 0x68) idc.PatchDword(addr + 1, data_addr) idc.PatchByte(data_addr, 0xEB) idc.PatchByte(data_addr + 1, data_size - 2) # idaapi.analyze_area(addr, data_addr + 2) uksize = 0 if fname != idc.GetFunctionName(target) and fname is not None: while uksize < 32: x = idautils.DecodeInstruction(target + uksize) if x is None: break uksize += x.size idc.MakeUnknown(target, uksize, idaapi.DOUNK_EXPAND | idaapi.DOUNK_DELNAMES) # idaapi.analyze_area(target, target + uksize + 1) idc.MakeComm(addr, "Original: " + orig_asm) idc.MakeComm(data_addr, "Original: %x" % orig_data)
def to_dbg(self, found_state): if type(found_state) == StateManager: return self.to_dbg(found_state.state) for key in self.symbolics: try: if key in load_project().arch.registers: r = found_state.solver.eval(self.symbolics[key][0], cast_to=int) idc.SetRegValue(r, key) else: r = found_state.solver.eval(self.symbolics[key][0], cast_to=str) for i in xrange(len(r)): idc.PatchByte(key + i, ord(r[i])) except Exception as ee: print " >> failed to write %s to debugger" % key
def patch_word(ea, value, wordsize=WORD_SIZE): """Patch the word at the given address. Words are patched using PatchByte(), PatchWord(), PatchDword(), or PatchQword(), as appropriate. """ if wordsize == 1: idc.PatchByte(ea, value) elif wordsize == 2: idc.PatchWord(ea, value) elif wordsize == 4: idc.PatchDword(ea, value) elif wordsize == 8: idc.PatchQword(ea, value) else: raise ValueError('Invalid argument: wordsize={}'.format(wordsize))
def resolve_opaque_jnz_jz(): PATTERNS = [ "0F 84 ?? ?? ?? ?? 0F 85 ?? ?? ?? ??", "0F 85 ?? ?? ?? ?? 0F 84 ?? ?? ?? ??" ] count_patched = 0 count_not_patched = 0 for pattern in PATTERNS: ea = 0 while ea != BADADDR: ea = idc.FindBinary(ea, SEARCH_NEXT | SEARCH_DOWN | SEARCH_CASE, pattern) ''' pattern: 0F 85 ?? ?? ?? ?? 0F 84 ?? ?? ?? ?? .text:0040690E 66 21 CF and di, cx .text:00406911 0F 85 AE F4 FF FF jnz loc_405DC5 <- j_1_pos .text:00406917 0F 84 A8 F4 FF FF jz loc_405DC5 <- j_2_pos patched: .text:0040690E 66 21 CF and di, cx .text:00406911 90 nop .text:00406912 90 nop .text:00406913 90 nop .text:00406914 90 nop .text:00406915 90 nop .text:00406916 90 nop .text:00406917 E9 A9 F4 FF FF jmp loc_405DC5 ''' if ea_in_bounds(ea): # .text:00406911 0F 85 AE F4 FF FF jnz loc_405DC5 <- j_1_pos # AE F4 FF FF <- j_1_value Relative offset value j_1_pos = ea j_1_value = Dword(j_1_pos + 0x2) j_2_pos = j_1_pos + 0x6 j_2_value = Dword(j_2_pos + 0x2) pos_jmp = j_1_pos + j_1_value + 0x6 if j_1_value - j_2_value == 0x6: addr_to_jmp = j_2_value + 0x1 # Patch the jz and jnz instructions with NOPs (12 bytes) for i in range(0, 12): idc.PatchByte(j_1_pos + i, 0x90) # Patch with a relative jmp (size = 5) in the position of the second conditional jmp idc.PatchByte(j_2_pos, 0xE9) idc.PatchDword(j_2_pos + 0x1, addr_to_jmp) idc.MakeCode(ea) count_patched += 1 else: count_not_patched += 1 print "\tPatched resolve_opaque_jnz_jz: {0}".format(count_patched) print "\tNot Patched resolve_opaque_jnz_jz: {0}".format(count_not_patched)
def patch(dest, seq): for i, c in enumerate(seq): idc.PatchByte(dest+i, ord(c))
def MemCopy( dest, src, length ) : for i in xrange(0, length): #if (i < 20): # print "set byte at %#x to %#x" % (dest+i, idc.Byte(src+i)) idc.PatchByte(dest+i,idc.Byte(src+i))
def fix_callgraph(msgsend, segname, class_param, sel_param): ''' fix_callgraph: msgsend, segname, class_param, sel_param Given the msgsend flavour address as a parameter, looks for the parameters (class and selector, identified by class_param and sel_param) and creates a new segment where it places a set of dummy calls named as classname_methodname (we use method instead of selector most of the time). ''' t1 = time.time() if not msgsend: print 'ERROR: msgSend not found' return total = 0 resolved = 0 call_table = dict() for xref in idautils.XrefsTo(msgsend, idaapi.XREF_ALL): total += 1 ea_call = xref.frm func_start = idc.GetFunctionAttr(ea_call, idc.FUNCATTR_START) if not func_start or func_start == idc.BADADDR: continue ea = ea_call method_name_ea = track_param(ea, func_start, idc.o_displ, sel_param) if method_name_ea: method_name = idc.GetString(method_name_ea, -1, idc.ASCSTR_C) if not method_name: method_name = '' else: method_name = '' class_name_ea = track_param(ea, func_start, idc.o_phrase, class_param) if class_name_ea: class_name = idc.GetString(class_name_ea, -1, idc.ASCSTR_C) if not class_name: class_name = '' else: class_name = '' if not method_name and not class_name: continue # Using this name convention, if the class and method # are identified by IDA, the patched call will point to # the REAL call and not one of our dummy functions # class_name = class_name.replace('_objc_class_name_', '') new_name = '_[' + class_name + '_' + method_name + ']' call_table[ea_call] = new_name resolved += 1 print '\nFinal stats:\n\t%d total calls, %d resolved' % (total, resolved) print '\tAnalysis took %.2f seconds' % (time.time() - t1) if resolved == 0: print 'Nothing to patch.' return print 'Adding new segment to store new nullsubs' # segment size = opcode ret (4 bytes) * num_calls seg_size = resolved * 4 seg_start = idc.MaxEA() + 4 idaapi.add_segm(0, seg_start, seg_start + seg_size, segname, 'CODE') print 'Patching database...' seg_ptr = seg_start for ea, new_name in call_table.items(): if idc.LocByName(new_name) != idc.BADADDR: offset = (idc.LocByName(new_name) - ea) & idc.BADADDR else: # create code and name it idc.PatchDword(seg_ptr, 0x90) # nop idc.MakeName(seg_ptr, new_name) idc.MakeCode(seg_ptr) idc.MakeFunction(seg_ptr, seg_ptr + 4) idc.MakeRptCmt(seg_ptr, new_name) offset = seg_ptr - ea seg_ptr += 4 dw = offset - 5 idc.PatchByte(ea, 0xE8) idc.PatchDword(ea + 1, dw)
def resolve_opaque_mov_push(): PATTERNS = [ "BB 00 00 00 00", "BB 01 00 00 00", "BB 02 00 00 00", "BB 03 00 00 00", "BB 04 00 00 00", "BB 05 00 00 00", "BB 06 00 00 00", "BB 07 00 00 00", "BB 08 00 00 00", "BB 09 00 00 00", "6A ?? 5B" ] count_patched = 0 count_not_patched = 0 for pattern in PATTERNS: ea = 0 print pattern while ea != BADADDR: ea = idc.FindBinary(ea, SEARCH_NEXT | SEARCH_DOWN | SEARCH_CASE, pattern) ''' pattern: BB 00 00 00 00 .text:00406A83 BB 00 00 00 00 mov ebx, 0 .text:00406A88 66 01 D0 add ax, dx .text:00406A8B 81 F1 B5 15 00 00 xor ecx, 15B5h .text:00406A91 66 35 7A 13 xor ax, 137Ah .text:00406A95 85 DB test ebx, ebx .text:00406A97 74 14 jz short loc_406AAD Patched EBX > 0 pattern: BB 09 00 00 00 .text:00406BE3 BB 09 00 00 00 mov ebx, 9 .text:00406BE8 20 D1 and cl, dl .text:00406BEA 66 09 D6 or si, dx .text:00406BED 66 89 D6 mov si, dx .text:00406BF0 85 DB test ebx, ebx .text:00406BF2 74 02 jz short loc_406BF6 Patched EBX == 0 .text:00406A83 90 nop .text:00406A84 90 nop .text:00406A85 90 nop .text:00406A86 90 nop .text:00406A87 90 nop .text:00406A88 90 nop .text:00406A89 90 nop .text:00406A8A 90 nop .text:00406A8B 90 nop .text:00406A8C 90 nop .text:00406A8D 90 nop .text:00406A8E 90 nop .text:00406A8F 90 nop .text:00406A90 90 nop .text:00406A91 90 nop .text:00406A92 90 nop .text:00406A93 90 nop .text:00406A94 90 nop .text:00406A95 90 nop .text:00406A96 90 nop .text:00406A97 EB 14 jmp short loc_406AAD ''' if ea_in_bounds(ea): ''' .text:00406A83 BB 00 00 00 00 mov ebx, <0-9> <- ebx_value .text:00406A88 66 01 D0 add ax, dx .text:00406A8B 81 F1 B5 15 00 00 xor ecx, 15B5h .text:00406A91 66 35 7A 13 xor ax, 137Ah .text:00406A95 85 DB test ebx, ebx .text:00406A97 74 14 jz short loc_406AAD ''' original_ea = ea ebx_value = Byte(ea + 1) instr = idautils.DecodeInstruction(ea) if instr: has_test = False # while not jmp related instruction found while ((instr.itype <= idaapi.NN_ja) or (instr.itype >= idaapi.NN_jmpshort)): # move to next instr ea = ea + instr.size instr = idautils.DecodeInstruction(ea) # Check in order to validate that has test func and is candidate to be patched if instr.itype == idaapi.NN_test: has_test = True # at this point "ea" variable contains the last instruction address # that is the conditional jump found if has_test: if instr.itype == idaapi.NN_jz: # ebx_value > 0 and NN_jz -> Patch with NOPs if ebx_value > 0: ''' .text:00406BE3 BB 09 00 00 00 mov ebx, 9 .text:00406BE8 20 D1 and cl, dl .text:00406BEA 66 09 D6 or si, dx .text:00406BED 66 89 D6 mov si, dx .text:00406BF0 85 DB test ebx, ebx .text:00406BF2 74 02 jz short loc_406BF6 ''' relative_offset = ea - original_ea # Patch the complete function number_nops = ea - original_ea + instr.size for i in range(0, number_nops): idc.PatchByte(original_ea + i, 0x90) idc.MakeCode(ea) # ebx_value = 0 and NN_jz -> Patch with JMP else: ''' .text:00406A83 BB 00 00 00 00 mov ebx, 0 .text:00406A88 66 01 D0 add ax, dx .text:00406A8B 81 F1 B5 15 00 00 xor ecx, 15B5h .text:00406A91 66 35 7A 13 xor ax, 137Ah .text:00406A95 85 DB test ebx, ebx .text:00406A97 74 14 jz short loc_406AAD ''' # ea contains the conditional jmp address relative_offset = Byte(ea + 1) # NOP number_nops = ea - original_ea + instr.size for i in range(0, number_nops): idc.PatchByte(original_ea + i, 0x90) # Patch the conditional jmp to unconditional jmp idc.PatchByte(ea, 0xEB) idc.PatchByte(ea + 1, relative_offset) idc.MakeCode(ea) count_patched += 1 else: count_not_patched += 1 print "\tPatched resolve_opaque_mov_push: {0}".format(count_patched) print "\tNot Patched resolve_opaque_mov_push: {0}".format( count_not_patched)
def load_file(li, neflags, format): li.seek(0) ndsRom = NintendoDSRom(li.read(li.size())) retval = 1 useArm9 = ask_yn( 1, "This ROM potentially contains both ARM9 and ARM7 code\nDo you want to load the ARM9 binary?" ) if (useArm9 == -1): useArm9 = 0 useArm9 = bool(useArm9) proc = "" startEA = 0 endEA = 0 offset = 0 entryAddr = 0 size = 0 name = "" rom = "" if (useArm9): name = "ARM9 ROM" proc = "ARM" entryAddr = ndsRom.arm9EntryAddress startEA = ndsRom.arm9RamAddress endEA = ndsRom.arm9RamAddress + ndsRom.arm9Len offset = ndsRom.arm9Offset size = ndsRom.arm9Len rom = ndsRom.arm9 else: name = "ARM7 ROM" proc = "ARM710A" entryAddr = ndsRom.arm7EntryAddress startEA = ndsRom.arm7RamAddress endEA = ndsRom.arm7RamAddress + ndsRom.arm7Len offset = ndsRom.arm7Offset size = ndsRom.arm7Len rom = ndsRom.arm7 idaapi.set_processor_type( proc, idaapi.SETPROC_LOADER_NON_FATAL | idaapi.SETPROC_LOADER) memory = \ [ [ startEA, endEA, "RAM" ], [ 0x04000000, 0x04001056, "General_Regs" ], [ 0x05000000, 0x05000600, "VMEM_Regs" ], ] if ((startEA < memory[0][0] or endEA > memory[0][1]) and (startEA < memory[1][0] or endEA > memory[1][1]) and (startEA < memory[2][0] or endEA > memory[2][1])): raise Exception("ROM not mapped into valid mem!") for segment in memory: idc.AddSeg(segment[0], segment[1], 0, 1, idaapi.saRelPara, idaapi.scPub) idc.RenameSeg(segment[0], segment[2]) if "RAM" not in segment[2]: for i in xrange(segment[0], segment[1]): idc.PatchByte(i, 0) idaapi.add_entry(entryAddr, entryAddr, "start", 1) idc.MakeNameEx(entryAddr, "start", idc.SN_NOCHECK | idc.SN_NOWARN) idaapi.cvar.inf.startIP = entryAddr idaapi.cvar.inf.beginEA = entryAddr ida_segment.set_selector(1, 0) idaapi.cvar.inf.startCS = 1 li.seek(0) li.file2base(offset, startEA, endEA, 1) idaapi.cvar.inf.startCS = 0 idaapi.cvar.inf.startIP = entryAddr idc.ExtLinA(startEA, 1, "; Title : " + str(ndsRom.name)) idc.ExtLinA(startEA, 1, "; Software Version: " + str(ndsRom.version)) # Add TwlHdr MakeVideoRegs() MakeVMemRegs() MakeJoypadRegs() MakeSystemRegs() if name == "ARM7 ROM": MakeARM7Regs() else: MakeARM9Regs() print("Done! Entry point @ " + hex(entryAddr)) return 1
def BuildAsmString(self, instr, function): if instr.GetMnem() == 'retn': mnem = 'ret ' else: if instr.GetMnemPrefix(1) != '': mnem = instr.GetMnemPrefix(1) + ' ' + instr.GetMnem(1) + ' ' else: mnem = instr.GetMnem(1) + ' ' refs = list(function.GetRefsFrom(instr.GetOriginEA())) if len(refs) > 1 and instr.GetMnem() != "jmp": key = refs[0][0] if refs[0][1] else refs[1][0] if self.nasm: self.NasmWriteToFile("\t%s L%08x" % (instr.GetMnem(1), key), instr) else: if self.bb_head_ea.has_key(key): mnem += '%09xh' % self.bb_head_ea[key] return mnem else: for i in xrange(0, 6): idc.PatchByte(self.free_ea + i, 0x90) if self.jmp_deps.has_key(key): self.jmp_deps[key].append(self.free_ea) else: self.jmp_deps[key] = [self.free_ea] self.ref_instr[self.free_ea] = instr self.free_ea += 6 return '' if instr.GetMnem() == 'call': if instr.GetOpndType(1) in [2, 5, 6]: mnem += '%09xh' % instr.GetOpndValue(1) elif instr.GetOpndType(1) == 7: comment = instr.GetComment() if comment != None and comment == "Artificial: Gen from call $+5": mnem += '$+5' else: mnem += '%09xh' % instr.GetOpndValue(1) elif instr.GetOpndType(1) == 1: mnem += instr.GetOpnd(1) else: mnem += instr.GetOpnd(1) if self.nasm: self.NasmWriteToFile("\t%s" % mnem, instr) return mnem elif instr.GetMnem() == 'jmp': if self.nasm: tref_from = list(function.GetRefsFrom( instr.GetOriginEA()))[0][0] if tref_from == None: #can be unknown if debug: print ">Assemble:BuildAsmString - tref_from = None @ [%08x] " % instr.GetOriginEA( ), instr.GetOpnd(1) if instr.GetOpndType(1) == 2: size_prefix = idaapi.get_item_size( instr.GetOpndValue(1)) self.NasmWriteToFile( "\tjmp %s 0x%08x" % (instr.BytesToPrefix(size_prefix), instr.GetOpndValue(1)), instr) else: self.NasmWriteToFile("\tjmp %s" % instr.GetOpnd(1), instr) else: if len(refs) > 1: instr_string = "jmp %s" % instr.GetOpnd(1).replace( "SUBS", "sj_%08x_1" % instr.GetOriginEA()) self.NasmWriteToFile("\t%s\n" % instr_string) self.jmp_table_refs.append(instr_string) self.jmp_table += "\n" #self.NasmWriteToFile("\tsj_%08x " % instr.GetOriginEA()) counter = 1 name = 'sj_%08x' % instr.GetOriginEA() for ref in refs: #self.NasmWriteToFile("%s_%d dd L%08x\n" % (name, counter, ref[0])) self.jmp_table += "%s_%d dd L%08x\n" % ( name, counter, ref[0]) counter += 1 else: self.NasmWriteToFile("\tjmp L%08x" % tref_from, instr) return None if debug: print ">Assemble:BuildAsmString - Got mnem JMP ", refs if len(refs) > 1: key = refs[0][0] if refs[0][1] else refs[1][0] else: key = refs[0][0] if self.bb_head_ea.has_key(key): mnem += '%xh' % self.bb_head_ea[key] return mnem elif refs[0][0] == None: #NOTICE! mnem += instr.GetOpnd(1) return mnem else: for i in xrange(0, 6): idc.PatchByte(self.free_ea + i, 0x90) if self.jmp_deps.has_key(key): self.jmp_deps[key].append(self.free_ea) else: self.jmp_deps[key] = [self.free_ea] self.ref_instr[self.free_ea] = instr self.free_ea += 6 return None #regular (non CFG) instruction if instr.GetOpnd(1) != None and instr.GetOpnd(1) != '': if instr.GetOpndType(1) == 2 and re.search( r"\*.*?\+", instr.GetOpnd(1)) == None: op_size = idaapi.get_item_size(instr.GetOpndValue(1)) op_pref_size = instr.GetOpndPrefixSize(1) if op_pref_size != None: op_size = op_pref_size op_size_2 = 0 if instr.GetOpndType(2) == 1: op_size_2 = instr.GetRegSize(instr.GetOpnd(2)) if op_size >= op_size_2: opnd = '%s [0x%08x]' % (instr.BytesToPrefix(op_size), instr.GetOpndValue(1)) else: opnd = '%s [0x%08x]' % (instr.BytesToPrefix(op_size_2), instr.GetOpndValue(1)) mnem += opnd elif instr.GetOpndPrefix(1) != None: mnem += '%s %s' % (instr.GetOpndPrefix(1), instr.GetOpnd(1)) else: mnem += instr.GetOpnd(1) if instr.GetOpnd(2) != None: if instr.GetOpndType(2) == 2 and re.search( r"\*+", instr.GetOpnd(2)) == None: op_disas = instr.GetOpnd(2) op_size = idaapi.get_item_size(instr.GetOpndValue(2)) op_pref_size = instr.GetOpndPrefixSize(1) if op_pref_size != None: op_size = op_pref_size op_size_2 = 0 if instr.GetOpndType(1) == 1: op_size_2 = instr.GetRegSize(instr.GetOpnd(1)) if op_size >= op_size_2: opnd = '%s [0x%08x]' % (instr.BytesToPrefix(op_size), instr.GetOpndValue(2)) else: opnd = '%s [0x%08x]' % (instr.BytesToPrefix(op_size_2), instr.GetOpndValue(2)) if instr.GetMnem() == "lea": opnd = '[0x%08x]' % (instr.GetOpndValue(2)) mnem += ', ' + opnd else: mnem += ', ' + opnd else: mnem += ', ' + instr.GetOpnd(2) elif instr.GetOpnd(2) != None and instr.GetOpnd(2) != '': if instr.GetOpndType(2) == 2 and re.search( r"\*+", instr.GetOpnd(2)) == None: op_disas = instr.GetOpnd(2) op_size = idaapi.get_item_size(instr.GetOpndValue(2)) op_pref_size = instr.GetOpndPrefixSize(2) if op_pref_size != None: op_size = op_pref_size op_size_2 = 0 if instr.GetOpndType(1) == 1: op_size_2 = instr.GetRegSize(instr.GetOpnd(1)) if op_size >= op_size_2: opnd = '%s [0x%08x]' % (instr.BytesToPrefix(op_size), instr.GetOpndValue(2)) else: opnd = '%s [0x%08x]' % (instr.BytesToPrefix(op_size_2), instr.GetOpndValue(2)) mnem += opnd elif instr.GetOpndType( 2) == 3 and instr.GetMnem().upper()[:2] == "FS": #this is a special case to handle FS* fp instructions that have 1st argument hidden in IDA #and to handle the difference in masm vs nasm (tbyte bs tword). #NOTE: GetOpnd(1,1) is used because the first argument is empty when it gets populated # by the taint function, so original operand is in GetOpnd(2,0) and cleand (no prefix) # operand is in GetOpnd(1,1) opnd = '%s %s' % (instr.BytesToPrefix( instr.GetOpndPrefixSize(1)), instr.StripOpndPrefix(instr.GetOpnd(2))) mnem += ' ' + opnd else: mnem += ' ' + instr.GetOpnd(2) if self.nasm: self.NasmWriteToFile("\t%s" % mnem, instr) return mnem
def NasmAssemble(self, function_ea, write_ea): dir = self.opty_dir nasm = "C:\\Program Files\\nasm\\nasm.exe" arg1 = "f_%08x.asm" % function_ea arg2 = "-o f_%08x.o" % function_ea arg3 = "-l f_%08x.lst" % function_ea arg4 = "-Ox" orig_dir = os.getcwd() os.chdir(dir) idc.Batch(0) while 1: try: p = subprocess.Popen([nasm, arg1, arg2, arg3, arg4], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) o, e = p.communicate() if o != "": print o if e != "": print e fop = open("f_%08x.o" % function_ea, "rb") ans = idaapi.askyn_c( 0, "HIDECANCEL\nDo you want to manually edit function before writing to IDA?" ) if ans == 1: os.startfile(arg1, "open") idaapi.warning( "Press OK button when you're done editing file.") fop.close() continue else: idc.Batch(1) break except: error_msg = '\n'.join(e.split("\n")[:15]) os.startfile(arg1, "open") ans = idaapi.askyn_c( 1, """HIDECANCEL\nNASM failed to assemble [f_%08x.o] file. You can manually edit and NASM this file [f_%08x.asm] and click Yes when you're done. File is located in directory where your IDB is located. If you want to skip this function press No. Nasm output: %s""" % (function_ea, function_ea, error_msg)) if ans == 1: continue else: os.chdir(orig_dir) idc.Batch(1) return None os.chdir(orig_dir) print ">>>Writing function [%08x] @ [%08x]" % (function_ea, write_ea) data = fop.read() data_len = len(data) for offset in xrange(0, data_len): idc.PatchByte(write_ea + offset, ord(data[offset])) fop.close() idc.MakeCode(write_ea) fp = open("%s\\f_%08x.lst" % (dir, function_ea), "r") asm_lst = fp.read() base_addr = re.search(r"ORG ([\dABCDEF]+)H", asm_lst).group(1) base_addr = int(base_addr, 16) for jt in self.jmp_table_refs: m = re.search(r"\s*\d+\s+([\dABCDEF]{8}).*?%s" % re.escape(jt), asm_lst, re.IGNORECASE) if m != None: jt_ea = int(m.group(1), 16) jt_str = re.search(r"SJ_.{8}", jt, re.IGNORECASE).group() for m in re.findall( r"(?i)\n\s*\d+\s+[\dABCDEF]{8}\s+.*?\s+%s" % re.escape(jt_str), asm_lst): r = re.search(r"\d+\s([\dABCDEF]{8})", m.strip(), re.IGNORECASE).group(1) #print "AddCodeXref(0x%08x, 0x%08x, idc.XREF_USER)" % (jt_ea+base_addr, idc.Dword(int(r, 16)+base_addr)) idc.AddCodeXref(jt_ea + base_addr, idc.Dword(int(r, 16) + base_addr), idc.XREF_USER) else: raise MiscError for line in asm_lst.split("\n"): comment = re.search(r"###(.*?)###", line) if comment != None: data = re.search(r"\s*\d+\s([\dABCDEF]+)\s([\dABCDEF\(\)]+)", line) if data != None: offset = int(data.group(1), 16) idc.MakeComm(write_ea + offset, comment.group(1)) fp.close() return write_ea + data_len + 10
def nop(start_address, end_address): for junk_code in range(start_address, end_address): idc.PatchByte(junk_code, 0x90)
path = '/'.join(GetInputFilePath().split('\\')[:-1]) path = idc.AskStr(path, 'Enter path of the dump directory: ') # Open the heap_map heapmap = open(path + "/heaps/heap_map.txt", 'r') if heapmap == None: print "Wrong path!\n" sys.exit(0) for line in heapmap: line = line.split(' ')[:-1] heap_bin = open(path + "/heaps/" + line[0], 'rb') heap_bin_size = os.fstat(heap_bin.fileno()).st_size start_addr = int(line[1], 16) end_addr = start_addr + int(line[2], 10) # Create a new section that will contain the heap data is32bitSeg = 1 SegAlignment = 32 idc.SegCreate(start_addr, end_addr, 0, is32bitSeg, SegAlignment, 0) # Copy from the heap dump the data inside the new created Section addr = start_addr for i in xrange(1, heap_bin_size): byte = ord(heap_bin.read(1)) idc.PatchByte(addr, byte) addr = NextAddr(addr)
def PatchArr(dest, str): for i, c in enumerate(str): if ord(c) != 0x99: idc.PatchByte(dest + i, ord(c))
def zeromemory(ea, size): for i in xrange(0, size): idc.PatchByte(ea + i, 0)
def resolve_fs30(): PATTERNS = ["64 ?? 30 00 00 00", "64 ?? ?? 30 00 00 00"] count_patched = 0 count_not_patched = 0 for pattern in PATTERNS: ea = 0 while ea != BADADDR: ''' pattern: 64 ?? 30 00 00 00 .text:00407644 64 A1 30 00 00 00 mov eax, large fs:30h .text:0040764A 50 push eax .text:0040764B 80 ED AD sub ch, 0ADh .text:0040764E B5 32 mov ch, 32h .text:00407650 66 21 D7 and di, dx .text:00407653 5A pop edx .text:00407654 8A 72 02 mov dh, [edx+2] .text:00407657 84 F6 test dh, dh .text:00407659 74 11 jz short loc_40766C pattern: 64 ?? ?? 30 00 00 00 .text:00406E42 64 8B 15 30 00 00 00 mov edx, large fs:30h .text:00406E49 52 push edx .text:00406E4A 66 35 3D 1B xor ax, 1B3Dh .text:00406E4E 20 CD and ch, cl .text:00406E50 28 D1 sub cl, dl .text:00406E52 59 pop ecx .text:00406E53 8A 51 02 mov dl, [ecx+2] .text:00406E56 84 D2 test dl, dl .text:00406E58 74 11 jz short loc_406E6B patched: .text:00406E42 64 8B 15 30 00 00 00 mov edx, large fs:30h .text:00406E49 52 push edx .text:00406E4A 66 35 3D 1B xor ax, 1B3Dh .text:00406E4E 20 CD and ch, cl .text:00406E50 28 D1 sub cl, dl .text:00406E52 59 pop ecx .text:00406E53 8A 51 02 mov dl, [ecx+2] .text:00406E56 84 D2 test dl, dl .text:00406E58 EB 11 jmp short loc_406E6B <----- ''' ea = idc.FindBinary(ea, SEARCH_NEXT | SEARCH_DOWN | SEARCH_CASE, pattern) if ea_in_bounds(ea): instr = idautils.DecodeInstruction(ea) if instr: # while not jmp related instruction found while ((instr.itype <= idaapi.NN_ja) or (instr.itype >= idaapi.NN_jmpshort)): # move to next instr ea = ea + instr.size instr = idautils.DecodeInstruction(ea) # ea contains conditional jmp instruction # Patch with relative unconditional jmp idc.PatchByte(ea, 0xEB) idc.MakeCode(ea) count_patched += 1 else: count_not_patched += 1 print "\tPatched resolve_fs30: {0}".format(count_patched) print "\tNot Patched resolve_fs30: {0}".format(count_not_patched)
def patch(self, temp=None): #用object.buffer中的数据给idb打补丁 if temp != None: self.buffer = temp for index, byte in enumerate(self.buffer): idc.PatchByte(self.start + index, ord(byte))
def reset(): idc.MakeUnknown(idc.MinEA(), 0x1000, 0) for i in range(0x1000): idc.PatchByte(idc.MinEA() + i, 0)
def memset_seg(ea, size): for i in xrange(0, size): idc.PatchByte(ea + i, 0)
def AsmAndWrite(self, mnem, instr=None, write_ea=None, function=None): if mnem == '': return if write_ea != None: ea_write = write_ea else: ea_write = self.free_ea idc.MakeUnkn(ea_write, 0) #tmp = idaapi.assemble(self.free_ea, self.segment_start, self.free_ea, 1, mnem) if debug: print ">Assemble:AsmAndWrite - !Writing @ ea[%08x] ip[%08x] instr[%s]" % ( ea_write, ea_write, mnem) tmp = idaapi.assemble(ea_write, 0, ea_write, 1, mnem) if instr != None: idaapi.set_cmt(ea_write, "%08x" % instr.GetOriginEA(), 0) if tmp == 0: if instr == None and function != None: raise MiscError if debug: print '>Assemble:AsmAndWrite - !Messy instruction', mnem print '>Assemble:AsmAndWrite - Trying original opcodes!' refs_from = [ x for x in function.GetRefsFrom(instr.GetOriginEA()) if x != None ] if len(refs_from) == 0: if instr.GetIsModified() == True: raise MiscError instr_op = instr.GetOpcode() for pos in xrange(0, len(instr_op)): idc.PatchByte(ea_write + pos, ord(instr_op[pos])) if idc.MakeCode(ea_write) == 0: raise MiscError ea_write += idc.ItemSize(ea_write) elif len(refs_from) == 1: instr_op = instr.GetOpcode() for pos in xrange(0, len(instr_op)): idc.PatchByte(ea_write + pos, ord(instr_op[pos])) if idc.MakeCode(ea_write) == 0: raise MiscError ea_write += idc.ItemSize(ea_write) else: #print '>Assemble:AsmAndWrite - GetRefsFrom(%08x)' % instr.GetOriginEA(), [hex(x) for x in function.GetRefsFrom(instr.GetOriginEA()) if x != None] print '>Assemble:AsmAndWrite - refs_from', refs_from print '>Assemble:AsmAndWrite - ea_write [%08x]' % ea_write print '>Assemble:AsmAndWrite - mnem', mnem print '>Assemble:AsmAndWrite - instr.GetMnem', instr.GetMnem() print '>Assemble:AsmAndWrite - instr.GetDisasm', instr.GetDisasm( ) raise MiscError else: if idc.MakeCode(ea_write) == 0: raise MiscError ea_write += idc.ItemSize(ea_write) if write_ea == None: self.free_ea = ea_write
def __to_native(self, function, is_recursive_mode=False, fixupfile=None): """ Process translating VM byte code to native machine code :param function: Function descriptor :param is_recursive_mode: Enable recursive mode :param fixupfile: Path to fixup file :return: Address of new segment where were native code wrote """ print "[+] Translate begin. Function %s" % hex(function.begin_ea) ks = None if self.mode == 32: ks = Ks(KS_ARCH_X86, KS_MODE_32) else: ks = Ks(KS_ARCH_X86, KS_MODE_64) locs = { function.graph.nodes[na].begin_ea for na in function.graph.nodes } func_addresses = [] for x in function.graph.nodes: for y in range( function.graph.nodes[x].begin_ea, function.graph.nodes[x].end_ea + self.instruction_size, self.instruction_size): func_addresses.append(y) func_ins = {x: self.instructions[x] for x in func_addresses} vr0x86_blocks = self.get_vr0blocks(func_ins) print "[+] Blocks for %s" % hex(function.begin_ea).replace("L", "") i = 0 for pc in vr0x86_blocks.keys(): c_vr0, c_end_vr0_block = vr0x86_blocks[pc] print "----BLOCK %s----" % i print " -> [?] Block begin: %s" % hex(pc).replace("L", "") print " -> [?] Block end: %s" % hex(c_end_vr0_block).replace( "L", "") print " -> [?] Block vr0: %s" % c_vr0 i += 1 labels = {} li = 0 for loc in locs: labels[loc] = "label_%s" % li li += 1 function_real_base = function.machine_handler print "[+] Real function base: %s" % hex(function_real_base).replace( "L", "") if self.mode == 32: asm_text = self._asm_creator_x86( func_ins, labels, vr0x86_blocks, function.machine_handler, is_recursive_mode=is_recursive_mode, fixupfile=fixupfile) else: asm_text = self._asm_creator_x64( func_ins, labels, vr0x86_blocks, function.machine_handler, is_recursive_mode=is_recursive_mode, fixupfile=fixupfile) print asm_text segs = list(idautils.Segments()) last_seg_ea = idc.SegEnd(segs[len(segs) - 1]) encoding, count = ks.asm(asm_text, addr=last_seg_ea) seg_name = ".devirt_%s" % function.name seg_size = len(encoding) + self.machine_word_size if not idc.AddSeg(last_seg_ea, last_seg_ea + seg_size, 0, 1, 0, idaapi.scPub): print "[~] Can't create segment at address %s" % hex(last_seg_ea) return if not idc.RenameSeg(last_seg_ea, seg_name): print "[!] Failed rename segment. Segment name %s" % seg_name if not idc.SetSegClass(last_seg_ea, 'CODE'): print "[!] Failed set CODE class. Segment name %s" % seg_name if not idc.SegAlign(last_seg_ea, idc.saRelPara): print "[!] Failed set align. Segment name %s" % seg_name bitness = 1 if self.mode == 64: bitness = 2 if not idc.SetSegAddressing(last_seg_ea, bitness): print "[!] Failed set bitness. Segment name %s" % seg_name if self.mode == 32: idc.PatchDword(last_seg_ea, 0) else: idc.PatchQword(last_seg_ea, 0) last_seg_ea += self.machine_word_size waddr = last_seg_ea for b in encoding: idc.PatchByte(waddr, b) waddr += 1 print "[+] Write binary to: %s" % hex(last_seg_ea).replace("L", "") self.devirtualized_functions[function.begin_ea] = (last_seg_ea, len(encoding) + 4) return last_seg_ea