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.mutators; 18 19 import dexfuzz.Log; 20 import dexfuzz.MutationStats; 21 import dexfuzz.program.MInsn; 22 import dexfuzz.program.MutatableCode; 23 import dexfuzz.program.Mutation; 24 import dexfuzz.rawdex.Instruction; 25 import dexfuzz.rawdex.Opcode; 26 27 import java.util.List; 28 import java.util.Random; 29 30 public class NonsenseStringPrinter extends CodeMutator { 31 /** 32 * Every CodeMutator has an AssociatedMutation, representing the 33 * mutation that this CodeMutator can perform, to allow separate 34 * generateMutation() and applyMutation() phases, allowing serialization. 35 */ 36 public static class AssociatedMutation extends Mutation { 37 public int insertionIdx; 38 public String nonsenseString; 39 40 @Override getString()41 public String getString() { 42 StringBuilder builder = new StringBuilder(); 43 builder.append(insertionIdx).append(" "); 44 builder.append(nonsenseString); 45 return builder.toString(); 46 } 47 48 @Override parseString(String[] elements)49 public void parseString(String[] elements) { 50 insertionIdx = Integer.parseInt(elements[2]); 51 nonsenseString = elements[3]; 52 } 53 } 54 55 // The following two methods are here for the benefit of MutationSerializer, 56 // so it can create a CodeMutator and get the correct associated Mutation, as it 57 // reads in mutations from a dump of mutations. 58 @Override getNewMutation()59 public Mutation getNewMutation() { 60 return new AssociatedMutation(); 61 } 62 NonsenseStringPrinter()63 public NonsenseStringPrinter() { } 64 NonsenseStringPrinter(Random rng, MutationStats stats, List<Mutation> mutations)65 public NonsenseStringPrinter(Random rng, MutationStats stats, List<Mutation> mutations) { 66 super(rng, stats, mutations); 67 likelihood = 10; 68 } 69 70 @Override generateMutation(MutatableCode mutatableCode)71 protected Mutation generateMutation(MutatableCode mutatableCode) { 72 // Find the insertion point 73 int insertionIdx = 0; 74 boolean foundInsn = false; 75 76 while (!foundInsn) { 77 insertionIdx = rng.nextInt(mutatableCode.getInstructionCount()); 78 MInsn insertionPoint = 79 mutatableCode.getInstructionAt(insertionIdx); 80 foundInsn = true; 81 82 // Don't want to insert instructions where there are raw instructions for now. 83 if (insertionPoint.insn.justRaw) { 84 foundInsn = false; 85 } 86 } 87 88 AssociatedMutation mutation = new AssociatedMutation(); 89 mutation.setup(this.getClass(), mutatableCode); 90 mutation.insertionIdx = insertionIdx; 91 mutation.nonsenseString = getRandomString(); 92 return mutation; 93 } 94 95 @Override applyMutation(Mutation uncastMutation)96 protected void applyMutation(Mutation uncastMutation) { 97 // Cast the Mutation to our AssociatedMutation, so we can access its fields. 98 AssociatedMutation mutation = (AssociatedMutation) uncastMutation; 99 MutatableCode mutatableCode = mutation.mutatableCode; 100 101 int outFieldIdx = mutatableCode.program.getNewItemCreator().findOrCreateFieldId( 102 "Ljava/lang/System;", 103 "Ljava/io/PrintStream;", 104 "out"); 105 int printMethodIdx = mutatableCode.program.getNewItemCreator().findOrCreateMethodId( 106 "Ljava/io/PrintStream;", 107 "print", 108 "(Ljava/lang/String;)V"); 109 int nonsenseStringIdx = mutatableCode.program.getNewItemCreator().findOrCreateString( 110 mutation.nonsenseString); 111 112 MInsn insertionPoint = mutatableCode.getInstructionAt(mutation.insertionIdx); 113 114 mutatableCode.allocateTemporaryVRegs(2); 115 116 int streamRegister = mutatableCode.getTemporaryVReg(0); 117 int stringRegister = mutatableCode.getTemporaryVReg(1); 118 119 // Load into string and stream into the temporary registers. 120 // then call print(stream, string) 121 MInsn constStringInsn = new MInsn(); 122 constStringInsn.insn = new Instruction(); 123 constStringInsn.insn.info = Instruction.getOpcodeInfo(Opcode.CONST_STRING); 124 constStringInsn.insn.vregB = nonsenseStringIdx; 125 constStringInsn.insn.vregA = stringRegister; 126 127 MInsn streamLoadInsn = new MInsn(); 128 streamLoadInsn.insn = new Instruction(); 129 streamLoadInsn.insn.info = Instruction.getOpcodeInfo(Opcode.SGET_OBJECT); 130 streamLoadInsn.insn.vregB = outFieldIdx; 131 streamLoadInsn.insn.vregA = streamRegister; 132 133 MInsn invokeInsn = new MInsn(); 134 invokeInsn.insn = new Instruction(); 135 invokeInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_VIRTUAL_RANGE); 136 invokeInsn.insn.vregA = 2; 137 invokeInsn.insn.vregB = printMethodIdx; 138 invokeInsn.insn.vregC = streamRegister; 139 140 Log.info(String.format("Printing nonsense string '%s', inserting at %s", 141 mutation.nonsenseString, insertionPoint)); 142 143 stats.incrementStat("Printed nonsense string"); 144 145 mutatableCode.insertInstructionAt(invokeInsn, mutation.insertionIdx); 146 mutatableCode.insertInstructionAt(streamLoadInsn, mutation.insertionIdx); 147 mutatableCode.insertInstructionAt(constStringInsn, mutation.insertionIdx); 148 149 mutatableCode.finishedUsingTemporaryVRegs(); 150 } 151 getRandomString()152 private String getRandomString() { 153 int size = rng.nextInt(10); 154 int start = (int) 'A'; 155 int end = (int) 'Z'; 156 StringBuilder builder = new StringBuilder(); 157 for (int i = 0; i < size; i++) { 158 builder.append((char) (rng.nextInt((end + 1) - start) + start)); 159 } 160 return builder.toString(); 161 } 162 } 163