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 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()); } } } }