Esempio n. 1
0
/** Define an RVA/Size pair in the PE file header. */
void
SgAsmPEFileHeader::set_rvasize_pair(PairPurpose idx, SgAsmPESection *section)
{
    ROSE_ASSERT(get_rvasize_pairs()!=NULL);
    ROSE_ASSERT(section->get_parent()!=NULL);
    ROSE_ASSERT(isSgAsmPEFileHeader(section->get_header())!=NULL);

    if (idx>=16)
        fprintf(stderr, "SgAsmPEFileHeader::set_rvasize_pair: warning: index %u exceeds specification limit\n", (unsigned)idx);

    /* Extend array of rva/size pairs if necessary */
    if ((size_t)idx>=get_rvasize_pairs()->get_pairs().size()) {
        get_rvasize_pairs()->get_pairs().resize(idx+1, NULL);
        for (size_t i=0; i<=(size_t)idx; i++) {
            if (NULL==get_rvasize_pairs()->get_pairs()[i]) {
                SgAsmPERVASizePair *pair = new SgAsmPERVASizePair(get_rvasize_pairs(), 0, 0);
                get_rvasize_pairs()->get_pairs()[i] = pair;
            }
        }
    }

    SgAsmPERVASizePair *pair = get_rvasize_pairs()->get_pairs()[idx];
    ROSE_ASSERT(pair!=NULL);
    pair->set_e_rva(rose_rva_t(section->get_mapped_preferred_rva(), section));
    pair->set_e_size(section->get_mapped_size());
    pair->set_section(section);

    /* If the section has no name then give it one based on the RVA/Size index. This is mostly for convenience and debugging
     * since the name is never stored in the file. */
    if (section->get_name()->get_string().empty()) {
        const char *short_name;
        section->get_name()->set_string(rvasize_pair_name(idx, &short_name));
        section->set_short_name(short_name);
    }
}
Esempio n. 2
0
/* This algorithm was implemented based on an e-mail from Cory Cohen at CERT and inspection of PE::ConvertRvaToFilePosition()
 * as defined in "PE.cpp 2738 2009-06-05 15:09:11Z murawski_dev". [RPM 2009-08-17] */
BinaryLoader::MappingContribution
BinaryLoaderPe::align_values(SgAsmGenericSection *section, MemoryMap *map,
                             rose_addr_t *malign_lo_p, rose_addr_t *malign_hi_p,
                             rose_addr_t *va_p, rose_addr_t *mem_size_p,
                             rose_addr_t *offset_p, rose_addr_t *file_size_p, bool *map_private_p,
                             rose_addr_t *va_offset_p, bool *anon_lo_p, bool *anon_hi_p,
                             ConflictResolution *resolve_p)
{
    SgAsmGenericHeader *header = isSgAsmPEFileHeader(section);
    if (!header) header = section->get_header();
    ROSE_ASSERT(header!=NULL);

    if (!section->is_mapped())
        return CONTRIBUTE_NONE;

    /* File and memory alignment must be between 1 and 0x200 (512), inclusive */
    rose_addr_t file_alignment = section->get_file_alignment();
    if (file_alignment>0x200 || 0==file_alignment)
        file_alignment = 0x200;
    rose_addr_t mapped_alignment = section->get_mapped_alignment();
    if (0==mapped_alignment)
        mapped_alignment = 0x200;

    /* Align file size upward even before we align the file offset downward. */
    rose_addr_t file_size = ALIGN_UP(section->get_size(), file_alignment);

    /* Map the entire section's file content (aligned) or the requested map size, whichever is larger. */
    rose_addr_t mapped_size = std::max(section->get_mapped_size(), file_size);

    /* Align file offset downward but do not adjust file size. */
    rose_addr_t file_offset = ALIGN_DN(section->get_offset(), file_alignment);

    /* Align the preferred relative virtual address downward without regard for the base virtual address, and do not adjust
     * mapped size. */
    rose_addr_t mapped_va = header->get_base_va() + ALIGN_DN(section->get_mapped_preferred_rva(), mapped_alignment);

    *malign_lo_p = mapped_alignment;
    *malign_hi_p = 1;
    *va_p = mapped_va;
    *mem_size_p = mapped_size;
    *offset_p = file_offset;
    *file_size_p = file_size;
    *map_private_p = false;
    *va_offset_p = 0;
    *anon_lo_p = *anon_hi_p = true;
    *resolve_p = RESOLVE_OVERMAP;
    return CONTRIBUTE_ADD;
}
Esempio n. 3
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()) {
        SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT: table is not associated with a section: %s\n",
                                          table_start.to_string().c_str());
        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) {
        SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT: table at 0x%08"PRIx64" is truncated from %zu to"
                                          " %zu entries (inc. zero terminator) due to allocation constraints.\n",
                                          table_start.to_string().c_str(), imports.size()+1, nelmts);
    }

    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)) {
                SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at rva 0x%08"PRIx64
                                                  ": ordinal is out of bounds: %u (truncated to 16 bits)\n",
                                                  idx, entry_rva.get_rva(), imports[idx]->get_ordinal());
            }
            entry_word |= by_ordinal_bit | (imports[idx]->get_ordinal() & 0xffff);
        } else if (0==hn_rva.get_rva() || NULL==hn_rva.get_section()) {
            SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at rva 0x%08"PRIx64
                                              ": non-ordinal entry has invalid hint/name pair address %s or is"
                                              " not associated with any section.\n",
                                              idx, entry_rva.get_rva(), hn_rva.to_string().c_str());
            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()) {
                SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at rva 0x%08"PRIx64
                                                  ": insufficient space (%zu byte%s) allocated for hint/name pair at"
                                                  " rva %s (need %zu bytes)\n",
                                                  idx, entry_rva.get_rva(), bufsz, 1==bufsz?"":"s",
                                                  hn_rva.to_string().c_str(), imports[idx]->hintname_required_size());
            }
            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)) {
                    SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at rva 0x%08"PRIx64
                                                      ": hint is out of bounds: %u (truncated to 16 bits)\n",
                                                      idx, entry_rva.get_rva(), hint);
                }
                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)) {
                    SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at rva 0x%08"PRIx64
                                                      ": hint/name pair rva 0x%08"PRIx64" has by_ordinal bit set\n",
                                                      idx, entry_rva.get_rva(), hn_rva.get_rva());
                }
                delete[] buf;
            }
            entry_word = hn_rva.get_rva() & ~by_ordinal_bit;
        }

        /* Write the IAT/ILT entry */
        if (0==entry_word) {
            SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at rva 0x%08"PRIx64
                                              ": zero table entry will cause table to be truncated when read\n",
                                              idx, entry_rva.get_rva());
        }
        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);
    }
}
Esempio n. 4
0
/** 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::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at VA 0x%08"PRIx64
                                                  ": table entry is outside mapped memory\n", idx, entry_va) && e.map) {
                fprintf(stderr, "    Memory map in effect at time of error:\n");
                e.map->dump(stderr, "        ");
            }
        }
        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()) {
                SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: IAT entry #%zu at va 0x%08"PRIx64
                                                  ": IAT zero termination occurs before end of corresponding ILT;"
                                                  " %scontinuing to read IAT entries.\n", idx, entry_va,
                                                  (assume_bound?"":"assuming this is a bound null address and "));
                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()) {
            SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: IAT entry #%zu at va 0x%08"PRIx64
                                              ": IAT extends beyond end of corresponding ILT (it is not zero terminated)"
                                              "; forcibly terminating here\n", idx, entry_va);
            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) {
                SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at va 0x%08"PRIx64
                                                  ": bound address 0x%08"PRIx64" conflicts with bound address already"
                                                  " discovered 0x%08"PRIx64" (using new value)\n", idx, entry_va, entry_word,
                                                  import_item->get_bound_rva().get_rva());
            }
            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)) {
                SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at va 0x%08"PRIx64
                                                  ": ordinal %"PRIu64" conflicts with ordinal already discovered %u"
                                                  " (assuming this is a bound address)\n", idx, entry_va, (entry_word&0xffff),
                                                  import_item->get_ordinal());
                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))) {
                    SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at va 0x%08"PRIx64
                                                      ": contains extra bits in violation of PE specification"
                                                      " (extra bits removed)\n", idx, entry_va);
                }
            }
        } 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()) {
                SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at va 0x%08"PRIx64
                                                  ": entry type \"hint/name\" conflicts with entry type already discovered"
                                                  " \"ordinal\" (assuming this is a bound address)\n", idx, entry_va);
                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) {
                SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at va 0x%08"PRIx64
                                                  ": hint/name rva 0x%08"PRIx64" conflicts with hint/name rva already"
                                                  " discovered 0x%08"PRIx64" (assuming this is a bound address)\n",
                                                  idx, entry_va, entry_word, import_item->get_hintname_rva().get_rva());
                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::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at va 0x%08"PRIx64
                                                          ": points to unmapped Hint/Name pair at va 0x%08"PRIx64
                                                          " (when reading hint)\n", idx, entry_va, entry_word_va) &&
                        e.map) {
                        fprintf(stderr, "    Memory map in effect at time of error:\n");
                        e.map->dump(stderr, "        ");
                    }
                    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::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at va 0x%08"PRIx64
                                                          ": points to an unmapped Hint/Name pair at va 0x%08"PRIx64
                                                          " (when reading name)\n", idx, entry_va, entry_word_va) &&
                        e.map) {
                        fprintf(stderr, "    Memory map in effect at time of error:\n");
                        e.map->dump(stderr, "        ");
                    }
                    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::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at va 0x%08"PRIx64
                                                              ": points to an unmapped Hint/Name pair at va 0x%08"PRIx64
                                                              " (when reading pad byte)\n", idx, entry_va, entry_word_va) &&
                            e.map) {
                            fprintf(stderr, "    Memory map in effect at time of error:\n");
                            e.map->dump(stderr, "        ");
                        }
                        continue; // to next ILT/IAT entry
                    }
                    if (byte) {
                        SgAsmPEImportSection::import_mesg("SgAsmPEImportDirctory: ILT/IAT entry #%zu at va 0x%08"PRIx64
                                                          ": has a non-zero padding byte: 0x%02x\n", idx, entry_va, byte);
                    }
                }
                import_item->set_hintname_nalloc(import_item->hintname_required_size());
            }
        }
    }
}
Esempio n. 5
0
/* This binary loader can handle all PE files. */
bool
BinaryLoaderPe::can_load(SgAsmGenericHeader *hdr) const
{
    return isSgAsmPEFileHeader(hdr)!=NULL;
}