Coverage for /Syzygy/pe/serialization.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
59.0%1021730.C++source

Line-by-line coverage:

   1    :  // Copyright 2012 Google Inc.
   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/serialization.h"
  16    :  
  17    :  #include "base/bind.h"
  18    :  #include "base/file_util.h"
  19    :  #include "syzygy/block_graph/typed_block.h"
  20    :  #include "syzygy/pe/find.h"
  21    :  #include "syzygy/pe/image_layout.h"
  22    :  #include "syzygy/pe/metadata.h"
  23    :  #include "syzygy/pe/pe_file.h"
  24    :  
  25    :  namespace pe {
  26    :  
  27    :  namespace {
  28    :  
  29    :  using block_graph::BlockGraph;
  30    :  using block_graph::BlockGraphSerializer;
  31    :  
  32    :  // Used for versioning the serialized stream. Be sure to change this if
  33    :  // non-backwards compatible changes are made to the stream layout.
  34    :  static const uint32 kSerializedBlockGraphAndImageLayoutVersion = 0;
  35    :  
  36  E :  bool MetadataMatchesPEFile(const Metadata& metadata, const PEFile& pe_file) {
  37  E :    PEFile::Signature pe_signature;
  38  E :    pe_file.GetSignature(&pe_signature);
  39    :  
  40  E :    if (!metadata.IsConsistent(pe_signature))
  41  i :      return false;
  42    :  
  43  E :    return true;
  44  E :  }
  45    :  
  46  E :  bool FindPEFile(const Metadata& metadata, PEFile* pe_file) {
  47  E :    DCHECK(pe_file != NULL);
  48    :  
  49  E :    LOG(INFO) << "Searching for module to use in deserialization.";
  50    :  
  51    :    // We search for a PE file in the following sequence:
  52    :    // (1) If pe_file is already initialized, try to use it.
  53    :    // (2) Look for a PE file using the path stored in metadata.
  54    :    // (3) Search for a matching PE file in the already initialized pe_file
  55    :    //     directory (if provided), and the metadata directory.
  56    :    // (4) Search for a matching PE file using a system wide search.
  57  E :    std::wstring search_path;
  58    :  
  59    :    // Approach 1: If we already have a PE file initialized, see if it matches the
  60    :    // signature of the one we serialized.
  61  E :    if (!pe_file->path().empty()) {
  62  i :      LOG(INFO) << "Attempting to use provided module in deserialization: "
  63    :                << pe_file->path().value();
  64    :  
  65  i :      if (MetadataMatchesPEFile(metadata, *pe_file))
  66  i :        return true;
  67    :  
  68    :      // Save the directory of the provided PE file in the search path.
  69  i :      search_path.append(pe_file->path().DirName().value());
  70  i :      search_path.append(L";");
  71  i :      LOG(WARNING) << "Metadata signature does not match provided module: "
  72    :                   << pe_file->path().value();
  73    :    }
  74    :  
  75    :    // Approach 2: Try to use the path provided in the metadata itself.
  76  E :    FilePath metadata_path(metadata.module_signature().path);
  77  E :    LOG(INFO) << "Attempting to use metadata path in deserialization: "
  78    :              << metadata_path.value();
  79  E :    if (!file_util::PathExists(metadata_path) || !pe_file->Init(metadata_path)) {
  80  i :      LOG(WARNING) << "Unable to read module:" << metadata_path.value();
  81  i :    } else {
  82  E :      if (MetadataMatchesPEFile(metadata, *pe_file))
  83  E :        return true;
  84    :  
  85    :      // Append the directory to the search path if it exists.
  86  i :      FilePath dir = metadata_path.DirName();
  87  i :      if (file_util::DirectoryExists(dir))
  88  i :        search_path.append(metadata_path.DirName().value());
  89    :  
  90  i :      LOG(WARNING) << "Metadata signature does not match metadata module: "
  91    :                   << metadata_path.value();
  92  i :    }
  93    :  
  94  i :    FilePath module_path;
  95    :  
  96    :    // Approach 3: Use an explicit search in the provided paths.
  97  i :    if (!search_path.empty()) {
  98  i :      LOG(INFO) << "Searching for module in provided paths: " << search_path;
  99    :      if (!FindModuleBySignature(metadata.module_signature(),
 100    :                                 search_path.c_str(),
 101  i :                                 &module_path)) {
 102  i :        LOG(WARNING) << "FindModuleBySignature failed.";
 103    :      }
 104    :    }
 105    :  
 106    :    // Approach 4: Do a system-wide search.
 107  i :    if (module_path.empty()) {
 108  i :      LOG(INFO) << "Searching for module using system paths.";
 109    :      if (!FindModuleBySignature(metadata.module_signature(),
 110  i :                                 &module_path)) {
 111  i :        LOG(ERROR) << "FindModuleBySignature failed.";
 112  i :        return false;
 113    :      }
 114    :    }
 115    :  
 116    :    // No module found in either of the above two searches?
 117  i :    if (module_path.empty()) {
 118  i :      LOG(ERROR) << "No module found in FindModuleBySignature.";
 119  i :      return false;
 120    :    }
 121    :  
 122    :    // If we get here, we've found a module. However, we don't just accept that
 123    :    // fact.
 124    :  
 125  i :    if (!pe_file->Init(module_path)) {
 126  i :      LOG(ERROR) << "Failed to read module: " << module_path.value();
 127  i :      return false;
 128    :    }
 129    :  
 130  i :    if (!MetadataMatchesPEFile(metadata, *pe_file)) {
 131  i :      LOG(ERROR) << "Metadata signature does not match found module: "
 132    :                 << module_path.value();
 133  i :      return false;
 134    :    }
 135    :  
 136  i :    LOG(INFO) << "Found module with matching signature: " << module_path.value();
 137    :  
 138  i :    return true;
 139  E :  }
 140    :  
 141    :  // This callback is used to save the data in a block by simply savings its
 142    :  // address in the image-layout.
 143    :  bool SaveBlockData(const ImageLayout* image_layout,
 144    :                     bool data_already_saved,
 145    :                     const BlockGraph::Block& block,
 146  E :                     core::OutArchive* out_archive) {
 147  E :    DCHECK(image_layout != NULL);
 148  E :    DCHECK(out_archive != NULL);
 149    :  
 150    :    // We're always in OUTPUT_NO_DATA mode, so either the data hasn't yet been
 151    :    // saved or there was no data to save.
 152  E :    DCHECK(block.data_size() == 0 || !data_already_saved);
 153    :  
 154  E :    core::RelativeAddress block_addr;
 155  E :    if (!image_layout->blocks.GetAddressOf(&block, &block_addr)) {
 156  i :      LOG(ERROR) << "Block with id " << block.id() << " not in image-layout.";
 157  i :      return false;
 158    :    }
 159    :  
 160    :    // Save the address of the block wrt to the provided image-layout. This will
 161    :    // be sufficient for us to lookup the block data in the PE file afterwards.
 162  E :    if (!out_archive->Save(block_addr)) {
 163  i :      LOG(ERROR) << "Unable to save address of block with id " << block.id()
 164    :                 << ".";
 165  i :      return false;
 166    :    }
 167    :  
 168  E :    return true;
 169  E :  }
 170    :  
 171    :  // This callback is used to load the data in a block. It also simultaneously
 172    :  // constructs the image-layout.
 173    :  bool LoadBlockData(const PEFile* pe_file,
 174    :                     ImageLayout* image_layout,
 175    :                     bool need_to_set_data,
 176    :                     size_t data_size,
 177    :                     BlockGraph::Block* block,
 178  E :                     core::InArchive* in_archive) {
 179  E :    DCHECK(pe_file != NULL);
 180  E :    DCHECK(image_layout != NULL);
 181  E :    DCHECK(block != NULL);
 182  E :    DCHECK(in_archive != NULL);
 183    :  
 184  E :    core::RelativeAddress block_addr;
 185  E :    if (!in_archive->Load(&block_addr)) {
 186  i :      LOG(ERROR) << "Unable to load address in image-layout of block with id "
 187    :                 << block->id() << ".";
 188  i :      return false;
 189    :    }
 190    :  
 191    :    // Insert the block in the image layout.
 192  E :    if (!image_layout->blocks.InsertBlock(block_addr, block)) {
 193  i :      LOG(ERROR) << "Unable to insert block with id " << block->id() << " into "
 194    :                 << "image-layout.";
 195  i :      return false;
 196    :    }
 197    :  
 198    :    // If we have no data in this block then there's no need to load any.
 199  E :    if (data_size == 0)
 200  E :      return true;
 201    :  
 202    :    // We're in OUTPUT_NO_DATA mode, so we should always be responsible for
 203    :    // setting the block data.
 204  E :    DCHECK(need_to_set_data);
 205  E :    DCHECK_EQ(0u, block->data_size());
 206  E :    DCHECK(block->data() == NULL);
 207    :  
 208  E :    const uint8* data = pe_file->GetImageData(block_addr, data_size);
 209  E :    if (data == NULL) {
 210  i :      LOG(ERROR) << "Unable to get data from PE file for block with id "
 211    :                 << block->id() << ".";
 212  i :      return false;
 213    :    }
 214    :  
 215  E :    block->SetData(data, data_size);
 216    :  
 217  E :    return true;
 218  E :  }
 219    :  
 220    :  bool LoadBlockGraphAndImageLayout(
 221    :      const PEFile& pe_file,
 222    :      PEFile* pe_file_ptr,
 223    :      block_graph::BlockGraphSerializer::Attributes* attributes,
 224    :      ImageLayout* image_layout,
 225  E :      core::InArchive* in_archive) {
 226  E :    DCHECK(pe_file_ptr == NULL || pe_file_ptr == &pe_file);
 227  E :    DCHECK(image_layout != NULL);
 228  E :    DCHECK(in_archive != NULL);
 229    :  
 230  E :    BlockGraph* block_graph = image_layout->blocks.graph();
 231    :  
 232    :    // Load and check the stream version. This is where we could dispatch to
 233    :    // different handlers for old versions of the stream if we wish to maintain
 234    :    // backwards compatibility.
 235  E :    uint32 stream_version = 0;
 236  E :    if (!in_archive->Load(&stream_version)) {
 237  i :      LOG(ERROR) << "Unable to load serialized stream version.";
 238  i :      return false;
 239    :    }
 240  E :    if (stream_version != kSerializedBlockGraphAndImageLayoutVersion) {
 241  E :      LOG(ERROR) << "Invalid stream version " << stream_version << ", expected "
 242    :                 << kSerializedBlockGraphAndImageLayoutVersion << ".";
 243  E :      return false;
 244    :    }
 245    :  
 246    :    // Load the metadata.
 247  E :    Metadata metadata;
 248  E :    if (!in_archive->Load(&metadata)) {
 249  i :      LOG(ERROR) << "Unable to load metadata.";
 250  i :      return false;
 251    :    }
 252    :  
 253  E :    if (pe_file_ptr != NULL) {
 254    :      // If we've been given a modifiable PE-file, then we can be more intelligent
 255    :      // about our search. This call logs verbosely on failure so we don't have
 256    :      // to.
 257  E :      if (!FindPEFile(metadata, pe_file_ptr))
 258  i :        return false;
 259  E :    } else {
 260  E :      if (!MetadataMatchesPEFile(metadata, pe_file)) {
 261  i :        LOG(ERROR) << "Provided PE file does not match signature in serialized "
 262    :                   << "stream.";
 263  i :        return false;
 264    :      }
 265    :    }
 266    :  
 267    :    // Set up the serializer.
 268  E :    BlockGraphSerializer bgs;
 269    :    bgs.set_load_block_data_callback(
 270    :        base::Bind(&LoadBlockData,
 271    :                   base::Unretained(&pe_file),
 272  E :                   base::Unretained(image_layout)));
 273    :  
 274    :    // Now deserialize the block-graph. This will simultaneously deserialize the
 275    :    // image-layout address-space.
 276  E :    if (!bgs.Load(block_graph, in_archive)) {
 277  i :      LOG(ERROR) << "Unable to load block-graph.";
 278  i :      return false;
 279    :    }
 280    :  
 281    :    // Return the attributes if asked to.
 282  E :    if (attributes != NULL)
 283  E :      *attributes = bgs.attributes();
 284    :  
 285    :    // We can now recreate the rest of the image-layout from the block-graph.
 286    :    // Start by retrieving the DOS header block, which is always at the start of
 287    :    // the image.
 288    :    BlockGraph::Block* dos_header_block =
 289  E :        image_layout->blocks.GetBlockByAddress(core::RelativeAddress());
 290  E :    if (dos_header_block == NULL) {
 291  i :      LOG(ERROR) << "Unable to find DOS header in image-layout address-space.";
 292  i :      return false;
 293    :    }
 294    :  
 295    :    // Cast this as an IMAGE_DOS_HEADER.
 296  E :    block_graph::ConstTypedBlock<IMAGE_DOS_HEADER> dos_header;
 297  E :    if (!dos_header.Init(0, dos_header_block)) {
 298  i :      LOG(ERROR) << "Unable to cast DOS header block to IMAGE_DOS_HEADER.";
 299  i :      return false;
 300    :    }
 301    :  
 302    :    // Get the NT headers.
 303  E :    block_graph::ConstTypedBlock<IMAGE_NT_HEADERS> nt_headers;
 304  E :    if (!dos_header.Dereference(dos_header->e_lfanew, &nt_headers)) {
 305  i :      LOG(ERROR) << "Unable to dereference NT headers from DOS header.";
 306  i :      return false;
 307    :    }
 308    :  
 309    :    // Finally, use these headers to populate the section info vector of the
 310    :    // image-layout.
 311  E :    if (!CopyHeaderToImageLayout(nt_headers.block(), image_layout)) {
 312  i :      LOG(ERROR) << "Unable to copy NT headers to image-layout.";
 313  i :      return false;
 314    :    }
 315    :  
 316  E :    return true;
 317  E :  }
 318    :  
 319    :  }  // namespace
 320    :  
 321    :  bool SaveBlockGraphAndImageLayout(
 322    :      const PEFile& pe_file,
 323    :      block_graph::BlockGraphSerializer::Attributes attributes,
 324    :      const ImageLayout& image_layout,
 325  E :      core::OutArchive* out_archive) {
 326  E :    DCHECK(out_archive != NULL);
 327    :  
 328  E :    const BlockGraph& block_graph = *image_layout.blocks.graph();
 329    :  
 330  E :    if (!out_archive->Save(kSerializedBlockGraphAndImageLayoutVersion)) {
 331  i :      LOG(ERROR) << "Unable to save serialized stream version.";
 332  i :      return false;
 333    :    }
 334    :  
 335    :    // Get the metadata for this module and the toolchain. This will
 336    :    // allow us to validate input files in other pieces of the toolchain.
 337  E :    Metadata metadata;
 338  E :    PEFile::Signature pe_file_signature;
 339  E :    pe_file.GetSignature(&pe_file_signature);
 340  E :    if (!metadata.Init(pe_file_signature)) {
 341  i :      LOG(ERROR) << "Unable to initialize metadata for PE file \""
 342    :                 << pe_file.path().value() << "\".";
 343  i :      return false;
 344    :    }
 345    :  
 346    :    // Save the metadata.
 347  E :    if (!out_archive->Save(metadata)) {
 348  i :      LOG(ERROR) << "Unable to save metadata for PE file \""
 349    :                 << pe_file.path().value() << "\".";
 350  i :      return false;
 351    :    }
 352    :  
 353    :    // Initialize the serializer. We don't save any of the data because it can all
 354    :    // be retrieved from the PE file.
 355  E :    BlockGraphSerializer bgs;
 356  E :    bgs.set_data_mode(BlockGraphSerializer::OUTPUT_NO_DATA);
 357  E :    bgs.set_attributes(attributes);
 358    :    bgs.set_save_block_data_callback(base::Bind(
 359    :        &SaveBlockData,
 360  E :        base::Unretained(&image_layout)));
 361    :  
 362    :    // Write the block-graph. This also simultaneously serializes the
 363    :    // address-space portion of the image-layout.
 364  E :    if (!bgs.Save(block_graph, out_archive)) {
 365  i :      LOG(ERROR) << "Unable to save block-graph.";
 366  i :      return false;
 367    :    }
 368    :  
 369  E :    return true;
 370  E :  }
 371    :  
 372    :  bool LoadBlockGraphAndImageLayout(
 373    :      const PEFile& pe_file,
 374    :      block_graph::BlockGraphSerializer::Attributes* attributes,
 375    :      ImageLayout* image_layout,
 376  E :      core::InArchive* in_archive) {
 377    :    if (!LoadBlockGraphAndImageLayout(pe_file, NULL, attributes,
 378  E :                                      image_layout, in_archive)) {
 379  i :      return false;
 380    :    }
 381    :  
 382  E :    return true;
 383  E :  }
 384    :  
 385    :  bool LoadBlockGraphAndImageLayout(
 386    :      PEFile* pe_file,
 387    :      block_graph::BlockGraphSerializer::Attributes* attributes,
 388    :      ImageLayout* image_layout,
 389  E :      core::InArchive* in_archive) {
 390  E :    DCHECK(pe_file != NULL);
 391  E :    DCHECK(image_layout != NULL);
 392  E :    DCHECK(in_archive != NULL);
 393    :  
 394    :    if (!LoadBlockGraphAndImageLayout(*pe_file, pe_file, attributes,
 395  E :                                      image_layout, in_archive)) {
 396  E :      return false;
 397    :    }
 398    :  
 399  E :    return true;
 400  E :  }
 401    :  
 402    :  }  // namespace pe

Coverage information generated Thu Sep 06 11:30:46 2012.