• 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 #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