Coverage for /Syzygy/pe/metadata.cc

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

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