Coverage for /Syzygy/zap_timestamp/zap_timestamp.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
77.5%3374350.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    :  // ZapTimestamps uses PEFile/ImageLayout/BlockGraph to represent a PE file in
  16    :  // memory, and TypedBlock to navigate through the PE structures of the file.
  17    :  // We don't use a full decomposition of the image, but rather only decompose
  18    :  // the PE headers and structures. As such, ZapTimetamps can be seen as a
  19    :  // lightweight decomposer. It would be better do this directly using the
  20    :  // internal intermediate representation formats of PEFileParser, but this
  21    :  // functionality would require some refactoring.
  22    :  //
  23    :  // Changes that are required to be made to the PE file are represented by an
  24    :  // address space, mapping replacement data to file offsets. This address-space
  25    :  // can then be simply 'stamped' on to the PE file to be modified.
  26    :  //
  27    :  // The matching PDB file is completely rewritten to guarantee that it is
  28    :  // canonical (as long as the underlying PdbWriter doesn't change). We load all
  29    :  // of the streams into memory, reach in and make local modifications, and
  30    :  // rewrite the entire file to disk.
  31    :  
  32    :  #include "syzygy/zap_timestamp/zap_timestamp.h"
  33    :  
  34    :  #include "base/at_exit.h"
  35    :  #include "base/bind.h"
  36    :  #include "base/command_line.h"
  37    :  #include "base/file_util.h"
  38    :  #include "base/logging.h"
  39    :  #include "base/md5.h"
  40    :  #include "base/stringprintf.h"
  41    :  #include "base/files/scoped_temp_dir.h"
  42    :  #include "syzygy/block_graph/typed_block.h"
  43    :  #include "syzygy/pdb/pdb_byte_stream.h"
  44    :  #include "syzygy/pdb/pdb_constants.h"
  45    :  #include "syzygy/pdb/pdb_reader.h"
  46    :  #include "syzygy/pdb/pdb_util.h"
  47    :  #include "syzygy/pdb/pdb_writer.h"
  48    :  #include "syzygy/pe/find.h"
  49    :  #include "syzygy/pe/pdb_info.h"
  50    :  #include "syzygy/pe/pe_data.h"
  51    :  #include "syzygy/pe/pe_file_parser.h"
  52    :  #include "syzygy/pe/pe_file_writer.h"
  53    :  
  54    :  namespace zap_timestamp {
  55    :  
  56    :  namespace {
  57    :  
  58    :  using block_graph::BlockGraph;
  59    :  using block_graph::ConstTypedBlock;
  60    :  using core::FileOffsetAddress;
  61    :  using core::RelativeAddress;
  62    :  using pdb::PdbByteStream;
  63    :  using pdb::PdbFile;
  64    :  using pdb::PdbReader;
  65    :  using pdb::PdbStream;
  66    :  using pdb::PdbWriter;
  67    :  using pdb::WritablePdbStream;
  68    :  using pe::ImageLayout;
  69    :  using pe::PEFile;
  70    :  using pe::PEFileParser;
  71    :  
  72    :  typedef ConstTypedBlock<IMAGE_DEBUG_DIRECTORY> ImageDebugDirectory;
  73    :  typedef ConstTypedBlock<IMAGE_DOS_HEADER> DosHeader;
  74    :  typedef ConstTypedBlock<IMAGE_NT_HEADERS> NtHeaders;
  75    :  typedef ConstTypedBlock<pe::CvInfoPdb70> CvInfoPdb;
  76    :  typedef ZapTimestamp::PatchAddressSpace PatchAddressSpace;
  77    :  typedef ZapTimestamp::PatchData PatchData;
  78    :  
  79    :  const char kUsageFormatStr[] =
  80    :      "Usage: %ls <PE files>\n"
  81    :      "\n"
  82    :      "  A tool that normalizes the GUID and timestamps associated with a given\n"
  83    :      "  PE/PDB file pair. The PDB files matching each given PE file will be\n"
  84    :      "  tracked down automatically.\n";
  85    :  
  86    :  void PrintUsage(FILE* out,
  87    :                  const base::FilePath& program,
  88    :                  const base::StringPiece& message) {
  89    :    if (!message.empty()) {
  90    :      ::fwrite(message.data(), 1, message.length(), out);
  91    :      ::fprintf(out, "\n\n");
  92    :    }
  93    :  
  94    :    ::fprintf(out, kUsageFormatStr, program.BaseName().value().c_str());
  95    :  }
  96    :  
  97    :  // An intermediate reference type used to track references generated by
  98    :  // PEFileParser.
  99    :  struct IntermediateReference {
 100    :    BlockGraph::ReferenceType type;
 101    :    BlockGraph::Size size;
 102    :    RelativeAddress address;
 103    :  };
 104    :  
 105    :  // A map of intermediate references. This tracks references created by the
 106    :  // PEFileParser.
 107    :  typedef std::map<RelativeAddress, IntermediateReference>
 108    :      IntermediateReferenceMap;
 109    :  
 110    :  // Adds a reference to the given intermediate reference map. Used as a callback
 111    :  // to PEFileParser.
 112    :  bool AddReference(IntermediateReferenceMap* references,
 113    :                    RelativeAddress source,
 114    :                    BlockGraph::ReferenceType type,
 115    :                    BlockGraph::Size size,
 116  E :                    RelativeAddress destination) {
 117  E :    DCHECK(references != NULL);
 118    :  
 119  E :    IntermediateReference ref = { type, size, destination };
 120  E :    return references->insert(std::make_pair(source, ref)).second;
 121  E :  }
 122    :  
 123    :  // Returns the block and offset into the block associated with the given
 124    :  // address and size. Sets block pointer to NULL and offset to 0 if no block
 125    :  // was found for the address and size. Returns true if a block is found, false
 126    :  // otherwise.
 127    :  bool LookupBlockOffset(const ImageLayout& image_layout,
 128    :                         RelativeAddress address,
 129    :                         size_t size,
 130    :                         BlockGraph::Block** block,
 131  E :                         BlockGraph::Offset* offset) {
 132  E :    DCHECK(block != NULL);
 133  E :    DCHECK(offset != NULL);
 134    :  
 135  E :    *block = image_layout.blocks.GetContainingBlock(address, size);
 136  E :    if (*block == NULL) {
 137  E :      *offset = 0;
 138  E :      return false;
 139    :    }
 140    :  
 141  E :    *offset = address - (*block)->addr();
 142  E :    return true;
 143  E :  }
 144    :  
 145    :  // Performs a decomposition of the given PE file, only parsing out the PE
 146    :  // data blocks and references between them.
 147    :  bool MiniDecompose(const PEFile& pe_file,
 148    :                     ImageLayout* image_layout,
 149  E :                     BlockGraph::Block** dos_header_block) {
 150  E :    DCHECK(image_layout != NULL);
 151  E :    DCHECK(dos_header_block != NULL);
 152    :  
 153  E :    IntermediateReferenceMap references;
 154    :  
 155    :    PEFileParser::AddReferenceCallback add_reference = base::Bind(
 156  E :        &AddReference, base::Unretained(&references));
 157  E :    PEFileParser pe_file_parser(pe_file, &image_layout->blocks, add_reference);
 158    :  
 159  E :    PEFileParser::PEHeader pe_header;
 160  E :    if (!pe_file_parser.ParseImage(&pe_header)) {
 161  i :      LOG(ERROR) << "Failed to parse PE file: " << pe_file.path().value();
 162  i :      return false;
 163    :    }
 164    :  
 165  E :    if (!pe::CopyHeaderToImageLayout(pe_header.nt_headers, image_layout)) {
 166  i :      LOG(ERROR) << "Failed to copy NT headers to image layout.";
 167  i :      return false;
 168    :    }
 169    :  
 170    :    // Finalize the intermediate references. We only finalize those that are
 171    :    // within the closed set of blocks.
 172  E :    IntermediateReferenceMap::const_iterator ref_it = references.begin();
 173  E :    for (; ref_it != references.end(); ++ref_it) {
 174  E :      BlockGraph::Block* src_block = NULL;
 175  E :      BlockGraph::Offset src_offset = 0;
 176    :      if (!LookupBlockOffset(*image_layout, ref_it->first, ref_it->second.size,
 177  E :                             &src_block, &src_offset)) {
 178  E :        continue;
 179    :      }
 180    :  
 181  E :      BlockGraph::Block* dst_block = NULL;
 182  E :      BlockGraph::Offset dst_offset = 0;
 183    :      if (!LookupBlockOffset(*image_layout, ref_it->second.address, 1,
 184  E :                             &dst_block, &dst_offset)) {
 185  E :        continue;
 186    :      }
 187    :  
 188    :      // Make the final reference.
 189    :      BlockGraph::Reference ref(ref_it->second.type, ref_it->second.size,
 190  E :                                dst_block, dst_offset, dst_offset);
 191  E :      CHECK(src_block->SetReference(src_offset, ref));
 192  E :    }
 193    :  
 194  E :    *dos_header_block = pe_header.dos_header;
 195    :  
 196  E :    return true;
 197  E :  }
 198    :  
 199    :  // Marks the range of data at @p rel_addr and of size @p size as needing to be
 200    :  // changed. It will be replaced with the data in @p data, and marked with the
 201    :  // description @p name (for debugging purposes). The change is recorded in the
 202    :  // provided PatchAddressSpace @p file_addr_space in terms of file offsets.
 203    :  // This performs the necessary address space translations via @p pe_file and
 204    :  // ensures that the change does not conflict with any other required changes.
 205    :  bool MarkData(const PEFile& pe_file,
 206    :                RelativeAddress rel_addr,
 207    :                size_t size,
 208    :                const uint8* data,
 209    :                const base::StringPiece& name,
 210  E :                PatchAddressSpace* file_addr_space) {
 211  E :    DCHECK(file_addr_space);
 212    :  
 213  E :    FileOffsetAddress file_addr;
 214  E :    if (!pe_file.Translate(rel_addr, &file_addr)) {
 215  i :      LOG(ERROR) << "Failed to translate " << rel_addr << " to file offset.";
 216  i :      return false;
 217    :    }
 218    :  
 219    :    if (!file_addr_space->Insert(PatchAddressSpace::Range(file_addr, size),
 220  E :                                 PatchData(data, name))) {
 221  i :      LOG(ERROR) << "Failed to insert file range at " << file_addr
 222    :                 << " of length " << size << ".";
 223  i :      return false;
 224    :    }
 225    :  
 226  E :    return true;
 227  E :  }
 228    :  
 229    :  // Given a data directory of type T containing a member variable named
 230    :  // TimeDateStamp, this will mark the timestamp for changing to the value
 231    :  // provided in @p timestamp_data. The change will be recorded in the provided
 232    :  // PatchAddressSpace @p file_addr_space.
 233    :  template<typename T>
 234    :  bool MarkDataDirectoryTimestamps(const PEFile& pe_file,
 235    :                                   NtHeaders& nt_headers,
 236    :                                   size_t data_dir_index,
 237    :                                   const char* data_dir_name,
 238    :                                   const uint8* timestamp_data,
 239  E :                                   PatchAddressSpace* file_addr_space) {
 240  E :    DCHECK_GT(arraysize(nt_headers->OptionalHeader.DataDirectory),
 241    :              data_dir_index);
 242  E :    DCHECK(timestamp_data != NULL);
 243  E :    DCHECK(file_addr_space != NULL);
 244    :  
 245    :    // It is not an error if the debug directory doesn't exist.
 246    :    const IMAGE_DATA_DIRECTORY& data_dir_info =
 247  E :        nt_headers->OptionalHeader.DataDirectory[data_dir_index];
 248  E :    if (!nt_headers.HasReference(data_dir_info.VirtualAddress)) {
 249  i :      DCHECK_EQ(0u, data_dir_info.VirtualAddress);
 250  i :      LOG(INFO) << "PE file contains no data directory " << data_dir_index << ".";
 251  i :      return true;
 252    :    }
 253    :  
 254  E :    ConstTypedBlock<T> data_dir;
 255  E :    if (!nt_headers.Dereference(data_dir_info.VirtualAddress, &data_dir)) {
 256  i :      LOG(ERROR) << "Failed to dereference data directory " << data_dir_index
 257    :                 << ".";
 258  i :      return false;
 259    :    }
 260    :  
 261  E :    FileOffsetAddress data_dir_addr;
 262  E :    if (!pe_file.Translate(data_dir.block()->addr(), &data_dir_addr)) {
 263  i :      LOG(ERROR) << "Failed to locate data directory " << data_dir_index << ".";
 264  i :      return false;
 265    :    }
 266    :  
 267  E :    if (data_dir->TimeDateStamp == 0)
 268  E :      return true;
 269    :  
 270    :    FileOffsetAddress timestamp_addr = data_dir_addr +
 271  E :        data_dir.OffsetOf(data_dir->TimeDateStamp);
 272    :  
 273  E :    std::string name = base::StringPrintf("%s Timestamp", data_dir_name);
 274    :    if (!file_addr_space->Insert(PatchAddressSpace::Range(timestamp_addr,
 275    :                                                          sizeof(DWORD)),
 276  E :                                 PatchData(timestamp_data, name))) {
 277  i :      LOG(ERROR) << "Failed to mark timestamp of data directory "
 278    :                 << data_dir_index << ".";
 279  i :      return false;
 280    :    }
 281    :  
 282  E :    return true;
 283  E :  }
 284    :  
 285  E :  bool Md5Consume(size_t bytes, FILE* file, base::MD5Context* context) {
 286  E :    char buffer[4096] = { 0 };
 287    :  
 288  E :    size_t cur = 0;
 289  E :    while (cur < bytes) {
 290  E :      size_t bytes_to_read = std::min(bytes - cur, sizeof(buffer));
 291  E :      size_t bytes_read = ::fread(buffer, 1, bytes_to_read, file);
 292  E :      if (bytes_read != bytes_to_read) {
 293  i :        LOG(ERROR) << "Error reading from file (got " << bytes_read
 294    :                   << ", expected " << bytes_to_read << ").";
 295  i :        return false;
 296    :      }
 297    :  
 298  E :      base::MD5Update(context, base::StringPiece(buffer, bytes_read));
 299  E :      cur += bytes_read;
 300  E :    }
 301  E :    DCHECK_EQ(cur, bytes);
 302    :  
 303  E :    return true;
 304  E :  }
 305    :  
 306    :  bool UpdateFileInPlace(const base::FilePath& path,
 307  E :                         const PatchAddressSpace& updates) {
 308  E :    LOG(INFO) << "Patching file: " << path.value();
 309    :  
 310  E :    file_util::ScopedFILE file(file_util::OpenFile(path, "rb+"));
 311  E :    if (file.get() == NULL) {
 312  i :      LOG(ERROR) << "Unable to open file for updating: " << path.value();
 313  i :      return false;
 314    :    }
 315    :  
 316  E :    PatchAddressSpace::const_iterator it = updates.begin();
 317  E :    for (; it != updates.end(); ++it) {
 318    :      // No data? Then nothing to update. This happens for the PE checksum, which
 319    :      // has a NULL data pointer. We update it later on in another pass.
 320  E :      if (it->second.data == NULL)
 321  E :        continue;
 322    :  
 323  E :      LOG(INFO) << "  Patching " << it->second.name << ", " << it->first.size()
 324    :                << " bytes at " << it->first.start();
 325    :  
 326    :      // Seek to the position to be updated.
 327  E :      if (::fseek(file.get(), it->first.start().value(), SEEK_SET) != 0) {
 328  i :        LOG(ERROR) << "Failed to seek to " << it->first.start() << " of file: "
 329    :                   << path.value();
 330  i :        return false;
 331    :      }
 332    :  
 333    :      // Write the updated data.
 334    :      size_t bytes_written = ::fwrite(it->second.data, 1, it->first.size(),
 335  E :                                      file.get());
 336  E :      if (bytes_written != it->first.size()) {
 337  i :        LOG(ERROR) << "Failed to write " << it->first.size() << " bytes to "
 338    :                   << "position " << it->first.start() << " of file: "
 339    :                   << path.value();
 340    :      }
 341  E :    }
 342    :  
 343  E :    LOG(INFO) << "Finished patching file: " << path.value();
 344  E :    file.reset();
 345    :  
 346  E :    return true;
 347  E :  }
 348    :  
 349    :  // Ensures that the stream with the given ID is writable, returning a scoped
 350    :  // pointer to it.
 351    :  scoped_refptr<PdbStream> GetWritablePdbStream(size_t index,
 352  E :                                                PdbFile* pdb_file) {
 353  E :    DCHECK(pdb_file != NULL);
 354  E :    DCHECK_GT(pdb_file->StreamCount(), index);
 355    :  
 356  E :    scoped_refptr<PdbStream> reader = pdb_file->GetStream(index);
 357    :  
 358    :    // Try and get the writer. If it's not available, then replace the stream
 359    :    // with a byte stream, which is in-place writable.
 360  E :    scoped_refptr<WritablePdbStream> writer = reader->GetWritablePdbStream();
 361  E :    if (writer.get() == NULL) {
 362  E :      scoped_refptr<PdbByteStream> byte_stream(new PdbByteStream());
 363  E :      byte_stream->Init(reader);
 364  E :      pdb_file->ReplaceStream(index, byte_stream);
 365  E :      reader = byte_stream;
 366  E :    }
 367    :  
 368  E :    return reader;
 369  E :  }
 370    :  
 371  E :  void OutputSummaryStats(base::FilePath& path) {
 372  E :    file_util::ScopedFILE file(file_util::OpenFile(path, "rb"));
 373  E :    if (file.get() == NULL) {
 374  i :      LOG(ERROR) << "Unable to open file for reading: " << path.value();
 375  i :      return;
 376    :    }
 377  E :    ::fseek(file.get(), 0, SEEK_END);
 378  E :    size_t file_size = ::ftell(file.get());
 379  E :    ::fseek(file.get(), 0, SEEK_SET);
 380    :  
 381    :    base::MD5Context md5_context;
 382  E :    base::MD5Init(&md5_context);
 383  E :    if (!Md5Consume(file_size, file.get(), &md5_context))
 384  i :      return;
 385    :  
 386    :    base::MD5Digest md5_digest;
 387  E :    base::MD5Final(&md5_digest, &md5_context);
 388  E :    std::string md5_string = base::MD5DigestToBase16(md5_digest);
 389    :  
 390  E :    LOG(INFO) << "Path: " << path.value();
 391  E :    LOG(INFO) << "  Size  : " << file_size;
 392  E :    LOG(INFO) << "  Digest: " << md5_string;
 393  E :  }
 394    :  
 395    :  bool NormalizeDbiStream(DWORD pdb_age_data,
 396  E :                          PdbByteStream* dbi_stream) {
 397  E :    DCHECK(dbi_stream != NULL);
 398    :  
 399  E :    LOG(INFO) << "Updating PDB DBI stream.";
 400    :  
 401  E :    uint8* dbi_data = dbi_stream->data();
 402  E :    if (dbi_stream->length() < sizeof(pdb::DbiHeader)) {
 403  i :      LOG(ERROR) << "DBI stream too short.";
 404  i :      return false;
 405    :    }
 406  E :    pdb::DbiHeader* dbi_header = reinterpret_cast<pdb::DbiHeader*>(dbi_data);
 407    :  
 408    :    // Update the age in the DbiHeader as well. This needs to match pdb_age
 409    :    // in the PDB header.
 410  E :    dbi_header->age = pdb_age_data;
 411  E :    dbi_data += sizeof(*dbi_header);
 412    :  
 413    :    // Ensure that the module information is addressable.
 414  E :    if (dbi_stream->length() < dbi_header->gp_modi_size) {
 415  i :      LOG(ERROR) << "Invalid DBI header gp_modi_size.";
 416  i :      return false;
 417    :    }
 418    :  
 419    :    // Run over the module information.
 420    :    // TODO(chrisha): Use BufferWriter to do this. We need to update it to handle
 421    :    //     type casts and bounds checking.
 422  E :    uint8* module_info_end = dbi_data + dbi_header->gp_modi_size;
 423  E :    while (dbi_data < module_info_end) {
 424    :      pdb::DbiModuleInfoBase* module_info =
 425  E :          reinterpret_cast<pdb::DbiModuleInfoBase*>(dbi_data);
 426  E :      module_info->offsets = 0;
 427  E :      dbi_data += sizeof(*module_info);
 428    :  
 429    :      // Skip two NULL terminated strings after the module info.
 430  E :      while (*dbi_data != 0) ++dbi_data;
 431  E :      ++dbi_data;
 432  E :      while (*dbi_data != 0) ++dbi_data;
 433  E :      ++dbi_data;
 434    :  
 435    :      // Skip until we're at a multiple of 4 position.
 436  E :      size_t offset = dbi_data - dbi_stream->data();
 437  E :      offset = ((offset + 3) / 4) * 4;
 438  E :      dbi_data = dbi_stream->data() + offset;
 439  E :    }
 440    :  
 441    :    // Ensure that the section contributions are addressable.
 442    :    size_t section_contrib_end_pos = dbi_header->gp_modi_size + sizeof(uint32) +
 443  E :        dbi_header->section_contribution_size;
 444  E :    if (dbi_stream->length() < section_contrib_end_pos) {
 445  i :      LOG(ERROR) << "Invalid DBI header gp_modi_size.";
 446  i :      return false;
 447    :    }
 448    :  
 449    :    // Run over the section contributions.
 450  E :    dbi_data += sizeof(uint32);  // Skip the signature.
 451  E :    uint8* section_contrib_end = dbi_data + dbi_header->section_contribution_size;
 452  E :    while (dbi_data < section_contrib_end) {
 453    :      pdb::DbiSectionContrib* section_contrib =
 454  E :          reinterpret_cast<pdb::DbiSectionContrib*>(dbi_data);
 455  E :      section_contrib->pad1 = 0;
 456  E :      section_contrib->pad2 = 0;
 457  E :      dbi_data += sizeof(*section_contrib);
 458  E :    }
 459    :  
 460  E :    return true;
 461  E :  }
 462    :  
 463  E :  bool NormalizeSymbolRecordStream(PdbByteStream* stream) {
 464  E :    DCHECK(stream != NULL);
 465    :  
 466  E :    uint8* data = stream->data();
 467  E :    uint8* data_end = data + stream->length();
 468    :  
 469  E :    while (data < data_end) {
 470    :      // Get the size of the symbol record and skip past it.
 471  E :      uint16* size = reinterpret_cast<uint16*>(data);
 472  E :      data += sizeof(*size);
 473    :  
 474    :      // The size of the symbol record, plus its uint16 length, must be a multiple
 475    :      // of 4. Each symbol record consists of the length followed by a symbol
 476    :      // type (also a short), so the size needs to be at least of length 2.
 477    :      // See http://code.google.com/p/sawbuck/wiki/PdbFileFormat for a discussion
 478    :      // of the format of this stream.
 479  E :      DCHECK_LE(2u, *size);
 480  E :      DCHECK_EQ(0u, ((*size + sizeof(*size)) % 4));
 481    :  
 482    :      // Up to the last 3 bytes are padding, as the record gets rounded up to
 483    :      // a multiple of 4 in size.
 484    :      static const size_t kMaxPadding = 3;
 485  E :      uint8* end = data + *size;
 486  E :      uint8* tail = end - kMaxPadding;
 487    :  
 488    :      // Skip past the symbol record.
 489  E :      data = end;
 490    :  
 491    :      // Find the null terminator for the record.
 492  E :      for (; tail + 1 < end && *tail != 0; ++tail) {
 493    :        // Intentionally empty.
 494  E :      }
 495    :  
 496    :      // Pad out the rest of the record with nulls (these are usually full of
 497    :      // junk bytes).
 498  E :      for (; tail < end; ++tail)
 499  E :        *tail = 0;
 500  E :    }
 501    :  
 502  E :    return true;
 503  E :  }
 504    :  
 505    :  }  // namespace
 506    :  
 507    :  ZapTimestamp::ZapTimestamp()
 508    :      : image_layout_(&block_graph_),
 509  E :        dos_header_block_(NULL) {
 510    :    // The timestamp can't just be set to zero as that represents a special
 511    :    // value in the PE file. We set it to some arbitrary fixed date in the past.
 512    :    // This is Y2K: Jan 1, 2000, 0:00:00 GMT.
 513  E :    timestamp_data_ = 946684800;
 514    :  
 515    :    // Initialize the age to 1.
 516  E :    pdb_age_data_ = 1;
 517  E :  }
 518    :  
 519  E :  bool ZapTimestamp::Init(const base::FilePath& pe_path) {
 520  E :    if (!Init(pe_path, base::FilePath()))
 521  E :      return false;
 522    :  
 523  E :    return true;
 524  E :  }
 525    :  
 526    :  bool ZapTimestamp::Init(const base::FilePath& pe_path,
 527  E :                          const base::FilePath& pdb_path) {
 528  E :    pe_path_ = pe_path;
 529  E :    pdb_path_ = pdb_path;
 530    :  
 531  E :    if (!ValidatePeAndPdbFiles())
 532  E :      return false;
 533    :  
 534  E :    if (!DecomposePeFile())
 535  i :      return false;
 536    :  
 537  E :    if (!MarkPeFileRanges())
 538  i :      return false;
 539    :  
 540  E :    if (!CalculatePdbGuid())
 541  i :      return false;
 542    :  
 543  E :    if (!LoadAndUpdatePdbFile())
 544  i :      return false;
 545    :  
 546  E :    return true;
 547  E :  }
 548    :  
 549  E :  bool ZapTimestamp::Zap(bool modify_pe, bool modify_pdb) {
 550  E :    if (modify_pe) {
 551  E :      if (!WritePeFile())
 552  i :        return false;
 553  E :      OutputSummaryStats(pe_path_);
 554    :    }
 555    :  
 556  E :    if (modify_pdb) {
 557  E :      if (!WritePdbFile())
 558  i :        return false;
 559  E :      OutputSummaryStats(pdb_path_);
 560    :    }
 561    :  
 562  E :    return true;
 563  E :  }
 564    :  
 565  E :  bool ZapTimestamp::ValidatePeAndPdbFiles() {
 566  E :    LOG(INFO) << "Analyzing PE file: " << pe_path_.value();
 567    :  
 568    :    if (!file_util::PathExists(pe_path_) ||
 569  E :        file_util::DirectoryExists(pe_path_)) {
 570  E :      LOG(ERROR) << "PE file not found: " << pe_path_.value();
 571  E :      return false;
 572    :    }
 573    :  
 574  E :    if (!pe_file_.Init(pe_path_)) {
 575  i :      LOG(ERROR) << "Failed to read PE file: " << pe_path_.value();
 576  i :      return false;
 577    :    }
 578    :  
 579  E :    if (pdb_path_.empty()) {
 580    :      // Find the matching PDB file.
 581  E :      if (!pe::FindPdbForModule(pe_path_, &pdb_path_)) {
 582  i :        LOG(ERROR) << "Error while searching for PDB file.";
 583  i :        return false;
 584    :      }
 585  E :      if (pdb_path_.empty()) {
 586  E :        LOG(ERROR) << "PDB file not found for PE file: " << pe_path_.value();
 587  E :        return false;
 588    :      }
 589  E :      DCHECK(file_util::PathExists(pdb_path_));
 590  E :    } else {
 591    :      if (!file_util::PathExists(pdb_path_) ||
 592  E :          file_util::DirectoryExists(pdb_path_)) {
 593  i :        LOG(ERROR) << "PDB file not found: " << pdb_path_.value();
 594    :      }
 595    :    }
 596    :  
 597    :    // Ensure that the PDB and the PE file are consistent with each other.
 598  E :    if (!pe::PeAndPdbAreMatched(pe_path_, pdb_path_))
 599  i :      return false;  // This logs verbosely.
 600    :  
 601  E :    LOG(INFO) << "Found matching PDB file: " << pdb_path_.value();
 602    :  
 603  E :    return true;
 604  E :  }
 605    :  
 606  E :  bool ZapTimestamp::DecomposePeFile() {
 607    :    // Decompose the image. This is a very high level decomposition only
 608    :    // chunking out the PE structures and references from/to PE blocks.
 609  E :    BlockGraph::Block* dos_header_block = NULL;
 610  E :    if (!MiniDecompose(pe_file_, &image_layout_, &dos_header_block_))
 611  i :      return false;
 612    :  
 613  E :    return true;
 614  E :  }
 615    :  
 616  E :  bool ZapTimestamp::MarkPeFileRanges() {
 617  E :    DCHECK(dos_header_block_ != NULL);
 618  E :    LOG(INFO) << "Finding PE fields that need updating.";
 619    :  
 620  E :    DosHeader dos_header;
 621  E :    if (!dos_header.Init(0, dos_header_block_)) {
 622  i :      LOG(ERROR) << "Failed to cast IMAGE_DOS_HEADER.";
 623  i :      return false;
 624    :    }
 625    :  
 626  E :    NtHeaders nt_headers;
 627  E :    if (!dos_header.Dereference(dos_header->e_lfanew, &nt_headers)) {
 628  i :      LOG(ERROR) << "Failed to dereference IMAGE_NT_HEADERS.";
 629  i :      return false;
 630    :    }
 631    :  
 632    :    // Mark the export data directory timestamp.
 633    :    if (!MarkDataDirectoryTimestamps<IMAGE_EXPORT_DIRECTORY>(
 634    :            pe_file_, nt_headers, IMAGE_DIRECTORY_ENTRY_EXPORT,
 635    :            "Export Directory",
 636    :            reinterpret_cast<const uint8*>(&timestamp_data_),
 637  E :            &pe_file_addr_space_)) {
 638    :      // This logs verbosely on failure.
 639  i :      return false;
 640    :    }
 641    :  
 642    :    // Mark the resource data directory timestamp.
 643    :    if (!MarkDataDirectoryTimestamps<IMAGE_RESOURCE_DIRECTORY>(
 644    :            pe_file_, nt_headers, IMAGE_DIRECTORY_ENTRY_RESOURCE,
 645    :            "Resource Directory",
 646    :            reinterpret_cast<const uint8*>(&timestamp_data_),
 647  E :            &pe_file_addr_space_)) {
 648    :      // This logs verbosely on failure.
 649  i :      return false;
 650    :    }
 651    :  
 652    :    // Find the debug directory.
 653    :    const IMAGE_DATA_DIRECTORY& debug_dir_info =
 654  E :        nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
 655  E :    if (!nt_headers.HasReference(debug_dir_info.VirtualAddress)) {
 656  i :      LOG(ERROR) << "PE file contains no debug directory.";
 657  i :      return false;
 658    :    }
 659  E :    ImageDebugDirectory debug_dir;
 660  E :    if (!nt_headers.Dereference(debug_dir_info.VirtualAddress, &debug_dir)) {
 661  i :      LOG(ERROR) << "Failed to dereference IMAGE_DEBUG_DIRECTORY.";
 662  i :      return false;
 663    :    }
 664    :  
 665  E :    RelativeAddress rel_addr;
 666    :  
 667    :    // Within that, find the codeview debug entry. We also update every other
 668    :    // debug timestamp.
 669  E :    CvInfoPdb cv_info_pdb;
 670  E :    for (size_t i = 0; i < debug_dir.ElementCount(); ++i) {
 671    :      rel_addr = debug_dir.block()->addr() +
 672  E :          debug_dir.OffsetOf(debug_dir[i].TimeDateStamp);
 673  E :      std::string name = base::StringPrintf("Debug Directory %d Timestamp", i);
 674    :      if (!MarkData(pe_file_, rel_addr, sizeof(timestamp_data_),
 675    :                    reinterpret_cast<const uint8*>(&timestamp_data_), name,
 676  E :                    &pe_file_addr_space_)) {
 677  i :        LOG(ERROR) << "Failed to mark TimeDateStamp of debug directory " << i
 678    :                   << ".";
 679  i :        return false;
 680    :      }
 681    :  
 682  E :      if (debug_dir[i].Type == IMAGE_DEBUG_TYPE_CODEVIEW) {
 683  E :        if (cv_info_pdb.block() != NULL) {
 684  i :          LOG(ERROR) << "Found multiple CodeView debug directories.";
 685  i :          return false;
 686    :        }
 687  E :        if (!debug_dir.Dereference(debug_dir[i].PointerToRawData, &cv_info_pdb)) {
 688  i :          LOG(ERROR) << "Failed to dereference CodeView debug directory.";
 689  i :          return false;
 690    :        }
 691    :      }
 692  E :    }
 693    :  
 694    :    // We should have found a code view debug directory pointing to the PDB file.
 695  E :    if (cv_info_pdb.block() == NULL) {
 696  i :      LOG(ERROR) << "Failed to find CodeView debug directory.";
 697  i :      return false;
 698    :    }
 699    :  
 700    :    // Get the file offset of the PDB age and mark it.
 701    :    rel_addr = cv_info_pdb.block()->addr() +
 702  E :        cv_info_pdb.OffsetOf(cv_info_pdb->pdb_age);
 703    :    if (!MarkData(pe_file_, rel_addr, sizeof(pdb_age_data_),
 704    :                  reinterpret_cast<const uint8*>(&pdb_age_data_),
 705  E :                  "PDB Age", &pe_file_addr_space_)) {
 706  i :      LOG(ERROR) << "Failed to mark PDB age.";
 707  i :      return false;
 708    :    }
 709    :  
 710    :    // Get the file offset of the PDB guid and mark it.
 711    :    rel_addr = cv_info_pdb.block()->addr() +
 712  E :        cv_info_pdb.OffsetOf(cv_info_pdb->signature);
 713    :    if (!MarkData(pe_file_, rel_addr, sizeof(pdb_guid_data_),
 714    :                  reinterpret_cast<const uint8*>(&pdb_guid_data_),
 715  E :                  "PDB GUID", &pe_file_addr_space_)) {
 716  i :      LOG(ERROR) << "Failed to mark PDB GUID.";
 717  i :      return false;
 718    :    }
 719    :  
 720    :    // Get the file offset of the PE checksum and mark it.
 721    :    rel_addr = nt_headers.block()->addr() +
 722  E :        nt_headers.OffsetOf(nt_headers->OptionalHeader.CheckSum);
 723    :    if (!MarkData(pe_file_, rel_addr, sizeof(DWORD), NULL,
 724  E :                  "PE Checksum", &pe_file_addr_space_)) {
 725  i :      LOG(ERROR) << "Failed to mark PE checksum.";
 726  i :      return false;
 727    :    }
 728    :  
 729    :    // Get the file offset of the PE timestamp and mark it.
 730    :    rel_addr = nt_headers.block()->addr() +
 731  E :        nt_headers.OffsetOf(nt_headers->FileHeader.TimeDateStamp);
 732    :    if (!MarkData(pe_file_, rel_addr, sizeof(timestamp_data_),
 733    :                  reinterpret_cast<uint8*>(&timestamp_data_), "PE Timestamp",
 734  E :                  &pe_file_addr_space_)) {
 735  i :      LOG(ERROR) << "Failed to mark PE timestamp.";
 736  i :      return false;
 737    :    }
 738    :  
 739  E :    return true;
 740  E :  }
 741    :  
 742  E :  bool ZapTimestamp::CalculatePdbGuid() {
 743  E :    LOG(INFO) << "Calculating PDB GUID from PE file contents.";
 744    :  
 745  E :    file_util::ScopedFILE pe_file(file_util::OpenFile(pe_path_, "rb"));
 746  E :    if (pe_file.get() == NULL) {
 747  i :      LOG(ERROR) << "Failed to open PE file for reading: " << pe_path_.value();
 748  i :      return false;
 749    :    }
 750    :  
 751    :    // Get the length of the entire file.
 752  E :    if (::fseek(pe_file.get(), 0, SEEK_END) != 0) {
 753  i :      LOG(ERROR) << "Failed to fseek to end of file.";
 754  i :      return false;
 755    :    }
 756  E :    FileOffsetAddress end(::ftell(pe_file.get()));
 757    :  
 758    :    // Seek back to the beginning.
 759  E :    if (::fseek(pe_file.get(), 0, SEEK_SET) != 0) {
 760  i :      LOG(ERROR) << "Failed to fseek to beginning of file.";
 761  i :      return false;
 762    :    }
 763    :  
 764    :    // Initialize the MD5 structure.
 765  E :    base::MD5Context md5_context = { 0 };
 766  E :    base::MD5Init(&md5_context);
 767    :  
 768    :    // We seek through the bits of the file that will be changed, and skip those.
 769    :    // The rest of the file (the static parts) are fed through an MD5 hash and
 770    :    // used to generated a unique and stable GUID.
 771  E :    FileOffsetAddress cur(0);
 772  E :    PatchAddressSpace::const_iterator range_it = pe_file_addr_space_.begin();
 773  E :    for (; range_it != pe_file_addr_space_.end(); ++range_it) {
 774    :      // Consume any data before this range.
 775  E :      size_t bytes_to_hash = 0;
 776  E :      if (cur < range_it->first.start()) {
 777  E :        bytes_to_hash = range_it->first.start() - cur;
 778  E :        if (!Md5Consume(bytes_to_hash, pe_file.get(), &md5_context))
 779  i :          return false;  // This logs verbosely for us.
 780    :      }
 781    :  
 782  E :      if (::fseek(pe_file.get(), range_it->first.size(), SEEK_CUR)) {
 783  i :        LOG(ERROR) << "Failed to fseek past marked range.";
 784    :      }
 785    :  
 786  E :      cur = range_it->first.end();
 787  E :    }
 788    :  
 789    :    // Consume any left-over data.
 790  E :    if (cur < end) {
 791  E :      if (!Md5Consume(end - cur, pe_file.get(), &md5_context))
 792  i :        return false;  // This logs verbosely for us.
 793    :    }
 794    :  
 795  E :    DCHECK_EQ(end.value(), static_cast<uint32>(::ftell(pe_file.get())));
 796    :  
 797    :    COMPILE_ASSERT(sizeof(base::MD5Digest) == sizeof(pdb_guid_data_),
 798    :                   md5_digest_and_guid_size_mismatch);
 799    :    base::MD5Final(reinterpret_cast<base::MD5Digest*>(&pdb_guid_data_),
 800  E :                   &md5_context);
 801  E :    LOG(INFO) << "Final GUID is " << base::MD5DigestToBase16(
 802    :        *reinterpret_cast<base::MD5Digest*>(&pdb_guid_data_)) << ".";
 803    :  
 804  E :    return true;
 805  E :  }
 806    :  
 807  E :  bool ZapTimestamp::LoadAndUpdatePdbFile() {
 808  E :    DCHECK(pdb_file_.get() == NULL);
 809    :  
 810  E :    pdb_file_.reset(new PdbFile());
 811  E :    PdbReader pdb_reader;
 812  E :    if (!pdb_reader.Read(pdb_path_, pdb_file_.get())) {
 813  i :      LOG(ERROR) << "Failed to read PDB file: " << pdb_path_.value();
 814  i :      return false;
 815    :    }
 816    :  
 817    :    // We turf the old directory stream as a fresh PDB does not have one. It's
 818    :    // also meaningless after we rewrite a PDB as the old blocks it refers to
 819    :    // will no longer exist.
 820  E :    pdb_file_->ReplaceStream(pdb::kPdbOldDirectoryStream, NULL);
 821    :  
 822    :    scoped_refptr<PdbStream> header_reader =
 823  E :        GetWritablePdbStream(pdb::kPdbHeaderInfoStream, pdb_file_.get());
 824  E :    if (header_reader.get() == NULL) {
 825  i :      LOG(ERROR) << "No header info stream in PDB file: " << pdb_path_.value();
 826  i :      return false;
 827    :    }
 828    :  
 829    :    scoped_refptr<WritablePdbStream> header_writer =
 830  E :        header_reader->GetWritablePdbStream();
 831  E :    DCHECK(header_writer.get() != NULL);
 832    :  
 833    :    // Update the timestamp, the age and the signature.
 834  E :    LOG(INFO) << "Updating PDB header.";
 835  E :    header_writer->set_pos(offsetof(pdb::PdbInfoHeader70, timestamp));
 836  E :    header_writer->Write(static_cast<uint32>(timestamp_data_));
 837  E :    header_writer->Write(static_cast<uint32>(pdb_age_data_));
 838  E :    header_writer->Write(pdb_guid_data_);
 839    :  
 840    :    // Normalize the DBI stream in place.
 841  E :    scoped_refptr<PdbByteStream> dbi_stream(new PdbByteStream());
 842  E :    CHECK(dbi_stream->Init(pdb_file_->GetStream(pdb::kDbiStream)));
 843  E :    pdb_file_->ReplaceStream(pdb::kDbiStream, dbi_stream);
 844  E :    if (!NormalizeDbiStream(pdb_age_data_, dbi_stream)) {
 845  i :      LOG(ERROR) << "Failed to normalize DBI stream.";
 846  i :      return false;
 847    :    }
 848    :  
 849  E :    uint8* dbi_data = dbi_stream->data();
 850  E :    pdb::DbiHeader* dbi_header = reinterpret_cast<pdb::DbiHeader*>(dbi_data);
 851    :  
 852    :    // Normalize the symbol record stream in place.
 853  E :    scoped_refptr<PdbByteStream> symrec_stream(new PdbByteStream());
 854    :    CHECK(symrec_stream->Init(pdb_file_->GetStream(
 855  E :        dbi_header->symbol_record_stream)));
 856  E :    pdb_file_->ReplaceStream(dbi_header->symbol_record_stream, symrec_stream);
 857  E :    if (!NormalizeSymbolRecordStream(symrec_stream)) {
 858  i :      LOG(ERROR) << "Failed to normalize symbol record stream.";
 859  i :      return false;
 860    :    }
 861    :  
 862    :    // Normalize the public symbol info stream. There's a DWORD of padding at
 863    :    // offset 24 that we want to zero.
 864    :    scoped_refptr<PdbStream> pubsym_reader = GetWritablePdbStream(
 865  E :        dbi_header->public_symbol_info_stream, pdb_file_.get());
 866    :    scoped_refptr<WritablePdbStream> pubsym_writer =
 867  E :        pubsym_reader->GetWritablePdbStream();
 868  E :    DCHECK(pubsym_writer.get() != NULL);
 869  E :    pubsym_writer->set_pos(24);
 870  E :    pubsym_writer->Write(static_cast<uint32>(0));
 871    :  
 872  E :    return true;
 873  E :  }
 874    :  
 875  E :  bool ZapTimestamp::WritePeFile() {
 876  E :    if (!UpdateFileInPlace(pe_path_, pe_file_addr_space_))
 877  i :      return false;
 878    :  
 879  E :    LOG(INFO) << "Updating checksum for PE file: " << pe_path_.value();
 880  E :    if (!pe::PEFileWriter::UpdateFileChecksum(pe_path_)) {
 881  i :      LOG(ERROR) << "Failed to update checksum for PE file: " << pe_path_.value();
 882  i :      return false;
 883    :    }
 884    :  
 885  E :    return true;
 886  E :  }
 887    :  
 888  E :  bool ZapTimestamp::WritePdbFile() {
 889    :    // We actually completely rewrite the PDB file to a temporary location, and
 890    :    // then move it over top of the existing one. This is because pdb_file_
 891    :    // actually has an open file handle to the original PDB.
 892    :  
 893    :    // We create a temporary directory alongside the file to be modified so as
 894    :    // not to cross volume boundaries.
 895  E :    base::FilePath pdb_dir = pdb_path_.DirName();
 896  E :    base::ScopedTempDir temp_dir;
 897  E :    if (!temp_dir.CreateUniqueTempDirUnderPath(pdb_dir)) {
 898  i :      LOG(ERROR) << "Failed to create temporary directory in \""
 899    :                 << pdb_dir.value() << "\".";
 900  i :      return false;
 901    :    }
 902    :  
 903    :    // Generate the path to the rewritten PDB.
 904  E :    base::FilePath temp_path = temp_dir.path().Append(pdb_path_.BaseName());
 905    :  
 906  E :    PdbWriter pdb_writer;
 907  E :    LOG(INFO) << "Creating temporary PDB file: " << temp_path.value();
 908  E :    if (!pdb_writer.Write(temp_path, *pdb_file_.get())) {
 909  i :      LOG(ERROR) << "Failed to write new PDB: " << temp_path.value();
 910  i :      return false;
 911    :    }
 912    :  
 913    :    // Free up the PDB file. This will close the open file handle to the original
 914    :    // PDB file.
 915  E :    pdb_file_.reset(NULL);
 916    :  
 917    :    // Copy over top of the original file.
 918  E :    LOG(INFO) << "Temporary PDB file replacing old PDB: "
 919    :              << pdb_path_.value();
 920  E :    if (!file_util::ReplaceFileW(temp_path, pdb_path_)) {
 921  i :      LOG(ERROR) << "Unable to replace PDB file.";
 922  i :      return false;
 923    :    }
 924    :  
 925  E :    return true;
 926  E :  }
 927    :  
 928    :  bool ZapTimestampApp::ParseCommandLine(const CommandLine* command_line) {
 929    :    DCHECK(command_line != NULL);
 930    :  
 931    :    CommandLine::StringVector args = command_line->GetArgs();
 932    :    if (args.empty()) {
 933    :      PrintUsage(out(), command_line->GetProgram(),
 934    :                 "You must provide at least one PE file.");
 935    :      return false;
 936    :    }
 937    :  
 938    :    for (size_t i = 0; i < args.size(); ++i) {
 939    :      base::FilePath path(args[i]);
 940    :      input_modules_.push_back(path);
 941    :    }
 942    :  
 943    :    return true;
 944    :  }
 945    :  
 946    :  int ZapTimestampApp::Run() {
 947    :    for (size_t i = 0; i < input_modules_.size(); ++i) {
 948    :      ZapTimestamp zap;
 949    :      if (!zap.Init(input_modules_[i]) || !zap.Zap(true, true))
 950    :        return 1;
 951    :    }
 952    :  
 953    :    return 0;
 954    :  }
 955    :  
 956    :  }  // namespace zap_timestamp

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