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
|