/** 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); } }