// 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>();
// 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);
}
}
}
/** *AssociatethenameoftheprovidedmethodwithitsCodeItem,ifit *hasone. * *@parammethodIdxThemethodindexofthelastEncodedMethodthatwashandledinthisclass. *@returnThemethodindexoftheEncodedMethodthathasjustbeenhandledinthisclass.
*/ privateint 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 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,basedonthecurrentoptionssuppliedtodexfuzz,aswellas *itscapabilities,iftheprovidedCodeItemcanbemutated. *@paramcodeItemTheCodeItemwemaywishtomutate. *@returnIftheCodeItemcanbemutated.
*/ privateboolean legalToMutate(CodeItem codeItem) { if (!Options.mutateLimit) {
Log.debug("Mutating everything."); returntrue;
} if (codeItem.meta.methodName.endsWith("_MUTATE")) {
Log.debug("Code item marked with _MUTATE."); returntrue;
}
Log.debug("Code item not marked with _MUTATE, but not mutating all code items."); returnfalse;
}
privateint 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));
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.");
}
// 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.");
}
if (Options.dumpMutations) {
writeMutationsToDisk(Options.dumpMutationsFile);
}
}
privatevoid 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...");
}
}
privatevoid 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...");
}
}
privatevoid 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;
}
/** *UsedbytheCodeMutatorstodeterminelegalindexvalues.
*/ publicint 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: return0; default:
} return0;
}
/** *UsedbytheCodeMutatorstolookupand/orcreateIds.
*/ public IdCreator getNewItemCreator() { return idCreator;
}
/** *UsedbyFieldFlagChanger,tofindanEncodedFieldforaspecifiedfieldinaninsn, *ifthatfieldisactuallydefinedinthisDEXfile.Ifnot,nullisreturned.
*/ 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)); returnnull;
}
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)); returnnull;
}
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.