1 /* 2 * Copyright (C) 2014 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 package dexfuzz.program; 18 19 import dexfuzz.Log; 20 import dexfuzz.MutationStats; 21 import dexfuzz.Options; 22 import dexfuzz.listeners.BaseListener; 23 import dexfuzz.program.mutators.ArithOpChanger; 24 import dexfuzz.program.mutators.BranchShifter; 25 import dexfuzz.program.mutators.CmpBiasChanger; 26 import dexfuzz.program.mutators.CodeMutator; 27 import dexfuzz.program.mutators.ConstantValueChanger; 28 import dexfuzz.program.mutators.ConversionRepeater; 29 import dexfuzz.program.mutators.FieldFlagChanger; 30 import dexfuzz.program.mutators.InstructionDeleter; 31 import dexfuzz.program.mutators.InstructionDuplicator; 32 import dexfuzz.program.mutators.InstructionSwapper; 33 import dexfuzz.program.mutators.InvokeChanger; 34 import dexfuzz.program.mutators.NewArrayLengthChanger; 35 import dexfuzz.program.mutators.NewInstanceChanger; 36 import dexfuzz.program.mutators.NewMethodCaller; 37 import dexfuzz.program.mutators.NonsenseStringPrinter; 38 import dexfuzz.program.mutators.OppositeBranchChanger; 39 import dexfuzz.program.mutators.PoolIndexChanger; 40 import dexfuzz.program.mutators.RandomBranchChanger; 41 import dexfuzz.program.mutators.RandomInstructionGenerator; 42 import dexfuzz.program.mutators.RegisterClobber; 43 import dexfuzz.program.mutators.SwitchBranchShifter; 44 import dexfuzz.program.mutators.TryBlockShifter; 45 import dexfuzz.program.mutators.ValuePrinter; 46 import dexfuzz.program.mutators.VRegChanger; 47 import dexfuzz.rawdex.ClassDataItem; 48 import dexfuzz.rawdex.ClassDefItem; 49 import dexfuzz.rawdex.CodeItem; 50 import dexfuzz.rawdex.DexRandomAccessFile; 51 import dexfuzz.rawdex.EncodedField; 52 import dexfuzz.rawdex.EncodedMethod; 53 import dexfuzz.rawdex.FieldIdItem; 54 import dexfuzz.rawdex.MethodIdItem; 55 import dexfuzz.rawdex.ProtoIdItem; 56 import dexfuzz.rawdex.RawDexFile; 57 import dexfuzz.rawdex.TypeIdItem; 58 import dexfuzz.rawdex.TypeList; 59 import dexfuzz.rawdex.formats.ContainsPoolIndex.PoolIndexKind; 60 61 import java.io.BufferedReader; 62 import java.io.BufferedWriter; 63 import java.io.FileReader; 64 import java.io.FileWriter; 65 import java.io.IOException; 66 import java.util.ArrayList; 67 import java.util.HashMap; 68 import java.util.List; 69 import java.util.Map; 70 import java.util.Random; 71 72 /** 73 * After the raw DEX file has been parsed, it is passed into this class 74 * that represents the program in a mutatable form. 75 * The class uses a CodeTranslator to translate between the raw DEX form 76 * for a method, and the mutatable form. It also controls all CodeMutators, 77 * deciding which ones should be applied to each CodeItem. 78 */ 79 public class Program { 80 /** 81 * The RNG used during mutation. 82 */ 83 private Random rng; 84 85 /** 86 * The seed that was given to the RNG. 87 */ 88 public long rngSeed; 89 90 /** 91 * The parsed raw DEX file. 92 */ 93 private RawDexFile rawDexFile; 94 95 /** 96 * The system responsible for translating from CodeItems to MutatableCode and vice-versa. 97 */ 98 private CodeTranslator translator; 99 100 /** 101 * Responsible for adding new class ID items, method ID items, etc. 102 */ 103 private IdCreator idCreator; 104 105 /** 106 * A list of all the MutatableCode that the CodeTranslator produced from 107 * CodeItems that are acceptable to mutate. 108 */ 109 private List<MutatableCode> mutatableCodes; 110 111 /** 112 * A list of all MutatableCode items that were mutated when mutateTheProgram() 113 * was called. updateRawDexFile() will update the relevant CodeItems when called, 114 * and then clear this list. 115 */ 116 private List<MutatableCode> mutatedCodes; 117 118 /** 119 * A list of all registered CodeMutators that this Program can use to mutate methods. 120 */ 121 private List<CodeMutator> mutators; 122 123 /** 124 * Used if we're loading mutations from a file, so we can find the correct mutator. 125 */ 126 private Map<Class<? extends CodeMutator>, CodeMutator> mutatorsLookupByClass; 127 128 /** 129 * Tracks mutation stats. 130 */ 131 private MutationStats mutationStats; 132 133 /** 134 * A list of all mutations used for loading/dumping mutations from/to a file. 135 */ 136 private List<Mutation> mutations; 137 138 /** 139 * The listener who is interested in events. 140 * May be a listener that is responsible for multiple listeners. 141 */ 142 private BaseListener listener; 143 144 /** 145 * Given a maximum number of mutations that can be performed on a method, n, 146 * give up after attempting (n * this value) mutations for any method. 147 */ 148 private static final int MAXIMUM_MUTATION_ATTEMPT_FACTOR = 10; 149 150 /** 151 * Construct the mutatable Program based on the raw DEX file that was parsed initially. 152 */ Program(RawDexFile rawDexFile, List<Mutation> previousMutations, BaseListener listener)153 public Program(RawDexFile rawDexFile, List<Mutation> previousMutations, 154 BaseListener listener) { 155 this.listener = listener; 156 157 idCreator = new IdCreator(rawDexFile); 158 159 // Set up the RNG. 160 rng = new Random(); 161 if (Options.usingProvidedSeed) { 162 rng.setSeed(Options.rngSeed); 163 rngSeed = Options.rngSeed; 164 } else { 165 long seed = System.currentTimeMillis(); 166 listener.handleSeed(seed); 167 rng.setSeed(seed); 168 rngSeed = seed; 169 } 170 171 if (previousMutations != null) { 172 mutations = previousMutations; 173 } else { 174 // Allocate the mutations list. 175 mutations = new ArrayList<Mutation>(); 176 177 // Read in the mutations if we need to. 178 if (Options.loadMutations) { 179 // Allocate the mutators lookup table. 180 mutatorsLookupByClass = new HashMap<Class<? extends CodeMutator>, CodeMutator>(); 181 loadMutationsFromDisk(Options.loadMutationsFile); 182 } 183 } 184 185 // Allocate the mutators list. 186 mutators = new ArrayList<CodeMutator>(); 187 188 this.rawDexFile = rawDexFile; 189 190 mutatableCodes = new ArrayList<MutatableCode>(); 191 mutatedCodes = new ArrayList<MutatableCode>(); 192 193 translator = new CodeTranslator(); 194 195 mutationStats = new MutationStats(); 196 197 // Register all the code mutators here. 198 registerMutator(new ArithOpChanger(rng, mutationStats, mutations)); 199 registerMutator(new BranchShifter(rng, mutationStats, mutations)); 200 registerMutator(new CmpBiasChanger(rng, mutationStats, mutations)); 201 registerMutator(new ConstantValueChanger(rng, mutationStats, mutations)); 202 registerMutator(new ConversionRepeater(rng, mutationStats, mutations)); 203 registerMutator(new FieldFlagChanger(rng, mutationStats, mutations)); 204 registerMutator(new InstructionDeleter(rng, mutationStats, mutations)); 205 registerMutator(new InstructionDuplicator(rng, mutationStats, mutations)); 206 registerMutator(new InstructionSwapper(rng, mutationStats, mutations)); 207 registerMutator(new InvokeChanger(rng, mutationStats, mutations)); 208 registerMutator(new NewArrayLengthChanger(rng, mutationStats, mutations)); 209 registerMutator(new NewInstanceChanger(rng, mutationStats, mutations)); 210 registerMutator(new NewMethodCaller(rng, mutationStats, mutations)); 211 registerMutator(new NonsenseStringPrinter(rng, mutationStats, mutations)); 212 registerMutator(new OppositeBranchChanger(rng, mutationStats, mutations)); 213 registerMutator(new PoolIndexChanger(rng, mutationStats, mutations)); 214 registerMutator(new RandomBranchChanger(rng, mutationStats, mutations)); 215 registerMutator(new RandomInstructionGenerator(rng, mutationStats, mutations)); 216 registerMutator(new RegisterClobber(rng, mutationStats, mutations)); 217 registerMutator(new SwitchBranchShifter(rng, mutationStats, mutations)); 218 registerMutator(new TryBlockShifter(rng, mutationStats, mutations)); 219 registerMutator(new ValuePrinter(rng, mutationStats, mutations)); 220 registerMutator(new VRegChanger(rng, mutationStats, mutations)); 221 222 associateClassDefsAndClassData(); 223 associateCodeItemsWithMethodNames(); 224 225 int codeItemIdx = 0; 226 for (CodeItem codeItem : rawDexFile.codeItems) { 227 if (legalToMutate(codeItem)) { 228 Log.debug("Legal to mutate code item " + codeItemIdx); 229 int mutatableCodeIdx = mutatableCodes.size(); 230 mutatableCodes.add(translator.codeItemToMutatableCode(this, codeItem, 231 codeItemIdx, mutatableCodeIdx)); 232 } else { 233 Log.debug("Not legal to mutate code item " + codeItemIdx); 234 } 235 codeItemIdx++; 236 } 237 } 238 registerMutator(CodeMutator mutator)239 private void registerMutator(CodeMutator mutator) { 240 if (mutator.canBeTriggered()) { 241 Log.debug("Registering mutator " + mutator.getClass().getSimpleName()); 242 mutators.add(mutator); 243 } 244 if (Options.loadMutations) { 245 mutatorsLookupByClass.put(mutator.getClass(), mutator); 246 } 247 } 248 249 /** 250 * Associate ClassDefItem to a ClassDataItem and vice-versa. 251 * This is so when we're associating method names with code items, 252 * we can find the name of the class the method belongs to. 253 */ associateClassDefsAndClassData()254 private void associateClassDefsAndClassData() { 255 for (ClassDefItem classDefItem : rawDexFile.classDefs) { 256 if (classDefItem.classDataOff.pointsToSomething()) { 257 ClassDataItem classDataItem = (ClassDataItem) 258 classDefItem.classDataOff.getPointedToItem(); 259 classDataItem.meta.classDefItem = classDefItem; 260 classDefItem.meta.classDataItem = classDataItem; 261 } 262 } 263 } 264 265 /** 266 * For each CodeItem, find the name of the method the item represents. 267 * This is done to allow the filtering of mutating methods based on if 268 * they have the name *_MUTATE, but also for debugging info. 269 */ associateCodeItemsWithMethodNames()270 private void associateCodeItemsWithMethodNames() { 271 // Associate method names with codeItems. 272 for (ClassDataItem classDataItem : rawDexFile.classDatas) { 273 274 String className = ""; 275 if (classDataItem.meta.classDefItem != null) { 276 int typeIdx = classDataItem.meta.classDefItem.classIdx; 277 TypeIdItem typeIdItem = rawDexFile.typeIds.get(typeIdx); 278 className = rawDexFile.stringDatas.get(typeIdItem.descriptorIdx).getString() + "."; 279 } 280 281 // Do direct methods... 282 // Track the current method index with this value, since the encoding in 283 // each EncodedMethod is the absolute index for the first EncodedMethod, 284 // and then relative index for the rest... 285 int methodIdx = 0; 286 for (EncodedMethod method : classDataItem.directMethods) { 287 methodIdx = associateMethod(method, methodIdx, className); 288 } 289 // Reset methodIdx for virtual methods... 290 methodIdx = 0; 291 for (EncodedMethod method : classDataItem.virtualMethods) { 292 methodIdx = associateMethod(method, methodIdx, className); 293 } 294 } 295 } 296 297 /** 298 * Associate the name of the provided method with its CodeItem, if it 299 * has one. 300 * 301 * @param methodIdx The method index of the last EncodedMethod that was handled in this class. 302 * @return The method index of the EncodedMethod that has just been handled in this class. 303 */ associateMethod(EncodedMethod method, int methodIdx, String className)304 private int associateMethod(EncodedMethod method, int methodIdx, String className) { 305 if (!method.codeOff.pointsToSomething()) { 306 // This method doesn't have a code item, so we won't encounter it later. 307 return methodIdx; 308 } 309 310 // First method index is an absolute index. 311 // The rest are relative to the previous. 312 // (so if methodIdx is initialised to 0, this single line works) 313 methodIdx = methodIdx + method.methodIdxDiff; 314 315 // Get the name. 316 MethodIdItem methodIdItem = rawDexFile.methodIds.get(methodIdx); 317 ProtoIdItem protoIdItem = rawDexFile.protoIds.get(methodIdItem.protoIdx); 318 String shorty = rawDexFile.stringDatas.get(protoIdItem.shortyIdx).getString(); 319 String methodName = className 320 + rawDexFile.stringDatas.get(methodIdItem.nameIdx).getString(); 321 322 // Get the codeItem. 323 if (method.codeOff.getPointedToItem() instanceof CodeItem) { 324 CodeItem codeItem = (CodeItem) method.codeOff.getPointedToItem(); 325 codeItem.meta.methodName = methodName; 326 codeItem.meta.shorty = shorty; 327 codeItem.meta.isStatic = method.isStatic(); 328 } else { 329 Log.errorAndQuit("You've got an EncodedMethod that points to an Offsettable" 330 + " that does not contain a CodeItem"); 331 } 332 333 return methodIdx; 334 } 335 336 /** 337 * Determine, based on the current options supplied to dexfuzz, as well as 338 * its capabilities, if the provided CodeItem can be mutated. 339 * @param codeItem The CodeItem we may wish to mutate. 340 * @return If the CodeItem can be mutated. 341 */ legalToMutate(CodeItem codeItem)342 private boolean legalToMutate(CodeItem codeItem) { 343 if (!Options.mutateLimit) { 344 Log.debug("Mutating everything."); 345 return true; 346 } 347 if (codeItem.meta.methodName.endsWith("_MUTATE")) { 348 Log.debug("Code item marked with _MUTATE."); 349 return true; 350 } 351 Log.debug("Code item not marked with _MUTATE, but not mutating all code items."); 352 return false; 353 } 354 getNumberOfMutationsToPerform()355 private int getNumberOfMutationsToPerform() { 356 // We want n mutations to be twice as likely as n+1 mutations. 357 // 358 // So if we have max 3, 359 // then 0 has 8 chances ("tickets"), 360 // 1 has 4 chances 361 // 2 has 2 chances 362 // and 3 has 1 chance 363 364 // Allocate the tickets 365 // n mutations need (2^(n+1) - 1) tickets 366 // e.g. 367 // 3 mutations => 15 tickets 368 // 4 mutations => 31 tickets 369 int tickets = (2 << Options.methodMutations) - 1; 370 371 // Pick the lucky ticket 372 int luckyTicket = rng.nextInt(tickets); 373 374 // The tickets are put into buckets with accordance with log-base-2. 375 // have to make sure it's luckyTicket + 1, because log(0) is undefined 376 // so: 377 // log_2(1) => 0 378 // log_2(2) => 1 379 // log_2(3) => 1 380 // log_2(4) => 2 381 // log_2(5) => 2 382 // log_2(6) => 2 383 // log_2(7) => 2 384 // log_2(8) => 3 385 // ... 386 // so to make the highest mutation value the rarest, 387 // subtract log_2(luckyTicket+1) from the maximum number 388 // log2(x) <=> 31 - Integer.numberOfLeadingZeros(x) 389 int luckyMutation = Options.methodMutations 390 - (31 - Integer.numberOfLeadingZeros(luckyTicket + 1)); 391 392 return luckyMutation; 393 } 394 395 /** 396 * Returns true if we completely failed to mutate this method's mutatable code after 397 * attempting to. 398 */ mutateAMutatableCode(MutatableCode mutatableCode)399 private boolean mutateAMutatableCode(MutatableCode mutatableCode) { 400 int mutations = getNumberOfMutationsToPerform(); 401 402 Log.info("Attempting " + mutations + " mutations for method " + mutatableCode.name); 403 404 int mutationsApplied = 0; 405 406 int maximumMutationAttempts = Options.methodMutations * MAXIMUM_MUTATION_ATTEMPT_FACTOR; 407 int mutationAttempts = 0; 408 boolean hadToBail = false; 409 410 while (mutationsApplied < mutations) { 411 int mutatorIdx = rng.nextInt(mutators.size()); 412 CodeMutator mutator = mutators.get(mutatorIdx); 413 Log.info("Running mutator " + mutator.getClass().getSimpleName()); 414 if (mutator.attemptToMutate(mutatableCode)) { 415 mutationsApplied++; 416 } 417 mutationAttempts++; 418 if (mutationAttempts > maximumMutationAttempts) { 419 Log.info("Bailing out on mutation for this method, tried too many times..."); 420 hadToBail = true; 421 break; 422 } 423 } 424 425 // If any of them actually mutated it, excellent! 426 if (mutationsApplied > 0) { 427 Log.info("Method was mutated."); 428 mutatedCodes.add(mutatableCode); 429 } else { 430 Log.info("Method was not mutated."); 431 } 432 433 return ((mutationsApplied == 0) && hadToBail); 434 } 435 436 /** 437 * Go through each mutatable method in turn, and attempt to mutate it. 438 * Afterwards, call updateRawDexFile() to apply the results of mutation to the 439 * original code. 440 */ mutateTheProgram()441 public void mutateTheProgram() { 442 if (Options.loadMutations) { 443 applyMutationsFromList(); 444 return; 445 } 446 447 // Typically, this is 2 to 10... 448 int methodsToMutate = Options.minMethods 449 + rng.nextInt((Options.maxMethods - Options.minMethods) + 1); 450 451 // Check we aren't trying to mutate more methods than we have. 452 if (methodsToMutate > mutatableCodes.size()) { 453 methodsToMutate = mutatableCodes.size(); 454 } 455 456 // Check if we're going to end up mutating all the methods. 457 if (methodsToMutate == mutatableCodes.size()) { 458 // Just do them all in order. 459 Log.info("Mutating all possible methods."); 460 for (MutatableCode mutatableCode : mutatableCodes) { 461 if (mutatableCode == null) { 462 Log.errorAndQuit("Why do you have a null MutatableCode?"); 463 } 464 mutateAMutatableCode(mutatableCode); 465 } 466 Log.info("Finished mutating all possible methods."); 467 } else { 468 // Pick them at random. 469 Log.info("Randomly selecting " + methodsToMutate + " methods to mutate."); 470 while (mutatedCodes.size() < methodsToMutate) { 471 int randomMethodIdx = rng.nextInt(mutatableCodes.size()); 472 MutatableCode mutatableCode = mutatableCodes.get(randomMethodIdx); 473 if (mutatableCode == null) { 474 Log.errorAndQuit("Why do you have a null MutatableCode?"); 475 } 476 if (!mutatedCodes.contains(mutatableCode)) { 477 boolean completelyFailedToMutate = mutateAMutatableCode(mutatableCode); 478 if (completelyFailedToMutate) { 479 methodsToMutate--; 480 } 481 } 482 } 483 Log.info("Finished mutating the methods."); 484 } 485 486 listener.handleMutationStats(mutationStats.getStatsString()); 487 488 if (Options.dumpMutations) { 489 writeMutationsToDisk(Options.dumpMutationsFile); 490 } 491 } 492 writeMutationsToDisk(String fileName)493 private void writeMutationsToDisk(String fileName) { 494 Log.debug("Writing mutations to disk."); 495 try { 496 BufferedWriter writer = new BufferedWriter(new FileWriter(fileName)); 497 for (Mutation mutation : mutations) { 498 MutationSerializer.writeMutation(writer, mutation); 499 } 500 writer.close(); 501 } catch (IOException e) { 502 Log.errorAndQuit("IOException while writing mutations to disk..."); 503 } 504 } 505 loadMutationsFromDisk(String fileName)506 private void loadMutationsFromDisk(String fileName) { 507 Log.debug("Loading mutations from disk."); 508 try { 509 BufferedReader reader = new BufferedReader(new FileReader(fileName)); 510 while (reader.ready()) { 511 Mutation mutation = MutationSerializer.readMutation(reader); 512 mutations.add(mutation); 513 } 514 reader.close(); 515 } catch (IOException e) { 516 Log.errorAndQuit("IOException while loading mutations from disk..."); 517 } 518 } 519 applyMutationsFromList()520 private void applyMutationsFromList() { 521 Log.info("Applying preloaded list of mutations..."); 522 for (Mutation mutation : mutations) { 523 // Repopulate the MutatableCode field from the recorded index into the Program's list. 524 mutation.mutatableCode = mutatableCodes.get(mutation.mutatableCodeIdx); 525 526 // Get the right mutator. 527 CodeMutator mutator = mutatorsLookupByClass.get(mutation.mutatorClass); 528 529 // Apply the mutation. 530 mutator.forceMutate(mutation); 531 532 // Add this mutatable code to the list of mutated codes, if we haven't already. 533 if (!mutatedCodes.contains(mutation.mutatableCode)) { 534 mutatedCodes.add(mutation.mutatableCode); 535 } 536 } 537 Log.info("...finished applying preloaded list of mutations."); 538 } 539 getMutations()540 public List<Mutation> getMutations() { 541 return mutations; 542 } 543 544 /** 545 * Updates any CodeItems that need to be updated after mutation. 546 */ updateRawDexFile()547 public boolean updateRawDexFile() { 548 boolean anythingMutated = !(mutatedCodes.isEmpty()); 549 for (MutatableCode mutatedCode : mutatedCodes) { 550 translator.mutatableCodeToCodeItem(rawDexFile.codeItems 551 .get(mutatedCode.codeItemIdx), mutatedCode); 552 } 553 mutatedCodes.clear(); 554 return anythingMutated; 555 } 556 writeRawDexFile(DexRandomAccessFile file)557 public void writeRawDexFile(DexRandomAccessFile file) throws IOException { 558 rawDexFile.write(file); 559 } 560 updateRawDexFileHeader(DexRandomAccessFile file)561 public void updateRawDexFileHeader(DexRandomAccessFile file) throws IOException { 562 rawDexFile.updateHeader(file); 563 } 564 565 /** 566 * Used by the CodeMutators to determine legal index values. 567 */ getTotalPoolIndicesByKind(PoolIndexKind poolIndexKind)568 public int getTotalPoolIndicesByKind(PoolIndexKind poolIndexKind) { 569 switch (poolIndexKind) { 570 case Type: 571 return rawDexFile.typeIds.size(); 572 case Field: 573 return rawDexFile.fieldIds.size(); 574 case String: 575 return rawDexFile.stringIds.size(); 576 case Method: 577 return rawDexFile.methodIds.size(); 578 case Invalid: 579 return 0; 580 default: 581 } 582 return 0; 583 } 584 585 /** 586 * Used by the CodeMutators to lookup and/or create Ids. 587 */ getNewItemCreator()588 public IdCreator getNewItemCreator() { 589 return idCreator; 590 } 591 592 /** 593 * Used by FieldFlagChanger, to find an EncodedField for a specified field in an insn, 594 * if that field is actually defined in this DEX file. If not, null is returned. 595 */ getEncodedField(int fieldIdx)596 public EncodedField getEncodedField(int fieldIdx) { 597 if (fieldIdx >= rawDexFile.fieldIds.size()) { 598 Log.debug(String.format("Field idx 0x%x specified is not defined in this DEX file.", 599 fieldIdx)); 600 return null; 601 } 602 FieldIdItem fieldId = rawDexFile.fieldIds.get(fieldIdx); 603 604 for (ClassDefItem classDef : rawDexFile.classDefs) { 605 if (classDef.classIdx == fieldId.classIdx) { 606 ClassDataItem classData = classDef.meta.classDataItem; 607 return classData.getEncodedFieldWithIndex(fieldIdx); 608 } 609 } 610 611 Log.debug(String.format("Field idx 0x%x specified is not defined in this DEX file.", 612 fieldIdx)); 613 return null; 614 } 615 616 /** 617 * Used to convert the type index into string format. 618 * @param typeIdx 619 * @return string format of type index. 620 */ getTypeString(int typeIdx)621 public String getTypeString(int typeIdx) { 622 TypeIdItem typeIdItem = rawDexFile.typeIds.get(typeIdx); 623 return rawDexFile.stringDatas.get(typeIdItem.descriptorIdx).getString(); 624 } 625 626 /** 627 * Used to convert the method index into string format. 628 * @param methodIdx 629 * @return string format of method index. 630 */ getMethodString(int methodIdx)631 public String getMethodString(int methodIdx) { 632 MethodIdItem methodIdItem = rawDexFile.methodIds.get(methodIdx); 633 return rawDexFile.stringDatas.get(methodIdItem.nameIdx).getString(); 634 } 635 636 /** 637 * Used to convert methodID to string format of method proto. 638 * @param methodIdx 639 * @return string format of shorty. 640 */ getMethodProto(int methodIdx)641 public String getMethodProto(int methodIdx) { 642 MethodIdItem methodIdItem = rawDexFile.methodIds.get(methodIdx); 643 ProtoIdItem protoIdItem = rawDexFile.protoIds.get(methodIdItem.protoIdx); 644 645 if (!protoIdItem.parametersOff.pointsToSomething()) { 646 return "()" + getTypeString(protoIdItem.returnTypeIdx); 647 } 648 649 TypeList typeList = (TypeList) protoIdItem.parametersOff.getPointedToItem(); 650 String typeItem = "("; 651 for (int i= 0; i < typeList.size; i++) { 652 typeItem = typeItem + typeList.list[i]; 653 } 654 return typeItem + ")" + getTypeString(protoIdItem.returnTypeIdx); 655 } 656 }