1 /* 2 * [The "BSD licence"] 3 * Copyright (c) 2010 Ben Gruver (JesusFreke) 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 package org.jf.dexlib.Code.Analysis; 30 31 import org.jf.dexlib.*; 32 33 import java.util.ArrayList; 34 import java.util.LinkedList; 35 import java.util.regex.Matcher; 36 import java.util.regex.Pattern; 37 38 public class DeodexUtil { 39 public static final int Virtual = 0; 40 public static final int Direct = 1; 41 public static final int Static = 2; 42 43 private final InlineMethodResolver inlineMethodResolver; 44 45 public final DexFile dexFile; 46 47 public DeodexUtil(DexFile dexFile) { 48 this.dexFile = dexFile; 49 OdexHeader odexHeader = dexFile.getOdexHeader(); 50 if (odexHeader == null) { 51 //if there isn't an odex header, why are we creating an DeodexUtil object? 52 assert false; 53 throw new RuntimeException("Cannot create a DeodexUtil object for a dex file without an odex header"); 54 } 55 inlineMethodResolver = InlineMethodResolver.createInlineMethodResolver(this, odexHeader.version); 56 } 57 58 public InlineMethod lookupInlineMethod(AnalyzedInstruction instruction) { 59 return inlineMethodResolver.resolveExecuteInline(instruction); 60 } 61 62 public FieldIdItem lookupField(ClassPath.ClassDef classDef, int fieldOffset) { 63 ClassPath.FieldDef field = classDef.getInstanceField(fieldOffset); 64 if (field == null) { 65 return null; 66 } 67 68 return parseAndResolveField(classDef, field); 69 } 70 71 private static final Pattern shortMethodPattern = Pattern.compile("([^(]+)\\(([^)]*)\\)(.+)"); 72 73 public MethodIdItem lookupVirtualMethod(ClassPath.ClassDef classDef, int methodIndex) { 74 String method = classDef.getVirtualMethod(methodIndex); 75 if (method == null) { 76 return null; 77 } 78 79 Matcher m = shortMethodPattern.matcher(method); 80 if (!m.matches()) { 81 assert false; 82 throw new RuntimeException("Invalid method descriptor: " + method); 83 } 84 85 String methodName = m.group(1); 86 String methodParams = m.group(2); 87 String methodRet = m.group(3); 88 89 if (classDef.isInterface()) { 90 classDef = classDef.getSuperclass(); 91 assert classDef != null; 92 } 93 94 return parseAndResolveMethod(classDef, methodName, methodParams, methodRet); 95 } 96 97 private MethodIdItem parseAndResolveMethod(ClassPath.ClassDef classDef, String methodName, String methodParams, 98 String methodRet) { 99 StringIdItem methodNameItem = StringIdItem.lookupStringIdItem(dexFile, methodName); 100 if (methodNameItem == null) { 101 return null; 102 } 103 104 LinkedList<TypeIdItem> paramList = new LinkedList<TypeIdItem>(); 105 106 for (int i=0; i<methodParams.length(); i++) { 107 TypeIdItem typeIdItem; 108 109 switch (methodParams.charAt(i)) { 110 case 'Z': 111 case 'B': 112 case 'S': 113 case 'C': 114 case 'I': 115 case 'J': 116 case 'F': 117 case 'D': 118 typeIdItem = TypeIdItem.lookupTypeIdItem(dexFile, methodParams.substring(i,i+1)); 119 break; 120 case 'L': 121 { 122 int end = methodParams.indexOf(';', i); 123 if (end == -1) { 124 throw new RuntimeException("invalid parameter in the method"); 125 } 126 127 typeIdItem = TypeIdItem.lookupTypeIdItem(dexFile, methodParams.substring(i, end+1)); 128 i = end; 129 break; 130 } 131 case '[': 132 { 133 int end; 134 int typeStart = i+1; 135 while (typeStart < methodParams.length() && methodParams.charAt(typeStart) == '[') { 136 typeStart++; 137 } 138 switch (methodParams.charAt(typeStart)) { 139 case 'Z': 140 case 'B': 141 case 'S': 142 case 'C': 143 case 'I': 144 case 'J': 145 case 'F': 146 case 'D': 147 end = typeStart; 148 break; 149 case 'L': 150 end = methodParams.indexOf(';', typeStart); 151 if (end == -1) { 152 throw new RuntimeException("invalid parameter in the method"); 153 } 154 break; 155 default: 156 throw new RuntimeException("invalid parameter in the method"); 157 } 158 159 typeIdItem = TypeIdItem.lookupTypeIdItem(dexFile, methodParams.substring(i, end+1)); 160 i = end; 161 break; 162 } 163 default: 164 throw new RuntimeException("invalid parameter in the method"); 165 } 166 167 if (typeIdItem == null) { 168 return null; 169 } 170 paramList.add(typeIdItem); 171 } 172 173 TypeListItem paramListItem = null; 174 if (paramList.size() > 0) { 175 paramListItem = TypeListItem.lookupTypeListItem(dexFile, paramList); 176 if (paramListItem == null) { 177 return null; 178 } 179 } 180 181 TypeIdItem retType = TypeIdItem.lookupTypeIdItem(dexFile, methodRet); 182 if (retType == null) { 183 return null; 184 } 185 186 ProtoIdItem protoItem = ProtoIdItem.lookupProtoIdItem(dexFile, retType, paramListItem); 187 if (protoItem == null) { 188 return null; 189 } 190 191 ClassPath.ClassDef methodClassDef = classDef; 192 193 do { 194 TypeIdItem classTypeItem = TypeIdItem.lookupTypeIdItem(dexFile, methodClassDef.getClassType()); 195 196 if (classTypeItem != null) { 197 MethodIdItem methodIdItem = MethodIdItem.lookupMethodIdItem(dexFile, classTypeItem, protoItem, methodNameItem); 198 if (methodIdItem != null) { 199 return methodIdItem; 200 } 201 } 202 203 methodClassDef = methodClassDef.getSuperclass(); 204 } while (methodClassDef != null); 205 return null; 206 } 207 208 private FieldIdItem parseAndResolveField(ClassPath.ClassDef classDef, ClassPath.FieldDef field) { 209 String definingClass = field.definingClass; 210 String fieldName = field.name; 211 String fieldType = field.type; 212 213 StringIdItem fieldNameItem = StringIdItem.lookupStringIdItem(dexFile, fieldName); 214 if (fieldNameItem == null) { 215 return null; 216 } 217 218 TypeIdItem fieldTypeItem = TypeIdItem.lookupTypeIdItem(dexFile, fieldType); 219 if (fieldTypeItem == null) { 220 return null; 221 } 222 223 ClassPath.ClassDef fieldClass = classDef; 224 225 ArrayList<ClassPath.ClassDef> parents = new ArrayList<ClassPath.ClassDef>(); 226 parents.add(fieldClass); 227 228 while (fieldClass != null && !fieldClass.getClassType().equals(definingClass)) { 229 fieldClass = fieldClass.getSuperclass(); 230 parents.add(fieldClass); 231 } 232 233 for (int i=parents.size()-1; i>=0; i--) { 234 fieldClass = parents.get(i); 235 236 TypeIdItem classTypeItem = TypeIdItem.lookupTypeIdItem(dexFile, fieldClass.getClassType()); 237 if (classTypeItem == null) { 238 continue; 239 } 240 241 FieldIdItem fieldIdItem = FieldIdItem.lookupFieldIdItem(dexFile, classTypeItem, fieldTypeItem, fieldNameItem); 242 if (fieldIdItem != null) { 243 return fieldIdItem; 244 } 245 } 246 return null; 247 } 248 249 public class InlineMethod { 250 public final int methodType; 251 public final String classType; 252 public final String methodName; 253 public final String parameters; 254 public final String returnType; 255 256 private MethodIdItem methodIdItem = null; 257 258 InlineMethod(int methodType, String classType, String methodName, String parameters, 259 String returnType) { 260 this.methodType = methodType; 261 this.classType = classType; 262 this.methodName = methodName; 263 this.parameters = parameters; 264 this.returnType = returnType; 265 } 266 267 public MethodIdItem getMethodIdItem() { 268 if (methodIdItem == null) { 269 loadMethod(); 270 } 271 return methodIdItem; 272 } 273 274 private void loadMethod() { 275 ClassPath.ClassDef classDef = ClassPath.getClassDef(classType); 276 277 this.methodIdItem = parseAndResolveMethod(classDef, methodName, parameters, returnType); 278 } 279 280 public String getMethodString() { 281 return String.format("%s->%s(%s)%s", classType, methodName, parameters, returnType); 282 } 283 } 284 } 285