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