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