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