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