Home | History | Annotate | Download | only in baksmali
      1 /*
      2  * Copyright 2016, Google Inc.
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  * Redistributions of source code must retain the above copyright
     10  * notice, this list of conditions and the following disclaimer.
     11  * Redistributions in binary form must reproduce the above
     12  * copyright notice, this list of conditions and the following disclaimer
     13  * in the documentation and/or other materials provided with the
     14  * distribution.
     15  * Neither the name of Google Inc. nor the names of its
     16  * contributors may be used to endorse or promote products derived from
     17  * this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 package org.jf.baksmali;
     33 
     34 import com.beust.jcommander.JCommander;
     35 import com.beust.jcommander.Parameter;
     36 import com.beust.jcommander.Parameters;
     37 import com.beust.jcommander.ParametersDelegate;
     38 import org.jf.baksmali.AnalysisArguments.CheckPackagePrivateArgument;
     39 import org.jf.dexlib2.AccessFlags;
     40 import org.jf.dexlib2.analysis.ClassProto;
     41 import org.jf.dexlib2.iface.ClassDef;
     42 import org.jf.dexlib2.iface.Method;
     43 import org.jf.util.jcommander.ExtendedParameter;
     44 import org.jf.util.jcommander.ExtendedParameters;
     45 
     46 import javax.annotation.Nonnull;
     47 import java.io.IOException;
     48 import java.util.List;
     49 
     50 @Parameters(commandDescription = "Lists the virtual method tables for classes in a dex file.")
     51 @ExtendedParameters(
     52         commandName = "vtables",
     53         commandAliases = { "vtable", "v" })
     54 public class ListVtablesCommand extends DexInputCommand {
     55 
     56     @Parameter(names = {"-h", "-?", "--help"}, help = true,
     57             description = "Show usage information")
     58     private boolean help;
     59 
     60     @ParametersDelegate
     61     private AnalysisArguments analysisArguments = new AnalysisArguments();
     62 
     63     @ParametersDelegate
     64     private CheckPackagePrivateArgument checkPackagePrivateArgument = new CheckPackagePrivateArgument();
     65 
     66     @Parameter(names = "--classes",
     67             description = "A comma separated list of classes. Only print the vtable for these classes")
     68     @ExtendedParameter(argumentNames = "classes")
     69     private List<String> classes = null;
     70 
     71     @Parameter(names = "--override-oat-version",
     72             description = "Uses a classpath for the given oat version, regardless of the actual oat version. This " +
     73                     "can be used, e.g. to list vtables from a dex file, as if they were in an oat file of the given " +
     74                     "version.")
     75     private int oatVersion = 0;
     76 
     77     public ListVtablesCommand(@Nonnull List<JCommander> commandAncestors) {
     78         super(commandAncestors);
     79     }
     80 
     81     @Override public void run() {
     82         if (help || inputList == null || inputList.isEmpty()) {
     83             usage();
     84             return;
     85         }
     86 
     87         if (inputList.size() > 1) {
     88             System.err.println("Too many files specified");
     89             usage();
     90             return;
     91         }
     92 
     93         String input = inputList.get(0);
     94         loadDexFile(input);
     95 
     96         BaksmaliOptions options = getOptions();
     97         if (options == null) {
     98             return;
     99         }
    100 
    101         try {
    102             if (classes != null && !classes.isEmpty()) {
    103                 for (String cls: classes) {
    104                     listClassVtable((ClassProto)options.classPath.getClass(cls));
    105                 }
    106                 return;
    107             }
    108 
    109             for (ClassDef classDef : dexFile.getClasses()) {
    110                 if (!AccessFlags.INTERFACE.isSet(classDef.getAccessFlags())) {
    111                     listClassVtable((ClassProto)options.classPath.getClass(classDef));
    112                 }
    113             }
    114         } catch (IOException ex) {
    115             throw new RuntimeException(ex);
    116         }
    117     }
    118 
    119     private void listClassVtable(ClassProto classProto) throws IOException {
    120         List<Method> methods = classProto.getVtable();
    121         String className = "Class " + classProto.getType() + " extends " + classProto.getSuperclass() +
    122                 " : " + methods.size() + " methods\n";
    123         System.out.write(className.getBytes());
    124         for (int i = 0; i < methods.size(); i++) {
    125             Method method = methods.get(i);
    126 
    127             String methodString = i + ":" + method.getDefiningClass() + "->" + method.getName() + "(";
    128             for (CharSequence parameter : method.getParameterTypes()) {
    129                 methodString += parameter;
    130             }
    131             methodString += ")" + method.getReturnType() + "\n";
    132             System.out.write(methodString.getBytes());
    133         }
    134         System.out.write("\n".getBytes());
    135     }
    136 
    137     protected BaksmaliOptions getOptions() {
    138         if (dexFile == null) {
    139             throw new IllegalStateException("You must call loadDexFile first");
    140         }
    141 
    142         final BaksmaliOptions options = new BaksmaliOptions();
    143 
    144         options.apiLevel = apiLevel;
    145 
    146         try {
    147             options.classPath = analysisArguments.loadClassPathForDexFile(inputFile.getAbsoluteFile().getParentFile(),
    148                     dexFile, checkPackagePrivateArgument.checkPackagePrivateAccess, oatVersion);
    149         } catch (Exception ex) {
    150             System.err.println("Error occurred while loading class path files.");
    151             ex.printStackTrace(System.err);
    152             return null;
    153         }
    154 
    155         return options;
    156     }
    157 }
    158