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