Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  Program.java

  Sprache: JAVA
 

/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package dexfuzz.program;

import dexfuzz.Log;
import dexfuzz.MutationStats;
import dexfuzz.Options;
import dexfuzz.listeners.BaseListener;
import dexfuzz.program.mutators.ArithOpChanger;
import dexfuzz.program.mutators.BranchShifter;
import dexfuzz.program.mutators.CmpBiasChanger;
import dexfuzz.program.mutators.CodeMutator;
import dexfuzz.program.mutators.ConstantValueChanger;
import dexfuzz.program.mutators.ConversionRepeater;
import dexfuzz.program.mutators.FieldFlagChanger;
import dexfuzz.program.mutators.InstructionDeleter;
import dexfuzz.program.mutators.InstructionDuplicator;
import dexfuzz.program.mutators.InstructionSwapper;
import dexfuzz.program.mutators.InvokeChanger;
import dexfuzz.program.mutators.NewArrayLengthChanger;
import dexfuzz.program.mutators.NewInstanceChanger;
import dexfuzz.program.mutators.NewMethodCaller;
import dexfuzz.program.mutators.NonsenseStringPrinter;
import dexfuzz.program.mutators.OppositeBranchChanger;
import dexfuzz.program.mutators.PoolIndexChanger;
import dexfuzz.program.mutators.RandomBranchChanger;
import dexfuzz.program.mutators.RandomInstructionGenerator;
import dexfuzz.program.mutators.RegisterClobber;
import dexfuzz.program.mutators.SwitchBranchShifter;
import dexfuzz.program.mutators.TryBlockShifter;
import dexfuzz.program.mutators.ValuePrinter;
import dexfuzz.program.mutators.VRegChanger;
import dexfuzz.rawdex.ClassDataItem;
import dexfuzz.rawdex.ClassDefItem;
import dexfuzz.rawdex.CodeItem;
import dexfuzz.rawdex.DexRandomAccessFile;
import dexfuzz.rawdex.EncodedField;
import dexfuzz.rawdex.EncodedMethod;
import dexfuzz.rawdex.FieldIdItem;
import dexfuzz.rawdex.MethodIdItem;
import dexfuzz.rawdex.ProtoIdItem;
import dexfuzz.rawdex.RawDexFile;
import dexfuzz.rawdex.TypeIdItem;
import dexfuzz.rawdex.TypeList;
import dexfuzz.rawdex.formats.ContainsPoolIndex.PoolIndexKind;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

/**
 * After the raw DEX file has been parsed, it is passed into this class
 * that represents the program in a mutatable form.
 * The class uses a CodeTranslator to translate between the raw DEX form
 * for a method, and the mutatable form. It also controls all CodeMutators,
 * deciding which ones should be applied to each CodeItem.
 */

public class Program {
  /**
   * The RNG used during mutation.
   */

  private Random rng;

  /**
   * The seed that was given to the RNG.
   */

  public long rngSeed;

  /**
   * The parsed raw DEX file.
   */

  private RawDexFile rawDexFile;

  /**
   * The system responsible for translating from CodeItems to MutatableCode and vice-versa.
   */

  private CodeTranslator translator;

  /**
   * Responsible for adding new class ID items, method ID items, etc.
   */

  private IdCreator idCreator;

  /**
   * A list of all the MutatableCode that the CodeTranslator produced from
   * CodeItems that are acceptable to mutate.
   */

  private List<MutatableCode> mutatableCodes;

  /**
   * A list of all MutatableCode items that were mutated when mutateTheProgram()
   * was called. updateRawDexFile() will update the relevant CodeItems when called,
   * and then clear this list.
   */

  private List<MutatableCode> mutatedCodes;

  /**
   * A list of all registered CodeMutators that this Program can use to mutate methods.
   */

  private List<CodeMutator> mutators;

  /**
   * Used if we're loading mutations from a file, so we can find the correct mutator.
   */

  private Map<Class<? extends CodeMutator>, CodeMutator> mutatorsLookupByClass;

  /**
   * Tracks mutation stats.
   */

  private MutationStats mutationStats;

  /**
   * A list of all mutations used for loading/dumping mutations from/to a file.
   */

  private List<Mutation> mutations;

  /**
   * The listener who is interested in events.
   * May be a listener that is responsible for multiple listeners.
   */

  private BaseListener listener;

  /**
   * Given a maximum number of mutations that can be performed on a method, n,
   * give up after attempting (n * this value) mutations for any method.
   */

  private static final int MAXIMUM_MUTATION_ATTEMPT_FACTOR = 10;

  /**
   * Construct the mutatable Program based on the raw DEX file that was parsed initially.
   */

  public Program(RawDexFile rawDexFile, List<Mutation> previousMutations,
      BaseListener listener) {
    this.listener = listener;

    idCreator = new IdCreator(rawDexFile);

    // Set up the RNG.
    rng = new Random();
    if (Options.usingProvidedSeed) {
      rng.setSeed(Options.rngSeed);
      rngSeed = Options.rngSeed;
    } else {
      long seed = System.currentTimeMillis();
      listener.handleSeed(seed);
      rng.setSeed(seed);
      rngSeed = seed;
    }

    if (previousMutations != null) {
      mutations = previousMutations;
    } else {
      // Allocate the mutations list.
      mutations = new ArrayList<Mutation>();

      // Read in the mutations if we need to.
      if (Options.loadMutations) {
        // Allocate the mutators lookup table.
        mutatorsLookupByClass = new HashMap<Class<? extends CodeMutator>, CodeMutator>();
        loadMutationsFromDisk(Options.loadMutationsFile);
      }
    }

    // Allocate the mutators list.
    mutators = new ArrayList<CodeMutator>();

    this.rawDexFile = rawDexFile;

    mutatableCodes = new ArrayList<MutatableCode>();
    mutatedCodes = new ArrayList<MutatableCode>();

    translator = new CodeTranslator();

    mutationStats = new MutationStats();

    // Register all the code mutators here.
    registerMutator(new ArithOpChanger(rng, mutationStats, mutations));
    registerMutator(new BranchShifter(rng, mutationStats, mutations));
    registerMutator(new CmpBiasChanger(rng, mutationStats, mutations));
    registerMutator(new ConstantValueChanger(rng, mutationStats, mutations));
    registerMutator(new ConversionRepeater(rng, mutationStats, mutations));
    registerMutator(new FieldFlagChanger(rng, mutationStats, mutations));
    registerMutator(new InstructionDeleter(rng, mutationStats, mutations));
    registerMutator(new InstructionDuplicator(rng, mutationStats, mutations));
    registerMutator(new InstructionSwapper(rng, mutationStats, mutations));
    registerMutator(new InvokeChanger(rng, mutationStats, mutations));
    registerMutator(new NewArrayLengthChanger(rng, mutationStats, mutations));
    registerMutator(new NewInstanceChanger(rng, mutationStats, mutations));
    registerMutator(new NewMethodCaller(rng, mutationStats, mutations));
    registerMutator(new NonsenseStringPrinter(rng, mutationStats, mutations));
    registerMutator(new OppositeBranchChanger(rng, mutationStats, mutations));
    registerMutator(new PoolIndexChanger(rng, mutationStats, mutations));
    registerMutator(new RandomBranchChanger(rng, mutationStats, mutations));
    registerMutator(new RandomInstructionGenerator(rng, mutationStats, mutations));
    registerMutator(new RegisterClobber(rng, mutationStats, mutations));
    registerMutator(new SwitchBranchShifter(rng, mutationStats, mutations));
    registerMutator(new TryBlockShifter(rng, mutationStats, mutations));
    registerMutator(new ValuePrinter(rng, mutationStats, mutations));
    registerMutator(new VRegChanger(rng, mutationStats, mutations));

    associateClassDefsAndClassData();
    associateCodeItemsWithMethodNames();

    int codeItemIdx = 0;
    for (CodeItem codeItem : rawDexFile.codeItems) {
      if (legalToMutate(codeItem)) {
        Log.debug("Legal to mutate code item " + codeItemIdx);
        int mutatableCodeIdx = mutatableCodes.size();
        mutatableCodes.add(translator.codeItemToMutatableCode(this, codeItem,
            codeItemIdx, mutatableCodeIdx));
      } else {
        Log.debug("Not legal to mutate code item " + codeItemIdx);
      }
      codeItemIdx++;
    }
  }

  private void registerMutator(CodeMutator mutator) {
    if (mutator.canBeTriggered()) {
      Log.debug("Registering mutator " + mutator.getClass().getSimpleName());
      mutators.add(mutator);
    }
    if (Options.loadMutations) {
      mutatorsLookupByClass.put(mutator.getClass(), mutator);
    }
  }

  /**
   * Associate ClassDefItem to a ClassDataItem and vice-versa.
   * This is so when we're associating method names with code items,
   * we can find the name of the class the method belongs to.
   */

  private void associateClassDefsAndClassData() {
    for (ClassDefItem classDefItem : rawDexFile.classDefs) {
      if (classDefItem.classDataOff.pointsToSomething()) {
        ClassDataItem classDataItem = (ClassDataItem)
            classDefItem.classDataOff.getPointedToItem();
        classDataItem.meta.classDefItem = classDefItem;
        classDefItem.meta.classDataItem = classDataItem;
      }
    }
  }

  /**
   * For each CodeItem, find the name of the method the item represents.
   * This is done to allow the filtering of mutating methods based on if
   * they have the name *_MUTATE, but also for debugging info.
   */

  private void associateCodeItemsWithMethodNames() {
    // Associate method names with codeItems.
    for (ClassDataItem classDataItem : rawDexFile.classDatas) {

      String className = "";
      if (classDataItem.meta.classDefItem != null) {
        int typeIdx = classDataItem.meta.classDefItem.classIdx;
        TypeIdItem typeIdItem = rawDexFile.typeIds.get(typeIdx);
        className = rawDexFile.stringDatas.get(typeIdItem.descriptorIdx).getString() + ".";
      }

      // Do direct methods...
      // Track the current method index with this value, since the encoding in
      // each EncodedMethod is the absolute index for the first EncodedMethod,
      // and then relative index for the rest...
      int methodIdx = 0;
      for (EncodedMethod method : classDataItem.directMethods) {
        methodIdx = associateMethod(method, methodIdx, className);
      }
      // Reset methodIdx for virtual methods...
      methodIdx = 0;
      for (EncodedMethod method : classDataItem.virtualMethods) {
        methodIdx = associateMethod(method, methodIdx, className);
      }
    }
  }

  /**
   * Associate the name of the provided method with its CodeItem, if it
   * has one.
   *
   * @param methodIdx The method index of the last EncodedMethod that was handled in this class.
   * @return The method index of the EncodedMethod that has just been handled in this class.
   */

  private int associateMethod(EncodedMethod method, int methodIdx, String className) {
    if (!method.codeOff.pointsToSomething()) {
      // This method doesn't have a code item, so we won't encounter it later.
      return methodIdx;
    }

    // First method index is an absolute index.
    // The rest are relative to the previous.
    // (so if methodIdx is initialised to 0, this single line works)
    methodIdx = methodIdx + method.methodIdxDiff;

    // Get the name.
    MethodIdItem methodIdItem = rawDexFile.methodIds.get(methodIdx);
    ProtoIdItem protoIdItem = rawDexFile.protoIds.get(methodIdItem.protoIdx);
    String shorty = rawDexFile.stringDatas.get(protoIdItem.shortyIdx).getString();
    String methodName = className
        + rawDexFile.stringDatas.get(methodIdItem.nameIdx).getString();

    // Get the codeItem.
    if (method.codeOff.getPointedToItem() instanceof CodeItem) {
      CodeItem codeItem = (CodeItem) method.codeOff.getPointedToItem();
      codeItem.meta.methodName = methodName;
      codeItem.meta.shorty = shorty;
      codeItem.meta.isStatic = method.isStatic();
    } else {
      Log.errorAndQuit("You've got an EncodedMethod that points to an Offsettable"
          + " that does not contain a CodeItem");
    }

    return methodIdx;
  }

  /**
   * Determine, based on the current options supplied to dexfuzz, as well as
   * its capabilities, if the provided CodeItem can be mutated.
   * @param codeItem The CodeItem we may wish to mutate.
   * @return If the CodeItem can be mutated.
   */

  private boolean legalToMutate(CodeItem codeItem) {
    if (!Options.mutateLimit) {
      Log.debug("Mutating everything.");
      return true;
    }
    if (codeItem.meta.methodName.endsWith("_MUTATE")) {
      Log.debug("Code item marked with _MUTATE.");
      return true;
    }
    Log.debug("Code item not marked with _MUTATE, but not mutating all code items.");
    return false;
  }

  private int getNumberOfMutationsToPerform() {
    // We want n mutations to be twice as likely as n+1 mutations.
    //
    // So if we have max 3,
    // then 0 has 8 chances ("tickets"),
    //      1 has 4 chances
    //      2 has 2 chances
    //  and 3 has 1 chance

    // Allocate the tickets
    // n mutations need (2^(n+1) - 1) tickets
    // e.g.
    // 3 mutations => 15 tickets
    // 4 mutations => 31 tickets
    int tickets = (2 << Options.methodMutations) - 1;

    // Pick the lucky ticket
    int luckyTicket = rng.nextInt(tickets);

    // The tickets are put into buckets with accordance with log-base-2.
    // have to make sure it's luckyTicket + 1, because log(0) is undefined
    // so:
    // log_2(1) => 0
    // log_2(2) => 1
    // log_2(3) => 1
    // log_2(4) => 2
    // log_2(5) => 2
    // log_2(6) => 2
    // log_2(7) => 2
    // log_2(8) => 3
    // ...
    // so to make the highest mutation value the rarest,
    //   subtract log_2(luckyTicket+1) from the maximum number
    // log2(x) <=> 31 - Integer.numberOfLeadingZeros(x)
    int luckyMutation = Options.methodMutations
        - (31 - Integer.numberOfLeadingZeros(luckyTicket + 1));

    return luckyMutation;
  }

  /**
   * Returns true if we completely failed to mutate this method's mutatable code after
   * attempting to.
   */

  private boolean mutateAMutatableCode(MutatableCode mutatableCode) {
    int mutations = getNumberOfMutationsToPerform();

    Log.info("Attempting " + mutations + " mutations for method " + mutatableCode.name);

    int mutationsApplied = 0;

    int maximumMutationAttempts = Options.methodMutations * MAXIMUM_MUTATION_ATTEMPT_FACTOR;
    int mutationAttempts = 0;
    boolean hadToBail = false;

    while (mutationsApplied < mutations) {
      int mutatorIdx = rng.nextInt(mutators.size());
      CodeMutator mutator = mutators.get(mutatorIdx);
      Log.info("Running mutator " + mutator.getClass().getSimpleName());
      if (mutator.attemptToMutate(mutatableCode)) {
        mutationsApplied++;
      }
      mutationAttempts++;
      if (mutationAttempts > maximumMutationAttempts) {
        Log.info("Bailing out on mutation for this method, tried too many times...");
        hadToBail = true;
        break;
      }
    }

    // If any of them actually mutated it, excellent!
    if (mutationsApplied > 0) {
      Log.info("Method was mutated.");
      mutatedCodes.add(mutatableCode);
    } else {
      Log.info("Method was not mutated.");
    }

    return ((mutationsApplied == 0) && hadToBail);
  }

  /**
   * Go through each mutatable method in turn, and attempt to mutate it.
   * Afterwards, call updateRawDexFile() to apply the results of mutation to the
   * original code.
   */

  public void mutateTheProgram() {
    if (Options.loadMutations) {
      applyMutationsFromList();
      return;
    }

    // Typically, this is 2 to 10...
    int methodsToMutate = Options.minMethods
        + rng.nextInt((Options.maxMethods - Options.minMethods) + 1);

    // Check we aren't trying to mutate more methods than we have.
    if (methodsToMutate > mutatableCodes.size()) {
      methodsToMutate = mutatableCodes.size();
    }

    // Check if we're going to end up mutating all the methods.
    if (methodsToMutate == mutatableCodes.size()) {
      // Just do them all in order.
      Log.info("Mutating all possible methods.");
      for (MutatableCode mutatableCode : mutatableCodes) {
        if (mutatableCode == null) {
          Log.errorAndQuit("Why do you have a null MutatableCode?");
        }
        mutateAMutatableCode(mutatableCode);
      }
      Log.info("Finished mutating all possible methods.");
    } else {
      // Pick them at random.
      Log.info("Randomly selecting " + methodsToMutate + " methods to mutate.");
      while (mutatedCodes.size() < methodsToMutate) {
        int randomMethodIdx = rng.nextInt(mutatableCodes.size());
        MutatableCode mutatableCode = mutatableCodes.get(randomMethodIdx);
        if (mutatableCode == null) {
          Log.errorAndQuit("Why do you have a null MutatableCode?");
        }
        if (!mutatedCodes.contains(mutatableCode)) {
          boolean completelyFailedToMutate = mutateAMutatableCode(mutatableCode);
          if (completelyFailedToMutate) {
            methodsToMutate--;
          }
        }
      }
      Log.info("Finished mutating the methods.");
    }

    listener.handleMutationStats(mutationStats.getStatsString());

    if (Options.dumpMutations) {
      writeMutationsToDisk(Options.dumpMutationsFile);
    }
  }

  private void writeMutationsToDisk(String fileName) {
    Log.debug("Writing mutations to disk.");
    try {
      BufferedWriter writer = new BufferedWriter(new FileWriter(fileName));
      for (Mutation mutation : mutations) {
        MutationSerializer.writeMutation(writer, mutation);
      }
      writer.close();
    } catch (IOException e) {
      Log.errorAndQuit("IOException while writing mutations to disk...");
    }
  }

  private void loadMutationsFromDisk(String fileName) {
    Log.debug("Loading mutations from disk.");
    try {
      BufferedReader reader = new BufferedReader(new FileReader(fileName));
      while (reader.ready()) {
        Mutation mutation = MutationSerializer.readMutation(reader);
        mutations.add(mutation);
      }
      reader.close();
    } catch (IOException e) {
      Log.errorAndQuit("IOException while loading mutations from disk...");
    }
  }

  private void applyMutationsFromList() {
    Log.info("Applying preloaded list of mutations...");
    for (Mutation mutation : mutations) {
      // Repopulate the MutatableCode field from the recorded index into the Program's list.
      mutation.mutatableCode = mutatableCodes.get(mutation.mutatableCodeIdx);

      // Get the right mutator.
      CodeMutator mutator = mutatorsLookupByClass.get(mutation.mutatorClass);

      // Apply the mutation.
      mutator.forceMutate(mutation);

      // Add this mutatable code to the list of mutated codes, if we haven't already.
      if (!mutatedCodes.contains(mutation.mutatableCode)) {
        mutatedCodes.add(mutation.mutatableCode);
      }
    }
    Log.info("...finished applying preloaded list of mutations.");
  }

  public List<Mutation> getMutations() {
    return mutations;
  }

  /**
   * Updates any CodeItems that need to be updated after mutation.
   */

  public boolean updateRawDexFile() {
    boolean anythingMutated = !(mutatedCodes.isEmpty());
    for (MutatableCode mutatedCode : mutatedCodes) {
      translator.mutatableCodeToCodeItem(rawDexFile.codeItems
          .get(mutatedCode.codeItemIdx), mutatedCode);
    }
    mutatedCodes.clear();
    return anythingMutated;
  }

  public void writeRawDexFile(DexRandomAccessFile file) throws IOException {
    rawDexFile.write(file);
  }

  public void updateRawDexFileHeader(DexRandomAccessFile file) throws IOException {
    rawDexFile.updateHeader(file);
  }

  /**
   * Used by the CodeMutators to determine legal index values.
   */

  public int getTotalPoolIndicesByKind(PoolIndexKind poolIndexKind) {
    switch (poolIndexKind) {
      case Type:
        return rawDexFile.typeIds.size();
      case Field:
        return rawDexFile.fieldIds.size();
      case String:
        return rawDexFile.stringIds.size();
      case Method:
        return rawDexFile.methodIds.size();
      case Invalid:
        return 0;
      default:
    }
    return 0;
  }

  /**
   * Used by the CodeMutators to lookup and/or create Ids.
   */

  public IdCreator getNewItemCreator() {
    return idCreator;
  }

  /**
   * Used by FieldFlagChanger, to find an EncodedField for a specified field in an insn,
   * if that field is actually defined in this DEX file. If not, null is returned.
   */

  public EncodedField getEncodedField(int fieldIdx) {
    if (fieldIdx >= rawDexFile.fieldIds.size()) {
      Log.debug(String.format("Field idx 0x%x specified is not defined in this DEX file.",
          fieldIdx));
      return null;
    }
    FieldIdItem fieldId = rawDexFile.fieldIds.get(fieldIdx);

    for (ClassDefItem classDef : rawDexFile.classDefs) {
      if (classDef.classIdx == fieldId.classIdx) {
        ClassDataItem classData = classDef.meta.classDataItem;
        return classData.getEncodedFieldWithIndex(fieldIdx);
      }
    }

    Log.debug(String.format("Field idx 0x%x specified is not defined in this DEX file.",
        fieldIdx));
    return null;
  }

  /**
   * Used to convert the type index into string format.
   * @param typeIdx
   * @return string format of type index.
   */

  public String getTypeString(int typeIdx) {
    TypeIdItem typeIdItem = rawDexFile.typeIds.get(typeIdx);
    return rawDexFile.stringDatas.get(typeIdItem.descriptorIdx).getString();
  }

  /**
   * Used to convert the method index into string format.
   * @param methodIdx
   * @return string format of method index.
   */

  public String getMethodString(int methodIdx) {
    MethodIdItem methodIdItem = rawDexFile.methodIds.get(methodIdx);
    return rawDexFile.stringDatas.get(methodIdItem.nameIdx).getString();
  }

  /**
   * Used to convert methodID to string format of method proto.
   * @param methodIdx
   * @return string format of shorty.
   */

  public String getMethodProto(int methodIdx) {
    MethodIdItem methodIdItem = rawDexFile.methodIds.get(methodIdx);
    ProtoIdItem protoIdItem = rawDexFile.protoIds.get(methodIdItem.protoIdx);

    if (!protoIdItem.parametersOff.pointsToSomething()) {
      return "()" + getTypeString(protoIdItem.returnTypeIdx);
    }

    TypeList typeList = (TypeList) protoIdItem.parametersOff.getPointedToItem();
    String typeItem = "(";
    for (int i= 0; i < typeList.size; i++) {
      typeItem = typeItem + typeList.list[i];
    }
    return typeItem + ")" + getTypeString(protoIdItem.returnTypeIdx);
  }
}

Messung V0.5 in Prozent
C=90 H=85 G=87

¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet am  2026-06-29) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik