Coverage for /Syzygy/pe/metadata.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
82.3%1491810.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/metadata.h"
  16    :  
  17    :  #include <time.h>
  18    :  
  19    :  #include "base/command_line.h"
  20    :  #include "base/values.h"
  21    :  #include "base/json/json_reader.h"
  22    :  #include "base/json/string_escape.h"
  23    :  #include "base/strings/stringprintf.h"
  24    :  #include "base/strings/utf_string_conversions.h"
  25    :  #include "syzygy/block_graph/block_graph.h"
  26    :  #include "syzygy/common/defs.h"
  27    :  #include "syzygy/core/json_file_writer.h"
  28    :  #include "syzygy/pe/pe_utils.h"
  29    :  
  30    :  namespace pe {
  31    :  
  32    :  using base::DictionaryValue;
  33    :  using block_graph::BlockGraph;
  34    :  using core::RelativeAddress;
  35    :  
  36    :  namespace {
  37    :  
  38    :  // Metadata JSON keys.
  39    :  const char kCommandLineKey[] = "command_line";
  40    :  const char kCreationTimeKey[] = "creation_time";
  41    :  const char kToolchainVersionKey[] = "toolchain_version";
  42    :  const char kModuleSignatureKey[] = "module_signature";
  43    :  
  44    :  // SyzygyVersion JSON keys.
  45    :  const char kMajorKey[] = "major";
  46    :  const char kMinorKey[] = "minor";
  47    :  const char kBuildKey[] = "build";
  48    :  const char kPatchKey[] = "patch";
  49    :  const char kLastChangeKey[] = "last_change";
  50    :  
  51    :  // PEFile::Signature JSON keys.
  52    :  const char kPathKey[] = "path";
  53    :  const char kBaseAddressKey[] = "base_address";
  54    :  const char kModuleSizeKey[] = "module_size";
  55    :  const char kModuleTimeDateStampKey[] = "module_time_date_stamp";
  56    :  const char kModuleChecksumKey[] = "module_checksum";
  57    :  
  58  E :  std::string TimeToString(const base::Time& time) {
  59    :    // Want the output format to be consistent with what Time::FromString
  60    :    // accepts as input. An example follows:
  61    :    // Tue, 15 Nov 1994 12:45:26 GMT.
  62    :    char buffer[64];
  63  E :    time_t tt = time.ToTimeT();
  64  E :    struct tm timeinfo = {};
  65  E :    gmtime_s(&timeinfo, &tt);
  66  E :    strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S GMT", &timeinfo);
  67  E :    return std::string(buffer);
  68  E :  }
  69    :  
  70  E :  bool StringToTime(const std::string& string, base::Time* time) {
  71  E :    return base::Time::FromString(string.c_str(), time);
  72  E :  }
  73    :  
  74    :  // Outputs a SyzygyVersion object in JSON format as a dictionary. Does not
  75    :  // output a newline after the dictionary.
  76    :  bool OutputSyzygyVersion(const version::SyzygyVersion& version,
  77  E :                           core::JSONFileWriter* json_file) {
  78  E :    DCHECK(json_file != NULL);
  79    :  
  80  E :    if (!json_file->OpenDict())
  81  i :      return false;
  82    :  
  83  E :    if (json_file->pretty_print()) {
  84  E :      std::string comment("Toolchain version: ");
  85  E :      comment.append(version.GetVersionString());
  86  E :      if (!json_file->OutputComment(comment))
  87  i :        return false;
  88  E :    }
  89    :  
  90    :    return json_file->OutputKey(kMajorKey) &&
  91    :        json_file->OutputInteger(version.major()) &&
  92    :        json_file->OutputKey(kMinorKey) &&
  93    :        json_file->OutputInteger(version.minor()) &&
  94    :        json_file->OutputKey(kBuildKey) &&
  95    :        json_file->OutputInteger(version.build()) &&
  96    :        json_file->OutputKey(kPatchKey) &&
  97    :        json_file->OutputInteger(version.patch()) &&
  98    :        json_file->OutputKey(kLastChangeKey) &&
  99    :        json_file->OutputString(version.last_change()) &&
 100  E :        json_file->CloseDict();
 101  E :  }
 102    :  
 103    :  // Outputs a PEFile::Signature in JSON format as a dictionary. Does not output
 104    :  // a newline after the dictionary.
 105    :  bool OutputPEFileSignature(const PEFile::Signature& signature,
 106  E :                             core::JSONFileWriter* json_file) {
 107  E :    DCHECK(json_file != NULL);
 108    :  
 109  E :    std::string path;
 110  E :    base::WideToUTF8(signature.path.c_str(), signature.path.size(), &path);
 111  E :    path = base::GetQuotedJSONString(path);
 112    :  
 113    :    std::string time_stamp(
 114  E :        base::StringPrintf("0x%llX", signature.module_time_date_stamp));
 115  E :    std::string checksum(base::StringPrintf("0x%X", signature.module_checksum));
 116    :  
 117    :    return json_file->OpenDict() &&
 118    :        json_file->OutputKey(kPathKey) &&
 119    :        json_file->OutputString(signature.path) &&
 120    :        json_file->OutputKey(kBaseAddressKey) &&
 121    :        json_file->OutputInteger(signature.base_address.value()) &&
 122    :        json_file->OutputKey(kModuleSizeKey) &&
 123    :        json_file->OutputInteger(signature.module_size) &&
 124    :        json_file->OutputKey(kModuleTimeDateStampKey) &&
 125    :        json_file->OutputString(time_stamp) &&
 126    :        json_file->OutputKey(kModuleChecksumKey) &&
 127    :        json_file->OutputString(checksum) &&
 128  E :        json_file->CloseDict();
 129  E :  }
 130    :  
 131    :  // Loads a syzygy version from a JSON dictionary.
 132    :  bool LoadSyzygyVersion(const DictionaryValue& dictionary,
 133  E :                         version::SyzygyVersion* version) {
 134  E :    DCHECK(version != NULL);
 135    :  
 136  E :    int major = 0;
 137  E :    int minor = 0;
 138  E :    int build = 0;
 139  E :    int patch = 0;
 140  E :    std::string last_change;
 141    :    if (!dictionary.GetInteger(kMajorKey, &major) ||
 142    :        !dictionary.GetInteger(kMinorKey, &minor) ||
 143    :        !dictionary.GetInteger(kBuildKey, &build) ||
 144    :        !dictionary.GetInteger(kPatchKey, &patch) ||
 145  E :        !dictionary.GetString(kLastChangeKey, &last_change)) {
 146  i :      LOG(ERROR) << "Unable to parse SyzygyVersion from JSON dictionary.";
 147  i :      return false;
 148    :    }
 149    :  
 150  E :    version->set_major(major);
 151  E :    version->set_minor(minor);
 152  E :    version->set_build(build);
 153  E :    version->set_patch(patch);
 154  E :    version->set_last_change(last_change.c_str());
 155    :  
 156  E :    return true;
 157  E :  }
 158    :  
 159    :  // Loads a PEFile::Signature from a JSON dictionary.
 160    :  bool LoadPEFileSignature(const DictionaryValue& dictionary,
 161  E :                           PEFile::Signature* signature) {
 162  E :    DCHECK(signature != NULL);
 163    :  
 164  E :    std::string path;
 165  E :    int base_address = 0;
 166  E :    int module_size = 0;
 167  E :    std::string stamp;
 168  E :    std::string checksum;
 169    :    if (!dictionary.GetString(kPathKey, &path) ||
 170    :        !dictionary.GetInteger(kBaseAddressKey, &base_address) ||
 171    :        !dictionary.GetInteger(kModuleSizeKey, &module_size) ||
 172    :        !dictionary.GetString(kModuleTimeDateStampKey, &stamp) ||
 173  E :        !dictionary.GetString(kModuleChecksumKey, &checksum)) {
 174  i :      LOG(ERROR) << "Unable to parse PEFile::Signature from JSON dictionary.";
 175  i :      return false;
 176    :    }
 177    :  
 178  E :    base::UTF8ToWide(path.c_str(), path.size(), &signature->path);
 179  E :    signature->base_address = PEFile::AbsoluteAddress(base_address);
 180  E :    signature->module_size = module_size;
 181    :  
 182  E :    char* end = NULL;
 183  E :    signature->module_time_date_stamp = _strtoui64(stamp.c_str(), &end, 16);
 184  E :    if (end == stamp.c_str()) {
 185  i :      LOG(ERROR) << "Unable to parse " << kModuleTimeDateStampKey << ".";
 186  i :      return false;
 187    :    }
 188    :  
 189  E :    signature->module_checksum = strtoul(checksum.c_str(), &end, 16);
 190  E :    if (end == checksum.c_str()) {
 191  i :      LOG(ERROR) << "Unable to parse " << kModuleChecksumKey << ".";
 192  i :      return false;
 193    :    }
 194    :  
 195  E :    return true;
 196  E :  }
 197    :  
 198    :  }  // namespace
 199    :  
 200  E :  Metadata::Metadata() {
 201  E :  }
 202    :  
 203  E :  bool Metadata::Init(const PEFile::Signature& module_signature) {
 204    :    // Populate the command line string.
 205  E :    base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
 206  E :    DCHECK(cmd_line != NULL);
 207    :    if (!base::WideToUTF8(cmd_line->GetCommandLineString().c_str(),
 208    :                    cmd_line->GetCommandLineString().size(),
 209  E :                    &command_line_)) {
 210  i :      LOG(ERROR) << "Unable to convert command-line to UTF8.";
 211  i :      return false;
 212    :    }
 213    :  
 214    :    // Set the remaining properties.
 215  E :    creation_time_ = base::Time::Now();
 216  E :    toolchain_version_ = version::kSyzygyVersion;
 217  E :    module_signature_ = module_signature;
 218    :  
 219  E :    return true;
 220  E :  }
 221    :  
 222  E :  bool Metadata::IsConsistent(const PEFile::Signature& module_signature) const {
 223  E :    if (!version::kSyzygyVersion.IsCompatible(toolchain_version_)) {
 224  i :      LOG(ERROR) << "Metadata is not compatible with current toolchain version.";
 225  i :      return false;
 226    :    }
 227    :  
 228  E :    if (!module_signature.IsConsistent(module_signature_)) {
 229  i :      LOG(ERROR) << "Metadata is not consistent with input module.";
 230  i :      return false;
 231    :    }
 232    :  
 233  E :    return true;
 234  E :  }
 235    :  
 236  E :  bool Metadata::SaveToJSON(core::JSONFileWriter* json_file) const {
 237  E :    DCHECK(json_file != NULL);
 238    :  
 239    :    return json_file->OpenDict() &&
 240    :        json_file->OutputKey(kCommandLineKey) &&
 241    :        json_file->OutputString(command_line_) &&
 242    :        json_file->OutputKey(kCreationTimeKey) &&
 243    :        json_file->OutputString(TimeToString(creation_time_)) &&
 244    :        json_file->OutputKey(kToolchainVersionKey) &&
 245    :        OutputSyzygyVersion(toolchain_version_, json_file) &&
 246    :        json_file->OutputKey(kModuleSignatureKey) &&
 247    :        OutputPEFileSignature(module_signature_, json_file) &&
 248  E :        json_file->CloseDict();
 249  E :  }
 250    :  
 251  E :  bool Metadata::LoadFromJSON(const DictionaryValue& metadata) {
 252  E :    std::string creation_time;
 253  E :    const DictionaryValue* toolchain_version_dict = NULL;
 254  E :    const DictionaryValue* module_signature_dict = NULL;
 255    :    if (!metadata.GetString(kCommandLineKey, &command_line_) ||
 256    :        !metadata.GetString(kCreationTimeKey, &creation_time) ||
 257    :        !metadata.GetDictionary(kToolchainVersionKey, &toolchain_version_dict) ||
 258  E :        !metadata.GetDictionary(kModuleSignatureKey, &module_signature_dict)) {
 259  i :      LOG(ERROR) << "Unable to parse metadata.";
 260  i :      return false;
 261    :    }
 262    :  
 263    :    if (!LoadSyzygyVersion(*toolchain_version_dict, &toolchain_version_) ||
 264  E :        !LoadPEFileSignature(*module_signature_dict, &module_signature_))
 265  i :      return false;
 266    :  
 267    :    // Parse the creation time from its string representation.
 268  E :    return StringToTime(creation_time, &creation_time_);
 269  E :  }
 270    :  
 271  E :  bool Metadata::SaveToBlock(BlockGraph::Block* block) const {
 272    :    // Serialize the metadata to a ByteVector.
 273  E :    core::ByteVector bytes;
 274  E :    core::ScopedOutStreamPtr out_stream;
 275  E :    out_stream.reset(core::CreateByteOutStream(std::back_inserter(bytes)));
 276  E :    core::NativeBinaryOutArchive out_archive(out_stream.get());
 277  E :    if (!out_archive.Save(*this))
 278  i :      return false;
 279    :  
 280    :    // Output some of the information in duplicate, in a human-readable form, so
 281    :    // that we can easily grep for this stuff in the actual binaries.
 282  E :    std::string path;
 283    :    if (!base::WideToUTF8(module_signature_.path.c_str(),
 284    :                          module_signature_.path.size(),
 285  E :                          &path)) {
 286  i :      LOG(ERROR) << "Unable to convert module path to UTF8.";
 287  i :      return false;
 288    :    }
 289  E :    std::string text("Command-line: ");
 290  E :    text.append(command_line_);
 291  E :    text.append("\nCreation time: ");
 292  E :    text.append(TimeToString(creation_time_));
 293  E :    text.append("\nToolchain version: ");
 294  E :    text.append(toolchain_version_.GetVersionString());
 295  E :    text.append("\nModule path: ");
 296  E :    text.append(path);
 297  E :    text.append("\n");
 298  E :    if (!out_archive.Save(text))
 299  i :      return false;
 300  E :    if (!out_archive.Flush())
 301  i :      return false;
 302    :  
 303  E :    block->SetData(NULL, 0);
 304  E :    block->set_size(bytes.size());
 305  E :    if (block->CopyData(bytes.size(), &bytes[0]) == NULL) {
 306  i :      LOG(ERROR) << "Unable to allocate metadata.";
 307  i :      return false;
 308    :    }
 309    :  
 310  E :    return true;
 311  E :  }
 312    :  
 313  E :  bool Metadata::LoadFromBlock(const BlockGraph::Block* block) {
 314    :    // Parse the metadata.
 315  E :    core::ScopedInStreamPtr in_stream;
 316    :    in_stream.reset(core::CreateByteInStream(block->data(),
 317  E :                                             block->data() + block->data_size()));
 318  E :    core::NativeBinaryInArchive in_archive(in_stream.get());
 319  E :    if (!in_archive.Load(this)) {
 320  i :      LOG(ERROR) << "Unable to parse module metadata.";
 321  i :      return false;
 322    :    }
 323    :  
 324  E :    return true;
 325  E :  }
 326    :  
 327  E :  bool Metadata::LoadFromPE(const PEFile& pe_file) {
 328    :    // Get the metadata section data.
 329    :    size_t metadata_id =
 330  E :        pe_file.GetSectionIndex(common::kSyzygyMetadataSectionName);
 331  E :    if (metadata_id == pe::kInvalidSection) {
 332  E :      LOG(ERROR) << "Module does not contain a metadata section.";
 333  E :      return false;
 334    :    }
 335  E :    const IMAGE_SECTION_HEADER* section = pe_file.section_header(metadata_id);
 336  E :    DCHECK(section != NULL);
 337  E :    RelativeAddress metadata_addr(section->VirtualAddress);
 338  E :    size_t metadata_size = section->Misc.VirtualSize;
 339    :    const core::Byte* metadata = pe_file.GetImageData(metadata_addr,
 340  E :                                                      metadata_size);
 341  E :    if (metadata == NULL) {
 342  i :      LOG(ERROR) << "Unable to get metadata section data.";
 343  i :      return false;
 344    :    }
 345    :  
 346    :    // Parse the metadata.
 347  E :    core::ScopedInStreamPtr in_stream;
 348  E :    in_stream.reset(core::CreateByteInStream(metadata, metadata + metadata_size));
 349  E :    core::NativeBinaryInArchive in_archive(in_stream.get());
 350  E :    if (!in_archive.Load(this)) {
 351  i :      LOG(ERROR) << "Unable to parse module metadata.";
 352  i :      return false;
 353    :    }
 354    :  
 355  E :    return true;
 356  E :  }
 357    :  
 358  E :  bool Metadata::operator==(const Metadata& rhs) const {
 359    :    return command_line_ == rhs.command_line_ &&
 360    :        creation_time_ == rhs.creation_time_ &&
 361    :        toolchain_version_ == rhs.toolchain_version_ &&
 362  E :        module_signature_ == rhs.module_signature_;
 363  E :  }
 364    :  
 365    :  // Serialization 'Save' implementation.
 366  E :  bool Metadata::Save(core::OutArchive* out_archive) const {
 367  E :    DCHECK(out_archive != NULL);
 368    :    return out_archive->Save(command_line_) &&
 369    :        out_archive->Save(creation_time_) &&
 370    :        out_archive->Save(toolchain_version_) &&
 371  E :        out_archive->Save(module_signature_);
 372  E :  }
 373    :  
 374    :  // Serialization 'Load' implementation.
 375  E :  bool Metadata::Load(core::InArchive* in_archive) {
 376  E :    DCHECK(in_archive != NULL);
 377    :    return in_archive->Load(&command_line_) &&
 378    :        in_archive->Load(&creation_time_) &&
 379    :        in_archive->Load(&toolchain_version_) &&
 380  E :        in_archive->Load(&module_signature_);
 381  E :  }
 382    :  
 383    :  }  // namespace pe

Coverage information generated Thu Jan 14 17:40:38 2016.