/* * Interactive disassembler (IDA). * Copyright (c) 1990-98 by Ilfak Guilfanov. * ALL RIGHTS RESERVED. * E-mail: ig@datarescue.com * FIDO: 2:5020/209 * * PEF Loader * ---------- * */ #include "../idaldr.h" #include #include "pef.hpp" #include "../coff/syms.h" #include "../../module/ppc/notify_codes.hpp" #include "common.cpp" static ea_t toc_ea; static netnode toc; //---------------------------------------------------------------------- static const char *get_sec_share_name(uint8 share, char *buf, size_t bufsize) { switch ( share ) { case PEF_SH_PROCESS: return "Shared within process"; case PEF_SH_GLOBAL : return "Shared between all processes"; case PEF_SH_PROTECT: return "Shared between all processes but protected"; default: qsnprintf(buf, bufsize, "Unknown code %d", share); return buf; } } //---------------------------------------------------------------------- static void process_vector(uint32 ea, const char *name) { op_plain_offset(ea, 0, 0); op_plain_offset(ea+4, 0, 0); uint32 mintoc = get_dword(ea+4); if ( segtype(mintoc) == SEG_DATA && mintoc < toc_ea ) { toc_ea = mintoc; ppc_module_t::set_toc(toc_ea); } set_name(ea, name, SN_IDBENC); char buf[MAXSTR]; qsnprintf(buf, sizeof(buf), ".%s", name); uint32 code = get_dword(ea); add_entry(code, code, buf, true, AEF_IDBENC); make_name_auto(code); } //---------------------------------------------------------------------- static void process_symbol_class(uint32 ea, uchar sclass, const char *name) { switch ( sclass ) { case kPEFCodeSymbol: case kPEFGlueSymbol: add_entry(ea, ea, name, true, AEF_IDBENC); break; case kPEFTVectSymbol: process_vector(ea, name); break; case kPEFTOCSymbol: if ( segtype(ea) == SEG_DATA && ea < toc_ea ) { toc_ea = ea; ppc_module_t::set_toc(toc_ea); } toc.charset_ea(ea, XMC_TD+1, 1); /* fall thru */ case kPEFDataSymbol: set_name(ea, name, SN_IDBENC); break; } } //---------------------------------------------------------------------- static void fixup(uint32 ea, uint32 delta, int extdef) { fixup_data_t fd(FIXUP_OFF32); if ( extdef ) fd.set_extdef(); segment_t *s = getseg(delta); fd.displacement = get_dword(ea); if ( s == NULL ) { fd.off = delta; } else { fd.sel = s->sel; fd.off = delta - get_segm_base(s); } fd.set(ea); uint32 target = get_dword(ea) + delta; put_dword(ea, target); op_plain_offset(ea, 0, 0); //cmd.ea = ea; ua_add_dref(0, target, dr_O); cmd.ea = BADADDR; if ( target != toc_ea && !has_name(get_flags(ea)) && has_name(get_flags(target)) ) { qstring buf; if ( get_name(&buf, target) > 0 ) { buf.insert("TC_"); force_name(ea, buf.begin()); make_name_auto(ea); } } // toc.charset_ea(ea, XMC_TC+1, 1); } //---------------------------------------------------------------------- static NORETURN void bad_loader_data(void) { loader_failure("Bad loader data"); } //---------------------------------------------------------------------- static NORETURN void bad_reloc_data(void) { loader_failure("Bad relocation info"); } //---------------------------------------------------------------------- inline bool good_string(const char *begin, const uchar *end, const char *p) { if ( p >= begin ) { while ( p < (const char *)end ) { if ( *p == '\0' ) return true; ++p; } } return false; } //---------------------------------------------------------------------- static void process_loader_data(bytevec_t &ldrdata, const qvector &sec) { pef_loader_data_t pd; elderr_t errcode = extract_loader_data(&pd, ldrdata, sec); if ( errcode != ELDERR_OK ) bad_loader_data(); pef_loader_t &pl = pd.pl; if ( pl.totalImportedSymbolCount != 0 ) { uint32 size = pl.totalImportedSymbolCount*4; ea_t undef = free_chunk(inf_get_max_ea(), size, -0xF); ea_t end = undef + size; set_selector(sec.size()+1, 0); if ( !add_segm(sec.size()+1, undef, end, "IMPORT", "XTRN") ) loader_failure(); for ( int i=0; i < pl.importLibraryCount; i++ ) { const pef_library_t &pil = pd.pil[i]; ea_t ea = undef + 4 * pil.firstImportedSymbol; const char *libname = pd.stable + pil.nameOffset; if ( !good_string(pd.stable, ldrdata.end(), libname) ) bad_loader_data(); add_extra_cmt(ea, true, "Imports from library %s", libname); if ( (pil.options & PEF_LIB_WEAK) != 0 ) add_extra_cmt(ea, true, "Library is weak"); } inf_set_specsegs(inf_is_64bit() ? 8 : 4); for ( int i=0; i < pl.totalImportedSymbolCount; i++ ) { uint32 sym = mflong(pd.impsym[i]); uchar sclass = uchar(sym >> 24); ea_t ea = undef + 4*i; const char *iname = get_impsym_name(pd.stable, ldrdata.end(), pd.impsym, i); if ( iname == NULL ) bad_loader_data(); set_name(ea, iname, SN_IDBENC); if ( (sclass & kPEFWeak) != 0 ) make_name_weak(ea); create_dword(ea, 4); put_dword(ea, 0); pd.impsym[i] = (uint32)ea; } } if ( pl.mainSection != -1 ) { uint32 ea = sec[pl.mainSection].defaultAddress + pl.mainOffset; toc_ea = sec[1].defaultAddress + get_dword(ea+4); ppc_module_t::set_toc(toc_ea); } else if ( pl.initSection != -1 ) { uint32 ea = sec[pl.initSection].defaultAddress + pl.initOffset; toc_ea = sec[1].defaultAddress + get_dword(ea+4); ppc_module_t::set_toc(toc_ea); } if ( qgetenv("IDA_NORELOC") ) goto EXPORTS; msg("Processing relocation information... "); for ( int i=0; i < pl.relocSectionCount; i++ ) { const pef_reloc_header_t &prh = pd.prh[i]; int sidx = prh.sectionIndex; if ( sidx >= sec.size() ) bad_reloc_data(); uint32 sea = sec[sidx].defaultAddress; const uint16 *ptr = pd.relptr + prh.firstRelocOffset; if ( !inside(ldrdata, ptr, prh.relocCount, sizeof(*ptr)) ) bad_reloc_data(); uint32 reladdr = sea; uint32 import = 0; uint32 code = sec.size() > 0 ? sec[0].defaultAddress : 0; uint32 data = sec.size() > 1 ? sec[1].defaultAddress : 0; int32 repeat = -1; for ( int j=0; j < prh.relocCount; ) { uint16 insn = mfshort(ptr[j++]); uint16 cnt = insn & 0x1FF; switch ( insn >> 9 ) { default: // kPEFRelocBySectDWithSkip= 0x00,/* binary: 00xxxxx */ if ( (insn & 0xC000) == 0 ) { int skipCount = (insn >> 6) & 0xFF; int relocCount = insn & 0x3F; reladdr += skipCount * 4; while ( relocCount > 0 ) { relocCount--; fixup(reladdr, data, 0); reladdr += 4; } break; } bad_reloc_data(); case kPEFRelocBySectC: // = 0x20, /* binary: 0100000 */ cnt++; while ( cnt > 0 ) { cnt--; fixup(reladdr, code, 0); reladdr += 4; } break; case kPEFRelocBySectD: cnt++; while ( cnt > 0 ) { cnt--; fixup(reladdr, data, 0); reladdr += 4; } break; case kPEFRelocTVector12: cnt++; while ( cnt > 0 ) { cnt--; fixup(reladdr, code, 0); reladdr += 4; fixup(reladdr, data, 0); reladdr += 4; reladdr += 4; } break; case kPEFRelocTVector8: cnt++; while ( cnt > 0 ) { cnt--; fixup(reladdr, code, 0); reladdr += 4; fixup(reladdr, data, 0); reladdr += 4; } break; case kPEFRelocVTable8: cnt++; while ( cnt > 0 ) { cnt--; fixup(reladdr, data, 0); reladdr += 4; reladdr += 4; } break; case kPEFRelocImportRun: cnt++; if ( import+cnt > pl.totalImportedSymbolCount ) bad_reloc_data(); while ( cnt > 0 ) { cnt--; fixup(reladdr, pd.impsym[import], 1); import++; reladdr += 4; } break; case kPEFRelocSmByImport: if ( cnt >= pl.totalImportedSymbolCount ) bad_reloc_data(); fixup(reladdr, pd.impsym[cnt], 1); reladdr += 4; import = cnt + 1; break; case kPEFRelocSmSetSectC: if ( cnt >= sec.size() ) bad_reloc_data(); code = sec[cnt].defaultAddress; break; case kPEFRelocSmSetSectD: if ( cnt >= sec.size() ) bad_reloc_data(); data = sec[cnt].defaultAddress; break; case kPEFRelocSmBySection: if ( cnt >= sec.size() ) bad_reloc_data(); fixup(reladdr, sec[cnt].defaultAddress, 0); reladdr += 4; break; case kPEFRelocIncrPosition: /* binary: 1000xxx */ case kPEFRelocIncrPosition+1: case kPEFRelocIncrPosition+2: case kPEFRelocIncrPosition+3: case kPEFRelocIncrPosition+4: case kPEFRelocIncrPosition+5: case kPEFRelocIncrPosition+6: case kPEFRelocIncrPosition+7: reladdr += (insn & 0x0FFF)+1; break; case kPEFRelocSmRepeat: /* binary: 1001xxx */ case kPEFRelocSmRepeat+1: case kPEFRelocSmRepeat+2: case kPEFRelocSmRepeat+3: case kPEFRelocSmRepeat+4: case kPEFRelocSmRepeat+5: case kPEFRelocSmRepeat+6: case kPEFRelocSmRepeat+7: if ( repeat == -1 ) repeat = (insn & 0xFF)+1; repeat--; if ( repeat != -1 ) j -= ((insn>>8) & 15)+1 + 1; break; case kPEFRelocSetPosition: /* binary: 101000x */ case kPEFRelocSetPosition+1: { ushort next = mfshort(ptr[j++]); uint32 offset = next | (uint32(insn & 0x3FF) << 16); reladdr = sea + offset; } break; case kPEFRelocLgByImport: /* binary: 101001x */ case kPEFRelocLgByImport+1: { ushort next = mfshort(ptr[j++]); uint32 index = next | (uint32(insn & 0x3FF) << 16); if ( index >= pl.totalImportedSymbolCount ) bad_reloc_data(); fixup(reladdr, pd.impsym[index], 1); reladdr += 4; import = index + 1; } break; case kPEFRelocLgRepeat: /* binary: 101100x */ case kPEFRelocLgRepeat+1: { ushort next = mfshort(ptr[j++]); if ( repeat == -1 ) repeat = next | (uint32(insn & 0x3F) << 16); repeat--; if ( repeat != -1 ) j -= ((insn >> 6) & 15) + 1 + 2; } break; case kPEFRelocLgSetOrBySection: /* binary: 101101x */ case kPEFRelocLgSetOrBySection+1: { ushort next = mfshort(ptr[j++]); uint32 index = next | (uint32(insn & 0x3F) << 16); if ( index >= sec.size() ) bad_reloc_data(); int subcode = (insn >> 6) & 15; switch ( subcode ) { case 0: fixup(reladdr, sec[index].defaultAddress, 0); reladdr += 4; break; case 1: code = sec[index].defaultAddress; break; case 2: data = sec[index].defaultAddress; break; } } break; } } } EXPORTS: for ( int i=0; i < pl.exportedSymbolCount; i++ ) { const pef_export_t &pe = pd.pe[i]; uchar sclass = uchar(pe.classAndName >> 24); char name[MAXSTR]; uint32 ea; switch ( pe.sectionIndex ) { case -3: { uint symidx = pe.symbolValue; if ( symidx >= pl.totalImportedSymbolCount ) bad_reloc_data(); ea = pd.impsym[symidx]; } break; case -2: // absolute symbol ask_for_feedback("Absolute symbols are not implemented"); continue; default: { uint secidx = pe.sectionIndex; if ( secidx >= sec.size() ) bad_reloc_data(); ea = sec[secidx].defaultAddress + pe.symbolValue; } break; } if ( !get_expsym_name(pd.stable, pd.keytable, pd.pe, i, ldrdata.end(), name, sizeof(name)) ) bad_loader_data(); process_symbol_class(ea, sclass & 0xF, name); } msg("done.\n"); if ( pl.mainSection >= 0 && pl.mainSection < sec.size() ) { uint32 ea = sec[pl.mainSection].defaultAddress + pl.mainOffset; process_vector(ea, "start"); inf_set_start_cs(0); inf_set_start_ip(get_dword(ea)); } if ( pl.initSection >= 0 && pl.initSection < sec.size() ) { uint32 ea = sec[pl.initSection].defaultAddress + pl.initOffset; process_vector(ea, "INIT_VECTOR"); } if ( pl.termSection >= 0 && pl.termSection < sec.size() ) { uint32 ea = sec[pl.termSection].defaultAddress + pl.termOffset; process_vector(ea, "TERM_VECTOR"); } if ( toc_ea != BADADDR ) set_name(toc_ea, "TOC"); } //-------------------------------------------------------------------------- static NORETURN void bad_packed_data(void) { loader_failure("Illegal compressed data"); } //-------------------------------------------------------------------------- static uint32 read_number(const uchar *&packed, const uchar *end) { uint32 arg = 0; for ( int i=0; ; i++ ) { if ( packed >= end ) bad_packed_data(); uchar b = *packed++; arg <<= 7; arg |= (b & 0x7F); if ( (b & 0x80) == 0 ) break; if ( i > 4 ) bad_packed_data(); } return arg; } //-------------------------------------------------------------------------- static void unpack_section( const bytevec_t &packedvec, ea_t start, uint32 usize) { bytevec_t unpacked; const uchar *packed = packedvec.begin(); const uchar *pckend = packedvec.begin() + packedvec.size(); while ( packed < pckend ) { uchar code = *packed++; uint32 arg = code & 0x1F; if ( arg == 0 ) arg = read_number(packed, pckend); switch ( code >> 5 ) { case 0: // Zero unpacked.growfill(arg); break; case 1: // blockCopy { const uchar *end = packed + arg; if ( end < packed || end > pckend ) bad_packed_data(); unpacked.append(packed, arg); packed += arg; } break; case 2: // repeatedBlock { int32 repeat = read_number(packed, pckend) + 1; const uchar *end = packed + arg; if ( end < packed || end > pckend ) bad_packed_data(); while ( --repeat >= 0 ) unpacked.append(packed, arg); packed += arg; } break; case 3: // interleaveRepeatBlockWithBlockCopy { int32 commonSize = arg; int32 customSize = read_number(packed, pckend); int32 repeatCount = read_number(packed, pckend); const uchar *common = packed; packed += commonSize; if ( packed < common || packed > pckend ) bad_packed_data(); while ( --repeatCount >= 0 ) { const uchar *end = packed + customSize; if ( end < packed || end > pckend ) bad_packed_data(); unpacked.append(common, commonSize); unpacked.append(packed, customSize); packed += customSize; } unpacked.append(common, commonSize); } break; case 4: // interleaveRepeatBlockWithZero { int32 commonSize = arg; int32 customSize = read_number(packed, pckend); int32 repeatCount = read_number(packed, pckend); while ( --repeatCount >= 0 ) { const uchar *end = packed + customSize; if ( end < packed || end > pckend ) bad_packed_data(); unpacked.growfill(commonSize); unpacked.append(packed, customSize); packed += customSize; } unpacked.growfill(commonSize); } break; default: bad_packed_data(); } } if ( unpacked.size() < usize ) unpacked.growfill(usize-unpacked.size()); if ( unpacked.size() != usize ) bad_packed_data(); mem2base(unpacked.begin(), start, start+unpacked.size(), FILEREG_NOTPATCHABLE); } //-------------------------------------------------------------------------- static void load_section( int i, linput_t *li, pef_section_t &ps, const char *sname, const char *classname, int is_packed) { uint32 size = ps.totalSize; ea_t base = ps.defaultAddress ? ps.defaultAddress : to_ea(inf_get_baseaddr(), 0); ea_t start = free_chunk(base, size, 1-(1 << ps.alignment)); ea_t end = start + size; if ( is_packed ) { bytevec_t packed; packed.resize(ps.packedSize); qlseek(li, ps.containerOffset); lread(li, packed.begin(), packed.size()); unpack_section(packed, start, ps.unpackedSize); } else { file2base(li, ps.containerOffset, start, start+ps.unpackedSize, FILEREG_PATCHABLE); } set_selector(i+1, 0); if ( !add_segm(i+1, start, end, sname, classname, ADDSEG_SPARSE) ) loader_failure(); ps.defaultAddress = start; if ( start < inf_get_lowoff() ) inf_set_lowoff(start); } //-------------------------------------------------------------------------- // // load file into the database. // void idaapi load_file(linput_t *li, ushort /*neflag*/, const char * /*fileformatname*/) { pef_t pef; toc_ea = BADADDR; toc.create("$ toc"); qlseek(li, 0); lread(li, &pef, sizeof(pef_t)); swap_pef(pef); const char *proc = get_pef_processor(pef); if ( proc != NULL ) { set_processor_type(proc, SETPROC_LOADER); if ( PH.id == PLFM_PPC ) { // Mac OS Runtime Architecture for the PowerPC is very similar to AIX set_abi_name("aix"); } } // read section headers qvector sec; if ( pef.sectionCount != 0 ) { sec.resize(pef.sectionCount); lread(li, sec.begin(), sec.size()*sizeof(pef_section_t)); } // swap section headers and find the loader section pef_section_t *loader = NULL; for ( int i=0; i < sec.size(); i++ ) { swap_pef_section(sec[i]); if ( sec[i].sectionKind == PEF_SEC_LOADER ) loader = &sec[i]; } int32 snames_table = sizeof(pef_t) + sizeof(pef_section_t)*sec.size(); for ( int i=0; i < sec.size(); i++ ) { char buf[MAXSTR]; char *secname = get_string(li, snames_table, sec[i].nameOffset, buf, sizeof(buf)); switch ( sec[i].sectionKind ) { case PEF_SEC_PDATA : // Pattern initialized data segment load_section(i, li, sec[i], secname, CLASS_DATA, 1); break; case PEF_SEC_CODE : // Code segment case PEF_SEC_EDATA : // Executable data segment load_section(i, li, sec[i], secname, CLASS_CODE, 0); break; case PEF_SEC_DATA: // Unpacked data segment load_section(i, li, sec[i], secname, sec[i].unpackedSize != 0 ? CLASS_DATA : CLASS_BSS, 0); break; case PEF_SEC_CONST: // Read only data load_section(i, li, sec[i], secname, CLASS_CONST, 0); break; case PEF_SEC_LOADER: // Loader section case PEF_SEC_DEBUG : // Reserved for future use case PEF_SEC_EXCEPT: // Reserved for future use case PEF_SEC_TRACEB: // Reserved for future use continue; default: ask_for_feedback("Unknown section type"); continue; } if ( i == 0 ) create_filename_cmt(); add_extra_cmt(sec[i].defaultAddress, true, "Segment share type: %s\n", get_sec_share_name(sec[i].shareKind, buf, sizeof(buf))); } if ( loader != NULL ) { bytevec_t ldrdata; ldrdata.resize(loader->packedSize); qlseek(li, loader->containerOffset); lread(li, ldrdata.begin(), ldrdata.size()); process_loader_data(ldrdata, sec); } } //-------------------------------------------------------------------------- // // check input file format. if recognized, then return 1 // and fill 'fileformatname'. // otherwise return 0 // static int idaapi accept_file( qstring *fileformatname, qstring *processor, linput_t *li, const char *) { const char *proc = get_pef_processor(li); if ( proc == NULL ) return 0; *fileformatname = "PEF (Mac OS or Be OS executable)"; *processor = proc; return 1; } //---------------------------------------------------------------------- // // LOADER DESCRIPTION BLOCK // //---------------------------------------------------------------------- loader_t LDSC = { IDP_INTERFACE_VERSION, 0, // loader flags // // check input file format. if recognized, then return 1 // and fill 'fileformatname'. // otherwise return 0 // accept_file, // // load file into the database. // load_file, // // create output file from the database. // this function may be absent. // NULL, // take care of a moved segment (fix up relocations, for example) NULL, NULL, };