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