Example #1
0
/** 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);
    }
}