Home | History | Annotate | Download | only in dexter
      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;")
     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 
     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 
     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
    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
    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
    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
    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 //
    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
    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