1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "dexter.h"
18 #include "experimental.h"
19 #include "slicer/common.h"
20 #include "slicer/scopeguard.h"
21 #include "slicer/reader.h"
22 #include "slicer/writer.h"
23 #include "slicer/chronometer.h"
24
25 #include <getopt.h>
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <memory>
29 #include <sstream>
30 #include <map>
31
32 // Converts a class name to a type descriptor
33 // (ex. "java.lang.String" to "Ljava/lang/String;")
ClassNameToDescriptor(const char * class_name)34 static std::string ClassNameToDescriptor(const char* class_name) {
35 std::stringstream ss;
36 ss << "L";
37 for (auto p = class_name; *p != '\0'; ++p) {
38 ss << (*p == '.' ? '/' : *p);
39 }
40 ss << ";";
41 return ss.str();
42 }
43
PrintHelp()44 void Dexter::PrintHelp() {
45 printf("\nDex manipulation tool %s\n\n", VERSION);
46 printf("dexter [flags...] [-e classname] [-o outfile] <dexfile>\n");
47 printf(" -h : help\n");
48 printf(" -s : print stats\n");
49 printf(" -e : extract a single class\n");
50 printf(" -l : list the classes defined in the dex file\n");
51 printf(" -v : verbose output\n");
52 printf(" -o : output a new .dex file\n");
53 printf(" -d : dissasemble method bodies\n");
54 printf(" -m : print .dex layout map\n");
55 printf(" --cfg : generate control flow graph (compact|verbose)\n");
56 printf("\n");
57 }
58
Run()59 int Dexter::Run() {
60 // names for the CFG options
61 const std::map<std::string, DexDissasembler::CfgType> cfg_type_names = {
62 { "none", DexDissasembler::CfgType::None },
63 { "compact", DexDissasembler::CfgType::Compact },
64 { "verbose", DexDissasembler::CfgType::Verbose },
65 };
66
67 // long cmdline options
68 enum { kBuildCFG = 1 };
69 const option long_options[] = {
70 { "cfg", required_argument, 0, kBuildCFG },
71 { 0, 0, 0, 0}
72 };
73
74 bool show_help = false;
75 int opt = 0;
76 int opt_index = 0;
77 while ((opt = ::getopt_long(argc_, argv_, "hlsvdmo:e:x:", long_options, &opt_index)) != -1) {
78 switch (opt) {
79 case kBuildCFG: {
80 auto it = cfg_type_names.find(::optarg);
81 if (it == cfg_type_names.end()) {
82 printf("Invalid --cfg type\n");
83 show_help = true;
84 }
85 cfg_type_ = it->second;
86 } break;
87 case 's':
88 stats_ = true;
89 break;
90 case 'v':
91 verbose_ = true;
92 break;
93 case 'l':
94 list_classes_ = true;
95 break;
96 case 'e':
97 extract_class_ = ::optarg;
98 break;
99 case 'd':
100 dissasemble_ = true;
101 break;
102 case 'm':
103 print_map_ = true;
104 break;
105 case 'o':
106 out_dex_filename_ = ::optarg;
107 break;
108 case 'x':
109 experiments_.push_back(::optarg);
110 break;
111 default:
112 show_help = true;
113 break;
114 }
115 }
116
117 if (show_help || ::optind + 1 != argc_) {
118 PrintHelp();
119 return 1;
120 }
121
122 dex_filename_ = argv_[::optind];
123 return ProcessDex();
124 }
125
126 // Print the layout map of the .dex sections
PrintDexMap(const dex::Reader & reader)127 static void PrintDexMap(const dex::Reader& reader) {
128 printf("\nSections summary: name, offset, size [count]\n");
129
130 const dex::MapList& dexMap = *reader.DexMapList();
131 for (dex::u4 i = 0; i < dexMap.size; ++i) {
132 const dex::MapItem& section = dexMap.list[i];
133 const char* sectionName = "UNKNOWN";
134 switch (section.type) {
135 case dex::kHeaderItem:
136 sectionName = "HeaderItem";
137 break;
138 case dex::kStringIdItem:
139 sectionName = "StringIdItem";
140 break;
141 case dex::kTypeIdItem:
142 sectionName = "TypeIdItem";
143 break;
144 case dex::kProtoIdItem:
145 sectionName = "ProtoIdItem";
146 break;
147 case dex::kFieldIdItem:
148 sectionName = "FieldIdItem";
149 break;
150 case dex::kMethodIdItem:
151 sectionName = "MethodIdItem";
152 break;
153 case dex::kClassDefItem:
154 sectionName = "ClassDefItem";
155 break;
156 case dex::kMapList:
157 sectionName = "MapList";
158 break;
159 case dex::kTypeList:
160 sectionName = "TypeList";
161 break;
162 case dex::kAnnotationSetRefList:
163 sectionName = "AnnotationSetRefList";
164 break;
165 case dex::kAnnotationSetItem:
166 sectionName = "AnnotationSetItem";
167 break;
168 case dex::kClassDataItem:
169 sectionName = "ClassDataItem";
170 break;
171 case dex::kCodeItem:
172 sectionName = "CodeItem";
173 break;
174 case dex::kStringDataItem:
175 sectionName = "StringDataItem";
176 break;
177 case dex::kDebugInfoItem:
178 sectionName = "DebugInfoItem";
179 break;
180 case dex::kAnnotationItem:
181 sectionName = "AnnotationItem";
182 break;
183 case dex::kEncodedArrayItem:
184 sectionName = "EncodedArrayItem";
185 break;
186 case dex::kAnnotationsDirectoryItem:
187 sectionName = "AnnotationsDirectoryItem";
188 break;
189 }
190
191 dex::u4 sectionByteSize = (i == dexMap.size - 1)
192 ? reader.Header()->file_size - section.offset
193 : dexMap.list[i + 1].offset - section.offset;
194
195 printf(" %-25s : %8x, %8x [%u]\n", sectionName, section.offset,
196 sectionByteSize, section.size);
197 }
198 }
199
200 // Print .dex IR stats
PrintDexIrStats(std::shared_ptr<const ir::DexFile> dex_ir)201 static void PrintDexIrStats(std::shared_ptr<const ir::DexFile> dex_ir) {
202 printf("\n.dex IR statistics:\n");
203 printf(" strings : %zu\n", dex_ir->strings.size());
204 printf(" types : %zu\n", dex_ir->types.size());
205 printf(" protos : %zu\n", dex_ir->protos.size());
206 printf(" fields : %zu\n", dex_ir->fields.size());
207 printf(" encoded_fields : %zu\n", dex_ir->encoded_fields.size());
208 printf(" methods : %zu\n", dex_ir->methods.size());
209 printf(" encoded_methods : %zu\n", dex_ir->encoded_methods.size());
210 printf(" classes : %zu\n", dex_ir->classes.size());
211 printf(" type_lists : %zu\n", dex_ir->type_lists.size());
212 printf(" code : %zu\n", dex_ir->code.size());
213 printf(" debug_info : %zu\n", dex_ir->debug_info.size());
214 printf(" encoded_values : %zu\n", dex_ir->encoded_values.size());
215 printf(" encoded_arrays : %zu\n", dex_ir->encoded_arrays.size());
216 printf(" annotations : %zu\n", dex_ir->annotations.size());
217 printf(" annotation_elements : %zu\n", dex_ir->annotation_elements.size());
218 printf(" annotation_sets : %zu\n", dex_ir->annotation_sets.size());
219 printf(" annotation_set_ref_lists : %zu\n", dex_ir->annotation_set_ref_lists.size());
220 printf(" annotations_directories : %zu\n", dex_ir->annotations_directories.size());
221 printf(" field_annotations : %zu\n", dex_ir->field_annotations.size());
222 printf(" method_annotations : %zu\n", dex_ir->method_annotations.size());
223 printf(" param_annotations : %zu\n", dex_ir->param_annotations.size());
224 printf("\n");
225 }
226
227 // Print .dex file stats
PrintDexFileStats(const dex::Reader & reader)228 static void PrintDexFileStats(const dex::Reader& reader) {
229 auto header = reader.Header();
230 auto map_list = reader.DexMapList();
231 printf("\n.dex file statistics:\n");
232 printf(" file_size : %u\n", header->file_size);
233 printf(" data_size : %u\n", header->data_size);
234 printf(" link_size : %u\n", header->link_size);
235 printf(" class_defs_size : %u\n", header->class_defs_size);
236 printf(" string_ids_size : %u\n", header->string_ids_size);
237 printf(" type_ids_size : %u\n", header->type_ids_size);
238 printf(" proto_ids_size : %u\n", header->proto_ids_size);
239 printf(" field_ids_size : %u\n", header->field_ids_size);
240 printf(" method_ids_size : %u\n", header->method_ids_size);
241 printf(" map_list_size : %u\n", map_list->size);
242 printf("\n");
243 }
244
245 // List all the classes defined in the dex file
ListClasses(const dex::Reader & reader)246 static void ListClasses(const dex::Reader& reader) {
247 printf("\nClass definitions:\n");
248 auto classes = reader.ClassDefs();
249 auto types = reader.TypeIds();
250 for (dex::u4 i = 0; i < classes.size(); ++i) {
251 auto typeId = types[classes[i].class_idx];
252 const char* descriptor = reader.GetStringMUTF8(typeId.descriptor_idx);
253 printf(" %s\n", dex::DescriptorToDecl(descriptor).c_str());
254 }
255 printf("\n");
256 }
257
258 // Create a new in-memory .dex image and optionally write it to disk
259 //
260 // NOTE: we always create the new in-memory image, even if we don't write it
261 // to an output file, for aggresive code coverage and perf timings
262 //
CreateNewImage(std::shared_ptr<ir::DexFile> dex_ir)263 bool Dexter::CreateNewImage(std::shared_ptr<ir::DexFile> dex_ir) {
264 // our custom (and trivial) allocator for dex::Writer
265 struct Allocator : public dex::Writer::Allocator {
266 virtual void* Allocate(size_t size) { return ::malloc(size); }
267 virtual void Free(void* ptr) { ::free(ptr); }
268 };
269
270 size_t new_image_size = 0;
271 dex::u1* new_image = nullptr;
272 Allocator allocator;
273
274 {
275 slicer::Chronometer chrono(writer_time_);
276
277 dex::Writer writer(dex_ir);
278 new_image = writer.CreateImage(&allocator, &new_image_size);
279 }
280
281 if (new_image == nullptr) {
282 printf("ERROR: Can't create a new .dex image\n");
283 return false;
284 }
285
286 SLICER_SCOPE_EXIT {
287 allocator.Free(new_image);
288 };
289
290 // write the new .dex image to disk?
291 if (out_dex_filename_ != nullptr) {
292 if (verbose_) {
293 printf("\nWriting: %s\n", out_dex_filename_);
294 }
295
296 // create output file
297 FILE* out_file = fopen(out_dex_filename_, "wb");
298 if (out_file == nullptr) {
299 printf("ERROR: Can't create output .dex file (%s)\n", out_dex_filename_);
300 return false;
301 }
302
303 SLICER_SCOPE_EXIT {
304 fclose(out_file);
305 };
306
307 // write the new image
308 SLICER_CHECK(fwrite(new_image, 1, new_image_size, out_file) == new_image_size);
309 }
310
311 return true;
312 }
313
314 // Main entry point for processing an input .dex file
ProcessDex()315 int Dexter::ProcessDex() {
316 if (verbose_) {
317 printf("\nReading: %s\n", dex_filename_);
318 }
319
320 // open input file
321 FILE* in_file = fopen(dex_filename_, "rb");
322 if (in_file == nullptr) {
323 printf("ERROR: Can't open input .dex file (%s)\n", dex_filename_);
324 return 1;
325 }
326
327 SLICER_SCOPE_EXIT {
328 fclose(in_file);
329 };
330
331 // calculate file size
332 fseek(in_file, 0, SEEK_END);
333 size_t in_size = ftell(in_file);
334
335 // allocate the in-memory .dex image buffer
336 std::unique_ptr<dex::u1[]> in_buff(new dex::u1[in_size]);
337
338 // read input .dex file
339 fseek(in_file, 0, SEEK_SET);
340 SLICER_CHECK(fread(in_buff.get(), 1, in_size, in_file) == in_size);
341
342 // initialize the .dex reader
343 dex::Reader reader(in_buff.get(), in_size);
344
345 // print the .dex map?
346 if (print_map_) {
347 PrintDexMap(reader);
348 }
349
350 // list classes?
351 if (list_classes_) {
352 ListClasses(reader);
353 }
354
355 // parse the .dex image
356 {
357 slicer::Chronometer chrono(reader_time_);
358
359 if (extract_class_ != nullptr) {
360 // extract the specified class only
361 std::string descriptor = ClassNameToDescriptor(extract_class_);
362 dex::u4 class_idx = reader.FindClassIndex(descriptor.c_str());
363 if (class_idx != dex::kNoIndex) {
364 reader.CreateClassIr(class_idx);
365 } else {
366 printf("ERROR: Can't find class (%s)\n", extract_class_);
367 return 1;
368 }
369 } else {
370 // build the full .dex IR
371 reader.CreateFullIr();
372 }
373 }
374
375 auto dex_ir = reader.GetIr();
376
377 // experiments
378 for (auto experiment : experiments_) {
379 slicer::Chronometer chrono(experiments_time_, true);
380 if (verbose_) {
381 printf("\nRunning experiment '%s' ... \n", experiment);
382 }
383 experimental::Run(experiment, dex_ir);
384 }
385
386 // dissasemble method bodies?
387 if (dissasemble_) {
388 DexDissasembler disasm(dex_ir, cfg_type_);
389 disasm.DumpAllMethods();
390 }
391
392 if(!CreateNewImage(dex_ir)) {
393 return 1;
394 }
395
396 // stats?
397 if (stats_) {
398 PrintDexFileStats(reader);
399 PrintDexIrStats(dex_ir);
400 }
401
402 if (verbose_) {
403 printf("\nDone (reader: %.3fms, writer: %.3fms", reader_time_, writer_time_);
404 if (!experiments_.empty()) {
405 printf(", experiments: %.3fms", experiments_time_);
406 }
407 printf(")\n");
408 }
409
410 // done
411 return 0;
412 }
413