Coverage for /Syzygy/pe/decompose_image_to_text_app.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
76.2%1441890.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/decompose_image_to_text_app.h"
  16    :  
  17    :  #include "syzygy/block_graph/basic_block_decomposer.h"
  18    :  #include "syzygy/pe/block_util.h"
  19    :  #include "syzygy/pe/decomposer.h"
  20    :  #include "syzygy/pe/pe_file.h"
  21    :  
  22    :  #include "distorm.h"  // NOLINT
  23    :  
  24    :  namespace pe {
  25    :  
  26    :  using block_graph::BasicBlockDecomposer;
  27    :  using core::RelativeAddress;
  28    :  using pe::Decomposer;
  29    :  using pe::ImageLayout;
  30    :  using pe::PEFile;
  31    :  
  32    :  namespace {
  33    :  
  34    :  const char kUsageFormatStr[] =
  35    :    "Usage: %ls [options]\n"
  36    :    "\n"
  37    :    "  A tool that decomposes a given image file, and decomposes it to a\n"
  38    :    "  human-readable textual description.\n"
  39    :    "\n"
  40    :    "Available options\n"
  41    :    "  --basic-blocks\n"
  42    :    "    Breaks each function down to basic blocks and dumps it at that level.\n"
  43    :    "  --image=<image file>\n";
  44    :  
  45    :  using block_graph::BlockGraph;
  46    :  using block_graph::BasicBlock;
  47    :  using block_graph::BasicBlockReference;
  48    :  
  49  E :  void DumpReference(const BasicBlockReference& ref, FILE* out) {
  50  E :    DCHECK(out != NULL);
  51    :  
  52  E :    switch (ref.referred_type()) {
  53    :      case BasicBlockReference::REFERRED_TYPE_BLOCK: {
  54  E :          const BlockGraph::Block* block = ref.block();
  55  E :          if (ref.offset() == 0) {
  56  E :            ::fprintf(out, " ; (%s)", block->name().c_str());
  57  E :          } else if (ref.offset() < 0) {
  58  i :            ::fprintf(out, " ; (%s%d)", block->name().c_str(), ref.offset());
  59  i :          } else {
  60  E :            BlockGraph::Label label;
  61  E :            if (block->GetLabel(ref.offset(), &label)) {
  62    :              ::fprintf(out, " ; (%s:%s)",
  63    :                        block->name().c_str(),
  64  E :                        label.ToString().c_str());
  65  E :            } else {
  66  E :              ::fprintf(out, " ; (%s+%d)", block->name().c_str(), ref.offset());
  67    :            }
  68  E :          }
  69    :        }
  70  E :        break;
  71    :  
  72    :      case BasicBlockReference::REFERRED_TYPE_BASIC_BLOCK: {
  73  E :          const BasicBlock* bb = ref.basic_block();
  74  E :          DCHECK(ref.offset() == 0);
  75    :  
  76  E :          ::fprintf(out, " ; (%s)", bb->name().c_str());
  77    :        }
  78  E :        break;
  79    :  
  80    :      case BasicBlockReference::REFERRED_TYPE_UNKNOWN:
  81    :      default:
  82  i :        NOTREACHED() << "All references should be typed.";
  83    :        break;
  84    :    }
  85  E :  }
  86    :  
  87  i :  void HexDump(const uint8* data, size_t size, FILE* out) {
  88  i :    for (size_t i = 0; i < size; ++i)
  89  i :      ::fprintf(out, "%02x", data[i]);
  90  i :  }
  91    :  
  92    :  }  // namespace
  93    :  
  94    :  
  95    :  DecomposeImageToTextApp::DecomposeImageToTextApp()
  96    :      : common::AppImplBase("Image To Text Decomposer"),
  97    :        dump_basic_blocks_(false),
  98  E :        num_refs_(0) {
  99  E :  }
 100    :  
 101    :  void DecomposeImageToTextApp::PrintUsage(const FilePath& program,
 102  E :                                           const base::StringPiece& message) {
 103  E :    if (!message.empty()) {
 104  E :      ::fwrite(message.data(), 1, message.length(), out());
 105  E :      ::fprintf(out(), "\n\n");
 106    :    }
 107    :  
 108  E :    ::fprintf(out(), kUsageFormatStr, program.BaseName().value().c_str());
 109  E :  }
 110    :  
 111    :  bool DecomposeImageToTextApp::ParseCommandLine(
 112  E :      const CommandLine* cmd_line) {
 113  E :    image_path_ = cmd_line->GetSwitchValuePath("image");
 114  E :    if (image_path_.empty()) {
 115    :      PrintUsage(cmd_line->GetProgram(),
 116  E :                 "You must provide the path to an image file.");
 117  E :      return false;
 118    :    }
 119    :  
 120  E :    dump_basic_blocks_ = cmd_line->HasSwitch("basic-blocks");
 121    :  
 122  E :    return true;
 123  E :  }
 124    :  
 125  E :  int DecomposeImageToTextApp::Run() {
 126  E :    DCHECK(!image_path_.empty());
 127    :  
 128  E :    if (!DumpImageToText(image_path_))
 129  i :      return 1;
 130    :  
 131  E :    return 0;
 132  E :  }
 133    :  
 134    :  void DecomposeImageToTextApp::DumpAddressSpaceToText(
 135  E :      const BlockGraph::AddressSpace& address_space) {
 136    :    BlockGraph::AddressSpace::RangeMap::const_iterator block_it(
 137  E :      address_space.address_space_impl().ranges().begin());
 138    :    BlockGraph::AddressSpace::RangeMap::const_iterator block_end(
 139  E :      address_space.address_space_impl().ranges().end());
 140    :  
 141  E :    for (; block_it != block_end; ++block_it) {
 142  E :      const BlockGraph::Block* block = block_it->second;
 143  E :      RelativeAddress addr = block_it->first.start();
 144    :  
 145  E :      DumpBlockToText(addr, block);
 146  E :    }
 147  E :  }
 148    :  
 149    :  void DecomposeImageToTextApp::DumpSubGraphToText(
 150  E :      BasicBlockSubGraph& subgraph) {
 151    :    typedef BasicBlockSubGraph::BlockDescription BlockDescription;
 152    :    typedef BasicBlockSubGraph::BasicBlockOrdering BasicBlockOrdering;
 153    :    typedef block_graph::BasicBlock BasicBlock;
 154    :    typedef block_graph::BasicBlockReference BasicBlockReference;
 155    :  
 156    :    // Post-decomposition we have a single description only.
 157  E :    DCHECK_EQ(1U, subgraph.block_descriptions().size());
 158  E :    DCHECK(subgraph.original_block() != NULL);
 159    :  
 160  E :    const BlockGraph::Block* block = subgraph.original_block();
 161  E :    const BlockDescription& descr = subgraph.block_descriptions().front();
 162  E :    BasicBlockOrdering::const_iterator bb_it(descr.basic_block_order.begin());
 163  E :    for (; bb_it != descr.basic_block_order.end(); ++bb_it) {
 164  E :      const BasicBlock* bb = *bb_it;
 165  E :      DCHECK(bb != NULL);
 166    :  
 167    :      // Print the BB's name for an identifying label.
 168  E :      ::fprintf(out(), "%s:\n", bb->name().c_str());
 169    :  
 170  E :      switch (bb->type()) {
 171    :        case BasicBlock::BASIC_CODE_BLOCK:
 172  E :          DumpCodeBBToText(block, bb);
 173  E :          break;
 174    :  
 175    :        case BasicBlock::BASIC_DATA_BLOCK:
 176    :        case BasicBlock::BASIC_PADDING_BLOCK:
 177  i :          DumpDataBBToText(block, bb);
 178  i :          break;
 179    :  
 180    :        default:
 181  i :          NOTREACHED();
 182    :          break;
 183    :      }
 184  E :    }
 185  E :  }
 186    :  
 187    :  void DecomposeImageToTextApp::DumpCodeBBToText(
 188  E :      const BlockGraph::Block* block, const BasicBlock* bb) {
 189    :    BasicBlock::Instructions::const_iterator instr_it(
 190  E :        bb->instructions().begin());
 191  E :    for (; instr_it != bb->instructions().end(); ++instr_it) {
 192  E :      const block_graph::Instruction& instr = *instr_it;
 193    :  
 194  E :      _CodeInfo code = {};
 195  E :      code.codeOffset = 0;
 196  E :      code.code = instr.data();
 197  E :      code.codeLen = instr.size();
 198  E :      code.dt = Decode32Bits;
 199  E :      _DecodedInst decoded = {};
 200  E :      _DInst dinst = instr.representation();
 201    :  
 202  E :      dinst.addr = 0;
 203  E :      distorm_format(&code, &dinst, &decoded);
 204    :      ::fprintf(out(), "  %-14s %s %s",
 205    :                decoded.instructionHex.p,
 206    :                decoded.mnemonic.p,
 207  E :                decoded.operands.p);
 208    :  
 209    :      BasicBlock::BasicBlockReferenceMap::const_iterator ref_it(
 210  E :          instr_it->references().begin());
 211  E :      for (; ref_it != instr_it->references().end(); ++ref_it) {
 212  E :        DumpReference(ref_it->second, out());
 213  E :      }
 214  E :      ::fprintf(out(), "\n");
 215  E :    }
 216    :  
 217  E :    BasicBlock::Successors::const_iterator succ_it(bb->successors().begin());
 218  E :    for (; succ_it != bb->successors().end(); ++succ_it) {
 219  E :      const block_graph::Successor& succ = *succ_it;
 220    :  
 221    :      // Shortcut alert! As we know the blocks are in-order right after
 222    :      // decomposition, we can get away with just disassembling the (sole)
 223    :      // successor that has a size.
 224    :      // The other successor, if any, will be fall-through.
 225  E :      const char* opcode = NULL;
 226  E :      if (succ.instruction_size()) {
 227  E :        _CodeInfo code = {};
 228  E :        code.codeOffset = 0;
 229  E :        code.code = block->data() + succ.instruction_offset();
 230  E :        code.codeLen = succ.instruction_size();
 231  E :        code.dt = Decode32Bits;
 232  E :        _DecodedInst decoded = {};
 233  E :        _DInst instr = {};
 234    :  
 235  E :        unsigned int count = 0;
 236  E :        distorm_decompose64(&code, &instr, 1, &count);
 237  E :        instr.addr = 0;
 238  E :        distorm_format(&code, &instr, &decoded);
 239    :        ::fprintf(out(), "  %-14s %s %s",
 240    :                  decoded.instructionHex.p,
 241    :                  decoded.mnemonic.p,
 242  E :                  decoded.operands.p);
 243    :  
 244  E :        DumpReference(succ.reference(), out());
 245  E :        ::fprintf(out(), "\n");
 246    :      }
 247  E :    }
 248  E :  }
 249    :  
 250    :  void DecomposeImageToTextApp::DumpDataBBToText(
 251  i :      const BlockGraph::Block* block, const BasicBlock* bb) {
 252    :    // Here we proceed by dumping a hex chunk up to the next reference, then
 253    :    // the reference and so on.
 254  i :    size_t curr_start = 0;
 255    :  
 256  i :    while (curr_start < bb->size()) {
 257    :      BasicBlock::BasicBlockReferenceMap::const_iterator it(
 258  i :          bb->references().lower_bound(curr_start));
 259    :  
 260  i :      size_t next_chunk_end = bb->size();
 261  i :      if (it != bb->references().end())
 262  i :        next_chunk_end = it->first;
 263  i :      if (next_chunk_end == curr_start) {
 264    :        // We're on a reference, dump it and it's reference.
 265  i :        switch (it->second.size()) {
 266    :          case 1:
 267  i :            ::fprintf(out(), "  DB ");
 268  i :            break;
 269    :          case 2:
 270  i :            ::fprintf(out(), "  DW ");
 271  i :            break;
 272    :          case 4:
 273  i :            ::fprintf(out(), "  DD ");
 274  i :            break;
 275    :          default:
 276  i :            NOTREACHED();
 277    :            break;
 278    :        }
 279  i :        HexDump(bb->data() + curr_start, it->second.size(), out());
 280  i :        DumpReference(it->second, out());
 281  i :        ::fprintf(out(), "\n");
 282    :  
 283  i :        curr_start += it->second.size();
 284  i :      } else {
 285  i :        if (next_chunk_end - curr_start > 16)
 286  i :          next_chunk_end = curr_start + 16;
 287    :  
 288  i :        ::fprintf(out(), "  DB ");
 289  i :        HexDump(bb->data() + curr_start, next_chunk_end - curr_start, out());
 290  i :        ::fprintf(out(), "\n");
 291    :  
 292  i :        curr_start = next_chunk_end;
 293    :      }
 294  i :    }
 295  i :  }
 296    :  
 297    :  void DecomposeImageToTextApp::DumpBlockToText(
 298  E :      core::RelativeAddress addr, const BlockGraph::Block* block) {
 299    :    ::fprintf(out(), "0x%08X(%d): %s\n",
 300    :              addr.value(),
 301    :              block->size(),
 302  E :              block->name().c_str());
 303    :  
 304    :    // Attempt basic block decomposition if BB-dumping is requested.
 305    :    // Note that on success we return early from here.
 306    :    // TODO(siggi): Remove the cl consistent check and section contrib checks
 307    :    //     once the BB decomposer is no longer asserting on non-consistent inputs.
 308    :    if (dump_basic_blocks_ &&
 309    :        block->type() == BlockGraph::CODE_BLOCK &&
 310    :        block->attributes() == BlockGraph::SECTION_CONTRIB &&
 311  E :        pe::CodeBlockIsClConsistent(block)) {
 312  E :      BasicBlockSubGraph subgraph;
 313  E :      BasicBlockDecomposer decomposer(block, &subgraph);
 314    :  
 315  E :      if (decomposer.Decompose()) {
 316  E :        DumpSubGraphToText(subgraph);
 317  E :        return;
 318    :      }
 319    :      // Fall through on failure to decompose.
 320  i :    }
 321    :  
 322    :    BlockGraph::Block::LabelMap::const_iterator
 323  E :        label_it(block->labels().begin());
 324  E :    for (; label_it != block->labels().end(); ++label_it) {
 325    :      ::fprintf(out(), "\t+0x%04X: %s\n",
 326    :                label_it->first,
 327  E :                label_it->second.ToString().c_str());
 328  E :    }
 329    :  
 330    :    BlockGraph::Block::ReferenceMap::const_iterator ref_it(
 331  E :        block->references().begin());
 332  E :    for (; ref_it != block->references().end(); ++ref_it) {
 333  E :      ++num_refs_;
 334  E :      const BlockGraph::Reference& ref = ref_it->second;
 335  E :      if (ref.offset() == 0) {
 336    :        ::fprintf(out(), "\t+0x%04X->%s(%d)\n",
 337    :                  ref_it->first,
 338    :                  ref.referenced()->name().c_str(),
 339  E :                  ref.size());
 340  E :      } else {
 341    :        // See if there's a label at the desination's offset, and if so
 342    :        // use that in preference to a raw numeric offset.
 343    :        BlockGraph::Block::LabelMap::const_iterator label =
 344  E :            ref.referenced()->labels().find(ref.offset());
 345  E :        if (label != ref.referenced()->labels().end()) {
 346    :          ::fprintf(out(), "\t+0x%04X->%s:%s[%d]\n",
 347    :                    ref_it->first,
 348    :                    ref.referenced()->name().c_str(),
 349    :                    label->second.ToString().c_str(),
 350  E :                    ref.size());
 351  E :        } else {
 352    :          ::fprintf(out(), "\t+0x%04X->%s+0x%04X(%d)\n",
 353    :                    ref_it->first,
 354    :                    ref.referenced()->name().c_str(),
 355    :                    ref.offset(),
 356  E :                    ref.size());
 357    :        }
 358  E :      }
 359  E :    }
 360  E :  }
 361    :  
 362  E :  bool DecomposeImageToTextApp::DumpImageToText(const FilePath& image_path) {
 363    :    // Load the image file.
 364  E :    PEFile image_file;
 365  E :    if (!image_file.Init(image_path)) {
 366  i :      LOG(ERROR) << "Unable to initialize image " << image_path.value();
 367  i :      return false;
 368    :    }
 369    :  
 370    :    // And decompose it to an ImageLayout.
 371  E :    Decomposer decomposer(image_file);
 372  E :    BlockGraph block_graph;
 373  E :    ImageLayout image_layout(&block_graph);
 374  E :    if (!decomposer.Decompose(&image_layout)) {
 375  i :      LOG(ERROR) << "Unable to decompose image \"" << image_path.value() << "\".";
 376  i :      return false;
 377    :    }
 378    :  
 379  E :    num_refs_ = 0;
 380  E :    DumpAddressSpaceToText(image_layout.blocks);
 381    :  
 382    :    ::fprintf(out(), "Discovered: %d blocks\nand %d references.\n",
 383    :              block_graph.blocks().size(),
 384  E :              num_refs_);
 385    :  
 386  E :    return true;
 387  E :  }
 388    :  
 389    :  }  // namespace pe

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