Coverage for /Syzygy/swapimport/swapimport_app.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
82.5%991200.C++source

Line-by-line coverage:

   1    :  // Copyright 2014 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    :  // Defines the SwapImportApp class, which implements the command-line
  16    :  // "swapimport" tool.
  17    :  
  18    :  #include "syzygy/swapimport/swapimport_app.h"
  19    :  
  20    :  #include "base/files/file_util.h"
  21    :  #include "base/strings/string_util.h"
  22    :  #include "base/strings/utf_string_conversions.h"
  23    :  #include "syzygy/core/file_util.h"
  24    :  #include "syzygy/pe/pe_file.h"
  25    :  #include "syzygy/pe/pe_file_writer.h"
  26    :  
  27    :  namespace swapimport {
  28    :  
  29    :  namespace {
  30    :  
  31    :  static const char kUsageFormatStr[] = "Usage: %ls [options] IMPORT\n"
  32    :      "  Required Options:\n"
  33    :      "    --input-image=PATH    Path of the input image.\n"
  34    :      "    --output-image=PATH   Path where the output image will be written.\n"
  35    :      "                          The generated image will still be paired to\n"
  36    :      "                          the original PDB file.\n"
  37    :      "    --x64                 Decompose a 64-bit binary rather than a\n"
  38    :      "                          32-bit one.\n"
  39    :      "  Options:\n"
  40    :      "    --overwrite           Allow output files to be overwritten.\n"
  41    :      "    --verbose             Log verbosely.\n"
  42    :      "\n";
  43    :  
  44    :  }  // namespace
  45    :  
  46  E :  bool SwapImportApp::ParseCommandLine(const base::CommandLine* cmd_line) {
  47  E :    DCHECK_NE(reinterpret_cast<const base::CommandLine*>(NULL), cmd_line);
  48    :  
  49  E :    if (cmd_line->HasSwitch("help"))
  50  E :      return Usage(cmd_line, "");
  51    :  
  52  E :    if (cmd_line->HasSwitch("verbose")) {
  53  E :      logging::SetMinLogLevel(logging::LOG_VERBOSE);
  54  E :      VLOG(1) << "Parsed --verbose switch.";
  55  E :    } else {
  56  E :      logging::SetMinLogLevel(logging::LOG_ERROR);
  57    :    }
  58    :  
  59  E :    input_image_ = cmd_line->GetSwitchValuePath("input-image");
  60  E :    if (input_image_.empty()) {
  61  E :      LOG(ERROR) << "Must specify --input-image!";
  62  E :      return false;
  63    :    }
  64    :  
  65  E :    output_image_ = cmd_line->GetSwitchValuePath("output-image");
  66  E :    if (output_image_.empty()) {
  67  E :      LOG(ERROR) << "Must specify --output-image!";
  68  E :      return false;
  69    :    }
  70    :  
  71  E :    overwrite_ = cmd_line->HasSwitch("overwrite");
  72  E :    if (overwrite_)
  73  E :      VLOG(1) << "Parsed --overwrite switch.";
  74    :  
  75  E :    base::CommandLine::StringVector args = cmd_line->GetArgs();
  76  E :    if (args.size() != 1) {
  77  E :      LOG(ERROR) << "Expect exactly one import name.";
  78  E :      return false;
  79    :    }
  80  E :    import_name_ = base::WideToUTF8(args[0]);
  81    :  
  82  E :    x64_ = cmd_line->HasSwitch("x64");
  83  E :    if (x64_)
  84  E :      VLOG(1) << "Parsed --x64 switch.";
  85    :  
  86  E :    return true;
  87  E :  }
  88    :  
  89    :  template <typename PEFileType>
  90  E :  int SwapImportApp::SwapImports() {
  91    :    // Parse the input file as a PE image.
  92  E :    PEFileType pe_file;
  93  E :    if (!pe_file.Init(input_image_)) {
  94  E :      LOG(ERROR) << "Failed to parse image as a PE file: "
  95    :                 << input_image_.value();
  96  E :        return 1;
  97    :    }
  98    :  
  99    :    // Read the entire input into memory.
 100  E :    VLOG(1) << "Reading \"" << input_image_.value() << "\" into memory.";
 101  E :    int64 image_size = 0;
 102  E :    if (!base::GetFileSize(input_image_, &image_size)) {
 103  i :      LOG(ERROR) << "Failed to get image size: " << input_image_.value();
 104  i :      return 1;
 105    :    }
 106  E :    std::vector<unsigned char> image(image_size, 0);
 107    :    if (!base::ReadFile(input_image_,
 108    :                             reinterpret_cast<char*>(image.data()),
 109  E :                             image_size)) {
 110  i :      LOG(ERROR) << "Failed to read image to memory: " << input_image_.value();
 111  i :      return 1;
 112    :    }
 113    :  
 114    :    // Keeps track of matched imports, and how many have been swapped.
 115  E :    size_t imports_swapped = 0;
 116  E :    size_t imports_matched = 0;
 117    :  
 118    :    // Look up the import directory.
 119  E :    LOG(INFO) << "Processing NT headers.";
 120    :    const IMAGE_DATA_DIRECTORY* data_dir =
 121    :        pe_file.nt_headers()->OptionalHeader.DataDirectory +
 122  E :            IMAGE_DIRECTORY_ENTRY_IMPORT;
 123  E :    if (data_dir->Size != 0) {
 124  E :      LOG(INFO) << "Processing imports.";
 125    :  
 126  E :      pe::PEFile::FileOffsetAddress import_offset;
 127    :      if (!pe_file.Translate(
 128    :              pe::PEFile::RelativeAddress(data_dir->VirtualAddress),
 129  E :              &import_offset)) {
 130  i :        LOG(ERROR) << "Failed to translate import directory address.";
 131  i :        return 1;
 132    :      }
 133    :  
 134    :      // Walk over the imports.
 135    :      IMAGE_IMPORT_DESCRIPTOR* imports_begin =
 136    :          reinterpret_cast<IMAGE_IMPORT_DESCRIPTOR*>(
 137  E :              image.data() + import_offset.value());
 138    :      IMAGE_IMPORT_DESCRIPTOR* imports_end =
 139    :          reinterpret_cast<IMAGE_IMPORT_DESCRIPTOR*>(
 140  E :              image.data() + import_offset.value() + data_dir->Size);
 141  E :      IMAGE_IMPORT_DESCRIPTOR* imports = imports_begin;
 142  E :      size_t import_index = 0;
 143  E :      while (imports < imports_end && imports->Characteristics != 0) {
 144    :        // Look up the import name.
 145  E :        pe::PEFile::FileOffsetAddress name_offset;
 146    :        if (!pe_file.Translate(
 147    :                pe::PEFile::RelativeAddress(imports->Name),
 148  E :                &name_offset)) {
 149  i :          LOG(ERROR) << "Failed to translate import name.";
 150  i :          return 1;
 151    :        }
 152    :  
 153    :        // Compare the import name.
 154    :        const char* name = reinterpret_cast<const char*>(
 155  E :            image.data() + name_offset.value());
 156  E :        VLOG(1) << "Processing import " << import_index << " \""
 157    :                << name << "\".";
 158  E :        if (base::CompareCaseInsensitiveASCII(import_name_.c_str(), name) == 0) {
 159  E :          VLOG(1) << "Import " << import_index << " matches import name.";
 160  E :          ++imports_matched;
 161    :  
 162  E :          if (import_index > imports_swapped) {
 163    :            // Do the actual swapping of the imports.
 164  E :            LOG(INFO) << "Swapping imports " << imports_swapped << " and "
 165    :                      << import_index;
 166    :  
 167  E :            IMAGE_IMPORT_DESCRIPTOR temp_iid = *imports;
 168  E :            *imports = imports_begin[imports_swapped];
 169  E :            imports_begin[imports_swapped] = temp_iid;
 170    :  
 171  E :            ++imports_swapped;
 172    :          }
 173    :        }
 174    :  
 175  E :        ++imports;
 176  E :        ++import_index;
 177  E :      }
 178    :    }
 179    :  
 180    :    // We expect to have matched the specified import at least once.
 181  E :    if (imports_matched == 0) {
 182  E :      LOG(ERROR) << "Did not find an import matching \"" << import_name_ << "\".";
 183  E :      return 1;
 184    :    }
 185    :  
 186    :    // Write the actual output.
 187  E :    LOG(INFO) << "Writing output to \"" << output_image_.value() << "\".";
 188  E :    base::ScopedFILE output(base::OpenFile(output_image_, "wb"));
 189  E :    if (output.get() == NULL) {
 190  i :      LOG(ERROR) << "Failed to open \"" << output_image_.value() << "\" for "
 191    :                 << "writing.";
 192  i :      return 1;
 193    :    }
 194    :    if (::fwrite(image.data(), sizeof(image[0]), image.size(), output.get()) !=
 195  E :            image.size()) {
 196  i :      LOG(ERROR) << "Failed to write output: " << output_image_.value();
 197  i :      return 1;
 198    :    }
 199  E :    output.reset();
 200    :  
 201    :    // Finalize the image by updating the checksum.
 202  E :    LOG(INFO) << "Updating output image checksum.";
 203  E :    if (!pe::PEFileWriter::UpdateFileChecksum(output_image_)) {
 204  i :      LOG(ERROR) << "Failed to update image checksum.";
 205  i :      return 1;
 206    :    }
 207    :  
 208  E :    return 0;
 209  E :  }
 210    :  
 211  E :  int SwapImportApp::Run() {
 212    :    // Check the input.
 213  E :    if (!base::PathExists(input_image_)) {
 214  i :      LOG(ERROR) << "Path does not exist: " << input_image_.value();
 215  i :      return 1;
 216    :    }
 217    :  
 218    :    // Check the output unless we're overwriting.
 219  E :    if (!overwrite_) {
 220  E :      if (base::PathExists(output_image_)) {
 221  E :        LOG(ERROR) << "Output path exists: " << output_image_.value();
 222  E :        LOG(ERROR) << "Did you mean to specify --overwrite?";
 223  E :        return 1;
 224    :      }
 225    :  
 226    :      core::FilePathCompareResult result = core::CompareFilePaths(
 227  E :          input_image_, output_image_);
 228  E :      if (result == core::kEquivalentFilePaths) {
 229  i :        LOG(ERROR) << "Output image path equivalent to input image path.";
 230  i :        return 1;
 231    :      }
 232    :    }
 233    :  
 234  E :    if (x64_) {
 235  E :      return SwapImports<pe::PEFile64>();
 236  i :    } else {
 237  E :      return SwapImports<pe::PEFile>();
 238    :    }
 239  E :  }
 240    :  
 241    :  bool SwapImportApp::Usage(const base::CommandLine* cmd_line,
 242  E :                            const base::StringPiece& message) const {
 243  E :    if (!message.empty()) {
 244  i :      ::fwrite(message.data(), 1, message.length(), err());
 245  i :      ::fprintf(err(), "\n\n");
 246    :    }
 247    :  
 248    :    ::fprintf(err(),
 249    :              kUsageFormatStr,
 250  E :              cmd_line->GetProgram().BaseName().value().c_str());
 251    :  
 252  E :    return false;
 253  E :  }
 254    :  
 255    :  }  // namespace swapimport

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