Baksmali.java revision dea5f8d544040e506b550116bd7874d27436b99b
136836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com/*
236836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com * [The "BSD licence"]
300fc68adf2e39aeb9fed35293f2576bbe729ec4bJesusFreke@JesusFreke.com * Copyright (c) 2010 Ben Gruver (JesusFreke)
436836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com * All rights reserved.
536836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com *
636836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com * Redistribution and use in source and binary forms, with or without
736836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com * modification, are permitted provided that the following conditions
836836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com * are met:
936836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com * 1. Redistributions of source code must retain the above copyright
1036836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com *    notice, this list of conditions and the following disclaimer.
1136836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com * 2. Redistributions in binary form must reproduce the above copyright
1236836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com *    notice, this list of conditions and the following disclaimer in the
1336836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com *    documentation and/or other materials provided with the distribution.
1436836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com * 3. The name of the author may not be used to endorse or promote products
1536836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com *    derived from this software without specific prior written permission.
1636836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com *
1736836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1836836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1936836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2036836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2136836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2236836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2336836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2436836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2536836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2636836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2736836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com */
2836836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com
2936836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.compackage org.jf.baksmali;
3036836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com
31987cdc3e0e1ee99c104837192ea1f63e4fa5565aBen Gruverimport com.google.common.collect.ImmutableList;
32c91b03ba45ccacfa7b0ad52592a42e8fd8c18da1Ben Gruverimport com.google.common.collect.Iterables;
337e25c35df7786c98bc6fa96958e93146ca73367aBen Gruverimport com.google.common.collect.Lists;
346ef13753e78bb7abc7e7683d5e533c3395d4a9b6JesusFreke@JesusFreke.comimport org.jf.baksmali.Adaptors.ClassDefinition;
35c91b03ba45ccacfa7b0ad52592a42e8fd8c18da1Ben Gruverimport org.jf.dexlib2.analysis.ClassPath;
368b1508ee58f4918835d8c01483725b508d21be29Ben Gruverimport org.jf.dexlib2.iface.ClassDef;
378b1508ee58f4918835d8c01483725b508d21be29Ben Gruverimport org.jf.dexlib2.iface.DexFile;
382b8845bb247e3e5ee154966866b53fa9887e2609Ben Gruverimport org.jf.dexlib2.util.SyntheticAccessorResolver;
39bbf4dbba6127ef96e316060b2b4ec292627a4078JesusFreke@JesusFreke.comimport org.jf.util.ClassFileNameHandler;
404b72225e9d81201838f387171a68a832486903f9JesusFreke@JesusFreke.comimport org.jf.util.IndentingWriter;
4136836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com
42e9ee92dc4c0848146e00d5607eb4baa5750361c8JesusFreke@JesusFreke.comimport java.io.*;
43c91b03ba45ccacfa7b0ad52592a42e8fd8c18da1Ben Gruverimport java.util.*;
447e25c35df7786c98bc6fa96958e93146ca73367aBen Gruverimport java.util.concurrent.*;
4536836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com
4636836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.compublic class baksmali {
474b171afedb983fb811990beeec6a15e30a90b455Ben Gruver
48dea5f8d544040e506b550116bd7874d27436b99bBen Gruver    public static boolean disassembleDexFile(DexFile dexFile, final baksmaliOptions options) {
494b171afedb983fb811990beeec6a15e30a90b455Ben Gruver        if (options.registerInfo != 0 || options.deodex) {
50e9b722eab0b0932be59cb99c8c6f403b00abad6fJesusFreke@JesusFreke.com            try {
514b171afedb983fb811990beeec6a15e30a90b455Ben Gruver                Iterable<String> extraClassPathEntries;
524b171afedb983fb811990beeec6a15e30a90b455Ben Gruver                if (options.extraClassPathEntries != null) {
534b171afedb983fb811990beeec6a15e30a90b455Ben Gruver                    extraClassPathEntries = options.extraClassPathEntries;
54c91b03ba45ccacfa7b0ad52592a42e8fd8c18da1Ben Gruver                } else {
554b171afedb983fb811990beeec6a15e30a90b455Ben Gruver                    extraClassPathEntries = ImmutableList.of();
5678bde01ad4bf31ad44ad7bd0279b07fd2696b53cJesusFreke@JesusFreke.com                }
575967598d012839eb25d50d9fa63952ac802e05ddBen Gruver
584b171afedb983fb811990beeec6a15e30a90b455Ben Gruver                options.classPath = ClassPath.fromClassPath(options.bootClassPathDirs,
594b171afedb983fb811990beeec6a15e30a90b455Ben Gruver                        Iterables.concat(options.bootClassPathEntries, extraClassPathEntries), dexFile,
604b171afedb983fb811990beeec6a15e30a90b455Ben Gruver                        options.apiLevel);
61e9b722eab0b0932be59cb99c8c6f403b00abad6fJesusFreke@JesusFreke.com            } catch (Exception ex) {
62e9b722eab0b0932be59cb99c8c6f403b00abad6fJesusFreke@JesusFreke.com                System.err.println("\n\nError occured while loading boot class path files. Aborting.");
63e9b722eab0b0932be59cb99c8c6f403b00abad6fJesusFreke@JesusFreke.com                ex.printStackTrace(System.err);
64e9b722eab0b0932be59cb99c8c6f403b00abad6fJesusFreke@JesusFreke.com                System.exit(1);
65e9b722eab0b0932be59cb99c8c6f403b00abad6fJesusFreke@JesusFreke.com            }
66c91b03ba45ccacfa7b0ad52592a42e8fd8c18da1Ben Gruver        }
67e2684fa2191e04f27faba763f2bcc19593513b25JesusFreke@JesusFreke.com
684b171afedb983fb811990beeec6a15e30a90b455Ben Gruver        File outputDirectoryFile = new File(options.outputDirectory);
6981014659d928284a14fafc23bc239e39de836d83JesusFreke@JesusFreke.com        if (!outputDirectoryFile.exists()) {
7081014659d928284a14fafc23bc239e39de836d83JesusFreke@JesusFreke.com            if (!outputDirectoryFile.mkdirs()) {
714b171afedb983fb811990beeec6a15e30a90b455Ben Gruver                System.err.println("Can't create the output directory " + options.outputDirectory);
72e9ee92dc4c0848146e00d5607eb4baa5750361c8JesusFreke@JesusFreke.com                System.exit(1);
73e9ee92dc4c0848146e00d5607eb4baa5750361c8JesusFreke@JesusFreke.com            }
74e9ee92dc4c0848146e00d5607eb4baa5750361c8JesusFreke@JesusFreke.com        }
75e9ee92dc4c0848146e00d5607eb4baa5750361c8JesusFreke@JesusFreke.com
76a6e5671a627284347484db96f40a29a45e4e4ed1JesusFreke@JesusFreke.com        //sort the classes, so that if we're on a case-insensitive file system and need to handle classes with file
77a6e5671a627284347484db96f40a29a45e4e4ed1JesusFreke@JesusFreke.com        //name collisions, then we'll use the same name for each class, if the dex file goes through multiple
78a6e5671a627284347484db96f40a29a45e4e4ed1JesusFreke@JesusFreke.com        //baksmali/smali cycles for some reason. If a class with a colliding name is added or removed, the filenames
79a6e5671a627284347484db96f40a29a45e4e4ed1JesusFreke@JesusFreke.com        //may still change of course
80987cdc3e0e1ee99c104837192ea1f63e4fa5565aBen Gruver        List<ClassDef> classDefs = new ArrayList<ClassDef>(dexFile.getClasses());
81987cdc3e0e1ee99c104837192ea1f63e4fa5565aBen Gruver        Collections.sort(classDefs, new Comparator<ClassDef>() {
82987cdc3e0e1ee99c104837192ea1f63e4fa5565aBen Gruver            public int compare(ClassDef classDef1, ClassDef classDef2) {
83987cdc3e0e1ee99c104837192ea1f63e4fa5565aBen Gruver                return classDef1.getType().compareTo(classDef2.getType());
84a6e5671a627284347484db96f40a29a45e4e4ed1JesusFreke@JesusFreke.com            }
85987cdc3e0e1ee99c104837192ea1f63e4fa5565aBen Gruver        });
86987cdc3e0e1ee99c104837192ea1f63e4fa5565aBen Gruver        classDefs = ImmutableList.copyOf(classDefs);
87987cdc3e0e1ee99c104837192ea1f63e4fa5565aBen Gruver
884b171afedb983fb811990beeec6a15e30a90b455Ben Gruver        if (!options.noAccessorComments) {
8993aa50139c4641d931b05608f73af8879c0de1c2Ben Gruver            options.syntheticAccessorResolver = new SyntheticAccessorResolver(classDefs);
90987cdc3e0e1ee99c104837192ea1f63e4fa5565aBen Gruver        }
91a6e5671a627284347484db96f40a29a45e4e4ed1JesusFreke@JesusFreke.com
927e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver        final ClassFileNameHandler fileNameHandler = new ClassFileNameHandler(outputDirectoryFile, ".smali");
93a6e5671a627284347484db96f40a29a45e4e4ed1JesusFreke@JesusFreke.com
947e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver        ExecutorService executor = Executors.newFixedThreadPool(options.jobs);
95dea5f8d544040e506b550116bd7874d27436b99bBen Gruver        List<Future<Boolean>> tasks = Lists.newArrayList();
967e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver
977e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver        for (final ClassDef classDef: classDefs) {
98dea5f8d544040e506b550116bd7874d27436b99bBen Gruver            tasks.add(executor.submit(new Callable<Boolean>() {
99dea5f8d544040e506b550116bd7874d27436b99bBen Gruver                @Override public Boolean call() throws Exception {
100dea5f8d544040e506b550116bd7874d27436b99bBen Gruver                    return disassembleClass(classDef, fileNameHandler, options);
1017e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver                }
1027e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver            }));
1037e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver        }
1047e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver
105dea5f8d544040e506b550116bd7874d27436b99bBen Gruver        boolean errorOccurred = false;
106dea5f8d544040e506b550116bd7874d27436b99bBen Gruver        for (Future<Boolean> task: tasks) {
1077e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver            while(true) {
1087e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver                try {
109dea5f8d544040e506b550116bd7874d27436b99bBen Gruver                    if (!task.get()) {
110dea5f8d544040e506b550116bd7874d27436b99bBen Gruver                        errorOccurred = true;
111dea5f8d544040e506b550116bd7874d27436b99bBen Gruver                    }
1127e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver                } catch (InterruptedException ex) {
1137e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver                    continue;
1147e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver                } catch (ExecutionException ex) {
1157e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver                    throw new RuntimeException(ex);
1167e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver                }
1177e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver                break;
1187e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver            }
1194b171afedb983fb811990beeec6a15e30a90b455Ben Gruver        }
1207e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver
1217e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver        executor.shutdown();
122dea5f8d544040e506b550116bd7874d27436b99bBen Gruver        return !errorOccurred;
1234b171afedb983fb811990beeec6a15e30a90b455Ben Gruver    }
12481014659d928284a14fafc23bc239e39de836d83JesusFreke@JesusFreke.com
125dea5f8d544040e506b550116bd7874d27436b99bBen Gruver    private static boolean disassembleClass(ClassDef classDef, ClassFileNameHandler fileNameHandler,
126dea5f8d544040e506b550116bd7874d27436b99bBen Gruver                                            baksmaliOptions options) {
1274b171afedb983fb811990beeec6a15e30a90b455Ben Gruver        /**
1284b171afedb983fb811990beeec6a15e30a90b455Ben Gruver         * The path for the disassembly file is based on the package name
1294b171afedb983fb811990beeec6a15e30a90b455Ben Gruver         * The class descriptor will look something like:
1304b171afedb983fb811990beeec6a15e30a90b455Ben Gruver         * Ljava/lang/Object;
1314b171afedb983fb811990beeec6a15e30a90b455Ben Gruver         * Where the there is leading 'L' and a trailing ';', and the parts of the
1324b171afedb983fb811990beeec6a15e30a90b455Ben Gruver         * package name are separated by '/'
1334b171afedb983fb811990beeec6a15e30a90b455Ben Gruver         */
1344b171afedb983fb811990beeec6a15e30a90b455Ben Gruver        String classDescriptor = classDef.getType();
1354b171afedb983fb811990beeec6a15e30a90b455Ben Gruver
1364b171afedb983fb811990beeec6a15e30a90b455Ben Gruver        //validate that the descriptor is formatted like we expect
1374b171afedb983fb811990beeec6a15e30a90b455Ben Gruver        if (classDescriptor.charAt(0) != 'L' ||
1384b171afedb983fb811990beeec6a15e30a90b455Ben Gruver                classDescriptor.charAt(classDescriptor.length()-1) != ';') {
1394b171afedb983fb811990beeec6a15e30a90b455Ben Gruver            System.err.println("Unrecognized class descriptor - " + classDescriptor + " - skipping class");
140dea5f8d544040e506b550116bd7874d27436b99bBen Gruver            return false;
1414b171afedb983fb811990beeec6a15e30a90b455Ben Gruver        }
14281014659d928284a14fafc23bc239e39de836d83JesusFreke@JesusFreke.com
1434b171afedb983fb811990beeec6a15e30a90b455Ben Gruver        File smaliFile = fileNameHandler.getUniqueFilenameForClass(classDescriptor);
14436836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com
1454b171afedb983fb811990beeec6a15e30a90b455Ben Gruver        //create and initialize the top level string template
1464b171afedb983fb811990beeec6a15e30a90b455Ben Gruver        ClassDefinition classDefinition = new ClassDefinition(options, classDef);
1476eae34831fee1f116f3a453bdc5e143d68e05e03JesusFreke@JesusFreke.com
1484b171afedb983fb811990beeec6a15e30a90b455Ben Gruver        //write the disassembly
1494b171afedb983fb811990beeec6a15e30a90b455Ben Gruver        Writer writer = null;
1504b171afedb983fb811990beeec6a15e30a90b455Ben Gruver        try
1514b171afedb983fb811990beeec6a15e30a90b455Ben Gruver        {
1524b171afedb983fb811990beeec6a15e30a90b455Ben Gruver            File smaliParent = smaliFile.getParentFile();
1534b171afedb983fb811990beeec6a15e30a90b455Ben Gruver            if (!smaliParent.exists()) {
1544b171afedb983fb811990beeec6a15e30a90b455Ben Gruver                if (!smaliParent.mkdirs()) {
1557e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver                    // check again, it's likely it was created in a different thread
1567e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver                    if (!smaliParent.exists()) {
1577e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver                        System.err.println("Unable to create directory " + smaliParent.toString() + " - skipping class");
158dea5f8d544040e506b550116bd7874d27436b99bBen Gruver                        return false;
1597e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver                    }
1604b171afedb983fb811990beeec6a15e30a90b455Ben Gruver                }
16181014659d928284a14fafc23bc239e39de836d83JesusFreke@JesusFreke.com            }
1624b171afedb983fb811990beeec6a15e30a90b455Ben Gruver
1634b171afedb983fb811990beeec6a15e30a90b455Ben Gruver            if (!smaliFile.exists()){
1644b171afedb983fb811990beeec6a15e30a90b455Ben Gruver                if (!smaliFile.createNewFile()) {
1654b171afedb983fb811990beeec6a15e30a90b455Ben Gruver                    System.err.println("Unable to create file " + smaliFile.toString() + " - skipping class");
166dea5f8d544040e506b550116bd7874d27436b99bBen Gruver                    return false;
16781014659d928284a14fafc23bc239e39de836d83JesusFreke@JesusFreke.com                }
168e9ee92dc4c0848146e00d5607eb4baa5750361c8JesusFreke@JesusFreke.com            }
1692371e35aaeaf2ed4d7c571fb3286090eb01b717dJesusFreke@JesusFreke.com
1704b171afedb983fb811990beeec6a15e30a90b455Ben Gruver            BufferedWriter bufWriter = new BufferedWriter(new OutputStreamWriter(
1714b171afedb983fb811990beeec6a15e30a90b455Ben Gruver                    new FileOutputStream(smaliFile), "UTF8"));
1724b171afedb983fb811990beeec6a15e30a90b455Ben Gruver
1734b171afedb983fb811990beeec6a15e30a90b455Ben Gruver            writer = new IndentingWriter(bufWriter);
1744b171afedb983fb811990beeec6a15e30a90b455Ben Gruver            classDefinition.writeTo((IndentingWriter)writer);
1754b171afedb983fb811990beeec6a15e30a90b455Ben Gruver        } catch (Exception ex) {
1764b171afedb983fb811990beeec6a15e30a90b455Ben Gruver            System.err.println("\n\nError occured while disassembling class " + classDescriptor.replace('/', '.') + " - skipping class");
1774b171afedb983fb811990beeec6a15e30a90b455Ben Gruver            ex.printStackTrace();
1784b171afedb983fb811990beeec6a15e30a90b455Ben Gruver            // noinspection ResultOfMethodCallIgnored
1794b171afedb983fb811990beeec6a15e30a90b455Ben Gruver            smaliFile.delete();
180dea5f8d544040e506b550116bd7874d27436b99bBen Gruver            return false;
1814b171afedb983fb811990beeec6a15e30a90b455Ben Gruver        }
1824b171afedb983fb811990beeec6a15e30a90b455Ben Gruver        finally
1834b171afedb983fb811990beeec6a15e30a90b455Ben Gruver        {
1844b171afedb983fb811990beeec6a15e30a90b455Ben Gruver            if (writer != null) {
1854b171afedb983fb811990beeec6a15e30a90b455Ben Gruver                try {
1864b171afedb983fb811990beeec6a15e30a90b455Ben Gruver                    writer.close();
1874b171afedb983fb811990beeec6a15e30a90b455Ben Gruver                } catch (Throwable ex) {
1884b171afedb983fb811990beeec6a15e30a90b455Ben Gruver                    System.err.println("\n\nError occured while closing file " + smaliFile.toString());
1894b171afedb983fb811990beeec6a15e30a90b455Ben Gruver                    ex.printStackTrace();
1904b171afedb983fb811990beeec6a15e30a90b455Ben Gruver                }
1912371e35aaeaf2ed4d7c571fb3286090eb01b717dJesusFreke@JesusFreke.com            }
192e9ee92dc4c0848146e00d5607eb4baa5750361c8JesusFreke@JesusFreke.com        }
193dea5f8d544040e506b550116bd7874d27436b99bBen Gruver        return true;
19478bde01ad4bf31ad44ad7bd0279b07fd2696b53cJesusFreke@JesusFreke.com    }
19536836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com}
196