1b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov/*
2b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov * Copyright 2013, Google Inc.
3b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov * All rights reserved.
4b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov *
5b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov * Redistribution and use in source and binary forms, with or without
6b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov * modification, are permitted provided that the following conditions are
7b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov * met:
8b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov *
9b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov *     * Redistributions of source code must retain the above copyright
10b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov * notice, this list of conditions and the following disclaimer.
11b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov *     * Redistributions in binary form must reproduce the above
12b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov * copyright notice, this list of conditions and the following disclaimer
13b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov * in the documentation and/or other materials provided with the
14b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov * distribution.
15b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov *     * Neither the name of Google Inc. nor the names of its
16b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov * contributors may be used to endorse or promote products derived from
17b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov * this software without specific prior written permission.
18b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov *
19b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov */
31b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov
32b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirovpackage org.jf.dexlib2.analysis;
33b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov
34b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirovimport com.google.common.base.Splitter;
35b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirovimport com.google.common.collect.Lists;
36b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirovimport org.apache.commons.cli.*;
37b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirovimport org.jf.dexlib2.DexFileFactory;
38b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirovimport org.jf.dexlib2.dexbacked.DexBackedDexFile;
39b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirovimport org.jf.dexlib2.iface.ClassDef;
40b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirovimport org.jf.dexlib2.iface.Method;
41b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirovimport org.jf.util.ConsoleUtil;
42b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov
43b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirovimport java.io.File;
44b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirovimport java.io.FileOutputStream;
45b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirovimport java.io.IOException;
46b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirovimport java.util.ArrayList;
47b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirovimport java.util.List;
48b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov
49b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirovpublic class DumpVtables {
50b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov    private static final Options options;
51b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov
52b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov    static {
53b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        options = new Options();
54b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        buildOptions();
55b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov    }
56b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov
57b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov    public static void main(String[] args) {
58b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        CommandLineParser parser = new PosixParser();
59b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        CommandLine commandLine;
60b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov
61b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        try {
62b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov            commandLine = parser.parse(options, args);
63b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        } catch (ParseException ex) {
64b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov            usage();
65b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov            return;
66b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        }
67b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov
68b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        String[] remainingArgs = commandLine.getArgs();
69b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov
70b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        Option[] parsedOptions = commandLine.getOptions();
71b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        ArrayList<String> bootClassPathDirs = Lists.newArrayList();
72b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        String outFile = "vtables.txt";
73b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        int apiLevel = 15;
74b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov
75b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        for (int i=0; i<parsedOptions.length; i++) {
76b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov            Option option = parsedOptions[i];
77b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov            String opt = option.getOpt();
78b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov
79b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov            switch (opt.charAt(0)) {
80b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov                case 'd':
81b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov                    bootClassPathDirs.add(option.getValue());
82b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov                    break;
83b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov                case 'o':
84b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov                    outFile = option.getValue();
85b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov                    break;
86b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov                case 'a':
87b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov                    apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
88b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov                    break;
89b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov                default:
90b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov                    assert false;
91b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov            }
92b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        }
93b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov
94b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        if (remainingArgs.length != 1) {
95b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov            usage();
96b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov            return;
97b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        }
98b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov
99b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        String inputDexFileName = remainingArgs[0];
100b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov
101b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        File dexFileFile = new File(inputDexFileName);
102b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        if (!dexFileFile.exists()) {
103b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov            System.err.println("Can't find the file " + inputDexFileName);
104b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov            System.exit(1);
105b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        }
106b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov
107b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        try {
108b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov            DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, apiLevel);
109b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov            Iterable<String> bootClassPaths = Splitter.on(":").split("core.jar:ext.jar:framework.jar:android.policy.jar:services.jar");
110b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov            ClassPath classPath = ClassPath.fromClassPath(bootClassPathDirs, bootClassPaths, dexFile, apiLevel);
111b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov            FileOutputStream outStream = new FileOutputStream(outFile);
112b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov
113b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov            for (ClassDef classDef: dexFile.getClasses()) {
114b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov                ClassProto classProto = (ClassProto) classPath.getClass(classDef);
115ec1348b46dd4d12d28998da9f99a22f110322960Ben Gruver                List<Method> methods = classProto.getVtable();
116ec1348b46dd4d12d28998da9f99a22f110322960Ben Gruver                String className = "Class "  + classDef.getType() + " extends " + classDef.getSuperclass() + " : " + methods.size() + " methods\n";
117b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov                outStream.write(className.getBytes());
118ec1348b46dd4d12d28998da9f99a22f110322960Ben Gruver                for (int i=0;i<methods.size();i++) {
119ec1348b46dd4d12d28998da9f99a22f110322960Ben Gruver                    Method method = methods.get(i);
120ec1348b46dd4d12d28998da9f99a22f110322960Ben Gruver
121ec1348b46dd4d12d28998da9f99a22f110322960Ben Gruver                    String methodString = i + ":" + method.getDefiningClass() + "->" + method.getName() + "(";
122ec1348b46dd4d12d28998da9f99a22f110322960Ben Gruver                    for (CharSequence parameter: method.getParameterTypes()) {
123ec1348b46dd4d12d28998da9f99a22f110322960Ben Gruver                        methodString += parameter;
124b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov                    }
125ec1348b46dd4d12d28998da9f99a22f110322960Ben Gruver                    methodString += ")" + method.getReturnType() + "\n";
126ec1348b46dd4d12d28998da9f99a22f110322960Ben Gruver                    outStream.write(methodString.getBytes());
127b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov                }
128b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov                outStream.write("\n".getBytes());
129b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov            }
130b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov            outStream.close();
131b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        } catch (IOException ex) {
132b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov            System.out.println("IOException thrown when trying to open a dex file or write out vtables: " + ex);
133b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        }
134b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov
135b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov    }
136b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov
137b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov    /**
138b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov     * Prints the usage message.
139b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov     */
140b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov    private static void usage() {
141b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        int consoleWidth = ConsoleUtil.getConsoleWidth();
142b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        if (consoleWidth <= 0) {
143b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov            consoleWidth = 80;
144b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        }
145b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov
146ea4ee3e8de24d40ad9ef6f0c11283cfb5b1b5993Izzat Bahadirov        System.out.println("java -cp baksmali.jar org.jf.dexlib2.analysis.DumpVtables -d path/to/framework/jar/files <dex-file>");
147b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov    }
148b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov
149b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov    private static void buildOptions() {
150b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        Option classPathDirOption = OptionBuilder.withLongOpt("bootclasspath-dir")
1516a2a627d3b132574c50b4882994393eecf58db5fIzzat Bahadirov                .withDescription("the base folder to look for the bootclasspath files in. Defaults to the current " +
1526a2a627d3b132574c50b4882994393eecf58db5fIzzat Bahadirov                        "directory")
1536a2a627d3b132574c50b4882994393eecf58db5fIzzat Bahadirov                .hasArg()
1546a2a627d3b132574c50b4882994393eecf58db5fIzzat Bahadirov                .withArgName("DIR")
1556a2a627d3b132574c50b4882994393eecf58db5fIzzat Bahadirov                .create("d");
156b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov
157b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        Option outputFileOption = OptionBuilder.withLongOpt("out-file")
1586a2a627d3b132574c50b4882994393eecf58db5fIzzat Bahadirov                .withDescription("output file")
1596a2a627d3b132574c50b4882994393eecf58db5fIzzat Bahadirov                .hasArg()
1606a2a627d3b132574c50b4882994393eecf58db5fIzzat Bahadirov                .withArgName("FILE")
1616a2a627d3b132574c50b4882994393eecf58db5fIzzat Bahadirov                .create("o");
162b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov
163b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        Option apiLevelOption = OptionBuilder.withLongOpt("api-level")
1646a2a627d3b132574c50b4882994393eecf58db5fIzzat Bahadirov                .withDescription("The numeric api-level of the file being disassembled. If not " +
1656a2a627d3b132574c50b4882994393eecf58db5fIzzat Bahadirov                        "specified, it defaults to 15 (ICS).")
1666a2a627d3b132574c50b4882994393eecf58db5fIzzat Bahadirov                .hasArg()
1676a2a627d3b132574c50b4882994393eecf58db5fIzzat Bahadirov                .withArgName("API_LEVEL")
1686a2a627d3b132574c50b4882994393eecf58db5fIzzat Bahadirov                .create("a");
169b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov
170b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        options.addOption(classPathDirOption);
171b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        options.addOption(outputFileOption);
172b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov        options.addOption(apiLevelOption);
173b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov    }
174b2ce899471be1c136aa13d502e885585fa59d460Izzat Bahadirov}
175