• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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