| // Copyright 2012 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| // The PEAddImportsTransform can be summed up as follows: |
| // |
| // (1) Make sure that the imports and IAT data directories exist. |
| // (2) For each module to be imported, either find it in the import data |
| // directory, or add a new entry. The entry is always added to the end |
| // of the list so that module indices are strictly increasing, allowing |
| // the transform to be stacked. Adding a new entry also causes the creation |
| // of two new blocks (for the INT and the module filename), as well as |
| // extends the existing IAT block. |
| // (3) For each symbol to be imported, either find it in the module's INT/IAT, |
| // or add a new entry. Adding a new entry causes the existing INT and IAT |
| // blocks to be extended. The new entry is always added to the end of the |
| // module's table so that symbol indices are strictly increasing, again |
| // allowing the transform to be stacked. Rather than allocating a new |
| // block for the name of the symbol we reuse the module filename block and |
| // insert the name of the symbol immediately prior to the module filename. |
| // This ensures that all of the strings for a module are laid out together, |
| // mimicking the observed behavior of the MS linker. |
| // |
| // We give a quick rundown of the PE structures involved, their layout in |
| // typical PE images and how we parse them into blocks. This helps visualize |
| // the work performed by the transform. |
| // |
| // headers: |
| // |
| // ... |
| // nt_headers |
| // DataDirectory |
| // ... |
| // IMAGE_DIRECTORY_ENTRY_IMPORT -> IMAGE_IMPORT_DESCRIPTOR array |
| // ... |
| // IMAGE_DIRECTORY_ENTRY_IAT -> Import Address Table |
| // ... |
| // |
| // .rdata: |
| // |
| // Import Address Table |
| // NOTE: All entries in this table must remain consecutive as it is also |
| // exposed directly via a data directory. At runtime these get patched to |
| // point to the actual functions rather than the thunks. This is stored |
| // at the very beginning of .rdata and parsed as a single Block. |
| // IAT[0,0] -> thunk[0, 0] \ |
| // ... | |
| // IAT[0,j] -> thunk[0, j] | |
| // NULL terminator | |
| // ... |- Block |
| // IAT[i,0] -> thunk[i, 0] | |
| // ... | |
| // IAT[i,k] -> thunk[i, k] | |
| // NULL terminator / |
| // |
| // ... whole bunch of other .rdata here ... |
| // NOTE: The following are stored at the end of .rdata, in the order |
| // shown (they are not quite last, being immediately prior to export |
| // information). |
| // |
| // IMAGE_IMPORT_DESCRIPTOR array \ |
| // IMAGE_IMPORT_DESCRIPTOR[0] | |
| // -> module_name[0] | |
| // -> INT[0,0] | |
| // -> IAT[0,0] | |
| // ... |- Block |
| // IMAGE_IMPORT_DESCRIPTOR[i] | |
| // -> module_name[i] | |
| // -> INT[i,0] | |
| // -> IAT[i,0] | |
| // NULL terminator / |
| // |
| // Import Name Table (also known as Hint Name Array) |
| // NOTE: The entries for each module need be consecutive. While the entries |
| // across all modules are consecutive, they need not be. |
| // INT[0,0] -> thunk[0, 0] \ |
| // ... |_ Block |
| // INT[0,j] -> thunk[0, j] | |
| // NULL terminator / |
| // ... |
| // INT[i,0] -> thunk[i, 0] \ |
| // ... |_ Block |
| // INT[i,k] -> thunk[i, k] | |
| // NULL terminator / |
| // |
| // Array of names |
| // NOTE: These are consecutive in typical PE images (with the layout shown |
| // below), but they need not be. |
| // thunk[0, 0] } Block |
| // ... |
| // thunk[0, j] } Block |
| // module_name[0] } Block |
| // ... |
| // thunk[i, 0] } Block |
| // ... |
| // thunk[i, k] } Block |
| // module_name[i] } Block |
| |
| #include "syzygy/pe/transforms/pe_add_imports_transform.h" |
| |
| #include "base/strings/string_piece.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "syzygy/block_graph/typed_block.h" |
| #include "syzygy/common/align.h" |
| #include "syzygy/pe/pe_utils.h" |
| |
| namespace pe { |
| namespace transforms { |
| |
| using block_graph::BlockGraph; |
| using block_graph::ConstTypedBlock; |
| using block_graph::TypedBlock; |
| using core::RelativeAddress; |
| |
| // A simple struct that can be used to let us access strings using TypedBlock. |
| struct StringStruct { |
| const char string[1]; |
| }; |
| |
| typedef BlockGraph::Offset Offset; |
| typedef TypedBlock<IMAGE_DELAYLOAD_DESCRIPTOR> ImageDelayLoadDescriptor; |
| typedef TypedBlock<IMAGE_DOS_HEADER> DosHeader; |
| typedef TypedBlock<IMAGE_IMPORT_BY_NAME> ImageImportByName; |
| typedef TypedBlock<IMAGE_IMPORT_DESCRIPTOR> ImageImportDescriptor; |
| typedef TypedBlock<IMAGE_NT_HEADERS> NtHeaders; |
| typedef TypedBlock<IMAGE_THUNK_DATA32> ImageThunkData32; |
| typedef TypedBlock<StringStruct> String; |
| |
| namespace { |
| |
| const size_t kPtrSize = sizeof(core::RelativeAddress); |
| const size_t kInvalidIndex = static_cast<size_t>(-1); |
| |
| // Looks up the given data directory and checks that it points to valid data. |
| // If it doesn't exist and find_only is false, it will allocate a block with |
| // the given name and size. |
| bool FindOrAddDataDirectory(bool find_only, |
| size_t directory_index, |
| const base::StringPiece& block_name, |
| size_t block_size, |
| BlockGraph* block_graph, |
| BlockGraph::Block* nt_headers_block, |
| BlockGraph::Block** directory_block) { |
| DCHECK_LT(directory_index, |
| static_cast<size_t>(IMAGE_NUMBEROF_DIRECTORY_ENTRIES)); |
| DCHECK_GT(block_size, 0u); |
| DCHECK_NE(reinterpret_cast<BlockGraph*>(NULL), block_graph); |
| DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), nt_headers_block); |
| DCHECK_NE(reinterpret_cast<BlockGraph::Block**>(NULL), directory_block); |
| |
| *directory_block = NULL; |
| |
| NtHeaders nt_headers; |
| if (!nt_headers.Init(0, nt_headers_block)) { |
| LOG(ERROR) << "Unable to cast NT headers."; |
| return false; |
| } |
| |
| IMAGE_DATA_DIRECTORY* data_directory = |
| nt_headers->OptionalHeader.DataDirectory + directory_index; |
| |
| BlockGraph::Offset offset = nt_headers.OffsetOf( |
| data_directory->VirtualAddress); |
| BlockGraph::Reference ref; |
| |
| // No entry? Then make a zero initialized block that is stored in .rdata, |
| // where all of these structures live. |
| if (!nt_headers_block->GetReference(offset, &ref)) { |
| // We don't need to create the entry if we're exploring only. |
| if (find_only) |
| return true; |
| |
| BlockGraph::Section* section = block_graph->FindOrAddSection( |
| kReadOnlyDataSectionName, kReadOnlyDataCharacteristics); |
| DCHECK(section != NULL); |
| |
| BlockGraph::Block* block = block_graph->AddBlock( |
| BlockGraph::DATA_BLOCK, block_size, block_name); |
| DCHECK(block != NULL); |
| block->set_section(section->id()); |
| block->set_attribute(BlockGraph::PE_PARSED); |
| |
| // We need to actually allocate the data so that future TypedBlock |
| // dereferences will work. |
| if (block->AllocateData(block_size) == NULL) { |
| LOG(ERROR) << "Failed to allocate block data."; |
| return false; |
| } |
| |
| // Hook it up to the NT header. |
| nt_headers.SetReference(BlockGraph::RELATIVE_REF, |
| data_directory->VirtualAddress, |
| block, |
| 0, 0); |
| data_directory->Size = block_size; |
| |
| *directory_block = block; |
| } else { |
| // If the directory already exists, return it. |
| if (ref.offset() != 0) { |
| LOG(ERROR) << "Existing \"" << block_name << "\" directory is not its " |
| << "own block."; |
| return false; |
| } |
| *directory_block = ref.referenced(); |
| } |
| |
| return true; |
| } |
| |
| bool ModuleNameMatches(const base::StringPiece& module_name, |
| const String& dll_name) { |
| size_t max_len = dll_name.ElementCount(); |
| if (max_len < module_name.size()) |
| return false; |
| return base::strncasecmp(dll_name->string, module_name.data(), max_len) == 0; |
| } |
| |
| bool SymbolNameMatches(const base::StringPiece& symbol_name, |
| const ImageImportByName& iibn) { |
| size_t max_len = iibn.block()->data_size() - iibn.offset() - |
| offsetof(IMAGE_IMPORT_BY_NAME, Name); |
| if (max_len < symbol_name.size()) |
| return false; |
| return ::strncmp(iibn->Name, symbol_name.data(), max_len) == 0; |
| } |
| |
| // Finds or creates an Image Import Descriptor block for the given library. |
| // Returns true on success, false otherwise. |
| bool FindOrAddImageImportDescriptor(bool find_only, |
| const char* module_name, |
| BlockGraph* block_graph, |
| BlockGraph::Block* iida_block, |
| BlockGraph::Block* iat_block, |
| ImageImportDescriptor* iid, |
| bool* added, |
| bool* exists) { |
| DCHECK(module_name != NULL); |
| DCHECK(block_graph != NULL); |
| DCHECK(iida_block != NULL); |
| DCHECK(iat_block != NULL); |
| DCHECK(iid != NULL); |
| DCHECK(added != NULL); |
| DCHECK(exists != NULL); |
| |
| *added = false; |
| *exists = false; |
| |
| ImageImportDescriptor iida; |
| if (!iida.Init(0, iida_block)) { |
| LOG(ERROR) << "Unable to cast Image Import Descriptor."; |
| return false; |
| } |
| |
| // The array is NULL terminated with a potentially incomplete descriptor so |
| // we can't use ElementCount - 1. |
| DCHECK_GT(iida_block->size(), 0U); |
| size_t descriptor_count = |
| (common::AlignUp(iida_block->size(), sizeof(IMAGE_IMPORT_DESCRIPTOR)) / |
| sizeof(IMAGE_IMPORT_DESCRIPTOR)) - 1; |
| |
| for (size_t iida_index = 0; iida_index < descriptor_count; ++iida_index) { |
| String dll_name; |
| if (!iida.Dereference(iida[iida_index].Name, &dll_name)) { |
| LOG(ERROR) << "Unable to dereference DLL name."; |
| return false; |
| } |
| |
| if (ModuleNameMatches(module_name, dll_name)) { |
| // This should never fail, but we sanity check it nonetheless. |
| bool result = iid->Init(iida.OffsetOf(iida[iida_index]), iida.block()); |
| DCHECK(result); |
| *exists = true; |
| return true; |
| } |
| } |
| |
| // If we get here then the entry doesn't exist. If we've been asked to only |
| // search for it then we can return early. |
| if (find_only) |
| return true; |
| |
| // Create room for the new descriptor, which we'll tack on to the end of the |
| // array, but before the NULL terminator. We use 'InsertData' so that all |
| // labels are patched up. |
| Offset new_iid_offset = descriptor_count * sizeof(IMAGE_IMPORT_DESCRIPTOR); |
| iida_block->InsertData( |
| new_iid_offset, sizeof(IMAGE_IMPORT_DESCRIPTOR), true); |
| iida_block->SetLabel( |
| new_iid_offset, |
| base::StringPrintf("Image Import Descriptor: %s", module_name), |
| BlockGraph::DATA_LABEL); |
| |
| // We expect the new entry to be dereferencable using iida[descriptor_count]. |
| DCHECK_GT(iida.ElementCount(), descriptor_count); |
| |
| // Create the various child structures that will be pointed to by the |
| // import descriptor. The INT block and the IAT block are NULL terminated |
| // lists of pointers, and the terminating NULL is allocated. We don't yet |
| // allocate a block to hold the import names, deferring that for later. |
| BlockGraph::SectionId iida_section_id = iida_block->section(); |
| size_t name_len = strlen(module_name); |
| BlockGraph::Block* int_block = block_graph->AddBlock( |
| BlockGraph::DATA_BLOCK, kPtrSize, |
| base::StringPrintf("Import Name Table: %s", module_name)); |
| BlockGraph::Block* dll_name_block = block_graph->AddBlock( |
| BlockGraph::DATA_BLOCK, name_len + 1, |
| base::StringPrintf("Import Name: %s", module_name)); |
| if (int_block == NULL || dll_name_block == NULL) { |
| LOG(ERROR) << "Unable to create blocks for Image Import Descriptor."; |
| return false; |
| } |
| |
| // NOTE: If PEParser was modified to parse a single INT block, we could be |
| // extending/reusing it rather than creating a new INT per module. |
| int_block->set_section(iida_section_id); |
| int_block->set_attribute(BlockGraph::PE_PARSED); |
| int_block->SetLabel( |
| 0, |
| base::StringPrintf("%s INT: NULL entry", module_name), |
| BlockGraph::DATA_LABEL); |
| if (int_block->AllocateData(kPtrSize) == NULL) { |
| LOG(ERROR) << "Failed to allocate block data."; |
| return false; |
| } |
| |
| // We use the DLL name block and extend it. This keeps things well ordered |
| // when writing back the image using a canonical ordering. |
| dll_name_block->set_section(iida_section_id); |
| dll_name_block->set_attribute(BlockGraph::PE_PARSED); |
| if (dll_name_block->CopyData(name_len + 1, module_name) == NULL) { |
| LOG(ERROR) << "Failed to copy block data."; |
| return false; |
| } |
| |
| // Add another NULL entry to the IAT block, but only if it does not already |
| // consist of a single NULL entry (meaning it was just created). We are purely |
| // extending this block, so no need to use the data insertion functions. |
| Offset iat_offset = 0; |
| if (iat_block->size() != kPtrSize) { |
| iat_offset = iat_block->size(); |
| size_t iat_size = iat_offset + kPtrSize; |
| iat_block->set_size(iat_size); |
| iat_block->ResizeData(iat_size); |
| DCHECK_EQ(iat_size, iat_block->size()); |
| DCHECK_EQ(iat_size, iat_block->data_size()); |
| } |
| |
| // Add a label for debugging purposes. |
| iat_block->SetLabel(iat_offset, |
| base::StringPrintf("%s: NULL thunk", module_name), |
| BlockGraph::DATA_LABEL); |
| |
| // Hook up these blocks. |
| iida.SetReference(BlockGraph::RELATIVE_REF, |
| iida[descriptor_count].OriginalFirstThunk, int_block, 0, 0); |
| iida.SetReference(BlockGraph::RELATIVE_REF, |
| iida[descriptor_count].FirstThunk, iat_block, iat_offset, |
| iat_offset); |
| iida.SetReference(BlockGraph::RELATIVE_REF, |
| iida[descriptor_count].Name, dll_name_block, 0, 0); |
| |
| // Finally, return the descriptor. |
| if (!iid->Init(new_iid_offset, iida_block)) { |
| LOG(ERROR) << "Unable to cast Image Import Descriptor."; |
| return false; |
| } |
| |
| *added = true; |
| *exists = true; |
| |
| return true; |
| } |
| |
| // Searches for the delay-load library with the given module name. Returns true |
| // on success, false otherwise. If found, returns the index. If not found |
| // sets the index to kInvalidIndex. |
| bool FindDelayLoadImportDescriptor(const base::StringPiece& module_name, |
| const ImageDelayLoadDescriptor& idld, |
| size_t* index) { |
| DCHECK_NE(reinterpret_cast<size_t*>(NULL), index); |
| |
| *index = kInvalidIndex; |
| |
| for (size_t i = 0; i < idld.ElementCount(); ++i) { |
| bool zero_data = idld[i].DllNameRVA == 0; |
| bool has_ref = idld.HasReference(idld[i].DllNameRVA); |
| |
| // Keep an eye out for null termination of the array. |
| if (zero_data && !has_ref) |
| return true; |
| |
| // If the data is not zero then we expect there to be a reference. |
| if (!zero_data && !has_ref) { |
| LOG(ERROR) << "Expected DllNameRVA reference at index " << i |
| << " of IMAGE_DELAYLOAD_DESCRIPTOR array."; |
| return false; |
| } |
| |
| String dll_name; |
| if (!idld.Dereference(idld[i].DllNameRVA, &dll_name)) { |
| LOG(ERROR) << "Failed to dereference DllNameRVA at index " << i |
| << " of IMAGE_DELAYLOAD_DESCRIPTOR array."; |
| return false; |
| } |
| |
| if (ModuleNameMatches(module_name, dll_name)) { |
| *index = i; |
| return true; |
| } |
| } |
| |
| return true; |
| } |
| |
| // Finds or adds an imported symbol to the given module (represented by its |
| // import descriptor). Returns true on success, false otherwise. On success |
| // returns a reference to the module's IAT entry. New entries are always added |
| // to the end of the table so as not to invalidate any other unlinked references |
| // (not part of the BlockGraph, so unable to be patched up) into the table. |
| bool FindOrAddImportedSymbol(bool find_only, |
| const char* symbol_name, |
| const ImageImportDescriptor& iid, |
| BlockGraph* block_graph, |
| BlockGraph::Block* iat_block, |
| size_t* iat_index, |
| bool* added) { |
| DCHECK(symbol_name != NULL); |
| DCHECK(block_graph != NULL); |
| DCHECK(iat_block != NULL); |
| DCHECK(iat_index != NULL); |
| DCHECK(added != NULL); |
| |
| *iat_index = kInvalidIndex; |
| *added = false; |
| |
| TypedBlock<IMAGE_IMPORT_BY_NAME*> hna, iat; |
| if (!iid.Dereference(iid->OriginalFirstThunk, &hna) || |
| !iid.Dereference(iid->FirstThunk, &iat)) { |
| LOG(ERROR) << "Unable to dereference OriginalFirstThunk/FirstThunk."; |
| return false; |
| } |
| |
| // Loop through the existing imports and see if we can't find a match. If so, |
| // we don't need to import the symbol as it is already imported. The array is |
| // NULL terminated so we loop through all elements except for the last one. |
| size_t i = 0; |
| for (; i < hna.ElementCount() && i < iat.ElementCount(); ++i) { |
| ConstTypedBlock<IMAGE_THUNK_DATA32> thunk; |
| if (!thunk.Init(hna.OffsetOf(hna[i]), hna.block())) { |
| LOG(ERROR) << "Unable to dereference IMAGE_THUNK_DATA32."; |
| return false; |
| } |
| |
| // Is this an ordinal import? Skip it, as we have no way of |
| // knowing the actual name of the symbol. |
| if (IMAGE_SNAP_BY_ORDINAL(thunk->u1.Ordinal)) |
| continue; |
| |
| // Have no reference? Then terminate the iteration. |
| if (!thunk.HasReference(thunk->u1.AddressOfData)) { |
| // We sanity check that the actual data is null. |
| DCHECK_EQ(0u, thunk->u1.AddressOfData); |
| break; |
| } |
| |
| // Otherwise this should point to an IMAGE_IMPORT_BY_NAME structure. |
| ImageImportByName iibn; |
| if (!hna.Dereference(hna[i], &iibn)) { |
| LOG(ERROR) << "Unable to dereference IMAGE_IMPORT_BY_NAME."; |
| return false; |
| } |
| |
| // Check to see if this symbol matches that of the current image import |
| // by name. |
| if (SymbolNameMatches(symbol_name, iibn)) { |
| *iat_index = i; |
| return true; |
| } |
| } |
| |
| // If we get here then the entry doesn't exist. If we've been asked to only |
| // search for it then we can return early. |
| if (find_only) |
| return true; |
| |
| // Figure out how large the data needs to be to hold the name of this exported |
| // symbol. The IMAGE_IMPORT_BY_NAME struct has a WORD ordinal and a variable |
| // sized field for the null-terminated function name. Each entry should be |
| // WORD aligned, and will be referenced from the import address table and the |
| // import name table. |
| size_t symbol_name_len = strlen(symbol_name); |
| size_t iibn_size = sizeof(WORD) + common::AlignUp(symbol_name_len + 1, |
| sizeof(WORD)); |
| |
| // Get the DLL name. We will be inserting the IIBN entry to the block |
| // containing it immediately prior to the DLL name. |
| String dll_name; |
| if (!iid.Dereference(iid->Name, &dll_name)) { |
| LOG(ERROR) << "Unable to dereference DLL name."; |
| return false; |
| } |
| Offset iibn_offset = dll_name.offset(); |
| dll_name.block()->InsertData(iibn_offset, iibn_size, true); |
| |
| // Populate the import struct. |
| TypedBlock<IMAGE_IMPORT_BY_NAME> iibn; |
| if (!iibn.InitWithSize(iibn_offset, iibn_size, dll_name.block())) { |
| LOG(ERROR) << "Unable to dereference new IMAGE_IMPORT_BY_NAME."; |
| return false; |
| } |
| iibn->Hint = 0; |
| base::strlcpy(reinterpret_cast<char*>(iibn->Name), symbol_name, |
| symbol_name_len + 1); |
| |
| // Make room in the INT and the IAT for the new symbol. We place it |
| // after the last entry for this module. |
| Offset int_offset = hna.OffsetOf(hna[i]); |
| Offset iat_offset = iat.OffsetOf(iat[i]); |
| // We're pointed at the terminating zero. The position we're pointing at can |
| // be the destination for references (in the normal case where someone is |
| // using the import). However, in the special case where the IAT and the INT |
| // are empty, our slot may also be pointed at by the import descriptor. |
| // If we were to insert data at this position, we'd push the import |
| // descriptor's pointer forward, past our new entry. To avoid this, we insert |
| // the new data after the terminating zero we're pointing at, then usurp the |
| // previously terminating zero for our entry. |
| hna.block()->InsertData(int_offset + kPtrSize, kPtrSize, true); |
| iat.block()->InsertData(iat_offset + kPtrSize, kPtrSize, true); |
| |
| // Because of the usurping mentioned above, we manually move any existing |
| // labels. |
| BlockGraph::Label label; |
| if (hna.block()->GetLabel(int_offset, &label)) { |
| hna.block()->RemoveLabel(int_offset); |
| hna.block()->SetLabel(int_offset + kPtrSize, label); |
| } |
| if (iat.block()->GetLabel(iat_offset, &label)) { |
| iat.block()->RemoveLabel(iat_offset); |
| iat.block()->SetLabel(iat_offset + kPtrSize, label); |
| } |
| |
| // Add the new labels. We have to get the module_name at this point |
| // because it may have been moved with our insertions above. |
| String module_name; |
| if (!iid.Dereference(iid->Name, &module_name)) { |
| LOG(ERROR) << "Unable to dereference import name."; |
| return false; |
| } |
| hna.block()->SetLabel( |
| int_offset, |
| base::StringPrintf("%s INT: %s", module_name->string, symbol_name), |
| BlockGraph::DATA_LABEL); |
| iat.block()->SetLabel( |
| iat_offset, |
| base::StringPrintf("%s IAT: %s", module_name->string, symbol_name), |
| BlockGraph::DATA_LABEL); |
| |
| // Hook up the newly created IMAGE_IMPORT_BY_NAME to both tables. |
| BlockGraph::Reference iibn_ref(BlockGraph::RELATIVE_REF, |
| kPtrSize, |
| iibn.block(), |
| iibn.offset(), |
| iibn.offset()); |
| hna.block()->SetReference(int_offset, iibn_ref); |
| iat.block()->SetReference(iat_offset, iibn_ref); |
| |
| // Return the reference to the IAT entry for the newly imported symbol. |
| *iat_index = i; |
| *added = true; |
| |
| return true; |
| } |
| |
| // Looks for the given symbol in the given delay-loaded library descriptor. |
| // Returns true on success, false otherwise. If the symbol was found sets |
| // |found| to true, and return a reference to it via |ref|. |
| bool FindDelayLoadSymbol(const base::StringPiece& symbol_name, |
| const ImageDelayLoadDescriptor& idld, |
| size_t module_index, |
| bool* found, |
| size_t* index, |
| BlockGraph::Reference* ref) { |
| DCHECK_NE(reinterpret_cast<bool*>(NULL), found); |
| DCHECK_NE(reinterpret_cast<size_t*>(NULL), index); |
| DCHECK_NE(reinterpret_cast<BlockGraph::Reference*>(NULL), ref); |
| |
| *found = false; |
| *index = kInvalidIndex; |
| |
| ImageThunkData32 addresses; |
| ImageThunkData32 names; |
| if (!idld.Dereference(idld[module_index].ImportAddressTableRVA, &addresses) || |
| !idld.Dereference(idld[module_index].ImportNameTableRVA, &names)) { |
| LOG(ERROR) << "Failed to dereference IAT/INT for delay-load library."; |
| return false; |
| } |
| |
| size_t count = std::min(addresses.ElementCount(), names.ElementCount()); |
| for (size_t i = 0; i < count; ++i) { |
| // Keep an eye out for zero-terminating IAT entries. |
| bool zero_data = addresses[i].u1.AddressOfData == 0; |
| bool has_ref = addresses.HasReference(addresses[i].u1.AddressOfData); |
| if (zero_data && !has_ref) |
| break; |
| if (!zero_data && !has_ref) { |
| LOG(ERROR) << "Expected reference at offset " << i |
| << " of delay-load IAT."; |
| return false; |
| } |
| |
| // Keep an eye out for zero-terminating INT entries. |
| zero_data = names[i].u1.AddressOfData == 0; |
| has_ref = names.HasReference(names[i].u1.AddressOfData); |
| if (zero_data && !has_ref) |
| break; |
| if (!zero_data && !has_ref) { |
| LOG(ERROR) << "Expected reference at offset " << i |
| << " of delay-load INT."; |
| return false; |
| } |
| |
| ImageImportByName iibn; |
| if (!names.Dereference(names[i].u1.AddressOfData, &iibn)) { |
| LOG(ERROR) << "Failed to dereference name of entry " << i |
| << " of delay-load INT."; |
| return false; |
| } |
| |
| if (SymbolNameMatches(symbol_name, iibn)) { |
| Offset offset = addresses.OffsetOf(addresses->u1.Function); |
| *ref = BlockGraph::Reference(BlockGraph::ABSOLUTE_REF, |
| BlockGraph::Reference::kMaximumSize, |
| addresses.block(), |
| offset, |
| offset); |
| *found = true; |
| *index = i; |
| return true; |
| } |
| } |
| |
| return true; |
| } |
| |
| } // namespace |
| |
| const char PEAddImportsTransform::kTransformName[] = "PEAddImportsTransform"; |
| |
| PEAddImportsTransform::PEAddImportsTransform() |
| : image_import_descriptor_block_(NULL), |
| import_address_table_block_(NULL) { |
| } |
| |
| bool PEAddImportsTransform::TransformBlockGraph( |
| const TransformPolicyInterface* policy, |
| BlockGraph* block_graph, |
| BlockGraph::Block* dos_header_block) { |
| DCHECK_NE(reinterpret_cast<TransformPolicyInterface*>(NULL), policy); |
| DCHECK_NE(reinterpret_cast<BlockGraph*>(NULL), block_graph); |
| DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), dos_header_block); |
| DCHECK_EQ(BlockGraph::PE_IMAGE, block_graph->image_format()); |
| |
| modules_added_ = 0; |
| symbols_added_ = 0; |
| |
| DosHeader dos_header; |
| NtHeaders nt_headers; |
| if (!dos_header.Init(0, dos_header_block) || |
| !dos_header.Dereference(dos_header->e_lfanew, &nt_headers)) { |
| LOG(ERROR) << "Unable to cast image headers."; |
| return false; |
| } |
| |
| // Find delay load imports. This is read-only, searching for existing |
| // imports but not injecting new ones. |
| if (!FindDelayLoadImports(block_graph, nt_headers.block())) |
| return false; |
| |
| // Before processing regular imports, let's determine if we're on a strictly |
| // exploratory mission. We don't want to add anything if all unresolved |
| // modules/symbols are 'find only'. |
| bool find_only = true; |
| for (size_t i = 0; i < imported_modules_.size(); ++i) { |
| for (size_t j = 0; j < imported_modules_[i]->size(); ++j) { |
| // If the symbol is resolved, we don't care about it. We don't want to |
| // unnecessarily add PE import structures if we're not creating any |
| // imports. |
| if (imported_modules_[i]->SymbolIsImported(j)) |
| continue; |
| if (imported_modules_[i]->GetSymbolMode(j) != ImportedModule::kFindOnly) { |
| find_only = false; |
| break; |
| } |
| } |
| } |
| |
| // Find normal imports. If the symbol is imported as both a delay-load and |
| // a regular import, then this will overwrite it. Thus, regular imports will |
| // be preferred. However, if the symbol was resolved as a delay-load import |
| // then this will not cause it to also be added as a regular import. |
| if (!FindOrAddImports(find_only, block_graph, nt_headers.block())) |
| return false; |
| |
| return true; |
| } |
| |
| bool PEAddImportsTransform::FindOrAddImports( |
| bool find_only, |
| BlockGraph* block_graph, |
| BlockGraph::Block* nt_headers_block) { |
| DCHECK_NE(reinterpret_cast<BlockGraph*>(NULL), block_graph); |
| DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), nt_headers_block); |
| |
| NtHeaders nt_headers; |
| CHECK(nt_headers.Init(0, nt_headers_block)); |
| |
| // Get the import data directory. |
| image_import_descriptor_block_ = NULL; |
| if (!FindOrAddDataDirectory(find_only, |
| IMAGE_DIRECTORY_ENTRY_IMPORT, |
| "Image Import Descriptor Array", |
| sizeof(IMAGE_IMPORT_DESCRIPTOR), |
| block_graph, |
| nt_headers.block(), |
| &image_import_descriptor_block_)) { |
| return false; |
| } |
| if (image_import_descriptor_block_ == NULL) |
| return find_only; |
| |
| // Similarly, get the import address table. |
| import_address_table_block_ = NULL; |
| if (!FindOrAddDataDirectory(find_only, |
| IMAGE_DIRECTORY_ENTRY_IAT, |
| "Import Address Table", |
| kPtrSize, |
| block_graph, |
| nt_headers.block(), |
| &import_address_table_block_)) { |
| return false; |
| } |
| if (import_address_table_block_ == NULL) |
| return find_only; |
| |
| // Handle each library individually. |
| for (size_t i = 0; i < imported_modules_.size(); ++i) { |
| ImportedModule* module = imported_modules_[i]; |
| |
| // First find or create an entry for this module in the Image Import |
| // Descriptor Array. |
| ImageImportDescriptor iid; |
| bool module_added = false; |
| bool module_exists = false; |
| if (!FindOrAddImageImportDescriptor( |
| module->mode() == ImportedModule::kFindOnly, |
| module->name().c_str(), |
| block_graph, |
| image_import_descriptor_block_, |
| import_address_table_block_, |
| &iid, |
| &module_added, |
| &module_exists)) { |
| LOG(ERROR) << "Failed to find or import module."; |
| return false; |
| } |
| |
| // If we're fact finding only and the module does not exist then we don't |
| // need to look up its symbols. |
| if (module->mode() == ImportedModule::kFindOnly && !module_exists) { |
| DCHECK(!module_added); |
| continue; |
| } |
| |
| DCHECK(module_exists); |
| UpdateModule(true, module_added, module); |
| modules_added_ += module_added; |
| |
| // Update the version date/time stamp if requested. |
| if (module->date() != ImportedModule::kInvalidDate) |
| iid->TimeDateStamp = module->date(); |
| |
| // Get a pointer to the import thunks. |
| ImageThunkData32 thunks; |
| if (!iid.Dereference(iid->FirstThunk, &thunks)) { |
| LOG(ERROR) << "Unable to dereference IMAGE_THUNK_DATA32."; |
| return false; |
| } |
| |
| for (size_t j = 0; j < module->size(); ++j) { |
| bool symbol_find_only = |
| module->GetSymbolMode(j) == ImportedModule::kFindOnly; |
| |
| // If the symbol was already resolved as a delay-load import, then |
| // don't allow it to also be added as a normal import. |
| if (module->SymbolIsImported(j)) |
| symbol_find_only = true; |
| |
| // Now, for each symbol get the offset of the IAT entry. This will create |
| // the entry (and all accompanying structures) if necessary. |
| size_t symbol_iat_index = kInvalidIndex; |
| bool symbol_added = false; |
| if (!FindOrAddImportedSymbol( |
| symbol_find_only, |
| module->GetSymbolName(j).c_str(), |
| iid, |
| block_graph, |
| import_address_table_block_, |
| &symbol_iat_index, |
| &symbol_added)) { |
| LOG(ERROR) << "Failed to find or import symbol."; |
| return false; |
| } |
| symbols_added_ += symbol_added; |
| |
| if (symbol_iat_index != kInvalidIndex) { |
| Offset offset = |
| thunks.OffsetOf(thunks[symbol_iat_index].u1.AddressOfData); |
| BlockGraph::Reference ref(BlockGraph::ABSOLUTE_REF, kPtrSize, |
| thunks.block(), offset, offset); |
| |
| UpdateModuleSymbolInfo(j, true, symbol_added, module); |
| UpdateModuleSymbolReference(j, ref, true, module); |
| } |
| } |
| } |
| |
| // Update the data directory sizes. |
| nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = |
| image_import_descriptor_block_->size(); |
| nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size = |
| import_address_table_block_->size(); |
| |
| return true; |
| } |
| |
| bool PEAddImportsTransform::FindDelayLoadImports( |
| BlockGraph* block_graph, BlockGraph::Block* nt_headers_block) { |
| DCHECK_NE(reinterpret_cast<BlockGraph*>(NULL), block_graph); |
| DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), nt_headers_block); |
| |
| NtHeaders nt_headers; |
| CHECK(nt_headers.Init(0, nt_headers_block)); |
| |
| // Get the delay-load import data directory. |
| image_delayload_descriptor_block_ = NULL; |
| if (!FindOrAddDataDirectory(true, |
| IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT, |
| "Image Delay Load Descriptor Array", |
| sizeof(IMAGE_DELAYLOAD_DESCRIPTOR), |
| block_graph, |
| nt_headers.block(), |
| &image_delayload_descriptor_block_)) { |
| return false; |
| } |
| if (image_delayload_descriptor_block_ == NULL) |
| return true; |
| |
| ImageDelayLoadDescriptor idld; |
| if (!idld.Init(0, image_delayload_descriptor_block_)) { |
| LOG(ERROR) << "Unable to cast IMAGE_DELAYLOAD_DESCRIPTOR."; |
| return false; |
| } |
| |
| for (size_t i = 0; i < imported_modules_.size(); ++i) { |
| ImportedModule* module = imported_modules_[i]; |
| |
| // Look for a descriptor corresponding to this module. |
| size_t module_index = kInvalidIndex; |
| if (!FindDelayLoadImportDescriptor(module->name(), idld, &module_index)) |
| return false; |
| if (module_index == kInvalidIndex) |
| continue; |
| |
| UpdateModule(true, false, module); |
| |
| // Iterate over the symbols. |
| for (size_t j = 0; j < module->size(); ++j) { |
| // Don't process symbols that are already imported. |
| if (module->SymbolIsImported(j)) |
| continue; |
| |
| // Look for a matching symbol. |
| bool found = false; |
| size_t index = kInvalidIndex; |
| BlockGraph::Reference ref; |
| if (!FindDelayLoadSymbol(module->GetSymbolName(j), idld, module_index, |
| &found, &index, &ref)) { |
| return false; |
| } |
| if (!found) |
| continue; |
| |
| // Update the various metadata associated with this symbol. |
| // TODO(chrisha): Currently the import index must be unique. This ensures |
| // uniqueness for delay-load imports by setting the MSB, and combining |
| // the module index with the symbol index. |
| UpdateModuleSymbolInfo(j, true, false, module); |
| UpdateModuleSymbolReference(j, ref, true, module); |
| } |
| } |
| |
| return true; |
| } |
| |
| } // namespace transforms |
| } // namespace pe |