/* Pre-unparsing updates */ bool SgAsmPESectionTable::reallocate() { bool reallocated = false; /* Resize based on section having largest ID */ SgAsmPEFileHeader *fhdr = dynamic_cast<SgAsmPEFileHeader*>(get_header()); ROSE_ASSERT(fhdr != NULL); SgAsmGenericSectionPtrList sections = fhdr->get_sections()->get_sections(); int max_id = 0; for (size_t i=0; i<sections.size(); i++) { max_id = std::max(max_id, sections[i]->get_id()); } size_t nsections = max_id; /*PE section IDs are 1-origin*/ size_t need = nsections * sizeof(SgAsmPESectionTableEntry::PESectionTableEntry_disk); if (need < get_size()) { if (is_mapped()) { ROSE_ASSERT(get_mapped_size()==get_size()); set_mapped_size(need); } set_size(need); reallocated = true; } else if (need > get_size()) { get_file()->shift_extend(this, 0, need-get_size(), SgAsmGenericFile::ADDRSP_ALL, SgAsmGenericFile::ELASTIC_HOLE); reallocated = true; } return reallocated; }
/** Number of bytes required for the table. Returns the number of bytes required for the entire IAT or ILT (including the zero * terminator) as it is currently defined in the Import Directory. The returned size does not include space required to store * any Hint/Name pairs, which are outside the ILT/IAT but pointed to by the ILT/IAT. */ size_t SgAsmPEImportDirectory::iat_required_size() const { SgAsmPEFileHeader *fhdr = SageInterface::getEnclosingNode<SgAsmPEFileHeader>(this); assert(fhdr!=NULL); return (p_imports->get_vector().size() + 1) * fhdr->get_word_size(); }
/** Attaches a previously unattached PE Section to the PE Section Table. This method complements * SgAsmPESection::init_from_section_table. This method initializes the section table from the section while * init_from_section_table() initializes the section from the section table. */ void SgAsmPESectionTable::add_section(SgAsmPESection *section) { ROSE_ASSERT(section!=NULL); ROSE_ASSERT(section->get_file()==get_file()); ROSE_ASSERT(section->get_header()==get_header()); ROSE_ASSERT(section->get_section_entry()==NULL); /* must not be in the section table yet */ SgAsmPEFileHeader *fhdr = dynamic_cast<SgAsmPEFileHeader*>(get_header()); ROSE_ASSERT(fhdr!=NULL); /* Assign an ID if there isn't one yet. */ if (section->get_id()<0) { SgAsmGenericSectionList *seclist = fhdr->get_sections();; int max_id=0; /*assume zero is used so we start at one*/ for (size_t i=0; i<seclist->get_sections().size(); i++) { SgAsmGenericSection *s = seclist->get_sections()[i]; max_id = std::max(max_id, s->get_id()); } section->set_id(max_id+1); } /* Create a new section table entry. */ SgAsmPESectionTableEntry *entry = new SgAsmPESectionTableEntry; entry->update_from_section(section); section->set_section_entry(entry); }
/* Constructor */ void SgAsmPEExportDirectory::ctor(SgAsmPEExportSection *section) { SgAsmPEFileHeader *fhdr = isSgAsmPEFileHeader(section->get_header()); ROSE_ASSERT(fhdr!=NULL); set_parent(section); /* Read disk-formatted export directory */ PEExportDirectory_disk disk; try { section->read_content(fhdr->get_loader_map(), section->get_mapped_actual_va(), &disk, sizeof disk); } catch (const MemoryMap::NotMapped &e) { if (mlog[ERROR]) { mlog[ERROR] <<"SgAsmPEExportDirectory::ctor: export directory at va " <<StringUtility::addrToString(section->get_mapped_actual_va()) <<" contains unmapped va " <<StringUtility::addrToString(e.va) <<"\n"; if (e.map) { mlog[ERROR] <<"Memory map in effect at time of error:\n"; e.map->dump(mlog[ERROR], " "); } } memset(&disk, 0, sizeof disk); } /* Convert disk-format data members to native format */ p_res1 = ByteOrder::le_to_host(disk.res1); p_timestamp = ByteOrder::le_to_host(disk.timestamp); p_vmajor = ByteOrder::le_to_host(disk.vmajor); p_vminor = ByteOrder::le_to_host(disk.vminor); p_name_rva = ByteOrder::le_to_host(disk.name_rva); p_name_rva.set_section(section); p_ord_base = ByteOrder::le_to_host(disk.ord_base); p_expaddr_n = ByteOrder::le_to_host(disk.expaddr_n); p_nameptr_n = ByteOrder::le_to_host(disk.nameptr_n); p_expaddr_rva = ByteOrder::le_to_host(disk.expaddr_rva); p_expaddr_rva.set_section(section); p_nameptr_rva = ByteOrder::le_to_host(disk.nameptr_rva); p_nameptr_rva.set_section(section); p_ordinals_rva = ByteOrder::le_to_host(disk.ordinals_rva); p_ordinals_rva.set_section(section); /* Read the name */ std::string name; try { name = section->read_content_str(fhdr->get_loader_map(), p_name_rva.get_va()); } catch (const MemoryMap::NotMapped &e) { if (mlog[WARN]) { mlog[WARN] <<"SgAsmPEExportDirectory::ctor: directory name at rva " <<StringUtility::addrToString(p_name_rva.get_rva()) <<" contains unmapped va " <<StringUtility::addrToString(e.va) <<"\n"; if (e.map) { mlog[WARN] <<"Memory map in effect at time of error:\n"; e.map->dump(mlog[WARN], " "); } } memset(&disk, 0, sizeof disk); } p_name = new SgAsmBasicString(name); p_name->set_parent(this); }
/* Constructor */ void SgAsmPESectionTable::ctor() { SgAsmPEFileHeader *fhdr = dynamic_cast<SgAsmPEFileHeader*>(get_header()); ROSE_ASSERT(fhdr!=NULL); fhdr->set_section_table(this); set_synthesized(true); set_name(new SgAsmBasicString("PE Section Table")); set_purpose(SP_HEADER); }
/* Parser */ SgAsmPESectionTable* SgAsmPESectionTable::parse() { SgAsmGenericSection::parse(); SgAsmPEFileHeader *fhdr = dynamic_cast<SgAsmPEFileHeader*>(get_header()); ROSE_ASSERT(fhdr!=NULL); /* Parse section table and construct section objects, but do not parse the sections yet. */ SgAsmGenericSectionPtrList pending; const size_t entsize = sizeof(SgAsmPESectionTableEntry::PESectionTableEntry_disk); for (size_t i=0; i<fhdr->get_e_nsections(); i++) { SgAsmPESectionTableEntry::PESectionTableEntry_disk disk; if (entsize!=read_content_local(i * entsize, &disk, entsize, false)) fprintf(stderr, "SgAsmPESectionTable::parse: warning: section table entry %" PRIuPTR " at file offset 0x%08"PRIx64 " extends beyond end of defined section table.\n", i, get_offset()+i*entsize); SgAsmPESectionTableEntry *entry = new SgAsmPESectionTableEntry(&disk); SgAsmPESection *section = NULL; if (entry->get_name() == ".idata") { section = new SgAsmPEImportSection(fhdr); } else { section = new SgAsmPESection(fhdr); } section->init_from_section_table(entry, i+1); pending.push_back(section); } /* Build the memory mapping like the real loader would do. This is the same code used by * SgAsmExecutableFileFormat::parseBinaryFormat() except we're doing it here early because we need it in the rest of the * PE parser. */ ROSE_ASSERT(NULL==fhdr->get_loader_map()); BinaryLoader *loader = BinaryLoader::lookup(fhdr); /*no need to clone; we're not changing any settings*/ ROSE_ASSERT(loader!=NULL); MemoryMap *loader_map = new MemoryMap; loader->remap(loader_map, fhdr); fhdr->set_loader_map(loader_map); /* Parse each section after the loader map is created */ for (size_t i=0; i<pending.size(); i++) pending[i]->parse(); return this; }
/* Writes the section table back to disk. */ void SgAsmPESectionTable::unparse(std::ostream &f) const { SgAsmPEFileHeader *fhdr = dynamic_cast<SgAsmPEFileHeader*>(get_header()); ROSE_ASSERT(fhdr != NULL); SgAsmGenericSectionPtrList sections = fhdr->get_sections()->get_sections(); for (size_t i = 0; i < sections.size(); i++) { if (sections[i]->get_id()>=0) { SgAsmPESection *section = isSgAsmPESection(sections[i]); ROSE_ASSERT(section!=NULL); /* Write the table entry */ ROSE_ASSERT(section->get_id() > 0); /*ID's are 1-origin in PE*/ size_t slot = section->get_id() - 1; SgAsmPESectionTableEntry *shdr = section->get_section_entry(); SgAsmPESectionTableEntry::PESectionTableEntry_disk disk; shdr->encode(&disk); write(f, slot*sizeof(disk), sizeof disk, &disk); } } }
SgAsmCoffSymbolTable* SgAsmCoffSymbolTable::parse() { /* Set the section size according to the number of entries indicated in the header. */ SgAsmPEFileHeader *fhdr = dynamic_cast<SgAsmPEFileHeader*>(get_header()); ROSE_ASSERT(fhdr!=NULL); set_offset(fhdr->get_e_coff_symtab()); set_size(fhdr->get_e_coff_nsyms()*SgAsmCoffSymbol::COFFSymbol_disk_size); SgAsmGenericSection::parse(); /* The string table immediately follows the symbols. The first four bytes of the string table are the size of the * string table in little endian. */ rose_addr_t strtab_offset = get_offset() + fhdr->get_e_coff_nsyms() * SgAsmCoffSymbol::COFFSymbol_disk_size; p_strtab = new SgAsmGenericSection(fhdr->get_file(), fhdr); p_strtab->set_offset(strtab_offset); p_strtab->set_size(sizeof(uint32_t)); p_strtab->set_synthesized(true); p_strtab->set_name(new SgAsmBasicString("COFF Symbol Strtab")); p_strtab->set_purpose(SP_HEADER); p_strtab->parse(); uint32_t word; p_strtab->read_content(0, &word, sizeof word); rose_addr_t strtab_size = ByteOrder::le_to_host(word); if (strtab_size < sizeof(uint32_t)) throw FormatError("COFF symbol table string table size is less than four bytes"); p_strtab->extend(strtab_size - sizeof(uint32_t)); /* Parse symbols until we've parsed the required number or we run off the end of the section. */ for (size_t i = 0; i < fhdr->get_e_coff_nsyms(); i++) { try { SgAsmCoffSymbol *symbol = new SgAsmCoffSymbol(fhdr, this, p_strtab, i); i += symbol->get_st_num_aux_entries(); p_symbols->get_symbols().push_back(symbol); } catch (const ShortRead &e) { fprintf(stderr, "SgAsmCoffSymbolTable::parse: warning: read past end of section \"%s\" [%d]\n" " symbol #%zu at file offset 0x%08"PRIx64"\n" " skipping %zu symbols (including this one)\n", get_name()->get_string(true).c_str(), get_id(), i, e.offset, fhdr->get_e_coff_nsyms()-i); break; } } return this; }
SgAsmPEExportSection* SgAsmPEExportSection::parse() { SgAsmPESection::parse(); SgAsmPEFileHeader *fhdr = dynamic_cast<SgAsmPEFileHeader*>(get_header()); ROSE_ASSERT(fhdr!=NULL); p_export_dir = new SgAsmPEExportDirectory(this); /* The export directory points to three parallel arrays: * 1. An array of RVAs that point to NUL-terminated names. * 2. An array of "ordinals" which serve as indices into the Export Address Table. * 3. An array of export addresses (see note below). */ for (size_t i=0; i<p_export_dir->get_nameptr_n(); i++) { /* Function name RVA (nameptr)*/ ExportNamePtr_disk nameptr_disk = 0; rose_addr_t nameptr_va = p_export_dir->get_nameptr_rva().get_va() + i*sizeof(nameptr_disk); bool badFunctionNameVa = false; try { read_content(fhdr->get_loader_map(), nameptr_va, &nameptr_disk, sizeof nameptr_disk); } catch (const MemoryMap::NotMapped &e) { badFunctionNameVa = true; if (mlog[ERROR]) { mlog[ERROR] <<"SgAsmPEExportSection::parse: export name #" <<i <<" at va " <<StringUtility::addrToString(nameptr_va) <<" contains unmapped va " <<StringUtility::addrToString(e.va) <<"\n"; if (e.map) { mlog[ERROR] <<"Memory map in effect at time of error:\n"; e.map->dump(mlog[ERROR], " "); } } nameptr_disk = 0; } rose_addr_t fname_rva = ByteOrder::le_to_host(nameptr_disk); rose_addr_t fname_va = fname_rva + fhdr->get_base_va(); /* Function name (fname) */ std::string s; try { s = read_content_str(fhdr->get_loader_map(), fname_va); } catch (const MemoryMap::NotMapped &e) { if (mlog[ERROR]) { mlog[ERROR] <<"SgAsmPEExportSection::parse: export name #" <<i <<" at va " <<StringUtility::addrToString(fname_va) <<" contains unmapped va " <<StringUtility::addrToString(e.va) <<"\n"; if (e.map) { mlog[ERROR] <<"Memory map in effect at time of error:\n"; e.map->dump(mlog[ERROR], " "); } } } SgAsmGenericString *fname = new SgAsmBasicString(s); /* Ordinal (sort of an index into the Export Address Table) */ ExportOrdinal_disk ordinal_disk = 0; rose_addr_t ordinal_va = p_export_dir->get_ordinals_rva().get_va() + i*sizeof(ordinal_disk); bool badOrdinalVa = false; try { read_content(fhdr->get_loader_map(), ordinal_va, &ordinal_disk, sizeof ordinal_disk); } catch (const MemoryMap::NotMapped &e) { badOrdinalVa = true; if (mlog[ERROR]) { mlog[ERROR] <<"SgAsmPEExportSection::parse: ordinal #" <<i <<" at va " <<StringUtility::addrToString(ordinal_va) <<" contains unmapped va " <<StringUtility::addrToString(e.va) <<"\n"; if (e.map) { mlog[ERROR] <<"Memory map in effect at time of error:\n"; e.map->dump(mlog[ERROR], " "); } } ordinal_disk = 0; } unsigned ordinal = ByteOrder::le_to_host(ordinal_disk); /* If the function name pointer and ordinal pointer are both in unmapped memory then give up. */ if (badFunctionNameVa && badOrdinalVa) { if (mlog[ERROR]) mlog[ERROR] <<"SgAsmPEExportSection::parse: giving up after trying to read entry #" <<i <<"\n"; break; } /* Export address. Convert the symbol's Ordinal into an index into the Export Address Table. The spec says to subtract * the ord_base from the Ordinal to get the index, but testing has shown this to be off by one (e.g., Windows-XP file * /WINDOWS/system32/msacm32.dll's Export Table's first symbol has the name "XRegThunkEntry" with an Ordinal of zero * and the ord_base is one. The index according to spec would be -1 rather than the correct value of zero.) */ rose_rva_t expaddr = 0; if (ordinal >= (p_export_dir->get_ord_base()-1)) { unsigned expaddr_idx = ordinal - (p_export_dir->get_ord_base()-1); ROSE_ASSERT(expaddr_idx < p_export_dir->get_expaddr_n()); ExportAddress_disk expaddr_disk = 0; rose_addr_t expaddr_va = p_export_dir->get_expaddr_rva().get_va() + expaddr_idx*sizeof(expaddr_disk); try { read_content(fhdr->get_loader_map(), expaddr_va, &expaddr_disk, sizeof expaddr_disk); } catch (const MemoryMap::NotMapped &e) { if (mlog[ERROR]) { mlog[ERROR] <<"SgAsmPEExportSection::parse: export address #" <<i <<" at va " <<StringUtility::addrToString(expaddr_va) <<" contains unmapped va " <<StringUtility::addrToString(e.va) <<"\n"; if (e.map) { mlog[ERROR] <<"Memory map in effect at time of error:\n"; e.map->dump(mlog[ERROR], " "); } } } expaddr = ByteOrder::le_to_host(expaddr_disk); expaddr.bind(fhdr); } else { expaddr = 0xffffffff; /*Ordinal out of range!*/ } /* If export address is within this section then it points to a NUL-terminated forwarder name. * FIXME: Is this the proper precondition? [RPM 2009-08-20] */ SgAsmGenericString *forwarder = NULL; if (expaddr.get_va()>=get_mapped_actual_va() && expaddr.get_va()<get_mapped_actual_va()+get_mapped_size()) { std::string s; try { s = read_content_str(fhdr->get_loader_map(), expaddr.get_va()); } catch (const MemoryMap::NotMapped &e) { if (mlog[ERROR]) { mlog[ERROR] <<"SgAsmPEExportSection::parse: forwarder " <<i <<" at rva " <<StringUtility::addrToString(expaddr.get_rva()) <<" contains unmapped va " <<StringUtility::addrToString(e.va) <<"\n"; if (e.map) { mlog[ERROR] <<"Memory map in effect at time of error:\n"; e.map->dump(mlog[ERROR], " "); } } } forwarder = new SgAsmBasicString(s); } SgAsmPEExportEntry *entry = new SgAsmPEExportEntry(fname, ordinal, expaddr, forwarder); add_entry(entry); } return this; }
/** Parse an import directory. The import directory is parsed from the specified virtual address via the PE header's loader * map. Return value is this directory entry on success, or the null pointer if the entry is all zero (which marks the end of * the directory list). */ SgAsmPEImportDirectory * SgAsmPEImportDirectory::parse(rose_addr_t idir_va) { SgAsmPEFileHeader *fhdr = SageInterface::getEnclosingNode<SgAsmPEFileHeader>(this); ROSE_ASSERT(fhdr!=NULL); /* Read the import directory from disk via loader map. */ PEImportDirectory_disk disk, zero; memset(&zero, 0, sizeof zero); size_t nread = fhdr->get_loader_map()->readQuick(&disk, idir_va, sizeof disk); if (nread!=sizeof disk) { if (SgAsmPEImportSection::show_import_mesg()) { mlog[WARN] <<"SgAsmPEImportDirectory::parse: short read (" <<StringUtility::plural(nread, "bytes") <<")" <<" of directory at va " <<StringUtility::addrToString(idir_va) <<" (needed " <<StringUtility::plural(sizeof disk, "bytes") <<")\n"; } memset(&disk, 0, sizeof disk); } /* An all-zero entry marks the end of the list. In this case return null. */ if (!memcmp(&disk, &zero, sizeof zero)) return NULL; p_ilt_rva = ByteOrder::le_to_host(disk.ilt_rva); p_time = ByteOrder::le_to_host(disk.time); p_forwarder_chain = ByteOrder::le_to_host(disk.forwarder_chain); p_dll_name_rva = ByteOrder::le_to_host(disk.dll_name_rva); p_iat_rva = ByteOrder::le_to_host(disk.iat_rva); /* Bind RVAs to best sections */ p_ilt_rva.bind(fhdr); p_dll_name_rva.bind(fhdr); p_iat_rva.bind(fhdr); /* Library name. The PE format specification does not require the name to be contained in the import section, but we issue * a warning because ROSE may have problems allocating new space for the name if it's changed. */ ROSE_ASSERT(p_dll_name!=NULL); std::string dll_name; if (0==p_dll_name_rva.get_section()) { if (SgAsmPEImportSection::show_import_mesg()) { mlog[WARN] <<"SgAsmPEImportDirectory::parse: import directory at va " <<StringUtility::addrToString(idir_va) <<" has bad DLL name rva " <<p_dll_name_rva <<"\n"; } } else { try { dll_name = p_dll_name_rva.get_section()->read_content_str(fhdr->get_loader_map(), p_dll_name_rva.get_va()); } catch (const MemoryMap::NotMapped &e) { if (SgAsmPEImportSection::show_import_mesg()) { mlog[WARN] <<"SgAsmPEImportDirectory::parse: short read of dll name at rva " << p_dll_name_rva <<" for import directory at va " <<StringUtility::addrToString(idir_va) <<"\n"; if (e.map) { mlog[WARN] <<" Memory map in effect at time of error is:\n"; e.map->dump(mlog[WARN], " "); } } } } p_dll_name->set_string(dll_name); p_dll_name_nalloc = dll_name.size() + 1; parse_ilt_iat(p_ilt_rva, false); p_ilt_nalloc = 0==p_ilt_rva.get_rva() ? 0 : iat_required_size(); parse_ilt_iat(p_iat_rva, false); // IAT initially is a copy of the ILT (i.e., objects are not bound to addresses yet) p_iat_nalloc = 0==p_iat_rva.get_rva() ? 0 : iat_required_size(); return this; }
/** Unparse a PE Import Lookup Table or Import Address Table. The @p table_start address is the location where the ILT or IAT * begins and should be bound to the section to which the table is being written. The table is filled with ordinals and/or * hint/name pairs unless @p assume_bound is set, in which case the table is filled with the bound addresses. The PE spec * requires that the IAT in the executable file has the exact same structure and content as the ILT, so this method is * normally called wtih @p assume_bound set to false. * * The @p tablesize argument is the number of bytes allocated for the table, and may be less than the number of bytes required * to write the entire table. This can happen, for instance, when the AST was modified by adding entries to the ILT/IAT but * the size (and probably location) of the table could not be changed. This is typical of IATs since code in the .text * section often references IAT entries via indirect jumps/calls and it is infeasible to move the IAT to a new location. If * unparsing is unable to write all table entries due to the @p tablesize limit, an error message is printed. */ void SgAsmPEImportDirectory::unparse_ilt_iat(std::ostream &f, const rose_rva_t &table_start, bool assume_bound, size_t tablesize) const { SgAsmPEImportSection *isec = SageInterface::getEnclosingNode<SgAsmPEImportSection>(this); assert(isec!=NULL); SgAsmPEFileHeader *fhdr = isSgAsmPEFileHeader(isec->get_header()); assert(fhdr!=NULL); assert(fhdr->get_word_size() <= sizeof(uint64_t)); const SgAsmPEImportItemPtrList &imports = get_imports()->get_vector(); size_t entry_size = fhdr->get_word_size(); uint64_t by_ordinal_bit = 1ull << (8*entry_size-1); if (0==table_start.get_rva() || imports.empty()) return; if (!table_start.get_section()) { if (SgAsmPEImportSection::show_import_mesg()) { mlog[WARN] <<"SgAsmPEImportDirectory: ILT/IAT: table is not associated with a section: " <<table_start.to_string() <<"\n"; } return; } /* Must we limit how many entries are written? Don't forget the zero terminator. */ size_t nelmts = std::min(tablesize/entry_size, imports.size()+1); if (nelmts<imports.size()+1) { if (SgAsmPEImportSection::show_import_mesg()) { mlog[WARN] <<"SgAsmPEImportDirectory: ILT/IAT: table at " <<table_start.to_string() <<" is truncated from " <<StringUtility::plural(imports.size()+1, "entries", "entry") <<" to " <<StringUtility::plural(nelmts, "entries", "entry") <<" (inc. zero terminator) due to allocation constraints.\n"; } } rose_rva_t entry_rva = table_start; for (size_t idx=0; idx<nelmts/*including zero terminator*/; ++idx, entry_rva.increment(entry_size)) { uint64_t entry_word = 0; /* Write the zero terminator? */ if (idx+1==nelmts) { uint64_t zero = 0; entry_rva.get_section()->write(f, entry_rva.get_rel(), entry_size, &zero); break; } rose_rva_t hn_rva = imports[idx]->get_hintname_rva(); /* Build the IAT/ILT entry */ if (assume_bound) { entry_word = imports[idx]->get_bound_rva().get_rva(); } else if (imports[idx]->get_by_ordinal()) { if (0!=(imports[idx]->get_ordinal() & ~0xffff)) { if (SgAsmPEImportSection::show_import_mesg()) { mlog[WARN] <<"SgAsmPEImportDirectory: ILT/IAT entry #" <<idx <<" at rva " <<entry_rva.get_rva() <<": ordinal is out of bounds: " <<imports[idx]->get_ordinal() <<" (truncated to 16 bits)\n"; } } entry_word |= by_ordinal_bit | (imports[idx]->get_ordinal() & 0xffff); } else if (0==hn_rva.get_rva() || NULL==hn_rva.get_section()) { if (SgAsmPEImportSection::show_import_mesg()) { mlog[WARN] <<"SgAsmPEImportDirectory: ILT/IAT entry #" <<idx <<" at rva " <<entry_rva.get_rva() <<": non-ordinal entry has invalid hint/name pair address " <<hn_rva <<" or is not associated with any section.\n"; } entry_word = hn_rva.get_rva(); // best we can do } else { size_t bufsz = std::min(imports[idx]->get_hintname_nalloc(), imports[idx]->hintname_required_size()); if (bufsz < imports[idx]->hintname_required_size()) { if (SgAsmPEImportSection::show_import_mesg()) { mlog[WARN] <<"SgAsmPEImportDirectory: ILT/IAT entry #" <<idx <<" at rva " <<entry_rva.get_rva() <<": insufficient space (" <<StringUtility::plural(bufsz, "bytes") <<")" <<" allocated for hint/name pair at rva " <<hn_rva <<" (need " <<StringUtility::plural(imports[idx]->hintname_required_size(), "bytes") <<")\n"; } } if (bufsz>=2) { uint8_t *buf = new uint8_t[bufsz]; unsigned hint = imports[idx]->get_hint(); std::string name = imports[idx]->get_name()->get_string(); if (0!=(hint & ~0xffff)) { if (SgAsmPEImportSection::show_import_mesg()) { mlog[WARN] <<"SgAsmPEImportDirectory: ILT/IAT entry #" <<idx <<" at rva " <<entry_rva.get_rva() <<": hint is out of bounds: " <<hint <<" (truncated to 16 bits)\n"; } } ByteOrder::host_to_le(hint, &hint); // nudge, nudge. Know what I mean? memcpy(buf, &hint, 2); memcpy(buf+2, name.c_str(), std::min(name.size()+1, bufsz-2)); if (bufsz>2) buf[bufsz-1] = '\0'; hn_rva.get_section()->write(f, hn_rva.get_rel(), bufsz, buf); if (0!=(hn_rva.get_rva() & by_ordinal_bit)) { if (SgAsmPEImportSection::show_import_mesg()) { mlog[WARN] <<"SgAsmPEImportDirectory: ILT/IAT entry #" <<idx <<" at rva " <<entry_rva.get_rva() <<": hint/name pair rva " <<hn_rva.get_rva() <<" has by_ordinal bit set\n"; } } delete[] buf; } entry_word = hn_rva.get_rva() & ~by_ordinal_bit; } /* Write the IAT/ILT entry */ if (0==entry_word) { if (SgAsmPEImportSection::show_import_mesg()) { mlog[WARN] <<"SgAsmPEImportDirectory: ILT/IAT entry #" <<idx <<" at rva " <<entry_rva.get_rva() <<": zero table entry will cause table to be truncated when read\n"; } } uint64_t disk = 0; // we might write only the first few bytes ByteOrder::host_to_le(entry_word, &disk); entry_rva.get_section()->write(f, entry_rva.get_rel(), entry_size, &disk); } }
/** Parse an Import Lookup Table or Import Address Table. The table begins at the specified address and consists of 32- or 64- * bit vectors which are (1) an ordinal number with high order bit set, (2) a relative virtual address of a hint/name pair, or * (3) a virtual address filled in by the dynamic linker when the shared object is bound. The last case is assumed if @p * @p assume_bound is set. * * Normally, the ILT and IAT of an executable file have identical structure and content and the IAT is changed only after the * shared objects are dynamically linked. However, we might encounter cases where we discover that the IAT has values that * are different than the ILT even when @p assume_bound is false. When this happens, we emit a warning message and treat the * conflicting IAT entry as a bound address. */ void SgAsmPEImportDirectory::parse_ilt_iat(const rose_rva_t &table_start, bool assume_bound) { SgAsmPEImportSection *isec = SageInterface::getEnclosingNode<SgAsmPEImportSection>(this); assert(isec!=NULL); SgAsmPEFileHeader *fhdr = isSgAsmPEFileHeader(isec->get_header()); assert(fhdr!=NULL); assert(fhdr->get_word_size() <= sizeof(uint64_t)); assert(get_imports()!=NULL); SgAsmPEImportItemPtrList &imports = get_imports()->get_vector(); bool processing_iat = !imports.empty(); // we always process the ILT first (but it might be empty) if (0==table_start.get_rva()) return; // no ILT/IAT present rose_addr_t entry_va=table_start.get_va(), entry_size=fhdr->get_word_size(); uint64_t by_ordinal_bit = 1ull << (8*entry_size-1); for (size_t idx=0; 1; ++idx, entry_va+=entry_size) { /* Read the 32- or 64-bit ILT/IAT entry from proces mapped memory. */ uint64_t entry_word = 0; try { isec->read_content(fhdr->get_loader_map(), entry_va, &entry_word, entry_size); } catch (const MemoryMap::NotMapped &e) { if (SgAsmPEImportSection::show_import_mesg()) { mlog[WARN] <<"SgAsmPEImportDirectory: ILT/IAT entry #" <<idx <<" at va " <<StringUtility::addrToString(entry_va) <<": table entry is outside mapped memory\n"; if (e.map) { mlog[WARN] <<" Memory map in effect at time of error:\n"; e.map->dump(mlog[WARN], " "); } } } entry_word = ByteOrder::le_to_host(entry_word); /* ILT/IAT are to be terminated by a zero word. However, the length of the IAT is required to be the same as the * length of the ILT. Handle three cases here: * (1) Termination of the ILT by a zero entry; stop now * (2) Premature termination of the IAT; continue processing * (3) Missing (or late) termination of the IAT; stop now */ if (0==entry_word) { if (idx<imports.size()) { if (SgAsmPEImportSection::show_import_mesg()) { mlog[ERROR] <<"SgAsmPEImportDirectory: IAT entry #" <<idx <<" at va " <<StringUtility::addrToString(entry_va) <<": IAT zero termination occurs before end of corresponding ILT;" <<(assume_bound?"":"assuming this is a bound null address and ") <<" continuing to read IAT entries.\n"; } assert(idx<imports.size()); imports[idx]->set_bound_rva(rose_rva_t(entry_word).bind(fhdr)); continue; } else { break; } } else if (processing_iat && idx>=imports.size()) { if (SgAsmPEImportSection::show_import_mesg()) { mlog[WARN] <<"SgAsmPEImportDirectory: IAT entry #" <<idx <<" at va " <<StringUtility::addrToString(entry_va) <<": IAT extends beyond end of corresponding ILT (it is not zero terminated)" <<"; forcibly terminating here\n"; } break; } /* Create the new table entry if necessary. */ bool entry_existed = true; if (idx>=imports.size()) { assert(idx==imports.size()); new SgAsmPEImportItem(get_imports()); // adds new item to imports list entry_existed = false; } SgAsmPEImportItem *import_item = imports[idx]; if (assume_bound) { /* The entry word is a virtual address. */ if (entry_existed && import_item->get_bound_rva().get_rva()>0 && import_item->get_bound_rva().get_rva()!=entry_word) { if (SgAsmPEImportSection::show_import_mesg()) { mlog[WARN] <<"SgAsmPEImportDirectory: ILT/IAT entry #" <<idx <<" at va " <<StringUtility::addrToString(entry_va) <<": bound address " <<StringUtility::addrToString(entry_word) <<" conflicts with bound address already discovered " <<import_item->get_bound_rva().get_rva() <<" (using new value)\n"; } } import_item->set_bound_rva(rose_rva_t(entry_word).bind(fhdr)); } else if (0!=(entry_word & by_ordinal_bit)) { /* The entry is an ordinal number. */ if (entry_existed && import_item->get_ordinal()!=0 && import_item->get_ordinal()!=(entry_word & 0xffff)) { if (SgAsmPEImportSection::show_import_mesg()) { mlog[WARN] <<"SgAsmPEImportDirectory: ILT/IAT entry #" <<idx <<" at va " <<StringUtility::addrToString(entry_va) <<": ordinal " <<(entry_word & 0xffff) <<" conflicts with ordinal already discovered " <<import_item->get_ordinal() <<" (assuming this is a bound address)\n"; } import_item->set_bound_rva(rose_rva_t(entry_word).bind(fhdr)); } else { import_item->set_by_ordinal(true); import_item->set_ordinal(entry_word & 0xffff); if (0!=(entry_word & ~(by_ordinal_bit | 0xffff))) { if (SgAsmPEImportSection::show_import_mesg()) { mlog[WARN] <<"SgAsmPEImportDirectory: ILT/IAT entry #" <<idx <<" at va " <<StringUtility::addrToString(entry_va) <<": contains extra bits in violation of PE specification" <<" (extra bits removed)\n"; } } } } else { /* The entry word points to a Hint/Name pair: a 2-byte, little endian hint, followed by a NUL-terminated string, * followed by an optional extra byte to make the total length a multiple of two. */ if (entry_existed && import_item->get_by_ordinal()) { if (SgAsmPEImportSection::show_import_mesg()) { mlog[WARN] <<"SgAsmPEImportDirectory: ILT/IAT entry #" <<idx <<" at va " <<StringUtility::addrToString(entry_va) <<": entry type \"hint/name\" conflicts with entry type already discovered" <<" \"ordinal\" (assuming this is a bound address)\n"; } import_item->set_bound_rva(rose_rva_t(entry_word).bind(fhdr)); } else if (entry_existed && import_item->get_hintname_rva().get_rva()>0 && import_item->get_hintname_rva().get_rva()!=entry_word) { if (SgAsmPEImportSection::show_import_mesg()) { mlog[WARN] <<"SgAsmPEImportDirectory: ILT/IAT entry #" <<idx <<" at va " <<StringUtility::addrToString(entry_va) <<": hint/name rva " <<entry_word <<" conflicts with hint/name rva already discovered " <<import_item->get_hintname_rva().get_rva() <<" (assuming this is a bound address)\n"; } import_item->set_bound_rva(rose_rva_t(entry_word).bind(fhdr)); } else { import_item->set_by_ordinal(false); import_item->set_hintname_rva(rose_rva_t(entry_word).bind(fhdr)); import_item->set_hintname_nalloc(0); // for now, will adjust after we read it rose_addr_t entry_word_va = entry_word + fhdr->get_base_va(); uint16_t hint; try { isec->read_content(fhdr->get_loader_map(), entry_word_va, &hint, sizeof hint); } catch (const MemoryMap::NotMapped &e) { import_item->set_hint(0); import_item->get_name()->set_string(""); if (SgAsmPEImportSection::show_import_mesg()) { mlog[WARN] <<"SgAsmPEImportDirectory: ILT/IAT entry #" <<idx <<" at va " <<StringUtility::addrToString(entry_va) <<": points to unmapped Hint/Name pair at va " <<StringUtility::addrToString(entry_word_va) <<"\n"; if (e.map) { mlog[WARN] <<" Memory map in effect at time of error:\n"; e.map->dump(mlog[WARN], " "); } } continue; // to next ILT/IAT entry } hint = ByteOrder::le_to_host(hint); import_item->set_hint(hint); std::string s; try { s = isec->read_content_str(fhdr->get_loader_map(), entry_word_va+2); } catch (const MemoryMap::NotMapped &e) { import_item->get_name()->set_string(""); if (SgAsmPEImportSection::show_import_mesg()) { mlog[WARN] <<"SgAsmPEImportDirectory: ILT/IAT entry #" <<idx <<" at va " <<StringUtility::addrToString(entry_va) <<": points to an unmapped Hint/Name pair at va " <<StringUtility::addrToString(entry_word_va) <<"\n"; if (e.map) { mlog[WARN] <<" Memory map in effect at time of error:\n"; e.map->dump(mlog[WARN], " "); } } continue; // to next ILT/IAT entry } import_item->get_name()->set_string(s); if ((s.size()+1) % 2) { uint8_t byte = 0; try { isec->read_content(fhdr->get_loader_map(), entry_word_va+2+s.size()+1, &byte, 1); } catch (const MemoryMap::NotMapped &e) { if (SgAsmPEImportSection::show_import_mesg()) { mlog[WARN] <<"SgAsmPEImportDirectory: ILT/IAT entry #" <<idx <<" at va " <<StringUtility::addrToString(entry_va) <<": points to an unmapped Hint/Name pair at va " <<StringUtility::addrToString(entry_word_va) <<" (when reading pad byte)\n"; if (e.map) { mlog[WARN] <<" Memory map in effect at time of error:\n"; e.map->dump(mlog[WARN], " "); } } continue; // to next ILT/IAT entry } if (byte) { if (SgAsmPEImportSection::show_import_mesg()) { mlog[WARN] <<"SgAsmPEImportDirctory: ILT/IAT entry #" <<idx <<" at va " <<StringUtility::addrToString(entry_va) <<": has a non-zero padding byte: " <<StringUtility::toHex2(byte, 8) <<"\n"; } } } import_item->set_hintname_nalloc(import_item->hintname_required_size()); } } } }
/** Update this section table entry with newer information from the section */ void SgAsmPESectionTableEntry::update_from_section(SgAsmPESection *section) { SgAsmPEFileHeader *fhdr = SageInterface::getEnclosingNode<SgAsmPEFileHeader>(section); ROSE_ASSERT(fhdr!=NULL); p_virtual_size = section->get_mapped_size(); p_rva = section->get_mapped_preferred_rva(); p_physical_size = section->get_size(); p_physical_offset = section->get_offset(); p_name = section->get_name()->get_string(); /* Mapping permissions */ if (section->get_mapped_rperm()) { p_flags |= SgAsmPESectionTableEntry::OF_READABLE; } else { p_flags &= ~SgAsmPESectionTableEntry::OF_READABLE; } if (section->get_mapped_wperm()) { p_flags |= SgAsmPESectionTableEntry::OF_WRITABLE; } else { p_flags &= ~SgAsmPESectionTableEntry::OF_WRITABLE; } if (section->get_mapped_xperm()) { p_flags |= SgAsmPESectionTableEntry::OF_EXECUTABLE; } else { p_flags &= ~SgAsmPESectionTableEntry::OF_EXECUTABLE; } /* Mapping alignment */ if (section->is_mapped() && section->get_mapped_alignment()!=fhdr->get_e_section_align()) { switch (section->get_mapped_alignment()) { case 0: case 1: p_flags &= ~SgAsmPESectionTableEntry::OF_ALIGN_MASK; p_flags |= SgAsmPESectionTableEntry::OF_ALIGN_1; break; case 2: p_flags &= ~SgAsmPESectionTableEntry::OF_ALIGN_MASK; p_flags |= SgAsmPESectionTableEntry::OF_ALIGN_2; break; case 4: p_flags &= ~SgAsmPESectionTableEntry::OF_ALIGN_MASK; p_flags |= SgAsmPESectionTableEntry::OF_ALIGN_4; break; case 8: p_flags &= ~SgAsmPESectionTableEntry::OF_ALIGN_MASK; p_flags |= SgAsmPESectionTableEntry::OF_ALIGN_8; break; case 16: p_flags &= ~SgAsmPESectionTableEntry::OF_ALIGN_MASK; p_flags |= SgAsmPESectionTableEntry::OF_ALIGN_16; break; case 32: p_flags &= ~SgAsmPESectionTableEntry::OF_ALIGN_MASK; p_flags |= SgAsmPESectionTableEntry::OF_ALIGN_32; break; case 64: p_flags &= ~SgAsmPESectionTableEntry::OF_ALIGN_MASK; p_flags |= SgAsmPESectionTableEntry::OF_ALIGN_64; break; case 128: p_flags &= ~SgAsmPESectionTableEntry::OF_ALIGN_MASK; p_flags |= SgAsmPESectionTableEntry::OF_ALIGN_128; break; case 256: p_flags &= ~SgAsmPESectionTableEntry::OF_ALIGN_MASK; p_flags |= SgAsmPESectionTableEntry::OF_ALIGN_256; break; case 512: p_flags &= ~SgAsmPESectionTableEntry::OF_ALIGN_MASK; p_flags |= SgAsmPESectionTableEntry::OF_ALIGN_512; break; case 1024: p_flags &= ~SgAsmPESectionTableEntry::OF_ALIGN_MASK; p_flags |= SgAsmPESectionTableEntry::OF_ALIGN_1k; break; case 2048: p_flags &= ~SgAsmPESectionTableEntry::OF_ALIGN_MASK; p_flags |= SgAsmPESectionTableEntry::OF_ALIGN_2k; break; case 4096: p_flags &= ~SgAsmPESectionTableEntry::OF_ALIGN_MASK; p_flags |= SgAsmPESectionTableEntry::OF_ALIGN_4k; break; case 8192: p_flags &= ~SgAsmPESectionTableEntry::OF_ALIGN_MASK; p_flags |= SgAsmPESectionTableEntry::OF_ALIGN_8k; break; default: break; /* leave as is */ } } #if 0 /*FIXME*/ p_coff_line_nums = 0; p_n_relocs = 0; p_n_coff_line_nums = 0; #endif }