Home | History | Annotate | Download | only in Analysis
      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