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 DeodexUtil(DexFile dexFile, InlineMethodResolver inlineMethodResolver) { 59 this.dexFile = dexFile; 60 this.inlineMethodResolver = inlineMethodResolver; 61 } 62 63 public InlineMethod lookupInlineMethod(AnalyzedInstruction instruction) { 64 return inlineMethodResolver.resolveExecuteInline(instruction); 65 } 66 67 public FieldIdItem lookupField(ClassPath.ClassDef accessingClass, ClassPath.ClassDef instanceClass, 68 int fieldOffset) { 69 ClassPath.FieldDef field = instanceClass.getInstanceField(fieldOffset); 70 if (field == null) { 71 return null; 72 } 73 74 return parseAndResolveField(accessingClass, instanceClass, field); 75 } 76 77 private static final Pattern shortMethodPattern = Pattern.compile("([^(]+)\\(([^)]*)\\)(.+)"); 78 79 public MethodIdItem lookupVirtualMethod(ClassPath.ClassDef accessingClass, ClassPath.ClassDef instanceClass, 80 int methodIndex) { 81 String method = instanceClass.getVirtualMethod(methodIndex); 82 if (method == null) { 83 return null; 84 } 85 86 Matcher m = shortMethodPattern.matcher(method); 87 if (!m.matches()) { 88 assert false; 89 throw new RuntimeException("Invalid method descriptor: " + method); 90 } 91 92 String methodName = m.group(1); 93 String methodParams = m.group(2); 94 String methodRet = m.group(3); 95 96 if (instanceClass instanceof ClassPath.UnresolvedClassDef) { 97 //if this is an unresolved class, the only way getVirtualMethod could have found a method is if the virtual 98 //method being looked up was a method on java.lang.Object. 99 instanceClass = ClassPath.getClassDef("Ljava/lang/Object;"); 100 } else if (instanceClass.isInterface()) { 101 instanceClass = instanceClass.getSuperclass(); 102 assert instanceClass != null; 103 } 104 105 return parseAndResolveMethod(accessingClass, instanceClass, methodName, methodParams, methodRet); 106 } 107 108 private MethodIdItem parseAndResolveMethod(ClassPath.ClassDef accessingClass, ClassPath.ClassDef definingClass, 109 String methodName, String methodParams, String methodRet) { 110 StringIdItem methodNameItem = StringIdItem.lookupStringIdItem(dexFile, methodName); 111 if (methodNameItem == null) { 112 return null; 113 } 114 115 LinkedList<TypeIdItem> paramList = new LinkedList<TypeIdItem>(); 116 117 for (int i=0; i<methodParams.length(); i++) { 118 TypeIdItem typeIdItem; 119 120 switch (methodParams.charAt(i)) { 121 case 'Z': 122 case 'B': 123 case 'S': 124 case 'C': 125 case 'I': 126 case 'J': 127 case 'F': 128 case 'D': 129 typeIdItem = TypeIdItem.lookupTypeIdItem(dexFile, methodParams.substring(i,i+1)); 130 break; 131 case 'L': 132 { 133 int end = methodParams.indexOf(';', i); 134 if (end == -1) { 135 throw new RuntimeException("invalid parameter in the method"); 136 } 137 138 typeIdItem = TypeIdItem.lookupTypeIdItem(dexFile, methodParams.substring(i, end+1)); 139 i = end; 140 break; 141 } 142 case '[': 143 { 144 int end; 145 int typeStart = i+1; 146 while (typeStart < methodParams.length() && methodParams.charAt(typeStart) == '[') { 147 typeStart++; 148 } 149 switch (methodParams.charAt(typeStart)) { 150 case 'Z': 151 case 'B': 152 case 'S': 153 case 'C': 154 case 'I': 155 case 'J': 156 case 'F': 157 case 'D': 158 end = typeStart; 159 break; 160 case 'L': 161 end = methodParams.indexOf(';', typeStart); 162 if (end == -1) { 163 throw new RuntimeException("invalid parameter in the method"); 164 } 165 break; 166 default: 167 throw new RuntimeException("invalid parameter in the method"); 168 } 169 170 typeIdItem = TypeIdItem.lookupTypeIdItem(dexFile, methodParams.substring(i, end+1)); 171 i = end; 172 break; 173 } 174 default: 175 throw new RuntimeException("invalid parameter in the method"); 176 } 177 178 if (typeIdItem == null) { 179 return null; 180 } 181 paramList.add(typeIdItem); 182 } 183 184 TypeListItem paramListItem = null; 185 if (paramList.size() > 0) { 186 paramListItem = TypeListItem.lookupTypeListItem(dexFile, paramList); 187 if (paramListItem == null) { 188 return null; 189 } 190 } 191 192 TypeIdItem retType = TypeIdItem.lookupTypeIdItem(dexFile, methodRet); 193 if (retType == null) { 194 return null; 195 } 196 197 ProtoIdItem protoItem = ProtoIdItem.lookupProtoIdItem(dexFile, retType, paramListItem); 198 if (protoItem == null) { 199 return null; 200 } 201 202 ClassPath.ClassDef methodClassDef = definingClass; 203 204 do { 205 TypeIdItem classTypeItem = TypeIdItem.lookupTypeIdItem(dexFile, methodClassDef.getClassType()); 206 207 if (classTypeItem != null) { 208 MethodIdItem methodIdItem = MethodIdItem.lookupMethodIdItem(dexFile, classTypeItem, protoItem, methodNameItem); 209 if (methodIdItem != null && checkClassAccess(accessingClass, methodClassDef)) { 210 return methodIdItem; 211 } 212 } 213 214 methodClassDef = methodClassDef.getSuperclass(); 215 } while (methodClassDef != null); 216 return null; 217 } 218 219 private static boolean checkClassAccess(ClassPath.ClassDef accessingClass, ClassPath.ClassDef definingClass) { 220 return definingClass.isPublic() || 221 getPackage(accessingClass.getClassType()).equals(getPackage(definingClass.getClassType())); 222 } 223 224 private static String getPackage(String classRef) { 225 int lastSlash = classRef.lastIndexOf('/'); 226 if (lastSlash < 0) { 227 return ""; 228 } 229 return classRef.substring(1, lastSlash); 230 } 231 232 /** 233 * 234 * @param accessingClass The class that contains the field reference. I.e. the class being deodexed 235 * @param instanceClass The inferred class type of the object that the field is being accessed on 236 * @param field The field being accessed 237 * @return The FieldIdItem of the resolved field 238 */ 239 private FieldIdItem parseAndResolveField(ClassPath.ClassDef accessingClass, ClassPath.ClassDef instanceClass, 240 ClassPath.FieldDef field) { 241 String definingClass = field.definingClass; 242 String fieldName = field.name; 243 String fieldType = field.type; 244 245 StringIdItem fieldNameItem = StringIdItem.lookupStringIdItem(dexFile, fieldName); 246 if (fieldNameItem == null) { 247 return null; 248 } 249 250 TypeIdItem fieldTypeItem = TypeIdItem.lookupTypeIdItem(dexFile, fieldType); 251 if (fieldTypeItem == null) { 252 return null; 253 } 254 255 ClassPath.ClassDef fieldClass = instanceClass; 256 257 ArrayList<ClassPath.ClassDef> parents = new ArrayList<ClassPath.ClassDef>(); 258 parents.add(fieldClass); 259 260 while (fieldClass != null && !fieldClass.getClassType().equals(definingClass)) { 261 fieldClass = fieldClass.getSuperclass(); 262 parents.add(fieldClass); 263 } 264 265 for (int i=parents.size()-1; i>=0; i--) { 266 fieldClass = parents.get(i); 267 268 TypeIdItem classTypeItem = TypeIdItem.lookupTypeIdItem(dexFile, fieldClass.getClassType()); 269 if (classTypeItem == null) { 270 continue; 271 } 272 273 FieldIdItem fieldIdItem = FieldIdItem.lookupFieldIdItem(dexFile, classTypeItem, fieldTypeItem, fieldNameItem); 274 if (fieldIdItem != null && checkClassAccess(accessingClass, fieldClass)) { 275 return fieldIdItem; 276 } 277 } 278 return null; 279 } 280 281 public static class InlineMethod { 282 public final int methodType; 283 public final String classType; 284 public final String methodName; 285 public final String parameters; 286 public final String returnType; 287 288 private MethodIdItem methodIdItem = null; 289 290 InlineMethod(int methodType, String classType, String methodName, String parameters, 291 String returnType) { 292 this.methodType = methodType; 293 this.classType = classType; 294 this.methodName = methodName; 295 this.parameters = parameters; 296 this.returnType = returnType; 297 } 298 299 public MethodIdItem getMethodIdItem(DeodexUtil deodexUtil) { 300 if (methodIdItem == null) { 301 loadMethod(deodexUtil); 302 } 303 return methodIdItem; 304 } 305 306 private void loadMethod(DeodexUtil deodexUtil) { 307 ClassPath.ClassDef classDef = ClassPath.getClassDef(classType); 308 309 this.methodIdItem = deodexUtil.parseAndResolveMethod(classDef, classDef, methodName, parameters, 310 returnType); 311 } 312 313 public String getMethodString() { 314 return String.format("%s->%s(%s)%s", classType, methodName, parameters, returnType); 315 } 316 } 317 } 318