def datify(self): n = 0 ea = self.get_start_ea(self.DATA) print "\nLooking for possible strings starting at: %s:0x%X" % (idc.SegName(ea), ea) for s in idautils.Strings(): if s.ea > ea: if not idc.isASCII(idc.GetFlags(s.ea)) and idc.MakeStr(s.ea, idc.BADADDR): n += 1 print "Created %d new ASCII strings" % n print "Converting remaining data to DWORDs...", while ea != idc.BADADDR: flags = idc.GetFlags(ea) if idc.isUnknown(flags) or idc.isByte(flags): idc.MakeDword(ea) idc.OpOff(ea, 0, 0) ea = idc.NextAddr(ea) print "done.\n"
def getOrigDisasm(self): # type: () -> str """ Gets the original disassembly without any further applied transformations However, the formatting is different from the original and is more convenient for parsing :return: the disassembly """ flags = idc.GetFlags(self.ea) if idc.isCode(flags): disasm = idc.GetDisasm(self.ea) disasm = self._filterComments(disasm) disasm = disasm.replace(' ', ' ') elif idc.isStruct(flags): disasm = self._getStructDisasm() # disasm = "INVALID" elif idc.isAlign(flags): disasm = idc.GetDisasm(self.ea) disasm = self._convertAlignDisasm(disasm) elif idc.isASCII(flags): content = self.getContent() numNewLines = content.count(0x0A) if numNewLines > 1: disasm = '.ascii "' else: disasm = '.asciz "' for i in range(len(content)): if content[i] == 0x00: disasm += '"' elif chr(content[i]) == '"': disasm += '\\\"' elif chr(content[i]) == '\\': disasm += '\\\\' elif content[i] == 0x0A: disasm += '\\n' numNewLines -= 1 if numNewLines > 1: disasm += '"\n\t.ascii "' elif numNewLines == 1: disasm += '"\n\t.asciz "' elif chr(content[i]) == ' ': disasm += ' ' elif not chr(content[i]).isspace(): disasm += chr(content[i]) else: # TODO [INVALID] arm-none-eabi doesn't recognize \xXX? \x seems to become a byte. disasm += '\\x%02X' % content[i] elif idc.isData(flags): disasm = self._getDataDisasm() else: disasm = idc.GetDisasm(self.ea) disasm = self._filterComments(disasm) disasm = disasm.replace(' ', ' ') # parse force command if '<force>' in self.getComment(): comment = self.getComment() disasm = comment[comment.index('<force> ') + len('<force> '):] return disasm
def stringify(self): n = 0 ea = self.get_start_ea(self.DATA) if ea == idc.BADADDR: ea = idc.FirstSeg() print ("Looking for possible strings starting at: %s:0x%X..." % (idc.SegName(ea), ea)), for s in idautils.Strings(): if s.ea > ea: if not idc.isASCII(idc.GetFlags(s.ea)) and idc.MakeStr(s.ea, idc.BADADDR): n += 1 print "created %d new ASCII strings" % n
def stringify(self): n = 0 ea = self.get_start_ea(self.DATA) if ea == idc.BADADDR: ea = idc.FirstSeg() print("Looking for possible strings starting at: %s:0x%X..." % (idc.SegName(ea), ea)), for s in idautils.Strings(): if s.ea > ea: if not idc.isASCII(idc.GetFlags(s.ea)) and idc.MakeStr( s.ea, idc.BADADDR): n += 1 print "created %d new ASCII strings" % n
def changeASCII(start_ea, ): """ finds all ascii named data and changes it to bytes and removes its name """ found = False for ea, name in idautils.Names(): d = Data.Data(ea) if idc.isASCII(d._getFlags()): found = True print("%07X: Make ASCII -> Byte" % ea) idc.MakeByte(ea) idc.MakeName(ea, '') if found: print("changed all ASCII data to byte data!") else: print("no ASCII data was found!")
def get_first_string(ea): """ walk up and grab a string like this push offset aGamegetpotionc ; "GameGetPotionColorUint" lea ecx, [ebp+var_244] ; int call makestr push offset sub_435ADA lea eax, [ebp+var_244] mov ecx, esi push eax call register_global ; <---- ea """ maybe_start = idc.get_func_attr(ea, idc.FUNCATTR_START) limit = 0 if maybe_start == idc.BADADDR: limit = 10 cur_ea = ea limit_count = 0 while cur_ea != idc.BADADDR: # are we over limit or up to the func start? limit_count += 1 limit_exceeded = (limit > 0 and limit_count > limit) too_far = (maybe_start != idc.BADADDR and cur_ea < maybe_start) if limit_exceeded or too_far: LOG.error( "Failed to find string walking backwards from {:08X}".format( ea)) return None prev_ins = idautils.DecodePreviousInstruction(cur_ea) prev_ea = prev_ins.ea # did we find it? if idc.GetMnem(prev_ea) == 'push': if idc.get_operand_type(prev_ea, 0) in [idc.o_mem, idc.o_imm]: # push offset found! pushed_addr = idc.GetOperandValue(prev_ea, 0) if idc.isASCII(idc.GetFlags(pushed_addr)): s = idc.GetString(pushed_addr, -1, idc.ASCSTR_C) #LOG.debug("Read string {} from {:08X} from instruction at {:08X}".format(repr(s), pushed_addr, prev_ea)) return s cur_ea = prev_ea
def removeText(start_ea, end_ea): """ removes all ASCII text within range :param start_ea: start of the range to remove :param end_ea: end of the range to remove """ found = False ea = start_ea while ea < end_ea: d = Data.Data(ea) if idc.isASCII(d.getFlags()): found = True print("%07X: Make text -> Byte" % ea) idc.MakeByte(ea) ea += d.getSize() if found: print("changed all ASCII data to byte data!") else: print("no ASCII data was found!")
def changeASCII(start_ea, end_ea): """ finds all ascii data which is not user named and changes it to bytes and removes its name """ found = False ea = start_ea while ea < end_ea: d = Data.Data(ea) if idc.isASCII(d.getFlags()): found = True print("%07X: Make ASCII -> Byte" % ea) idc.MakeByte(ea) idc.MakeName(ea, 'ASCII_%07X' % ea) ea += d.getSize() if found: print("changed all ASCII data to byte data!") else: print("no ASCII data was found!")
def nextascii(self, ea, ui=True): # type: (int) -> str """ returns the next data item containing ascii characters (seems valid for utf too) :param ea: the address to start searching from :param ui: if True, jump to address automatically :return: hex formatted str of the address of the next ascii item """ # don't count this item ea = Data.Data(ea).ea + Data.Data(ea).getSize() output = idaapi.BADADDR while ea < self.end_ea: d = Data.Data(ea) # ARM, unless it's a branch if idc.isASCII(d._getFlags()): output = ea break ea += d.getSize() if ui: idaapi.jumpto(output) return '%07X' % output
def _read_struct_member_once(ea, flags, size, member_sid, member_size, asobject): """Read part of a struct member for _read_struct_member.""" if idc.isByte(flags): return read_word(ea, 1), 1 elif idc.isWord(flags): return read_word(ea, 2), 2 elif idc.isDwrd(flags): return read_word(ea, 4), 4 elif idc.isQwrd(flags): return read_word(ea, 8), 8 elif idc.isOwrd(flags): return read_word(ea, 16), 16 elif idc.isASCII(flags): return idc.GetManyBytes(ea, size), size elif idc.isFloat(flags): return idc.Float(ea), 4 elif idc.isDouble(flags): return idc.Double(ea), 8 elif idc.isStruct(flags): value = read_struct(ea, sid=member_sid, asobject=asobject) return value, member_size return None, size
def isString(self, reference): return idc.isASCII(self.getFlags(reference))
def make_struc_member(self, object_version, address, member_type=ya.OBJECT_TYPE_STRUCT_MEMBER): struc_object_id = object_version.get_parent_object_id() struc_id = 0 try: struc_id = self.struc_ids[struc_object_id] except: return is_union = struc_id in self.union_ids offset = address if is_union: last_offset = idc.GetLastMember(struc_id) if last_offset == idc.BADADDR: last_offset = -1 if last_offset < offset: for i in xrange(last_offset + 1, offset + 1): idc.AddStrucMember(struc_id, "yaco_filler_%d" % i, 0, idc.FF_BYTE | idc.FF_DATA, -1, 1) # ensure that 'offset' fields are present member_size = object_version.get_size() member_name = object_version.get_name() flags = object_version.get_object_flags() if idc.isStruct(flags): # if the sub field is a struct, it must have a single Xref field with the struct object id try: sub_struc_object_id = object_version.getXRefIdsAt(0, 0)[0] sub_struc_id = self.struc_ids[sub_struc_object_id] # logger.debug("%20s: adding sub member at offset 0x%08X, # size=0x%08X (sub=0x%.016X, size=0x%08X) with name %s" % # ( # idc.GetStrucName(struc_id), offset, member_size, sub_struc_id, # idc.GetStrucSize(sub_struc_id), object_version.get_name() # )) sub_struc_size = idc.GetStrucSize(sub_struc_id) if sub_struc_size == 0: logger.error( "%20s: adding sub member at offset 0x%08X, size=0x%08X " "(sub=0x%.016X, size=0x%08X) with name %s : sub struc size is ZERO" % (idc.GetStrucName(struc_id), offset, member_size, sub_struc_id, idc.GetStrucSize(sub_struc_id), object_version.get_name())) else: nitems = member_size / sub_struc_size YaToolIDATools.SetStrucmember(struc_id, member_name, offset, flags, sub_struc_id, nitems) except KeyError: logger.error( "Error while looking for sub struc in struc %s, offset 0x%08X (field name='%s')" % (self.hash_provider.hash_to_string(struc_object_id), offset, object_version.get_name())) traceback.print_exc() elif idc.isEnum0(flags): # an enum is applied here try: sub_enum_object_id = object_version.getXRefIdsAt(0, 0)[0] sub_enum_id = self.enum_ids[sub_enum_object_id] name_ok = idc.SetMemberName(struc_id, offset, member_name) if name_ok is not True: logger.debug( "Error while setting member name (enum) : " "(struc=%s, member=%s, offset=0x%08X, mflags=%d, msize=%d, tid=0x%016X" % (name_ok, idc.GetStrucName(struc_id), member_name, offset, flags, member_size, sub_struc_id)) else: sub_enum_size = idc.GetEnumWidth(sub_enum_id) if sub_enum_size == 0: sub_enum_size = member_size nitems = member_size / sub_enum_size ret = idc.SetMemberType(struc_id, offset, flags, sub_enum_id, nitems) if ret == 0: logger.debug( "Error while setting member type (enum) : " "(struc=%s, member=%s, offset=0x%08X, mflags=%d, msize=%d, tid=0x%016X" % (ret, idc.GetStrucName(struc_id), member_name, offset, flags, member_size, sub_struc_id)) except KeyError: logger.error( "Error while looking for sub enum in struc %s, offset 0x%08X (field name='%s')" % (struc_object_id, offset, member_name)) traceback.print_exc() else: # logger.debug("%20s: adding member at offset 0x%08X, size=0x%08X with name %s" % # ( # idc.GetStrucName(struc_id), offset, member_size, object_version.get_name() # )) tid = -1 if idc.isASCII(flags): logger.debug( "object: %s : %s" % (self.hash_provider.hash_to_string( object_version.get_id()), object_version.get_name())) try: tid = object_version.get_string_type() except KeyError: tid = idc.ASCSTR_C name_ok = idc.SetMemberName(struc_id, offset, member_name) if name_ok is not True: logger.debug( "Error while setting member name :" + " (struc_id=0x%08X, struc=%s, member=%s, offset=0x%08X, mflags=%d, msize=%d)" % (struc_id, idc.GetStrucName(struc_id), member_name, offset, flags, member_size)) else: item_size = YaToolIDATools.get_field_size(flags, tid) nitems = member_size / item_size # IDA BUG : 4-byte chars are stored as 2 double words, thus me must # multiply nitem by 2! ret = idc.SetMemberType(struc_id, offset, flags, tid, nitems) if ret == 0: logger.debug( "Error while setting member type :" + " (struc=%s, member=%s, offset=0x%08X, mflags=%d, msize=%d)" % (idc.GetStrucName(struc_id), member_name, offset, flags, member_size)) try: repeatable_headercomment = self.sanitize_comment_to_ascii( object_version.get_header_comment(True)) idc.SetMemberComment(struc_id, offset, repeatable_headercomment, 1) except KeyError: pass try: nonrepeatable_headercomment = self.sanitize_comment_to_ascii( object_version.get_header_comment(False)) idc.SetMemberComment(struc_id, offset, nonrepeatable_headercomment, 0) except KeyError: pass member_id = idc.GetMemberId(struc_id, offset) self.set_struct_member_type(object_version, member_id) if object_version.get_type() == ya.OBJECT_TYPE_STRUCT_MEMBER: self.strucmember_ids[object_version.get_id()] = member_id
def make_data(self, object_version, address): size = 0 try: size = object_version.get_size() except KeyError: pass flags = None try: flags = object_version.get_object_flags() except KeyError: pass if size == 0: idc.MakeUnkn(address, idc.DOUNK_EXPAND) else: if flags is not None: if idc.isASCII(flags): try: str_type = object_version.get_string_type() YaToolIDATools.check_and_set_str_type(str_type) except KeyError: pass idc.MakeStr(address, address + size) idc.SetFlags(address, flags) if idc.isStruct(flags): found = False for xref_offset_operand, xref_id_attr in object_version.get_xrefed_id_map( ).iteritems(): (xref_offset, xref_operand) = xref_offset_operand for xref_hash, xref_attrs in xref_id_attr: if xref_hash in self.struc_ids: struc_id = self.struc_ids[xref_hash] if DEBUG_EXPORTER: logger.debug( "making unknown from 0x%08X to 0x%08X" % (address, address + size)) idaapi.do_unknown_range( address, size, idc.DOUNK_DELNAMES) # idc.MakeUnkn(address, DOUNK_SIMPLE | idc.DOUNK_DELNAMES) # for i in xrange(1, size): # MakeName(address + i, "") # idc.MakeUnkn(address + i, DOUNK_SIMPLE | idc.DOUNK_DELNAMES) # idc.MakeStructEx uses idaapi.doStruct (but asks for name while # we already have the struc id) if DEBUG_EXPORTER: logger.debug( "Making struc at %s : %s (sizeof(%s)=0x%08X, size=0x%08X, flags=0x%08X" % (self.yatools.address_to_hex_string( address), self.yatools.address_to_hex_string( struc_id), idaapi.get_struc_name(struc_id), idc.GetStrucSize(struc_id), size, flags)) idc.SetCharPrm(idc.INF_AUTO, True) idc.Wait() if idaapi.doStruct(address, size, struc_id) == 0: if DEBUG_EXPORTER: logger.warning("Making struc failed") idc.SetCharPrm(idc.INF_AUTO, False) # idc.SetFlags(address, flags) found = True else: logger.error( "bad struc flags : idc.isStruct is true but no xref available for object %s" % self.hash_provider.hash_to_string( object_version.get_id())) if not found: logger.error( "bad struc flags : idc.isStruct is true " "but no struc available for object %s (%s)" % (self.hash_provider.hash_to_string( object_version.get_id()), object_version.get_name())) else: idc.MakeData(address, flags & (idc.DT_TYPE | idc.MS_0TYPE), size, 0) else: idc.MakeData(address, idc.FF_BYTE, size, 0) self.make_name(object_version, address, False) self.set_type(object_version, address)
def is_ascii(self): return idc.isASCII(self.flags)
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 = trace_param(ea, func_start, idc.o_reg, sel_param) if method_name_ea and idc.isASCII(idc.GetFlags(method_name_ea)): method_name = idc.GetString(method_name_ea, -1, idc.ASCSTR_C) if not method_name: method_name = '_unk_method' else: method_name = '_unk_method' class_name_ea = trace_param(ea, func_start, idc.o_reg, class_param) if class_name_ea: class_name = idc.Name(class_name_ea) if not class_name: class_name = '_unk_class' else: class_name = '_unk_class' if method_name == '_unk_method' and class_name == '_unk_class': 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_$_', '') class_name = class_name.replace('_OBJC_METACLASS_$_', '') new_name = '_[' + class_name + '_' + method_name + ']' print '%08x: %s' % (ea_call, new_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 else: # create code and name it idc.PatchDword(seg_ptr, 0xE12FFF1E) # BX LR 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 # patch the msgsend call if idc.GetReg(ea, "T") == 1: if offset > 0 and offset & 0xFF800000: print 'Offset too far for Thumb (%08x) Stopping [%08x]' % (offset, ea) return off1 = (offset & 0x7FF000) >> 12 off2 = (offset & 0xFFF) / 2 w1 = (0xF000 | off1) w2 = (0xE800 | off2) - 1 idc.PatchWord(ea, w1) idc.PatchWord(ea + 2, w2) else: if offset > 0 and offset & 0xFF000000: print 'Offset too far (%08x) Stopping [%08x]' % (offset, ea) dw = (0xFA000000 | (offset - 8 >> 2)) if dw < 0: dw = dw & 0xFAFFFFFF idc.PatchDword(ea, dw)