Coverage for /Syzygy/pe/pe_utils.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
88.2%1571780.C++source

Line-by-line coverage:

   1    :  // Copyright 2012 Google Inc. All Rights Reserved.
   2    :  //
   3    :  // Licensed under the Apache License, Version 2.0 (the "License");
   4    :  // you may not use this file except in compliance with the License.
   5    :  // You may obtain a copy of the License at
   6    :  //
   7    :  //     http://www.apache.org/licenses/LICENSE-2.0
   8    :  //
   9    :  // Unless required by applicable law or agreed to in writing, software
  10    :  // distributed under the License is distributed on an "AS IS" BASIS,
  11    :  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12    :  // See the License for the specific language governing permissions and
  13    :  // limitations under the License.
  14    :  
  15    :  #include "syzygy/pe/pe_utils.h"
  16    :  
  17    :  #include "base/string_util.h"
  18    :  #include "syzygy/block_graph/typed_block.h"
  19    :  #include "syzygy/pe/dos_stub.h"
  20    :  
  21    :  namespace pe {
  22    :  
  23    :  using block_graph::BlockGraph;
  24    :  using block_graph::ConstTypedBlock;
  25    :  using block_graph::TypedBlock;
  26    :  using core::RelativeAddress;
  27    :  
  28    :  namespace {
  29    :  
  30    :  // A simple struct that can be used to let us access strings using TypedBlock.
  31    :  struct StringStruct {
  32    :    const char string[1];
  33    :  };
  34    :  
  35    :  typedef TypedBlock<IMAGE_DOS_HEADER> DosHeader;
  36    :  typedef TypedBlock<IMAGE_IMPORT_DESCRIPTOR> ImageImportDescriptor;
  37    :  typedef TypedBlock<IMAGE_NT_HEADERS> NtHeaders;
  38    :  typedef TypedBlock<StringStruct> String;
  39    :  
  40    :  template <typename BlockPtr>
  41    :  BlockPtr UncheckedGetNtHeadersBlockFromDosHeaderBlock(
  42  E :      BlockPtr dos_header_block) {
  43  E :    BlockGraph::Reference ref;
  44    :    if (!dos_header_block->GetReference(offsetof(IMAGE_DOS_HEADER, e_lfanew),
  45  E :                                        &ref)) {
  46    :      // No NT headers reference.
  47  E :      return NULL;
  48    :    }
  49    :  
  50    :    if (ref.offset() != 0 ||
  51    :        ref.type() != BlockGraph::RELATIVE_REF ||
  52  E :        ref.size() != sizeof(RelativeAddress)) {
  53    :      // The reference is of incorrect type.
  54  E :      return NULL;
  55    :    }
  56    :  
  57  E :    return ref.referenced();
  58  E :  }
  59    :  
  60    :  template <typename BlockPtr>
  61    :  BlockPtr CheckedGetNtHeadersBlockFromDosHeaderBlock(
  62  E :      BlockPtr dos_header_block) {
  63  E :    DCHECK(IsValidDosHeaderBlock(dos_header_block));
  64    :  
  65    :    BlockPtr nt_headers_block =
  66  E :        UncheckedGetNtHeadersBlockFromDosHeaderBlock(dos_header_block);
  67    :    if (nt_headers_block == NULL ||
  68  E :        !IsValidNtHeadersBlock(nt_headers_block)) {
  69  i :      return NULL;
  70    :    }
  71    :  
  72  E :    return nt_headers_block;
  73  E :  }
  74    :  
  75    :  }  // namespace
  76    :  
  77    :  const char kCodeSectionName[] = ".text";
  78    :  const char kReadOnlyDataSectionName[] = ".rdata";
  79    :  const char kReadWriteDataSectionName[] = ".data";
  80    :  const char kRelocSectionName[] = ".reloc";
  81    :  const char kResourceSectionName[] = ".rsrc";
  82    :  const char kTlsSectionName[] = ".tls";
  83    :  
  84    :  const DWORD kCodeCharacteristics =
  85    :      IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE;
  86    :  const DWORD kReadOnlyDataCharacteristics =
  87    :      IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
  88    :  const DWORD kReadWriteDataCharacteristics =
  89    :      IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
  90    :  const DWORD kRelocCharacteristics =
  91    :      IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_DISCARDABLE |
  92    :      IMAGE_SCN_MEM_READ;
  93    :  
  94  E :  bool IsValidDosHeaderBlock(const BlockGraph::Block* dos_header_block) {
  95  E :    ConstTypedBlock<IMAGE_DOS_HEADER> dos_header;
  96    :  
  97  E :    if (!dos_header.Init(0, dos_header_block)) {
  98    :      // Too small or no data.
  99  E :      return false;
 100    :    }
 101    :  
 102  E :    if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
 103    :      // Wrong signature.
 104  E :      return false;
 105    :    }
 106    :  
 107    :    // The "DOS file size" is encoded in a rather wonky manner.
 108    :    // - e_cp is the number of "pages" in the file, but
 109    :    // - e_cblp is the number of bytes used on the last page.
 110  E :    size_t dos_file_size = 512 * dos_header->e_cp;
 111  E :    if (dos_header->e_cblp != 0) {
 112    :      // Let's not go below zero size.
 113  E :      if (dos_file_size < 512)
 114  E :        return false;
 115    :  
 116  E :      dos_file_size -= 512;
 117  E :      dos_file_size += dos_header->e_cblp;
 118    :    }
 119    :    // The VC linker yields a DOS header with a size that's larger than
 120    :    // the DOS header and the NT headers combined. I wonder if anyone cares
 121    :    // about these sizes anymore.
 122  E :    if (dos_file_size < dos_header_block->size())
 123  E :      return false;
 124    :  
 125    :    // Check the paragraph size of the header.
 126  E :    if (dos_header->e_cparhdr * 16 < sizeof(IMAGE_DOS_HEADER))
 127  E :      return false;
 128    :  
 129    :    // Retrieve the NT headers.
 130    :    const BlockGraph::Block* nt_headers =
 131  E :        UncheckedGetNtHeadersBlockFromDosHeaderBlock(dos_header_block);
 132  E :    if (nt_headers == NULL) {
 133    :      // No DOS header reference.
 134  E :      return false;
 135    :    }
 136    :  
 137  E :    return true;
 138  E :  }
 139    :  
 140  E :  bool IsValidNtHeadersBlock(const BlockGraph::Block* nt_headers_block) {
 141    :    // Check the signatures.
 142  E :    ConstTypedBlock<IMAGE_NT_HEADERS> nt_headers;
 143    :  
 144  E :    if (!nt_headers.Init(0, nt_headers_block)) {
 145    :      // Short or no data.
 146  i :      return false;
 147    :    }
 148    :  
 149  E :    if (nt_headers->Signature!= IMAGE_NT_SIGNATURE) {
 150    :      // Wrong signature.
 151  E :      return false;
 152    :    }
 153    :    if (nt_headers->FileHeader.SizeOfOptionalHeader !=
 154  E :        sizeof(IMAGE_OPTIONAL_HEADER)) {
 155    :      // Wrong optional header size.
 156  E :      return false;
 157    :    }
 158  E :    if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) {
 159    :      // Wrong magic for optional header.
 160  E :      return false;
 161    :    }
 162    :  
 163    :    // Calculate the minimum size for the NT headers and the section header.
 164    :    size_t header_size = sizeof(IMAGE_NT_HEADERS) +
 165  E :        sizeof(IMAGE_SECTION_HEADER) * nt_headers->FileHeader.NumberOfSections;
 166    :  
 167    :    if (nt_headers_block->size() < header_size ||
 168  E :        nt_headers_block->data_size() < header_size) {
 169    :      // The block's size isn't large enough for the section headers.
 170  i :      return false;
 171    :    }
 172    :  
 173  E :    return true;
 174  E :  }
 175    :  
 176    :  const BlockGraph::Block* GetNtHeadersBlockFromDosHeaderBlock(
 177  E :      const BlockGraph::Block* dos_header_block) {
 178  E :    return CheckedGetNtHeadersBlockFromDosHeaderBlock(dos_header_block);
 179  E :  }
 180    :  
 181    :  BlockGraph::Block* GetNtHeadersBlockFromDosHeaderBlock(
 182  E :      BlockGraph::Block* dos_header_block) {
 183  E :    return CheckedGetNtHeadersBlockFromDosHeaderBlock(dos_header_block);
 184  E :  }
 185    :  
 186  E :  bool UpdateDosHeader(BlockGraph::Block* dos_header_block) {
 187  E :    DCHECK(dos_header_block != NULL);
 188    :  
 189    :    // The DOS header has to be a multiple of 16 bytes for historic reasons.
 190    :    size_t dos_header_size = common::AlignUp(
 191  E :        sizeof(IMAGE_DOS_HEADER) + pe::kDosStubSize, 16);
 192    :  
 193    :    // If the new header block is shorter than it was, go ahead and
 194    :    // trim the source ranges to match the new, shorter size.
 195  E :    if (dos_header_block->size() > dos_header_size) {
 196    :      BlockGraph::Block::DataRange range(
 197  E :          dos_header_size, dos_header_block->size() - dos_header_size);
 198  E :      dos_header_block->source_ranges().RemoveMappedRange(range);
 199    :    }
 200    :  
 201  E :    dos_header_block->ResizeData(dos_header_size);
 202  E :    dos_header_block->set_size(dos_header_size);
 203  E :    DCHECK_EQ(dos_header_size, dos_header_block->size());
 204  E :    DCHECK_EQ(dos_header_size, dos_header_block->data_size());
 205    :  
 206  E :    TypedBlock<IMAGE_DOS_HEADER> dos_header;
 207  E :    if (!dos_header.InitWithSize(0, dos_header_size, dos_header_block)) {
 208  i :      LOG(ERROR) << "Unable to cast IMAGE_DOS_HEADER.";
 209  i :      return false;
 210    :    }
 211    :  
 212    :    // Wipe the DOS header and fill in the stub.
 213  E :    memset(dos_header.Get(), 0, sizeof(IMAGE_DOS_HEADER));
 214  E :    memcpy(dos_header.Get() + 1, pe::kDosStub, pe::kDosStubSize);
 215    :  
 216  E :    dos_header->e_magic = IMAGE_DOS_SIGNATURE;
 217    :    // Calculate the number of bytes used on the last DOS executable "page".
 218  E :    dos_header->e_cblp = dos_header_size % 512;
 219    :    // Calculate the number of pages used by the DOS executable.
 220  E :    dos_header->e_cp = dos_header_size / 512;
 221    :    // Count the last page if we didn't have an even multiple
 222  E :    if (dos_header->e_cblp != 0)
 223  E :      dos_header->e_cp++;
 224    :  
 225    :    // Header length in "paragraphs".
 226  E :    dos_header->e_cparhdr = sizeof(*dos_header) / 16;
 227    :  
 228    :    // Set this to max allowed, just because.
 229  E :    dos_header->e_maxalloc = 0xFFFF;
 230    :  
 231    :    // Location of relocs - our header has zero relocs, but we set this anyway.
 232  E :    dos_header->e_lfarlc = sizeof(*dos_header);
 233    :  
 234  E :    DCHECK(IsValidDosHeaderBlock(dos_header_block));
 235    :  
 236  E :    return true;
 237  E :  }
 238    :  
 239    :  namespace {
 240    :  
 241    :  enum EntryPointTypeEnum { kExeEntryPoint, kDllEntryPoint };
 242    :  
 243    :  bool GetImageEntryPoint(BlockGraph::Block* dos_header_block,
 244    :                          EntryPointTypeEnum desired_entry_point_type,
 245  E :                          EntryPoint* entry_point) {
 246  E :    DCHECK(dos_header_block != NULL);
 247  E :    DCHECK(entry_point != NULL);
 248    :  
 249  E :    *entry_point = EntryPoint(static_cast<BlockGraph::Block*>(NULL), 0);
 250    :  
 251    :    BlockGraph::Block* nt_headers_block =
 252  E :        pe::GetNtHeadersBlockFromDosHeaderBlock(dos_header_block);
 253    :  
 254  E :    TypedBlock<IMAGE_NT_HEADERS> nt_headers;
 255  E :    if (nt_headers_block == NULL || !nt_headers.Init(0, nt_headers_block)) {
 256  i :      LOG(ERROR) << "Unable to retrieve NT Headers.";
 257  i :      return false;
 258    :    }
 259    :  
 260  E :    EntryPointTypeEnum entry_point_type = kExeEntryPoint;
 261  E :    if ((nt_headers->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0)
 262  E :      entry_point_type = kDllEntryPoint;
 263    :  
 264  E :    if (entry_point_type != desired_entry_point_type)
 265  E :      return true;
 266    :  
 267  E :    BlockGraph::Reference entry_point_ref;
 268    :    bool found = nt_headers.block()->GetReference(
 269    :        offsetof(IMAGE_NT_HEADERS, OptionalHeader.AddressOfEntryPoint),
 270  E :        &entry_point_ref);
 271    :  
 272  E :    if (!found && entry_point_type == kExeEntryPoint) {
 273  E :      LOG(ERROR) << "Malformed PE Headers: No entry point found for executable.";
 274  E :      return false;
 275    :    }
 276    :  
 277  E :    if (found) {
 278    :      *entry_point = EntryPoint(entry_point_ref.referenced(),
 279  E :                                entry_point_ref.offset());
 280    :    }
 281    :  
 282  E :    return true;
 283  E :  }
 284    :  
 285    :  }  // namespace
 286    :  
 287    :  bool GetExeEntryPoint(BlockGraph::Block* dos_header_block,
 288  E :                        EntryPoint* entry_point) {
 289  E :    return GetImageEntryPoint(dos_header_block, kExeEntryPoint, entry_point);
 290  E :  }
 291    :  
 292    :  bool GetDllEntryPoint(BlockGraph::Block* dos_header_block,
 293  E :                        EntryPoint* entry_point) {
 294  E :    return GetImageEntryPoint(dos_header_block, kDllEntryPoint, entry_point);
 295  E :  }
 296    :  
 297    :  bool GetTlsInitializers(BlockGraph::Block* dos_header_block,
 298  E :                          EntryPointSet* entry_points) {
 299  E :    DCHECK(dos_header_block != NULL);
 300  E :    DCHECK(entry_points != NULL);
 301    :  
 302    :    BlockGraph::Block* nt_headers_block =
 303  E :        pe::GetNtHeadersBlockFromDosHeaderBlock(dos_header_block);
 304    :  
 305  E :    TypedBlock<IMAGE_NT_HEADERS> nt_headers;
 306  E :    if (nt_headers_block == NULL || !nt_headers.Init(0, nt_headers_block)) {
 307  i :      LOG(ERROR) << "Unable to retrieve NT Headers.";
 308  i :      return false;
 309    :    }
 310    :  
 311    :    // If the module has no TLS directory then there are no TLS initializers
 312    :    // and hence nothing to do.
 313    :    const IMAGE_DATA_DIRECTORY& data_dir =
 314  E :        nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS];
 315  E :    if (data_dir.Size == 0 || !nt_headers.HasReference(data_dir.VirtualAddress)) {
 316  E :      return true;
 317    :    }
 318    :  
 319    :    // Find the TLS directory.
 320  E :    TypedBlock<IMAGE_TLS_DIRECTORY> tls_dir;
 321  E :    if (!nt_headers.Dereference(data_dir.VirtualAddress, &tls_dir)) {
 322  i :      LOG(ERROR) << "Failed to cast TLS directory.";
 323  i :      return false;
 324    :    }
 325    :  
 326    :    // Get the TLS initializer callbacks. We manually lookup the reference
 327    :    // because it is an indirect reference, which can't be dereferenced by
 328    :    // TypedBlock.
 329    :    typedef BlockGraph::Block::ReferenceMap ReferenceMap;
 330    :    ReferenceMap::const_iterator callback_ref =
 331    :        tls_dir.block()->references().find(
 332  E :            tls_dir.OffsetOf(tls_dir->AddressOfCallBacks));
 333  E :    if (callback_ref == tls_dir.block()->references().end()) {
 334  i :      LOG(ERROR) << "Failed to locate TLS initializers.";
 335  i :      return false;
 336    :    }
 337    :  
 338    :    // Note each of the TLS entry points.
 339  E :    const BlockGraph::Block* callbacks_block = callback_ref->second.referenced();
 340  E :    const ReferenceMap& ref_map = callbacks_block->references();
 341  E :    ReferenceMap::const_iterator iter = ref_map.begin();
 342  E :    for (; iter != ref_map.end(); ++iter) {
 343  E :      const BlockGraph::Reference& ref = iter->second;
 344  E :      DCHECK(ref.size() == sizeof(core::AbsoluteAddress));
 345    :      entry_points->insert(
 346  E :          std::make_pair(ref.referenced(), ref.offset()));
 347  E :    }
 348    :  
 349  E :    return true;
 350  E :  }
 351    :  
 352    :  bool HasImportEntry(block_graph::BlockGraph::Block* header_block,
 353    :                      const base::StringPiece& dll_name,
 354  E :                      bool* has_import_entry) {
 355  E :    DCHECK(header_block != NULL);
 356  E :    DCHECK(dll_name != NULL);
 357  E :    DCHECK(!dll_name.empty());
 358  E :    DCHECK(has_import_entry != NULL);
 359    :  
 360  E :    *has_import_entry = false;
 361    :  
 362  E :    DosHeader dos_header;
 363  E :    NtHeaders nt_headers;
 364    :    if (!dos_header.Init(0, header_block) ||
 365  E :        !dos_header.Dereference(dos_header->e_lfanew, &nt_headers)) {
 366  i :      LOG(ERROR) << "Unable to cast image headers.";
 367  i :      return false;
 368    :    }
 369    :  
 370    :    BlockGraph::Block* image_import_descriptor_block;
 371    :    IMAGE_DATA_DIRECTORY* import_directory =
 372  E :        nt_headers->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_IMPORT;
 373  E :    DCHECK(nt_headers.HasReference(import_directory->VirtualAddress));
 374    :  
 375  E :    ImageImportDescriptor image_import_descriptor;
 376    :    if (!nt_headers.Dereference(import_directory->VirtualAddress,
 377  E :                                &image_import_descriptor)) {
 378    :      // This could happen if the image import descriptor array is empty, and
 379    :      // terminated by a *partial* null entry. However, we've not yet seen that.
 380  i :      LOG(ERROR) << "Failed to dereference Image Import Descriptor Array.";
 381  i :      return false;
 382    :    }
 383    :  
 384  E :    image_import_descriptor_block = image_import_descriptor.block();
 385    :  
 386  E :    ImageImportDescriptor iida;
 387  E :    if (!iida.Init(0, image_import_descriptor_block)) {
 388  i :      LOG(ERROR) << "Unable to cast Image Import Descriptor.";
 389  i :      return false;
 390    :    }
 391    :  
 392    :    // The array is NULL terminated with a potentially incomplete descriptor so
 393    :    // we can't use ElementCount - 1.
 394  E :    DCHECK_GT(image_import_descriptor_block->size(), 0U);
 395    :    size_t descriptor_count =
 396    :        (common::AlignUp(image_import_descriptor_block->size(),
 397    :                         sizeof(IMAGE_IMPORT_DESCRIPTOR)) /
 398  E :         sizeof(IMAGE_IMPORT_DESCRIPTOR)) - 1;
 399    :  
 400  E :    for (size_t iida_index = 0; iida_index < descriptor_count; ++iida_index) {
 401  E :      String ref_dll_name;
 402  E :      if (!iida.Dereference(iida[iida_index].Name, &ref_dll_name)) {
 403  i :        LOG(ERROR) << "Unable to dereference DLL name.";
 404  i :        return false;
 405    :      }
 406    :  
 407  E :      size_t max_len = ref_dll_name.ElementCount();
 408    :      if (base::strncasecmp(ref_dll_name->string, dll_name.data(),
 409  E :                            max_len) == 0) {
 410  E :        *has_import_entry = true;
 411  E :        break;
 412    :      }
 413  E :    }
 414    :  
 415  E :    return true;
 416  E :  }
 417    :  
 418    :  }  // namespace pe

Coverage information generated Thu Jul 04 09:34:53 2013.