def relocate_bootblock(): bootblk_start = 0xF000 << 4 bootblk_end = 0x10000 << 4 bootblk_baseparagraph = 0xF000 bootblk_size = 0x10000 # 64 Kbyte idaapi.add_segm(bootblk_baseparagraph, bootblk_start, bootblk_end, "BOOTBLK", "CODE") bootblk = idaapi.get_segm_by_name("BOOTBLK") idaapi.set_segm_addressing(bootblk, 16) bootblk_addr = get_bootblock_start_address() print("Found bootblock at {0} addr!".format(hex(bootblk_addr))) relocate_segment(bootblk_addr, bootblk_start, bootblk_size)
def load_file(li, neflags, format): idaapi.set_processor_type("arm", SETPROC_ALL|SETPROC_FATAL) offs = 0 for s in segments: start = s["start"] length = s["len"] name = s["name"] seg_type = s["type"] li.file2base(offs, start, start+length, True) idaapi.add_segm(0, start, start+length, name, seg_type) offs += length create_modem_hdr_struct() add_modem_hdr_struct(8*4 + segments[0]["start"]) #this might fail unless we carve out a DATA segment from the CODE segment for it. print "Samsung Shannon image loaded." return 1
def load_cro(li, is_crs): if is_crs: base = 0 else: base = 0x00100000 # arbitrary li.seek(0x80) (Magic, NameOffset, NextCRO, PreviousCRO, FileSize, BssSize, FixedSize, UnknownZero, UnkSegmentTag, OnLoadSegmentTag, OnExitSegmentTag, OnUnresolvedSegmentTag, CodeOffset, CodeSize, DataOffset, DataSize, ModuleNameOffset, ModuleNameSize, SegmentTableOffset, SegmentNum, ExportNamedSymbolTableOffset, ExportNamedSymbolNum, ExportIndexedSymbolTableOffset, ExportIndexedSymbolNum, ExportStringsOffset, ExportStringsSize, ExportTreeTableOffset, ExportTreeNum, ImportModuleTableOffset, ImportModuleNum, ExternalPatchTableOffset, ExternalPatchNum, ImportNamedSymbolTableOffset, ImportNamedSymbolNum, ImportIndexedSymbolTableOffset, ImportIndexedSymbolNum, ImportAnonymousSymbolTableOffset, ImportAnonymousSymbolNum, ImportStringsOffset, ImportStringsSize, StaticAnonymousSymbolTableOffset, StaticAnonymousSymbolNum, InternalPatchTableOffset, InternalPatchNum, StaticPatchTableOffset, StaticPatchNum) = struct.unpack( "<IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", li.read(0x138 - 0x80)) if not is_crs: li.file2base(0, base, base + FileSize, 0) idaapi.add_segm(0, base, base + 0x80, "header", "RODATA") idaapi.add_segm(0, base + SegmentTableOffset, base + DataOffset, "tables", "RODATA") # set segments li.seek(SegmentTableOffset) segmentDic = [("CODE", ".text"), ("DATA", ".rodata"), ("DATA", ".data"), ("BSS", ".bss")] segmentAddress = [] for i in range(SegmentNum): SegmentOffset, SegmentSize, SegmentType = struct.unpack( "<III", li.read(12)) if SegmentType == 3: SegmentOffset = 0x08000000 idaapi.enable_flags(base + SegmentOffset, base + SegmentOffset + SegmentSize, idaapi.STT_VA) segmentAddress.append(base + SegmentOffset) if SegmentSize: idaapi.add_segm(0, segmentAddress[i], segmentAddress[i] + SegmentSize, segmentDic[SegmentType][1], segmentDic[SegmentType][0]) # do internal relocations li.seek(InternalPatchTableOffset) for i in range(InternalPatchNum): target, patch_type, source, _, _, shift = struct.unpack( "<IBBBBI", li.read(12)) target_offset = DecodeTag(segmentAddress, target) source_offset = segmentAddress[source] + shift if patch_type == 2: value = source_offset elif patch_type == 3: rel = source_offset - target_offset if rel < 0: rel += 0x100000000 value = rel idaapi.patch_long(target_offset, value) f = idaapi.fixup_data_t() f.type = idaapi.FIXUP_OFF32 f.off = value idaapi.set_fixup(target_offset, f) # import li.seek(ImportNamedSymbolTableOffset) importNamedSymbolTable = [] for i in range(ImportNamedSymbolNum): importNamedSymbolTable.append(struct.unpack('<II', li.read(8))) for importNamedSymbol in importNamedSymbolTable: nameOffset, batchOffset = importNamedSymbol li.seek(nameOffset) name = "" while True: c = li.read(1) if c == '\0': break name += c do_import_batch(li, segmentAddress, batchOffset, name) li.seek(ImportModuleTableOffset) module = [] for i in range(ImportModuleNum): module.append(struct.unpack('<IIIII', li.read(20))) for m in module: moduleNameOffset, indexed, indexedNum, anonymous, anonymousNum = m li.seek(moduleNameOffset) mname = "" while True: c = li.read(1) if c == '\0': break mname += c indexeds = [] li.seek(indexed) for i in range(indexedNum): indexeds.append(struct.unpack('<II', li.read(8))) anonymouses = [] li.seek(anonymous) for i in range(anonymousNum): anonymouses.append(struct.unpack('<II', li.read(8))) for i in indexeds: index, batchOffset = i do_import_batch(li, segmentAddress, batchOffset, "%s_%d" % (mname, index)) for i in anonymouses: tag, batchOffset = i do_import_batch(li, segmentAddress, batchOffset, "%s_%08X" % (mname, tag)) # export li.seek(ExportNamedSymbolTableOffset) exportNamedSymbolTable = [] for i in range(ExportNamedSymbolNum): exportNamedSymbolTable.append(struct.unpack('<II', li.read(8))) for exportNamedSymbol in exportNamedSymbolTable: nameOffset, target = exportNamedSymbol target_offset = DecodeTag(segmentAddress, target) li.seek(nameOffset) name = "" while True: c = li.read(1) if c == '\0': break name += c if idaapi.segtype(target_offset) == idaapi.SEG_CODE: target_offset &= ~1 idaapi.add_entry(target_offset, target_offset, name, idaapi.segtype(target_offset) == idaapi.SEG_CODE) idaapi.make_name_public(target_offset) li.seek(ExportIndexedSymbolTableOffset) for i in range(ExportIndexedSymbolNum): target, = struct.unpack('<I', li.read(4)) target_offset = DecodeTag(segmentAddress, target) if idaapi.segtype(target_offset) == idaapi.SEG_CODE: target_offset &= ~1 idaapi.add_entry(i, target_offset, "indexedExport_%d" % i, idaapi.segtype(target_offset) == idaapi.SEG_CODE) idaapi.make_name_public(target_offset)
def load_file(li, neflags, fmt): idaapi.set_processor_type( "arm", idaapi.SETPROC_LOADER_NON_FATAL | idaapi.SETPROC_LOADER) idc.set_inf_attr(idc.INF_LFLAGS, idc.get_inf_attr(idc.INF_LFLAGS) | idc.LFLG_64BIT) idc.set_inf_attr(idc.INF_DEMNAMES, idaapi.DEMNAM_GCC3) idaapi.set_compiler_id(idaapi.COMP_GNU) idaapi.add_til('gnulnx_arm64', 1) # Get the meta offset meta_ofs = find_kernel_meta(li) assert meta_ofs != -1 # Read important offsets. li.seek(meta_ofs + 0x40) text_base, init_base, init_base2, kernel_end = struct.unpack( '<QQQQ', li.read(0x20)) # Load the init segment. li.seek(0) init_data = li.read(KERNEL_INIT_SIZE) idaapi.mem2base(init_data, init_base) # Emulate the kernel init segment to determine interesting extents emu = Emulator() emu.run_emulator(init_base, init_data) builder = SegmentBuilder() builder.add_segment(init_base, KERNEL_INIT_SIZE, '.init', 'RWX') text_phys = 0 text_size = 0 ro_size = 0 rw_size = 0 core_dram_mappings = [] for (virt_addr, size, phys_addr, attr) in emu.mappings: print('%x, %x, %x, %x' % (virt_addr, size, phys_addr, attr)) assert attr in [ 0x4000000000078B, 0x78B, 0x6000000000078B, 0x6000000000070B, 0x60000000000607, 0x60000000000709 ] if attr == 0x78B or attr == 0x4000000000078B: # .text assert virt_addr == text_base builder.add_segment(virt_addr, size, '.text', 'CODE') li.seek(phys_addr - init_base) idaapi.mem2base(li.read(size), virt_addr) text_phys = phys_addr text_size = size elif attr == 0x6000000000078B: # .rodata assert text_size != 0 assert virt_addr == text_base + text_size builder.add_segment(virt_addr, size, '.rodata', 'CONST') li.seek(phys_addr - init_base) idaapi.mem2base(li.read(size), virt_addr) ro_size = size elif attr == 0x6000000000070B and virt_addr == text_base + text_size + ro_size: assert text_size != 0 assert ro_size != 0 # .rwdata builder.add_segment(virt_addr, size, '.rwdata', 'DATA') li.seek(phys_addr - init_base) idaapi.mem2base(li.read(size), virt_addr) rw_size = size elif attr == 0x60000000000607: # IO DEVICES = { (0x40000000, 0x40000): '.iram', (0x50041000, 0x01000): '.gicd', (0x50042000, 0x01000): '.gicc', (0x60001000, 0x01000): '.semaphore', (0x60004000, 0x01000): '.primary_ictlr', (0x60006000, 0x01000): '.clkrst', (0x60007000, 0x01000): '.flow_ctlr', (0x6000F000, 0x01000): '.evp', (0x70006000, 0x01000): '.uart', (0x7000E000, 0x01000): '.rtc_pmc', (0x70016000, 0x02000): '.atomics', (0x70019000, 0x01000): '.mc', (0x7001C000, 0x01000): '.mc0', (0x7001D000, 0x01000): '.mc1', } assert (phys_addr, size) in DEVICES.keys() name = DEVICES[(phys_addr, size)] builder.add_segment(virt_addr, size, name, 'IO') elif attr == 0x6000000000070B: # Kernel DRAM if phys_addr == (emu.ttbr & ~0xFFF) and size == 0x1000: builder.add_segment(virt_addr, size, '.ttbr1', 'DATA') else: core_dram_mappings.append( (virt_addr, size, phys_addr, attr)) else: # Linear DRAM assert attr == 0x60000000000709 idaapi.add_segm(0, virt_addr, virt_addr + size, '.linear_dram', 'DATA', idaapi.ADDSEG_SPARSE) segm = idaapi.get_segm_by_name('.linear_dram') segm.perm = idaapi.SEGPERM_READ | idaapi.SEGPERM_WRITE idaapi.update_segm(segm) idaapi.set_segm_addressing(segm, 2) assert len(core_dram_mappings) % 5 == 0 if len(core_dram_mappings) // 5 == 3: # 1.0.0-style core local mappings assert core_dram_mappings[ 0 + 0][2] == core_dram_mappings[0 + 3][2] - 0x2000 assert core_dram_mappings[ 0 + 3][2] == core_dram_mappings[0 + 6][2] - 0x2000 assert core_dram_mappings[ 0 + 6][2] == core_dram_mappings[0 + 9][2] - 0x2000 assert core_dram_mappings[0 + 0][2] == core_dram_mappings[0 + 12][2] assert core_dram_mappings[ 1 + 0][2] == core_dram_mappings[1 + 3][2] - 0x2000 assert core_dram_mappings[ 1 + 3][2] == core_dram_mappings[1 + 6][2] - 0x2000 assert core_dram_mappings[ 1 + 6][2] == core_dram_mappings[1 + 9][2] - 0x2000 assert core_dram_mappings[1 + 0][2] == core_dram_mappings[1 + 12][2] assert core_dram_mappings[ 2 + 0][2] == core_dram_mappings[2 + 3][2] - 0x1000 assert core_dram_mappings[ 2 + 3][2] == core_dram_mappings[2 + 6][2] - 0x1000 assert core_dram_mappings[ 2 + 6][2] == core_dram_mappings[2 + 9][2] - 0x1000 assert core_dram_mappings[2 + 0][2] == core_dram_mappings[2 + 12][2] builder.add_segment(core_dram_mappings[0][0], 0xA000 * 4, '.core_local_regions', 'DATA') builder.add_segment(core_dram_mappings[3 * 4 + 0][0], core_dram_mappings[3 * 4 + 0][1], '.current_common_stack', 'DATA') builder.add_segment(core_dram_mappings[3 * 4 + 1][0], core_dram_mappings[3 * 4 + 1][1], '.current_main_stack', 'DATA') builder.add_segment(core_dram_mappings[3 * 4 + 2][0], core_dram_mappings[3 * 4 + 2][1], '.current_context', 'DATA') elif len(core_dram_mappings) // 5 == 4: # 3.0.0-style core local mappings assert core_dram_mappings[ 0 + 0][2] == core_dram_mappings[0 + 4][2] - 0x2000 assert core_dram_mappings[ 0 + 4][2] == core_dram_mappings[0 + 8][2] - 0x2000 assert core_dram_mappings[ 0 + 8][2] == core_dram_mappings[0 + 12][2] - 0x2000 assert core_dram_mappings[0 + 0][2] == core_dram_mappings[0 + 16][2] assert core_dram_mappings[ 1 + 0][2] == core_dram_mappings[1 + 4][2] - 0x2000 assert core_dram_mappings[ 1 + 4][2] == core_dram_mappings[1 + 8][2] - 0x2000 assert core_dram_mappings[ 1 + 8][2] == core_dram_mappings[1 + 12][2] - 0x2000 assert core_dram_mappings[1 + 0][2] == core_dram_mappings[1 + 16][2] assert core_dram_mappings[ 2 + 0][2] == core_dram_mappings[2 + 4][2] - 0x2000 assert core_dram_mappings[ 2 + 4][2] == core_dram_mappings[2 + 8][2] - 0x2000 assert core_dram_mappings[ 2 + 8][2] == core_dram_mappings[2 + 12][2] - 0x2000 assert core_dram_mappings[2 + 0][2] == core_dram_mappings[2 + 16][2] assert core_dram_mappings[ 3 + 0][2] == core_dram_mappings[3 + 4][2] - 0x1000 assert core_dram_mappings[ 3 + 4][2] == core_dram_mappings[3 + 8][2] - 0x1000 assert core_dram_mappings[ 3 + 8][2] == core_dram_mappings[3 + 12][2] - 0x1000 assert core_dram_mappings[3 + 0][2] == core_dram_mappings[3 + 16][2] builder.add_segment(core_dram_mappings[0][0], 0xE000 * 4, '.core_local_regions', 'DATA') builder.add_segment(core_dram_mappings[4 * 4 + 0][0], core_dram_mappings[4 * 4 + 0][1], '.current_common_stack', 'DATA') builder.add_segment(core_dram_mappings[4 * 4 + 1][0], core_dram_mappings[4 * 4 + 1][1], '.current_main_stack', 'DATA') builder.add_segment(core_dram_mappings[4 * 4 + 2][0], core_dram_mappings[4 * 4 + 2][1], '.current_idle_stack', 'DATA') builder.add_segment(core_dram_mappings[4 * 4 + 3][0], core_dram_mappings[4 * 4 + 3][1], '.current_context', 'DATA') seg_prefix = '' for start, end, name, kind in builder.flatten(): idaapi.add_segm(0, start, end, seg_prefix + name, kind) segm = idaapi.get_segm_by_name(seg_prefix + name) if kind == 'CONST': segm.perm = idaapi.SEGPERM_READ elif kind == 'CODE': segm.perm = idaapi.SEGPERM_READ | idaapi.SEGPERM_EXEC elif kind == 'DATA': segm.perm = idaapi.SEGPERM_READ | idaapi.SEGPERM_WRITE elif kind == 'BSS': segm.perm = idaapi.SEGPERM_READ | idaapi.SEGPERM_WRITE elif kind == 'RWX': segm.perm = idaapi.SEGPERM_READ | idaapi.SEGPERM_WRITE | idaapi.SEGPERM_EXEC elif kind == 'IO': segm.perm = idaapi.SEGPERM_READ | idaapi.SEGPERM_WRITE idaapi.update_segm(segm) idaapi.set_segm_addressing(segm, 2) # Set vector types as code. for i, ctx in enumerate(['sp0', 'spx', 'a64', 'a32']): for j, kind in enumerate(['synch', 'irq', 'fiq', 'serror']): addr = emu.vbar + 0x200 * i + 0x80 * j name = '%s_%s_exception' % (kind, ctx) idc.create_insn(addr) idc.set_name(addr, name) # Set .init as code. idc.create_insn(init_base) idc.set_name(init_base, 'crt0') # Set text start as code. idc.create_insn(text_base) idc.set_name(text_base, 'HorizonKernelMain') # Patch movk instructions to be better understandable by ida. li.seek(text_phys - init_base) text = li.read(text_size) ro_rw = li.read(ro_size + rw_size) patch_movk_to_adrp(text_base, text, ro_rw, emu.mappings) return 1
def load_cro(li, is_crs): if is_crs: base = 0 else: base = 0x00100000 # arbitrary li.seek(0x80) (Magic, NameOffset, NextCRO, PreviousCRO, FileSize, BssSize, FixedSize, UnknownZero, UnkSegmentTag, OnLoadSegmentTag, OnExitSegmentTag, OnUnresolvedSegmentTag, CodeOffset, CodeSize, DataOffset, DataSize, ModuleNameOffset, ModuleNameSize, SegmentTableOffset, SegmentNum, ExportNamedSymbolTableOffset, ExportNamedSymbolNum, ExportIndexedSymbolTableOffset, ExportIndexedSymbolNum, ExportStringsOffset, ExportStringsSize, ExportTreeTableOffset, ExportTreeNum, ImportModuleTableOffset, ImportModuleNum, ExternalPatchTableOffset, ExternalPatchNum, ImportNamedSymbolTableOffset, ImportNamedSymbolNum, ImportIndexedSymbolTableOffset, ImportIndexedSymbolNum, ImportAnonymousSymbolTableOffset, ImportAnonymousSymbolNum, ImportStringsOffset, ImportStringsSize, StaticAnonymousSymbolTableOffset, StaticAnonymousSymbolNum, InternalPatchTableOffset, InternalPatchNum, StaticPatchTableOffset, StaticPatchNum) = struct.unpack("<IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", li.read(0x138 - 0x80)) if not is_crs: li.file2base(0, base, base + FileSize, 0) # set segments li.seek(SegmentTableOffset) segmentDic = [ ("CODE", ".text"), ("DATA", ".rodata"), ("DATA", ".data"), ("BSS", ".bss") ] segmentAddress = [] for i in range(SegmentNum): SegmentOffset, SegmentSize, SegmentType = struct.unpack("<III", li.read(12)) if SegmentType == 3: SegmentOffset = 0x08000000 idaapi.enable_flags(base + SegmentOffset, base + SegmentOffset + SegmentSize, idaapi.STT_VA) segmentAddress.append(base + SegmentOffset) if SegmentSize : idaapi.add_segm(0, segmentAddress[i], segmentAddress[i] + SegmentSize, segmentDic[SegmentType][1], segmentDic[SegmentType][0]) # do internal relocations li.seek(InternalPatchTableOffset) for i in range(InternalPatchNum): target, patch_type, source, _, _, shift = struct.unpack("<IBBBBI", li.read(12)) target_offset = DecodeTag(segmentAddress, target) source_offset = segmentAddress[source] + shift if patch_type == 2: value = source_offset elif patch_type == 3: rel = source_offset - target_offset if rel < 0: rel += 0x100000000 value = rel idaapi.patch_long(target_offset, value) f = idaapi.fixup_data_t() f.type = idaapi.FIXUP_OFF32 f.off = value idaapi.set_fixup(target_offset, f) # import li.seek(ImportNamedSymbolTableOffset) importNamedSymbolTable = [] for i in range(ImportNamedSymbolNum): importNamedSymbolTable.append(struct.unpack('<II', li.read(8))) for importNamedSymbol in importNamedSymbolTable: nameOffset, batchOffset = importNamedSymbol li.seek(nameOffset) name = "" while True: c = li.read(1) if c == '\0': break name += c do_import_batch(li, segmentAddress, batchOffset, name) li.seek(ImportModuleTableOffset) module = [] for i in range(ImportModuleNum): module.append(struct.unpack('<IIIII', li.read(20))) for m in module: moduleNameOffset, indexed, indexedNum, anonymous, anonymousNum = m li.seek(moduleNameOffset) mname = "" while True: c = li.read(1) if c == '\0': break mname += c indexeds = [] li.seek(indexed) for i in range(indexedNum): indexeds.append(struct.unpack('<II', li.read(8))) anonymouses = [] li.seek(anonymous) for i in range(anonymousNum): anonymouses.append(struct.unpack('<II', li.read(8))) for i in indexeds: index, batchOffset = i do_import_batch(li, segmentAddress, batchOffset, "%s_%d"%(mname, index)) for i in anonymouses: tag, batchOffset = i do_import_batch(li, segmentAddress, batchOffset, "%s_%08X"%(mname, tag)) # export li.seek(ExportNamedSymbolTableOffset) exportNamedSymbolTable = [] for i in range(ExportNamedSymbolNum): exportNamedSymbolTable.append(struct.unpack('<II', li.read(8))) for exportNamedSymbol in exportNamedSymbolTable: nameOffset, target = exportNamedSymbol target_offset = DecodeTag(segmentAddress, target) li.seek(nameOffset) name = "" while True: c = li.read(1) if c == '\0': break name += c idaapi.add_entry(target_offset, target_offset, name, idaapi.segtype(target_offset) == idaapi.SEG_CODE) idaapi.make_name_public(target_offset) li.seek(ExportIndexedSymbolTableOffset) for i in range(ExportIndexedSymbolNum): target, = struct.unpack('<I', li.read(4)) target_offset = DecodeTag(segmentAddress, target) idaapi.add_entry(i, target_offset, "indexedExport_%d" % i, idaapi.segtype(target_offset) == idaapi.SEG_CODE) idaapi.make_name_public(target_offset)
def load_file(f, neflags, format): print('# PS4 Kernel Loader') ps4 = Binary(f) # PS4 Processor, Compiler, Library bitness = ps4.procomp('metapc', CM_N64 | CM_M_NN | CM_CC_FASTCALL, 'gnulnx_x64') # Segment Loading... for segm in ps4.E_SEGMENTS: # Process Loadable Segments... if segm.name() in ['CODE', 'DATA', 'SCE_RELRO']: address = segm.MEM_ADDR size = segm.MEM_SIZE # Dumped Kernel Fix-ups if segm.name() in ['DATA', 'SCE_RELRO' ] and idaapi.get_segm_by_name( 'CODE').start_ea != 0xFFFFFFFF82200000: offset = address - idaapi.get_segm_by_name('CODE').start_ea dumped = segm.MEM_SIZE else: offset = segm.OFFSET dumped = segm.FILE_SIZE print('# Creating %s Segment...' % segm.name()) f.file2base(offset, address, address + dumped, FILEREG_PATCHABLE) idaapi.add_segm(0, address, address + size, segm.name(), segm.type(), ADDSEG_NOTRUNC | ADDSEG_FILLGAP) # Processor Specific Segment Details idc.set_segm_addressing(address, bitness) idc.set_segm_alignment(address, segm.alignment()) idc.set_segm_attr(address, SEGATTR_PERM, segm.flags()) # Process Dynamic Segment... elif segm.name() == 'DYNAMIC': code = idaapi.get_segm_by_name('CODE') data = idaapi.get_segm_by_name('DATA') relro = idaapi.get_segm_by_name('SCE_RELRO') # ------------------------------------------------------------------------------------------------------------ # Dynamic Tag Entry Structure members = [('tag', 'Tag', 0x8), ('value', 'Value', 0x8)] struct = segm.struct('Tag', members) # Dynamic Tag Table stubs = {} modules = {} location = segm.MEM_ADDR # Dumps are offset by a small amount if code.start_ea != 0xFFFFFFFF82200000: dumped = code.start_ea - 0xFFFFFFFF82200000 else: dumped = 0 f.seek(location - code.start_ea) for entry in xrange(segm.MEM_SIZE / 0x10): idaapi.create_struct(location + (entry * 0x10), 0x10, struct) idc.set_cmt(location + (entry * 0x10), Dynamic(f).process(dumped, stubs, modules), False) # ------------------------------------------------------------------------------------------------------------ # Hash Entry Structure members = [('bucket', 'Bucket', 0x2), ('chain', 'Chain', 0x2), ('buckets', 'Buckets', 0x2), ('chains', 'Chains', 0x2)] struct = segm.struct('Hash', members) # Hash Table try: location = Dynamic.HASHTAB size = Dynamic.HASHTABSZ except: location = Dynamic.HASH size = Dynamic.SYMTAB - location f.seek(location - code.start_ea) for entry in xrange(size / 0x8): idaapi.create_struct(location + (entry * 0x8), 0x8, struct) # -------------------------------------------------------------------------------------------------------- # Relocation Entry Structure (with specific addends) members = [('offset', 'Offset (String Index)', 0x8), ('info', 'Info (Symbol Index : Relocation Code)', 0x8), ('addend', 'AddEnd', 0x8)] struct = segm.struct('Relocation', members) # Relocation Table (with specific addends) location = Dynamic.RELATAB f.seek(location - code.start_ea) for entry in xrange(Dynamic.RELATABSZ / 0x18): idaapi.create_struct(location + (entry * 0x18), 0x18, struct) idc.set_cmt(location + (entry * 0x18), Relocation(f).process(dumped, code.end_ea), False) # Initialization Function idc.add_entry(Dynamic.INIT, Dynamic.INIT, '.init', True) address = code.start_ea # ELF Header Structure members = [('File format', 0x4), ('File class', 0x1), ('Data encoding', 0x1), ('File version', 0x1), ('OS/ABI', 0x1), ('ABI version', 0x1), ('Padding', 0x7), ('File type', 0x2), ('Machine', 0x2), ('File version', 0x4), ('Entry point', 0x8), ('PHT file offset', 0x8), ('SHT file offset', 0x8), ('Processor-specific flags', 0x4), ('ELF header size', 0x2), ('PHT entry size', 0x2), ('Number of entries in PHT', 0x2), ('SHT entry size', 0x2), ('Number of entries in SHT', 0x2), ('SHT entry index for string table\n', 0x2)] for (comment, size) in members: flags = idaapi.get_flags_by_size(size) idc.create_data(address, flags if flags != 0 else FF_STRLIT, size, BADNODE) idc.set_cmt(address, comment, False) address += size for index, entry in enumerate(ps4.E_SEGMENTS): # ELF Program Header Structure members = [('Type: %s' % entry.name(), 0x4), ('Flags', 0x4), ('File offset', 0x8), ('Virtual address', 0x8), ('Physical address', 0x8), ('Size in file image', 0x8), ('Size in memory image', 0x8), ('Alignment\n', 0x8)] for (comment, size) in members: flags = idaapi.get_flags_by_size(size) idc.create_data(address, flags if flags != 0 else FF_STRLIT, size, BADNODE) idc.set_cmt(address, comment, False) address += size # Start Function idc.add_entry(ps4.E_START_ADDR, ps4.E_START_ADDR, 'start', True) # Xfast_syscall address = idaapi.find_binary( code.start_ea, code.end_ea, '0F 01 F8 65 48 89 24 25 A8 02 00 00 65 48 8B 24', 0x10, SEARCH_DOWN) idaapi.do_unknown(address, 0) idaapi.create_insn(address) idaapi.add_func(address, BADADDR) idaapi.set_name(address, 'Xfast_syscall', SN_NOCHECK | SN_NOWARN | SN_FORCE) # -------------------------------------------------------------------------------------------------------- # Znullptr's syscalls print('# Processing Znullptr\'s Syscalls...') # Syscall Entry Structure members = [('narg', 'Number of Arguments', 0x4), ('_pad', 'Padding', 0x4), ('function', 'Function', 0x8), ('auevent', 'Augmented Event?', 0x2), ('_pad1', 'Padding', 0x2), ('_pad2', 'Padding', 0x4), ('trace_args_func', 'Trace Arguments Function', 0x8), ('entry', 'Entry', 0x4), ('return', 'Return', 0x4), ('flags', 'Flags', 0x4), ('thrcnt', 'Thread Count?', 0x4)] struct = segm.struct('Syscall', members) znullptr(code.start_ea, code.end_ea, '4F 52 42 49 53 20 6B 65 72 6E 65 6C 20 53 45 4C 46', struct) # -------------------------------------------------------------------------------------------------------- # Chendo's cdevsw con-struct-or print('# Processing Chendo\'s Structures...') # cdevsw Entry Structure members = [('d_version', 'Version', 0x4), ('d_flags', 'Flags', 0x4), ('d_name', 'Name', 0x8), ('d_open', 'Open', 0x8), ('d_fdopen', 'File Descriptor Open', 0x8), ('d_close', 'Close', 0x8), ('d_read', 'Read', 0x8), ('d_write', 'Write', 0x8), ('d_ioctl', 'Input/Ouput Control', 0x8), ('d_poll', 'Poll', 0x8), ('d_mmap', 'Memory Mapping', 0x8), ('d_strategy', 'Strategy', 0x8), ('d_dump', 'Dump', 0x8), ('d_kqfilter', 'KQFilter', 0x8), ('d_purge', 'Purge', 0x8), ('d_mmap_single', 'Single Memory Mapping', 0x8), ('d_spare0', 'Spare0', 0x8), ('d_spare1', 'Spare1', 0x8), ('d_spare2', 'Spare2', 0x8), ('d_spare3', 'Spare3', 0x8), ('d_spare4', 'Spare4', 0x8), ('d_spare5', 'Spare5', 0x8), ('d_spare6', 'Spare6', 0x4), ('d_spare7', 'Spare7', 0x4)] struct = segm.struct('cdevsw', members) chendo(data.start_ea, data.end_ea, '09 20 12 17', struct) # -------------------------------------------------------------------------------------------------------- # Pablo's IDC try: print('# Processing Pablo\'s Push IDC...') # Script 1) Push it real good... # Default patterns set pablo(0, code.start_ea, 0x10, '55 48 89') pablo(2, code.start_ea, code.end_ea, '90 90 55 48 ??') pablo(2, code.start_ea, code.end_ea, 'C3 90 55 48 ??') pablo(2, code.start_ea, code.end_ea, '66 90 55 48 ??') pablo(2, code.start_ea, code.end_ea, 'C9 C3 55 48 ??') pablo(2, code.start_ea, code.end_ea, '0F 0B 55 48 ??') pablo(2, code.start_ea, code.end_ea, 'EB ?? 55 48 ??') pablo(2, code.start_ea, code.end_ea, '5D C3 55 48 ??') pablo(2, code.start_ea, code.end_ea, '5B C3 55 48 ??') pablo(2, code.start_ea, code.end_ea, '90 90 55 41 ?? 41 ??') pablo(2, code.start_ea, code.end_ea, '66 90 48 81 EC ?? 00 00 00') pablo(2, code.start_ea, code.end_ea, '0F 0B 48 89 9D ?? ?? FF FF 49 89') pablo(2, code.start_ea, code.end_ea, '90 90 53 4C 8B 54 24 20') pablo(2, code.start_ea, code.end_ea, '90 90 55 41 56 53') pablo(2, code.start_ea, code.end_ea, '90 90 53 48 89') pablo(2, code.start_ea, code.end_ea, '90 90 41 ?? 41 ??') pablo(3, code.start_ea, code.end_ea, '0F 0B 90 55 48 ??') pablo(3, code.start_ea, code.end_ea, 'EB ?? 90 55 48 ??') pablo(3, code.start_ea, code.end_ea, '41 5F C3 55 48 ??') pablo(3, code.start_ea, code.end_ea, '41 5C C3 55 48 ??') pablo(3, code.start_ea, code.end_ea, '31 C0 C3 55 48 ??') pablo(3, code.start_ea, code.end_ea, '41 5D C3 55 48 ??') pablo(3, code.start_ea, code.end_ea, '41 5E C3 55 48 ??') pablo(3, code.start_ea, code.end_ea, '66 66 90 55 48 ??') pablo(3, code.start_ea, code.end_ea, '0F 1F 00 55 48 ??') pablo(3, code.start_ea, code.end_ea, '41 ?? C3 53 48') pablo(3, code.start_ea, code.end_ea, '0F 1F 00 48 81 EC ?? 00 00 00') pablo(4, code.start_ea, code.end_ea, '0F 1F 40 00 55 48 ??') pablo(4, code.start_ea, code.end_ea, '0F 1F 40 00 48 81 EC ?? 00 00 00') pablo(5, code.start_ea, code.end_ea, 'E9 ?? ?? ?? ?? 55 48 ??') pablo(5, code.start_ea, code.end_ea, 'E8 ?? ?? ?? ?? 55 48 ??') pablo(5, code.start_ea, code.end_ea, '48 83 C4 ?? C3 55 48 ??') pablo(5, code.start_ea, code.end_ea, '0F 1F 44 00 00 55 48 ??') pablo(5, code.start_ea, code.end_ea, '0F 1F 44 00 00 48 81 EC ?? 00 00 00') pablo(6, code.start_ea, code.end_ea, 'E9 ?? ?? ?? ?? 90 55 48 ??') pablo(6, code.start_ea, code.end_ea, 'E8 ?? ?? ?? ?? 90 55 48 ??') pablo(6, code.start_ea, code.end_ea, '66 0F 1F 44 00 00 55 48 ??') pablo(7, code.start_ea, code.end_ea, '0F 1F 80 00 00 00 00 55 48 ??') pablo(8, code.start_ea, code.end_ea, '0F 1F 84 00 00 00 00 00 55 48 ??') pablo(8, code.start_ea, code.end_ea, 'C3 0F 1F 80 00 00 00 00 48') pablo(8, code.start_ea, code.end_ea, '0F 1F 84 00 00 00 00 00 53 48 83 EC') # Special cases patterns set pablo(13, code.start_ea, code.end_ea, 'C3 90 90 90 90 90 90 90 90 90 90 90 90 48') pablo(13, code.start_ea, code.end_ea, 'C3 90 90 90 90 90 90 90 90 90 90 90 90 55') pablo(17, code.start_ea, code.end_ea, 'E9 ?? ?? ?? ?? 90 90 90 90 90 90 90 90 90 90 90 90 48') pablo(19, code.start_ea, code.end_ea, 'E9 ?? ?? ?? ?? 90 90 90 90 90 90 90 90 90 90 90 90 90 90 48') pablo(19, code.start_ea, code.end_ea, 'E8 ?? ?? ?? ?? 90 90 90 90 90 90 90 90 90 90 90 90 90 90 48') pablo( 20, code.start_ea, code.end_ea, 'E9 ?? ?? ?? ?? 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 48') pablo( 20, code.start_ea, code.end_ea, 'E9 ?? ?? ?? ?? 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 48') # Script 2) Fix-up Dumped Data Pointers... if dumped: print('# Processing Pablo\'s Dumped Data Pointers IDC...') pablo(0, data.start_ea, data.end_ea, '?? FF FF FF FF') except: pass # -------------------------------------------------------------------------------------------------------- # Kiwidog's __stack_chk_fail print('# Processing Kiwidog\'s Stack Functions...') kiwidog( code.start_ea, code.end_ea, '73 74 61 63 6B 20 6F 76 65 72 66 6C 6F 77 20 64 65 74 65 63 74 65 64 3B' ) print('# Done!') return 1
import os SEG_PROT_R = 4 SEG_PROT_W = 2 SEG_PROT_X = 1 cmd = 'vmmap' try: mappings = json.loads(idc.send_dbg_command("vmmap")) for start, end, prot, pathname in mappings: # if pathname == "<no name>": continue # Why we should ignore mmapped mapges & co.? perms = 0 if prot & SEG_PROT_R: perms |= idaapi.SEGPERM_READ if prot & SEG_PROT_W: perms |= idaapi.SEGPERM_WRITE if prot & SEG_PROT_X: perms |= idaapi.SEGPERM_EXEC name = os.path.basename(pathname) sclass = "DATA" # TODO recognize automatically sclass idaapi.add_segm(0, start, end, name, sclass) idc.set_segm_attr(start, SEGATTR_PERM, perms) idc.set_segm_attr(start, SEGATTR_ES, 0) idc.set_segm_attr(start, SEGATTR_CS, 0) idc.set_segm_attr(start, SEGATTR_SS, 0) idc.set_segm_attr(start, SEGATTR_DS, 0) idc.set_segm_attr(start, SEGATTR_FS, 0) idc.set_segm_attr(start, SEGATTR_GS, 0) except: #self.AddLine(idaapi.COLSTR("Debugger is not active or does not export send_dbg_command()", idaapi.SCOLOR_ERROR)) import traceback traceback.print_exc() print("\nDebugger is not active or does not export send_dbg_command()")
def load_file(li, neflags, format): # ensure we are not wrongly called if not format.startswith("Accessory Firmware Update"): return 0 li.seek(0) data = li.read(0x14) (magic, xxx1, fw_type, fw_ver, fw_len, unk1, product_id, hw_rev_id) = struct.unpack("<HHHHIIHH", data) li.seek(0x20) AFU_signature_header_data = li.read(24) (sig_magic, unknown1, unknown2, digest_type, digest_len, digest_offset, sig_type, sig_len, sig_offset) = struct.unpack("<IHHHHIHHI", AFU_signature_header_data) idaapi.set_processor_type("ARM:ARMv7-M", ida_idp.SETPROC_ALL) if product_id == 0x312: # Apple Pencil fw_base = 0x8006080 msp_base = fw_base elif product_id == 0x14c: # Apple Pencil 2 if fw_type == 1: fw_base = 0x08000980 msp_base = fw_base + 0x180 if fw_type == 0x20: fw_base = 0x0 msp_base = fw_base if fw_type == 0x30: fw_base = 0x0 msp_base = fw_base if fw_type == 0x50: fw_base = 0x08000000 msp_base = fw_base elif product_id == 0x26d: # Siri Remote 2 if fw_type == 1: fw_base = 0x8008080 msp_base = fw_base + 0x180 if fw_type == 0xb0: fw_base = 0x280 msp_base = fw_base + 0x180 elif product_id == 0x268: # Smart Keyboard 12.9" fw_base = 0x08002600 msp_base = fw_base elif product_id == 0x26A: # Smart Keyboard 9.7" fw_base = 0x08002600 msp_base = fw_base elif product_id == 0x26B: # Smart Keyboard 10.5" fw_base = 0x08002600 # don't really know, haven't seen an OTA so far msp_base = fw_base elif product_id == 0x292: # Smart Keyboard Folio 11" fw_base = 0x08000980 # seems to work msp_base = fw_base + 0x180 elif product_id == 0x293: # Smart Keyboard Folio 12.9" fw_base = 0x08000980 # seems to work msp_base = fw_base + 0x180 else: return 0 # for now a heuristic show_header = True if fw_type != 1 or fw_base == 0: show_header = False if show_header: li.file2base(0, fw_base - 0x80, fw_base, 1) li.file2base(0x80, fw_base, fw_base + fw_len, 1) if show_header: idaapi.add_segm(0, fw_base - 0x80, fw_base, "HEADER", "DATA") idaapi.add_segm(0, fw_base, fw_base + fw_len, "__TEXT", "CODE") idaapi.add_segm(0, 0xE000E000, 0xE000F000, "__SYSREG", "DATA") idaapi.add_segm(0, SRAM_BASE, SRAM_BASE + SRAM_SIZE, "__SRAM", "DATA") if show_header: idc.split_sreg_range(fw_base - 0x80, "T", 1) idc.split_sreg_range(fw_base, "T", 1) # register the structures register_structs() # apply the structure if show_header: idc.set_name(fw_base - 0x80, "AFU_HEADER") idc.create_struct(fw_base - 0x80, -1, "afu_full_header") ida_nalt.unhide_item(fw_base - 0x80 + 1) # handle the digest and signature if sig_magic == 0x61E34724: # apply the structure if show_header: idc.set_name(fw_base - 0x80 + 0x20, "AFU_SIG_HEADER") #idc.create_struct(fw_base - 0x80 + 0x20, -1, "afu_sig_header") #ida_nalt.unhide_item(fw_base - 0x80 + 0x20 + 1) # first handle the digest base = fw_base + fw_len li.file2base(digest_offset, base, base + digest_len, 1) idaapi.add_segm(0, base, base + digest_len, "__DIGEST", "DATA") idc.create_byte(base) idc.make_array(base, digest_len) idc.set_name(base, "AFU_DIGEST") # now handle the signature base += digest_len li.file2base(sig_offset, base, base + sig_len, 1) idaapi.add_segm(0, base, base + sig_len, "__SIGNATURE", "DATA") idc.create_byte(base) idc.make_array(base, sig_len) idc.set_name(base, "AFU_SIGNATURE") # check if __TEXT starts with an SRAM address # this is the initial MSP that is followed by exception vectors initMSP = idc.Dword(msp_base) print "initMSP 0x%x" % initMSP if (initMSP >= SRAM_BASE) and initMSP <= (SRAM_BASE + SRAM_SIZE): idc.set_name(msp_base, "init_MSP") idc.create_dword(msp_base) idc.op_plain_offset(msp_base, -1, 0) idc.set_cmt(msp_base, "Initial MSP value", 0) # these are now the exception vectors # determine how many exception vectors there are cnt = 0 handlers = {} last_multi = None multi = False while cnt < 255: ptr = idc.Dword(msp_base + 4 + 4 * cnt) if ptr != 0: # must be inside __TEXT if (ptr < fw_base) or (ptr > fw_base + fw_len): break if (ptr & 1) == 0: # must be thumb mode break # convert into a dword + offset idc.create_dword(msp_base + 4 + 4 * cnt) if ptr != 0: idc.op_offset(msp_base + 4 + 4 * cnt, 0, idc.REF_OFF32, -1, 0, 0) idc.set_cmt( msp_base + 4 + 4 * cnt, "exception %d: %s" % (cnt + 1, exception_table[cnt + 1]), 0) # should only RESET vector be our entrypoint? idc.add_entry(ptr & ~1, ptr & ~1, "", 1) # remember how often we see each handler if ptr != 0: if handlers.has_key(ptr): handlers[ptr] += 1 if last_multi != None: if last_multi != ptr: multi = True last_multi = ptr else: handlers[ptr] = 1 cnt += 1 print "cnt: %d" % cnt if cnt > 0: i = 1 while i <= cnt: ptr = idc.Dword(msp_base + 4 * i) if ptr != 0: # ensure this is if handlers[ptr] == 1: idc.set_name( ptr & ~1, "%s_%s" % (EXCEPTION_PREFIX, exception_table[i])) elif not multi: idc.set_name(ptr & ~1, "%s_%s" % (EXCEPTION_PREFIX, "UNIMPL")) i += 1 return 1
def load_file(li, neflags, format): if format != "NeoGeo 68k loader": return 0 idaapi.set_processor_type("68000", SETPROC_ALL | SETPROC_FATAL) idaapi.add_segm(0, 0x000000, 0x0FFFFF, "ROM", "CODE") idaapi.add_segm(0, 0x100000, 0x10F2FF, "WRAM", "DATA") idaapi.add_segm(0, 0x10F300, 0x10FFFF, "BIOSRAM", "DATA") idaapi.add_segm(0, 0x200000, 0x2FFFFF, "PORT", "DATA") idaapi.add_segm(0, 0x300000, 0x3FFFFF, "IO", "DATA") idaapi.add_segm(0, 0x400000, 0x401FFF, "PALETTES", "DATA") idaapi.add_segm(0, 0x800000, 0xBFFFFF, "MEMCARD", "DATA") idaapi.add_segm(0, 0xC00000, 0xC1FFFF, "SYSROM", "DATA") idaapi.add_segm(0, 0xD00000, 0xD0FFFF, "BRAM", "DATA") map_io_registers() li.seek(0, 2) file_size = li.tell() li.seek(0) # read p1 rom file_data = li.read(0x100000) file_remain = file_size - 0x100000 idaapi.mem2base(file_data, 0, 0x100000) # read p2 rom bank2_seg_offset = 0x01000000 bank2_seg_index = 0 while file_remain > 0: bank2_seg_start = bank2_seg_offset + 0x200000 bank2_seg_end = bank2_seg_start + 0x2FFFFF idaapi.add_segm(0, bank2_seg_start, bank2_seg_end, "BANK2_%d" % bank2_seg_index, "DATA") bytes_to_read = file_remain if bytes_to_read > 0x100000: bytes_to_read = 0x100000 file_data = li.read(bytes_to_read) idaapi.mem2base(file_data, bank2_seg_start, bank2_seg_start + len(file_data)) bank2_seg_offset += 0x01000000 bank2_seg_index += 1 file_remain -= bytes_to_read # http://ajworld.net/neogeodev/beginner/ name_long(0x000000, "InitSP") name_long(0x000004, "InitPC") name_long(0x000008, "BusError") name_long(0x00000C, "AddressError") name_long(0x000010, "IllegalInstruction") name_long(0x000014, "DivByZero") name_long(0x000018, "CHK") name_long(0x00001C, "TRAPV") name_long(0x000020, "PrivilegeViolation") name_long(0x000024, "Trace") name_long(0x000028, "Line1010Emu") name_long(0x00002C, "Line1111Emu") name_array(0x000030, "Reserved0", 0xC) name_long(0x000003C, "UnintializedInterruptVec") name_array(0x000040, "Reserved1", 0x20) name_long(0x000060, "VirtualInterrupt") name_long(0x000064, "Interrupt1") name_long(0x000068, "Interrupt2") name_long(0x00006C, "Interrupt3") name_long(0x000070, "Interrupt4") name_long(0x000074, "Interrupt5") name_long(0x000078, "Interrupt6") name_long(0x00007C, "Interrupt7") name_dword_array(0x000080, "Traps", 0x10) name_array(0x0000C0, "Reserved2", 0x40) # Neo-Geo header # https://wiki.neogeodev.org/index.php?title=68k_program_header idc.create_strlit(0x000100, 0x000107) idaapi.set_name(0x000100, "Magic") name_byte(0x000107, "SysVersion") name_word(0x000108, "GameID") name_long(0x00010A, "ProgramSize") name_long(0x00010E, "BackupRAMPtr") name_word(0x000112, "BackupRAMSize") name_byte(0x000114, "EyecatchFlag") name_byte(0x000115, "EyecatchSpriteBank") name_long(0x000116, "MenuJP") name_long(0x00011A, "MenuUS") name_long(0x00011E, "MenuEU") name_code(0x000122, "Routine_USER", 6) name_code(0x000128, "Routine_PLAYER_START", 6) name_code(0x00012E, "Routine_DEMO_END", 6) name_code(0x000134, "Routine_COIN_SOUND", 6) name_array(0x00013A, "Unknown0", 0x48) name_long(0x000182, "CartridgeRecognitionCodePtr") name_long(0x000186, "Unknown1") name_long(0x00018A, "Unknown2") name_long(0x00018E, "MenuES") # Spanish # BIOS RAM set_name_with_comment(0x10FD80, "BIOS_SYSTEM_MODE", "0x00 : System" "0x80 : Game") set_name_with_comment(0x10FD82, "BIOS_MVS_FLAG", "0 : HOME/AES\n" "1 : MVS") set_name_with_comment( 0x10FDAE, "BIOS_USER_REQUEST", "0 : Startup initialization\n" "1 : Eye-catcher\n" "2 : Demo Game / Game\n" "3 : Title Display") set_name_with_comment( 0x10FDAF, "BIOS_USER_MODE", "Current game status.\n" "0 : init/boot\n" "1 : title/demo\n" "2 : game") set_name_with_comment( 0x10FEC5, "BIOS_TITLE_MODE", "Newer games set this to 1 in their command 3 USER subroutine.\n" "It prevents the system ROM from calling command 3 twice after game over if credits are already in the system.\n" ) #idaapi.del_items(0x3C0000) # VRAM set_name_with_comment( 0x3C0000, "REG_VRAMADDR", "sets the VRAM address for the next read/write operation.") set_name_with_comment(0x3C0002, "REG_VRAMRW", "the data read or to write.") set_name_with_comment( 0x3C0004, "REG_VRAMMOD", "the signed value automatically added to the VRAM address after a write." ) set_name_with_comment( 0x3C000C, "LSPC_IRQ_ACK", "IRQ acknowledgement register.\nbit2 : Ack.VBlank | bit 1 : Ack.HBlank | bit 0 : IRQ3" ) idaapi.set_name(0xC00444, "SYSTEM_RETURN") idaapi.set_name(0xC0044A, "SYSTEM_IO") idaapi.set_name(0xD00100, "BOARD_CORRUPTED") #idaapi.set_cmt(0x3C0000, "Pouet.", 1) return 1
def add_mdec_regs(cls): idaapi.add_segm(0, 0x1F801820, 0x1F801828, 'MDEC_REGS', 'XTRN') cls.add_port_4(0x1F801820, 'MDEC_REG0') cls.add_port_4(0x1F801824, 'MDEC_REG1')
def create_got_fix(cls): idaapi.add_segm(0, 0x90000000, 0x90000001, '.got', 'DATA') seg = idaapi.getseg(0x90000000) idaapi.set_visible_segm(seg, False)
def add_dma(cls): idaapi.add_segm(0, 0x1F801080, 0x1F80108C, 'DMA_MDEC_IN', 'XTRN') idaapi.add_segm(0, 0x1F801090, 0x1F80109C, 'DMA_MDEC_OUT', 'XTRN') idaapi.add_segm(0, 0x1F8010A0, 0x1F8010AC, 'DMA_GPU', 'XTRN') idaapi.add_segm(0, 0x1F8010B0, 0x1F8010BC, 'DMA_CDROM', 'XTRN') idaapi.add_segm(0, 0x1F8010C0, 0x1F8010CC, 'DMA_SPU', 'XTRN') idaapi.add_segm(0, 0x1F8010D0, 0x1F8010DC, 'DMA_PIO', 'XTRN') idaapi.add_segm(0, 0x1F8010E0, 0x1F8010EC, 'DMA_OTC', 'XTRN') idaapi.add_segm(0, 0x1F8010F0, 0x1F8010F8, 'DMA_CTRL_INT', 'XTRN') cls.add_port_4(0x1F801080, 'DMA_MDEC_IN_MADR') cls.add_port_4(0x1F801084, 'DMA_MDEC_IN_BCR') cls.add_port_4(0x1F801088, 'DMA_MDEC_IN_CHCR') cls.add_port_4(0x1F801090, 'DMA_MDEC_OUT_MADR') cls.add_port_4(0x1F801094, 'DMA_MDEC_OUT_BCR') cls.add_port_4(0x1F801098, 'DMA_MDEC_OUT_CHCR') cls.add_port_4(0x1F8010A0, 'DMA_GPU_MADR') cls.add_port_4(0x1F8010A4, 'DMA_GPU_BCR') cls.add_port_4(0x1F8010A8, 'DMA_GPU_CHCR') cls.add_port_4(0x1F8010B0, 'DMA_CDROM_MADR') cls.add_port_4(0x1F8010B4, 'DMA_CDROM_BCR') cls.add_port_4(0x1F8010B8, 'DMA_CDROM_CHCR') cls.add_port_4(0x1F8010C0, 'DMA_SPU_MADR') cls.add_port_4(0x1F8010C4, 'DMA_SPU_BCR') cls.add_port_4(0x1F8010C8, 'DMA_SPU_CHCR') cls.add_port_4(0x1F8010D0, 'DMA_PIO_MADR') cls.add_port_4(0x1F8010D4, 'DMA_PIO_BCR') cls.add_port_4(0x1F8010D8, 'DMA_PIO_CHCR') cls.add_port_4(0x1F8010E0, 'DMA_OTC_MADR') cls.add_port_4(0x1F8010E4, 'DMA_OTC_BCR') cls.add_port_4(0x1F8010E8, 'DMA_OTC_CHCR') cls.add_port_4(0x1F8010F0, 'DMA_DPCR') cls.add_port_4(0x1F8010F4, 'DMA_DICR')
def add_gpu_regs(cls): idaapi.add_segm(0, 0x1F801810, 0x1F801818, 'GPU_REGS', 'XTRN') cls.add_port_4(0x1F801810, 'GPU_REG0') cls.add_port_4(0x1F801814, 'GPU_REG1')
def add_int_ctrl(cls): idaapi.add_segm(0, 0x1F801070, 0x1F801076, 'INT_CTRL', 'XTRN') cls.add_port_2(0x1F801070, 'I_STAT') cls.add_port_2(0x1F801074, 'I_MASK')
def add_mem_ctrl2(cls): idaapi.add_segm(0, 0x1F801060, 0x1F801064, 'MCTRL2', 'XTRN') cls.add_port_4(0x1F801060, 'RAM_SIZE')
def load_file(li, neflags, format): # Check the format we've been asked to load if format != FORMAT_NAME: return 0 # Datatrak 68K - set processor type idaapi.set_processor_type("68000", SETPROC_ALL | SETPROC_FATAL) # Add segments idaapi.add_segm(0, 0x000000, 0x03FFFF, "ROM", "CODE") # TODO validate ROM area idaapi.add_segm(0, 0x200000, 0x21FFFF, "RAM", "DATA") # TODO validate RAM area idaapi.add_segm(0, 0x220000, 0x23FFFF, "RAM2", "DATA") # TODO validate this, it's here just to fill space idaapi.add_segm(0, 0x240000, 0x2400FF, "IO_ADC", "DATA") # A/D converter idaapi.add_segm(0, 0x240100, 0x2401FF, "IO_UNK_01", "DATA") # idaapi.add_segm(0, 0x240200, 0x2402FF, "IO_RFPHA", "DATA") # RF phase detector idaapi.add_segm(0, 0x240300, 0x2403FF, "IO_UART", "DATA") # Dual UART idaapi.add_segm(0, 0x240400, 0x2404FF, "IO_UNK_04", "DATA") # idaapi.add_segm(0, 0x240500, 0x2405FF, "IO_UNK_05", "DATA") # idaapi.add_segm(0, 0x240600, 0x2406FF, "IO_UNK_06", "DATA") # idaapi.add_segm(0, 0x240700, 0x2407FF, "IO_UNK_07", "DATA") # idaapi.add_segm(0, 0x240800, 0x2408FF, "IO_UNK_08", "DATA") # idaapi.add_segm(0, 0x240900, 0x24FFFF, "IO_UNKNOWN", "DATA") # # Seek to EOF and get filesize li.seek(0, 2) size = li.tell() # Seek back to start of file, read the file into memory li.seek(0) file_data = li.read(size) idaapi.mem2base(file_data, 0, size) # data,start,end do_68k_vectors() # Get the initial program counter initPC = struct.unpack('>L', bytearray(file_data[4:8]))[0] # Hunt down the DATA segment initialiser, starting at the reset vector pattern = [ 0x41, 0xF9, 0x00, 0x20, 0x00, 0x00, # LEA (0x200000).L, A0 ; start of dseg in RAM 0x20, 0x3C, 0x00, None, None, None, # MOVE.L #EndOfDataSeg, D0 ; end of dseg in RAM 0x90, 0x88, # SUB.L A0, D0 ; D0 = D0 - A0 0x43, 0xF9, 0x00, None, None, None, # LEA (StartOfData), A1 ; start of dseg initialisation data 0x53, 0x80, # SUBQ.L #1, D0 ; D0 -- 0x10, 0xD9, # MOVE.B (A1)+, (A0)+ ; *a0++ = *a1++ 0x51, 0xC8, 0xFF, 0xFC # DBF D0, $-2 ; decrement d0, branch if >= 0 ] sh_reg = [0x00] * len(pattern) # TODO: Refactor this to use IDA's internal buffer instead of the file_data array? for addr in range(initPC, initPC + 0x100): # shift in next byte sh_reg = sh_reg[1:] sh_reg.append(ord(file_data[addr])) # check if we've found a match match = True for i in range(len(pattern)): if pattern[i] is not None and pattern[i] != sh_reg[i]: match = False break # exit the search loop if we found a match if match: break if match: # If we've exited the loop and have a match, fish the DSEG addresses # out of the instruction parameters. dsegRamStart = struct.unpack(">L", bytearray(sh_reg[2:6]))[0] dsegRamEnd = struct.unpack(">L", bytearray(sh_reg[8:12]))[0] dsegRomStart = struct.unpack(">L", bytearray(sh_reg[16:20]))[0] print("DSEG RAM Start %08X" % dsegRamStart) print("DSEG RAM End %08X" % dsegRamEnd) print("DSEG ROM Start %08X" % dsegRomStart) # Calculate initialised data segment end and end of the idata in ROM dsegLen = dsegRamEnd - dsegRamStart dsegRomEnd = dsegRomStart + dsegLen # Load the idata into RAM from the appropriate part of the ROM file idaapi.mem2base(file_data[dsegRomStart:dsegRomEnd], dsegRamStart, dsegRamEnd) # TODO: Designate the source idata as ROM DATA idaapi.add_segm(0, dsegRomStart, dsegRomEnd, "DATAINIT", "DATA") else: print("No Match") # TODO: Try to identify the boundary between CODE and ROMDATA and move that into a segment # Wait for autoanalysis to finish autoWait() # Convert all MOVEA source parameters into address references doMoveaAddrRefs() # TODO: Search for switch-case jump tables oper_re = re.compile(r'\(pc,([ad][0-9]).([bwl])\)') insnStack = [] for ea in Heads(0x33B0, 0x33D0): # Decode the instruction and operands insn = { 'ea': ea, 'disasm': GetDisasm(ea), 'mnem': GetMnem(ea), } opnds = [] for i in range(3): opnd = { 'type': get_operand_type(ea, i), 'opnd': GetOpnd(ea, i), 'value': GetOperandValue(ea, i) } # store non-empty operands only if opnd['type'] != 0: # FIXME use operand type constants opnds.append(opnd) insn['operands'] = opnds # push the instruction onto the stack, limiting the stack size insnStack.append(insn) insnStack = insnStack[-10:] # Look for this sequence at the end of the stack: # ADD.W d0,d0 # MOVE.W <table>(pc,d0.w) # JMP <table>(pc,d0.w) # if insnStack[-1]['mnem'] == 'jmp' and insnStack[-2]['mnem'].startswith( 'move.') and insnStack[-3]['mnem'].startswith('add.'): print("*** Potential jumptable sequence found at EOS") pprint(insnStack[-3]) pprint(insnStack[-2]) pprint(insnStack[-1]) # Check the argtypes # Jump and move should have identical first argument types if insnStack[-1]['operands'][0]['type'] != 4 or insnStack[-2][ 'operands'][0]['type'] != 4: continue # Add should be rigged to double the value of a register if insnStack[-3]['operands'][0]['type'] != 1 or insnStack[-3][ 'operands'][1]['type'] != 1: continue if insnStack[-3]['operands'][0]['opnd'] != insnStack[-3][ 'operands'][1]['opnd']: continue # Jumptable address is the offset in the jump instruction's first operand jumptableAddr = insnStack[-1]['operands'][0]['value'] # Jump instruction EA jumpinstrAddr = insnStack[-1]['ea'] # Jump register jumpReg = insnStack[-3]['operands'][0]['opnd'] # Is there a subtract instruction above the 'add R,R'? if insnStack[-4]['mnem'].startswith('subi.'): jumpOffset = insnStack[-4]['operands'][0]['value'] else: jumpOffset = 0 print( "Jumptable Debug: TADDR %08X, JADDR %08X, OFFSET %d, REGISTER '%s'" % (jumptableAddr, jumpinstrAddr, jumpOffset, jumpReg)) jtLowerBound = None jtUpperBound = None # Hunt backwards for the compare operations (to get the JT bounds) for i in reversed(range(3, 8)): # 10 - 2 curi = insnStack[-i] previ = insnStack[-(i + 1)] if previ['mnem'].startswith('cmpi.'): print("COMPARE value %d" % previ['operands'][0]['value']) if curi['mnem'].startswith('blt.'): jtLowerBound = previ['operands'][0]['value'] elif curi['mnem'].startswith('bgt.'): jtUpperBound = previ['operands'][0]['value'] print("Jumptable Debug: upperbound %s lowerbound %s" % (jtUpperBound, jtLowerBound)) # TODO: figure out the register size # Set jumptable data swInf = switch_info_t() swInf.set_jtable_element_size # TODO: use AddCodeXref(?) to link calls where a function address has been copied into an address register # TODO: Search for the task table? """ # ??? idaapi.do_unknown(0x3C0000, 1) idaapi.doByte(0x3C0000, 1) idaapi.set_name(0x3C0000, "REG_VRAMADDR") #idaapi.set_cmt(0x3C0000, "Pouet.", 1) """ return 1
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)
def load_one_file(li, options, idx, basename=None): bypass_plt = OPT_BYPASS_PLT in options f = load_nxo(li) if idx == 0: if f.armv7: idc.SetShortPrm(idc.INF_LFLAGS, idc.GetShortPrm(idc.INF_LFLAGS) | idc.LFLG_PC_FLAT) else: idc.SetShortPrm(idc.INF_LFLAGS, idc.GetShortPrm(idc.INF_LFLAGS) | idc.LFLG_64BIT) idc.SetCharPrm(idc.INF_DEMNAMES, idaapi.DEMNAM_GCC3) idaapi.set_compiler_id(idaapi.COMP_GNU) idaapi.add_til2('gnulnx_arm' if f.armv7 else 'gnulnx_arm64', 1) # don't create tails idc.set_inf_attr(idc.INF_AF, idc.get_inf_attr(idc.INF_AF) & ~idc.AF_FTAIL) if OPT_LOAD_31_BIT in options: loadbase = 0x8000000 step = 0x1000000 elif f.armv7: loadbase = 0x60000000 step = 0x10000000 else: loadbase = 0x7100000000 step = 0x100000000 loadbase += idx * step f.binfile.seek(0) as_string = f.binfile.read(f.bssoff) idaapi.mem2base(as_string, loadbase) seg_prefix = basename if basename is not None else '' for start, end, name, kind in f.sections: if name.startswith('.got'): kind = 'CONST' idaapi.add_segm(0, loadbase+start, loadbase+end, seg_prefix+name, kind) segm = idaapi.get_segm_by_name(seg_prefix+name) if kind == 'CONST': segm.perm = idaapi.SEGPERM_READ elif kind == 'CODE': segm.perm = idaapi.SEGPERM_READ | idaapi.SEGPERM_EXEC elif kind == 'DATA': segm.perm = idaapi.SEGPERM_READ | idaapi.SEGPERM_WRITE elif kind == 'BSS': segm.perm = idaapi.SEGPERM_READ | idaapi.SEGPERM_WRITE idaapi.update_segm(segm) idaapi.set_segm_addressing(segm, 1 if f.armv7 else 2) # do imports # TODO: can we make imports show up in "Imports" window? undef_count = 0 for s in f.symbols: if not s.shndx and s.name: undef_count += 1 last_ea = max(loadbase + end for start, end, name, kind in f.sections) undef_entry_size = 8 undef_ea = ((last_ea + 0xFFF) & ~0xFFF) + undef_entry_size # plus 8 so we don't end up on the "end" symbol undef_seg = basename + '.UNDEF' if basename is not None else 'UNDEF' idaapi.add_segm(0, undef_ea, undef_ea+undef_count*undef_entry_size, undef_seg, 'XTRN') segm = idaapi.get_segm_by_name(undef_seg) segm.type = idaapi.SEG_XTRN idaapi.update_segm(segm) for i,s in enumerate(f.symbols): if not s.shndx and s.name: idc.MakeQword(undef_ea) idaapi.do_name_anyway(undef_ea, s.name) s.resolved = undef_ea undef_ea += undef_entry_size elif i != 0: assert s.shndx s.resolved = loadbase + s.value if s.name: if s.type == STT_FUNC: idaapi.add_entry(s.resolved, s.resolved, s.name, 0) else: idaapi.do_name_anyway(s.resolved, s.name) else: # NULL symbol s.resolved = 0 funcs = set() for s in f.symbols: if s.name and s.shndx and s.value: if s.type == STT_FUNC: funcs.add(loadbase+s.value) symend = loadbase+s.value+s.size if Dword(symend) != 0: funcs.add(symend) got_name_lookup = {} for offset, r_type, sym, addend in f.relocations: target = offset + loadbase if r_type in (R_ARM_GLOB_DAT, R_ARM_JUMP_SLOT, R_ARM_ABS32): if not sym: print 'error: relocation at %X failed' % target else: idaapi.put_long(target, sym.resolved) elif r_type == R_ARM_RELATIVE: idaapi.put_long(target, idaapi.get_long(target) + loadbase) elif r_type in (R_AARCH64_GLOB_DAT, R_AARCH64_JUMP_SLOT, R_AARCH64_ABS64): idaapi.put_qword(target, sym.resolved + addend) if addend == 0: got_name_lookup[offset] = sym.name elif r_type == R_AARCH64_RELATIVE: idaapi.put_qword(target, loadbase + addend) if addend < f.textsize: funcs.add(loadbase + addend) else: print 'TODO r_type %d' % (r_type,) ida_make_offset(f, target) for func, target in f.plt_entries: if target in got_name_lookup: addr = loadbase + func funcs.add(addr) idaapi.do_name_anyway(addr, got_name_lookup[target]) if not f.armv7: funcs |= find_bl_targets(loadbase, loadbase+f.textsize) if bypass_plt: plt_lookup = f.plt_lookup for pco in xrange(0, f.textsize, 4): pc = loadbase + pco d = Dword(pc) if (d & 0x7c000000) == (0x94000000 & 0x7c000000): imm = d & 0x3ffffff if imm & 0x2000000: imm |= ~0x1ffffff if 0 <= imm <= 2: continue target = (pc + imm * 4) - loadbase if target in plt_lookup: new_target = plt_lookup[target] + loadbase new_instr = (d & ~0x3ffffff) | (((new_target - pc) / 4) & 0x3ffffff) idaapi.put_long(pc, new_instr) for pco in xrange(0, f.textsize, 4): pc = loadbase + pco d = Dword(pc) if d == 0x14000001: funcs.add(pc + 4) for pc, _ in f.eh_table: funcs.add(loadbase + pc) for addr in sorted(funcs, reverse=True): idaapi.auto_make_proc(addr) return 1
def go(self): print "0) Building cache..." self.load_nids() # Vita is ARM idaapi.set_processor_type("arm", idaapi.SETPROC_ALL | idaapi.SETPROC_FATAL) # Set compiler info cinfo = idaapi.compiler_info_t() cinfo.id = idaapi.COMP_GNU cinfo.cm = idaapi.CM_CC_CDECL | idaapi.CM_N32_F48 cinfo.size_s = 2 cinfo.size_i = cinfo.size_b = cinfo.size_e = cinfo.size_l = cinfo.defalign = 4 cinfo.size_ll = cinfo.size_ldbl = 8 idaapi.set_compiler(cinfo, 0) # Import types self.import_types() self.load_proto() print "1) Loading ELF segments" self.fin.seek(0) header = Ehdr(self.fin.read(Ehdr.SIZE)) self.fin.seek(header.e_phoff) phdrs = [Phdr(self.fin.read(header.e_phentsize)) for _ in xrange(header.e_phnum)] phdr_text = phdrs[0] for phdr in phdrs: if phdr.p_type == Phdr.PT_LOAD: idaapi.add_segm(0, phdr.p_vaddr, phdr.p_vaddr + phdr.p_memsz, ".text" if phdr.x else ".data", "CODE" if phdr.x else "DATA") seg = idaapi.getseg(phdr.p_vaddr) if phdr.x: seg.perm = idaapi.SEGPERM_EXEC | idaapi.SEGPERM_READ phdr_text = phdr else: seg.perm = idaapi.SEGPERM_READ | idaapi.SEGPERM_WRITE self.fin.file2base(phdr.p_offset, phdr.p_vaddr, phdr.p_vaddr + phdr.p_filesz, 1) if header.e_type == Ehdr.ET_SCE_EXEC: self.phdr_modinfo = phdr_text modinfo_off = phdr_text.p_offset + header.e_entry else: self.phdr_modinfo = phdrs[(header.e_entry & (0b11 << 30)) >> 30] modinfo_off = self.phdr_modinfo.p_offset + (header.e_entry & 0x3FFFFFFF) self.fin.seek(modinfo_off) modinfo = SceModuleInfo(self.fin.read(SceModuleInfo.SIZE)) modinfo_ea = idaapi.get_fileregion_ea(modinfo_off) apply_struct(modinfo_ea, SceModuleInfo._find_or_create_struct()) print "" print " Module: " + str(modinfo.name) print " NID: 0x{:08X}".format(modinfo.nid) print "" print "2) Parsing export tables" self.parse_impexp(modinfo.export_top, modinfo.export_end, SceModuleExports, self.cb_exp) print "3) Parsing import tables" self.parse_impexp(modinfo.import_top, modinfo.import_end, SceModuleImports, self.cb_imp) print "4) Waiting for IDA to analyze the program" idc.Wait() print "5) Analyzing system instructions" from highlight_arm_system_insn import run_script run_script() print "6) Adding MOVT/MOVW pair xrefs" add_xrefs()
def load_file(li, neflags, format): idaapi.set_processor_type("arm", idaapi.SETPROC_ALL | idaapi.SETPROC_FATAL) f = load_nxo(li) if f.armv7: SetShortPrm(INF_LFLAGS, GetShortPrm(INF_LFLAGS) | LFLG_PC_FLAT) else: SetShortPrm(INF_LFLAGS, GetShortPrm(INF_LFLAGS) | LFLG_64BIT) SetCharPrm(INF_DEMNAMES, idaapi.DEMNAM_GCC3) idaapi.set_compiler_id(idaapi.COMP_GNU) idaapi.add_til2('gnulnx_arm' if f.armv7 else 'gnulnx_arm64', 1) loadbase = 0x60000000 if f.armv7 else 0x7100000000 f.binfile.seek(0) as_string = f.binfile.read(f.bssoff) idaapi.mem2base(as_string, loadbase) if f.text[1] != None: li.file2base(f.text[1], loadbase + f.text[2], loadbase + f.text[2] + f.text[3], True) if f.ro[1] != None: li.file2base(f.ro[1], loadbase + f.ro[2], loadbase + f.ro[2] + f.ro[3], True) if f.data[1] != None: li.file2base(f.data[1], loadbase + f.data[2], loadbase + f.data[2] + f.data[3], True) for start, end, name, kind in f.sections: if name.startswith('.got'): kind = 'CONST' idaapi.add_segm(0, loadbase + start, loadbase + end, name, kind) segm = idaapi.get_segm_by_name(name) if kind == 'CONST': segm.perm = idaapi.SEGPERM_READ elif kind == 'CODE': segm.perm = idaapi.SEGPERM_READ | idaapi.SEGPERM_EXEC elif kind == 'DATA': segm.perm = idaapi.SEGPERM_READ | idaapi.SEGPERM_WRITE elif kind == 'BSS': segm.perm = idaapi.SEGPERM_READ | idaapi.SEGPERM_WRITE idaapi.update_segm(segm) idaapi.set_segm_addressing(segm, 1 if f.armv7 else 2) # do imports # TODO: can we make imports show up in "Imports" window? undef_count = 0 for s in f.symbols: if not s.shndx and s.name: undef_count += 1 last_ea = max(loadbase + end for start, end, name, kind in f.sections) undef_entry_size = 8 undef_ea = ( (last_ea + 0xFFF) & ~0xFFF ) + undef_entry_size # plus 8 so we don't end up on the "end" symbol idaapi.add_segm(0, undef_ea, undef_ea + undef_count * undef_entry_size, "UNDEF", "XTRN") segm = idaapi.get_segm_by_name("UNDEF") segm.type = idaapi.SEG_XTRN idaapi.update_segm(segm) for i, s in enumerate(f.symbols): if not s.shndx and s.name: MakeQword(undef_ea) idaapi.do_name_anyway(undef_ea, s.name) s.resolved = undef_ea undef_ea += undef_entry_size elif i != 0: assert s.shndx s.resolved = loadbase + s.value if s.name: if s.type == STT_FUNC: print hex(s.resolved), s.name idaapi.add_entry(s.resolved, s.resolved, s.name, 0) else: idaapi.do_name_anyway(s.resolved, s.name) else: # NULL symbol s.resolved = 0 funcs = set() for s in f.symbols: if s.name and s.shndx and s.value: if s.type == STT_FUNC: funcs.add(loadbase + s.value) got_name_lookup = {} for offset, r_type, sym, addend in f.relocations: target = offset + loadbase if r_type in (R_ARM_GLOB_DAT, R_ARM_JUMP_SLOT, R_ARM_ABS32): if not sym: print 'error: relocation at %X failed' % target else: idaapi.put_long(target, sym.resolved) elif r_type == R_ARM_RELATIVE: idaapi.put_long(target, idaapi.get_long(target) + loadbase) elif r_type in (R_AARCH64_GLOB_DAT, R_AARCH64_JUMP_SLOT, R_AARCH64_ABS64): idaapi.put_qword(target, sym.resolved + addend) if addend == 0: got_name_lookup[offset] = sym.name elif r_type == R_AARCH64_RELATIVE: idaapi.put_qword(target, loadbase + addend) if addend < f.textsize: funcs.add(loadbase + addend) else: print 'TODO r_type %d' % (r_type, ) ida_make_offset(f, target) for func, target in f.plt_entries: if target in got_name_lookup: addr = loadbase + func funcs.add(addr) idaapi.do_name_anyway(addr, got_name_lookup[target]) funcs |= find_bl_targets(loadbase, loadbase + f.textsize) for addr in sorted(funcs, reverse=True): AutoMark(addr, AU_CODE) AutoMark(addr, AU_PROC) return 1
def create_segments(self, li): li.seek(0x800) code = li.read(self.rom_size) idaapi.add_segm(0, 0x80000000, self.rom_addr, 'RAM', 'DATA') idc.set_default_sreg_value(0x80000000, 'ds', 0) seg = idaapi.getseg(0x80000000) seg.perm = idaapi.SEGPERM_EXEC | idaapi.SEGPERM_READ | idaapi.SEGPERM_WRITE idaapi.mem2base(code, self.rom_addr, 0x800) idaapi.add_segm(0, self.rom_addr, self.rom_addr + self.rom_size, 'CODE', 'CODE') idc.set_default_sreg_value(self.rom_addr, 'ds', 0) seg = idaapi.getseg(self.rom_addr) seg.perm = idaapi.SEGPERM_EXEC | idaapi.SEGPERM_READ | idaapi.SEGPERM_WRITE if self.data_addr != 0: idaapi.add_segm(0, self.data_addr, self.data_addr + self.data_size, '.data', 'DATA') idc.set_default_sreg_value(self.data_addr, 'ds', 0) seg = idaapi.getseg(self.data_addr) seg.perm = idaapi.SEGPERM_EXEC | idaapi.SEGPERM_READ | idaapi.SEGPERM_WRITE if self.bss_addr != 0: idaapi.add_segm(0, self.bss_addr, self.bss_addr + self.bss_size, '.bss', 'BSS') idc.set_default_sreg_value(self.bss_addr, 'ds', 0) seg = idaapi.getseg(self.bss_addr) seg.perm = idaapi.SEGPERM_EXEC | idaapi.SEGPERM_READ | idaapi.SEGPERM_WRITE idaapi.add_segm(0, self.rom_addr + self.rom_size, 0x80200000, 'RAM', 'DATA') idc.set_default_sreg_value(self.rom_addr + self.rom_size, 'ds', 0) seg = idaapi.getseg(self.rom_addr + self.rom_size) seg.perm = idaapi.SEGPERM_EXEC | idaapi.SEGPERM_READ | idaapi.SEGPERM_WRITE idaapi.add_segm(0, 0x1F800000, 0x1F800400, 'CACHE', 'DATA') idaapi.add_segm(0, 0x1F800400, 0x1F801000, 'UNK1', 'XTRN') self.add_mem_ctrl1() self.add_mem_ctrl2() self.add_periph_io() self.add_int_ctrl() self.add_dma() self.add_timers() self.add_cdrom_regs() self.add_gpu_regs() self.add_mdec_regs() self.add_spu_voices() self.add_spu_ctrl_regs() self.create_got_fix() idaapi.cvar.inf.start_ss = idaapi.cvar.inf.start_cs = 0 idaapi.cvar.inf.start_ip = idaapi.cvar.inf.start_ea = self.init_pc idaapi.cvar.inf.start_sp = self.sp_base + self.sp_offset
def load_file(f, neflags, format): ''' load the given file into the current IDA Pro database. Args: f (file): the file-like object to load. neflags (Any): unused format (Any): unused Returns: int: 1 on success, 0 on failure ''' # compute file size, then read the entire contents f.seek(0x0, os.SEEK_END) flen = f.tell() f.seek(0x0) buf = f.read(flen) # mark the proc type, so IDA can invoke the correct disassembler/processor. # this must match `processor.wasm_processor_t.psnames` idaapi.set_processor_type('wasm', idaapi.SETPROC_ALL) f.seek(0x0) # load the entire file directly at address zero. f.file2base(0, 0, len(buf), True) p = 0 sections = wasm.decode.decode_module(buf) for i, section in enumerate(sections): if i == 0: sname = 'header' else: if section.data.id == 0: # fetch custom name sname = '' else: sname = idawasm.const.WASM_SECTION_NAMES.get( section.data.id, 'unknown') if sname != 'header' and section.data.id in ( wasm.wasmtypes.SEC_CODE, wasm.wasmtypes.SEC_GLOBAL): stype = 'CODE' else: stype = 'DATA' # add IDA segment with type, name, size as appropriate slen = sum(section.data.get_decoder_meta()['lengths'].values()) idaapi.add_segm(0, p, p + slen, sname, stype) if sname != 'header': loader = SECTION_LOADERS.get(section.data.id) if loader is not None: loader(section, p) load_section(section, p) p += slen # magic idc.MakeDword(0x0) idc.MakeName(0x0, 'WASM_MAGIC') # version idc.MakeDword(0x4) idc.MakeName(0x4, 'WASM_VERSION') return 1
def load_file(f, neflags, format): print('# PS4 Kernel Loader') ps = Binary(f) # PS4 Processor, Compiler, Library bitness = ps.procomp('metapc', CM_N64 | CM_M_NN | CM_CC_FASTCALL, 'gnulnx_x64') # Segment Loading... for segm in ps.E_SEGMENTS: if segm.name() == 'PHDR': kASLR = False if segm.FILE_SIZE == 0x118 else True # Process Loadable Segments... if segm.name() in ['CODE', 'DATA', 'SCE_RELRO']: address = segm.MEM_ADDR size = segm.MEM_SIZE # Dumped Kernel Fix-ups if segm.name() in ['DATA', 'SCE_RELRO'] and (idaapi.get_segm_by_name('CODE').start_ea != 0xFFFFFFFF82200000 or not kASLR): offset = address - idaapi.get_segm_by_name('CODE').start_ea dumped = segm.MEM_SIZE else: offset = segm.OFFSET dumped = segm.FILE_SIZE print('# Creating %s Segment...' % segm.name()) f.file2base(offset, address, address + dumped, FILEREG_PATCHABLE) idaapi.add_segm(0, address, address + size, segm.name(), segm.type(), ADDSEG_NOTRUNC | ADDSEG_FILLGAP) # Processor Specific Segment Details idc.set_segm_addressing(address, bitness) idc.set_segm_alignment(address, segm.alignment()) idc.set_segm_attr(address, SEGATTR_PERM, segm.flags()) # Process Dynamic Segment... elif segm.name() == 'DYNAMIC': code = idaapi.get_segm_by_name('CODE') data = idaapi.get_segm_by_name('DATA') relro = idaapi.get_segm_by_name('SCE_RELRO') # ------------------------------------------------------------------------------------------------------------ # Dynamic Tag Entry Structure members = [('tag', 'Tag', 0x8), ('value', 'Value', 0x8)] struct = segm.struct('Tag', members) # Dynamic Tag Table stubs = {} modules = {} location = segm.MEM_ADDR # Dumps are offset by a small amount if code.start_ea != 0xFFFFFFFF82200000: dumped = code.start_ea - 0xFFFFFFFF82200000 else: dumped = 0 f.seek(location - code.start_ea) for entry in xrange(segm.MEM_SIZE / 0x10): idaapi.create_struct(location + (entry * 0x10), 0x10, struct) idc.set_cmt(location + (entry * 0x10), Dynamic(f).process(dumped, stubs, modules), False) # ------------------------------------------------------------------------------------------------------------ # Hash Entry Structure members = [('bucket', 'Bucket', 0x2), ('chain', 'Chain', 0x2), ('buckets', 'Buckets', 0x2), ('chains', 'Chains', 0x2)] struct = segm.struct('Hash', members) # Hash Table try: location = Dynamic.HASHTAB size = Dynamic.HASHTABSZ except: location = Dynamic.HASH size = Dynamic.SYMTAB - location f.seek(location - code.start_ea) for entry in xrange(size / 0x8): idaapi.create_struct(location + (entry * 0x8), 0x8, struct) if kASLR: # -------------------------------------------------------------------------------------------------------- # Relocation Entry Structure (with specific addends) members = [('offset', 'Offset (String Index)', 0x8), ('info', 'Info (Symbol Index : Relocation Code)', 0x8), ('addend', 'AddEnd', 0x8)] struct = segm.struct('Relocation', members) # Relocation Table (with specific addends) location = Dynamic.RELATAB f.seek(location - code.start_ea) for entry in xrange(Dynamic.RELATABSZ / 0x18): idaapi.create_struct(location + (entry * 0x18), 0x18, struct) idc.set_cmt(location + (entry * 0x18), Relocation(f).process(dumped, code.end_ea), False) # Initialization Function idc.add_entry(Dynamic.INIT, Dynamic.INIT, '.init', True) else: # -------------------------------------------------------------------------------------------------------- # Symbol Entry Structure members = [('name', 'Name (String Index)', 0x4), ('info', 'Info (Binding : Type)', 0x1), ('other', 'Other', 0x1), ('shtndx', 'Section Index', 0x2), ('offset', 'Value', 0x8), ('size', 'Size', 0x8)] struct = segm.struct('Symbol', members) # Symbol Table location = Dynamic.SYMTAB f.seek(location - code.start_ea) functions = {} # .symtab idc.add_entry(location, location, '.symtab', False) for entry in xrange((Dynamic.STRTAB - location) / 0x18): idaapi.create_struct(location + (entry * 0x18), 0x18, struct) idc.set_cmt(location + (entry * 0x18), Symbol(f).process(functions), False) # -------------------------------------------------------------------------------------------------------- # Dynamic String Table location = Dynamic.STRTAB # .strtab idc.add_entry(location, location, '.strtab', False) # Functions for key in functions: idc.create_strlit(location + key, BADADDR) functions[key] = idc.get_strlit_contents(location + key, BADADDR) idc.set_cmt(location + key, 'Function', False) functions = sorted(functions.iteritems(), key = operator.itemgetter(0)) #print('Functions: %s' % functions) # Resolve Functions location = Dynamic.SYMTAB f.seek(location - code.start_ea + 0x18) for entry in xrange((Dynamic.STRTAB - location - 0x18) / 0x18): Symbol(f).resolve(functions[entry][1]) # Fix-up if kASLR: address = relro.start_ea del_items(address, DELIT_SIMPLE, relro.end_ea - address) while address < relro.end_ea: create_data(address, FF_QWORD, 0x8, BADNODE) address += 0x8 address = code.start_ea # ELF Header Structure members = [('File format', 0x4), ('File class', 0x1), ('Data encoding', 0x1), ('File version', 0x1), ('OS/ABI', 0x1), ('ABI version', 0x1), ('Padding', 0x7), ('File type', 0x2), ('Machine', 0x2), ('File version', 0x4), ('Entry point', 0x8), ('PHT file offset', 0x8), ('SHT file offset', 0x8), ('Processor-specific flags', 0x4), ('ELF header size', 0x2), ('PHT entry size', 0x2), ('Number of entries in PHT', 0x2), ('SHT entry size', 0x2), ('Number of entries in SHT', 0x2), ('SHT entry index for string table\n', 0x2)] for (comment, size) in members: flags = idaapi.get_flags_by_size(size) idc.create_data(address, flags if flags != 0 else FF_STRLIT, size, BADNODE) idc.set_cmt(address, comment, False) address += size for index, entry in enumerate(ps.E_SEGMENTS): # ELF Program Header Structure members = [('Type: %s' % entry.name(), 0x4), ('Flags', 0x4), ('File offset', 0x8), ('Virtual address', 0x8), ('Physical address', 0x8), ('Size in file image', 0x8), ('Size in memory image', 0x8), ('Alignment\n', 0x8)] for (comment, size) in members: flags = idaapi.get_flags_by_size(size) idc.create_data(address, flags if flags != 0 else FF_STRLIT, size, BADNODE) idc.set_cmt(address, comment, False) address += size # Wait for the AutoAnalyzer to Complete... print('# Waiting for the AutoAnalyzer to Complete...') idaapi.auto_wait() if kASLR: # Start Function idc.add_entry(ps.E_START_ADDR, ps.E_START_ADDR, 'start', True) # Xfast_syscall address = idaapi.find_binary(code.start_ea, code.end_ea, '0F 01 F8 65 48 89 24 25 A8 02 00 00 65 48 8B 24', 0x10, SEARCH_DOWN) idaapi.do_unknown(address, 0) idaapi.create_insn(address) idaapi.add_func(address, BADADDR) idaapi.set_name(address, 'Xfast_syscall', SN_NOCHECK | SN_NOWARN) # -------------------------------------------------------------------------------------------------------- # Znullptr's syscalls print('# Processing Znullptr\'s Syscalls...') # Syscall Entry Structure members = [('narg', 'Number of Arguments', 0x4), ('_pad', 'Padding', 0x4), ('function', 'Function', 0x8), ('auevent', 'Augmented Event?', 0x2), ('_pad1', 'Padding', 0x2), ('_pad2', 'Padding', 0x4), ('trace_args_func', 'Trace Arguments Function', 0x8), ('entry', 'Entry', 0x4), ('return', 'Return', 0x4), ('flags', 'Flags', 0x4), ('thrcnt', 'Thread Count?', 0x4)] struct = segm.struct('Syscall', members) znullptr(code.start_ea, code.end_ea, '4F 52 42 49 53 20 6B 65 72 6E 65 6C 20 53 45 4C 46', struct) # -------------------------------------------------------------------------------------------------------- # Chendo's cdevsw con-struct-or print('# Processing Chendo\'s cdevsw structs...') # cdevsw Entry Structure members = [('d_version', 'Version', 0x4), ('d_flags', 'Flags', 0x4), ('d_name', 'Name', 0x8), ('d_open', 'Open', 0x8), ('d_fdopen', 'File Descriptor Open', 0x8), ('d_close', 'Close', 0x8), ('d_read', 'Read', 0x8), ('d_write', 'Write', 0x8), ('d_ioctl', 'Input/Ouput Control', 0x8), ('d_poll', 'Poll', 0x8), ('d_mmap', 'Memory Mapping', 0x8), ('d_strategy', 'Strategy', 0x8), ('d_dump', 'Dump', 0x8), ('d_kqfilter', 'KQFilter', 0x8), ('d_purge', 'Purge', 0x8), ('d_mmap_single', 'Single Memory Mapping', 0x8), ('d_spare0', 'Spare0', 0x8), ('d_spare1', 'Spare1', 0x8), ('d_spare2', 'Spare2', 0x8), ('d_spare3', 'Spare3', 0x8), ('d_spare4', 'Spare4', 0x8), ('d_spare5', 'Spare5', 0x8), ('d_spare6', 'Spare6', 0x4), ('d_spare7', 'Spare7', 0x4)] struct = segm.struct('cdevsw', members) chendo(data.start_ea, data.end_ea, '09 20 12 17', struct) # -------------------------------------------------------------------------------------------------------- # Pablo's IDC try: print('# Processing Pablo\'s Push IDC...') # Script 1) Push it real good... pablo(code.start_ea, code.end_ea, 'C5 FA 5A C0 C5 F2 5A C9 C5 EA 5A D2 C5 FB 59 C1') pablo(code.start_ea, code.end_ea, 'C5 F9 7E C0 31 C9') pablo(code.start_ea, code.end_ea, '48 89 E0 55 53') pablo(code.start_ea, code.end_ea, 'B8 2D 00 00 00 C3') pablo(code.start_ea, code.end_ea, '31 C0 C3') pablo(code.start_ea, code.end_ea, '55 48 89') pablo(code.start_ea, code.end_ea, '48 81 EC A0 00 00 00 C7') pablo(code.start_ea, code.end_ea, '48 81 EC A8 00 00 00') # Script 2) Fix-up Dumped Data Pointers... if dumped or not kASLR: print('# Processing Pablo\'s Dumped Data Pointers IDC...') pablo(data.start_ea, data.end_ea, '?? FF FF FF FF') except: pass # -------------------------------------------------------------------------------------------------------- # Kiwidog's __stack_chk_fail if kASLR: print('# Processing Kiwidog\'s Stack Functions...') kiwidog(code.start_ea, code.end_ea, '73 74 61 63 6B 20 6F 76 65 72 66 6C 6F 77 20 64 65 74 65 63 74 65 64 3B') # -------------------------------------------------------------------------------------------------------- # Final Pass print('# Performing Final Pass...') address = code.start_ea while address < code.end_ea: address = idaapi.find_not_func(address, SEARCH_DOWN) if idaapi.isUnknown(idaapi.getFlags(address)): idaapi.create_insn(address) else: idc.add_func(address) address += 4 print('# Done!') return 1
def load_file(li, neflags, format): # Read in the bFLT header fields li.seek(0) (magic, version, entry, data_start, data_end, bss_end, stack_size, reloc_start, reloc_count, flags) = struct.unpack(">IIIIIIIIII", li.read(4 * 10)) # Check for the GZIP flag. # The loader doesn't de-compress GZIP'd files, as these can be easily decompressed with external tools prior to loading the file into IDA if (flags & FLAGS_GZIP) == FLAGS_GZIP: Warning( "Code/data is GZIP compressed. You probably want to decompress the bFLT file with the flthdr or gunzip_bflt utilities before loading it into IDA." ) # Load the file data into IDA li.file2base(BFLT_HEADER_SIZE, BFLT_HEADER_SIZE, data_end, True) # Add the .text .data and .bss segments idaapi.add_segm(0, BFLT_HEADER_SIZE, data_start, ".text", "CODE") idaapi.add_segm(0, data_start, data_end, ".data", "DATA") idaapi.add_segm(0, data_end, bss_end, ".bss", "BSS") if DEBUG: print "Created File Segments: " print "\t.text 0x%.8X - 0x%.8X" % (BFLT_HEADER_SIZE, data_start) print "\t.data 0x%.8X - 0x%.8X" % (data_start, data_end) print "\t.bss 0x%.8X - 0x%.8X" % (data_end, bss_end) # Entry point is at the beginning of the .text section idaapi.add_entry(entry, entry, "_start", 1) # Set default processor idaapi.set_processor_type(DEFAULT_CPU, SETPROC_ALL) # Explicitly set 32 bit addressing on .text segment idaapi.set_segm_addressing(idaapi.getseg(entry), 1) # prepare structure for set_fixup() fd = idaapi.fixup_data_t() fd.type = idaapi.FIXUP_OFF32 # Is there a global offset table? if (flags & FLAGS_GOTPIC) == FLAGS_GOTPIC: # Add a reptable comment and name the offset so that all references to GOT are obvious MakeRptCmt(data_start, "GLOBAL_OFFSET_TABLE") MakeName(data_start, "GOT") if DEBUG: print "Global Offset Table detected, patching..." # GOT starts at the beginning of the data section; loop through the data section, patching up valid GOT entries. i = data_start while i < data_end: # Get the next GOT entry li.seek(i) got_entry = struct.unpack("<I", li.read(4))[0] # The last GOT entry is -1 if got_entry == 0xFFFFFFFF: if DEBUG: print "Finished processing Global Offset Table." break # All other non-zero entries are valid GOT entries elif got_entry > 0: # The actual data is located at <original GOT entry> + <BFLT_HEADER_SIZE> new_entry = got_entry + BFLT_HEADER_SIZE if DEBUG: print "Replacing GOT entry value 0x%.8X with 0x%.8X at offset 0x%.8X" % ( got_entry, new_entry, i) # Replace the GOT entry with the correct pointer idaapi.put_long(i, new_entry) # add info about relocation to help analyzer fd.off = new_entry idaapi.set_fixup(i, fd) # Make each GOT entry a DWORD MakeDword(i) # Point i at the next GOT entry address i = i + 4 # Patch relocation addresses for i in range(0, reloc_count): try: # Get the next relocation entry. # Relocation entry = <address of bytes to be patched> - <BFLT_HEADER_SIZE> li.seek(reloc_start + (i * 4)) reloc_offset = struct.unpack(">I", li.read(4))[0] + BFLT_HEADER_SIZE # Sanity check, make sure the relocation offset is in a defined segment if reloc_offset < bss_end: try: # reloc_offset + base_offset == <pointer to actual data> - <BFLT_HEADER_SIZE> li.seek(reloc_offset) reloc_val = struct.unpack(">I", li.read(4))[0] if reloc_val == 0: # skip zero relocs # see fs/binfmt_flat.c if DEBUG: print "Skipping zero reloc at (0x%.8X)" % reloc_offset continue reloc_data_offset = reloc_val + BFLT_HEADER_SIZE if DEBUG: print "Patching reloc: (0x%.8X) == 0x%.8X" % ( reloc_offset, reloc_data_offset) # Replace pointer at reloc_offset with the address of the actual data idaapi.put_long(reloc_offset, reloc_data_offset) # add info about relocation to help analyzer fd.off = reloc_data_offset idaapi.set_fixup(reloc_offset, fd) except Exception, e: print "Error patching relocation entry #%d: %s" % (i, str(e)) elif DEBUG: print "Relocation entry #%d outside of defined file sections, skipping..." % i
def load_file(li, neflags, format): if format != "NeoGeo 68k loader": return 0 idaapi.set_processor_type("68000", SETPROC_ALL | SETPROC_FATAL) idaapi.add_segm(0, 0x000000, 0x0FFFFF, "ROM", "CODE") idaapi.add_segm(0, 0x100000, 0x10F2FF, "WRAM", "DATA") idaapi.add_segm(0, 0x10F300, 0x10FFFF, "BIOSRAM", "DATA") idaapi.add_segm(0, 0x200000, 0x2FFFFF, "PORT", "DATA") idaapi.add_segm(0, 0x300000, 0x3FFFFF, "IO", "DATA") idaapi.add_segm(0, 0x400000, 0x401FFF, "PALETTES", "DATA") idaapi.add_segm(0, 0x800000, 0xBFFFFF, "MEMCARD", "DATA") idaapi.add_segm(0, 0xC00000, 0xC1FFFF, "SYSROM", "DATA") idaapi.add_segm(0, 0xD00000, 0xD0FFFF, "BRAM", "DATA") li.seek(0, 2) size = li.tell() li.seek(0) file_data = li.read(size) idaapi.mem2base(file_data, 0, 0x100000) name_long(0x000000, "InitSP") name_long(0x000004, "InitPC") idaapi.do_unknown(0x3C0000, 1) idaapi.doByte(0x3C0000, 1) idaapi.set_name(0x3C0000, "REG_VRAMADDR") #idaapi.set_cmt(0x3C0000, "Pouet.", 1) return 1
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)