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 317e25c35df7786c98bc6fa96958e93146ca73367aBen Gruverimport com.google.common.collect.Lists; 3213705697c23cb2d72bea75d661390be26fea50a4Ben Gruverimport com.google.common.collect.Ordering; 336ef13753e78bb7abc7e7683d5e533c3395d4a9b6JesusFreke@JesusFreke.comimport org.jf.baksmali.Adaptors.ClassDefinition; 348b1508ee58f4918835d8c01483725b508d21be29Ben Gruverimport org.jf.dexlib2.iface.ClassDef; 358b1508ee58f4918835d8c01483725b508d21be29Ben Gruverimport org.jf.dexlib2.iface.DexFile; 36bbf4dbba6127ef96e316060b2b4ec292627a4078JesusFreke@JesusFreke.comimport org.jf.util.ClassFileNameHandler; 374b72225e9d81201838f387171a68a832486903f9JesusFreke@JesusFreke.comimport org.jf.util.IndentingWriter; 3836836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com 3922e85fc3ff3d85e8a30fc42438ccb942fc5d80dfBen Gruverimport javax.annotation.Nullable; 40e9ee92dc4c0848146e00d5607eb4baa5750361c8JesusFreke@JesusFreke.comimport java.io.*; 4122e85fc3ff3d85e8a30fc42438ccb942fc5d80dfBen Gruverimport java.util.HashSet; 4213705697c23cb2d72bea75d661390be26fea50a4Ben Gruverimport java.util.List; 4322e85fc3ff3d85e8a30fc42438ccb942fc5d80dfBen Gruverimport java.util.Set; 447e25c35df7786c98bc6fa96958e93146ca73367aBen Gruverimport java.util.concurrent.*; 4536836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com 465a5eafb818cc18baeef8bdae1940401da3735f25Ben Gruverpublic class Baksmali { 475a5eafb818cc18baeef8bdae1940401da3735f25Ben Gruver public static boolean disassembleDexFile(DexFile dexFile, File outputDir, int jobs, final BaksmaliOptions options) { 4822e85fc3ff3d85e8a30fc42438ccb942fc5d80dfBen Gruver return disassembleDexFile(dexFile, outputDir, jobs, options, null); 4922e85fc3ff3d85e8a30fc42438ccb942fc5d80dfBen Gruver } 5022e85fc3ff3d85e8a30fc42438ccb942fc5d80dfBen Gruver 5122e85fc3ff3d85e8a30fc42438ccb942fc5d80dfBen Gruver public static boolean disassembleDexFile(DexFile dexFile, File outputDir, int jobs, final BaksmaliOptions options, 5222e85fc3ff3d85e8a30fc42438ccb942fc5d80dfBen Gruver @Nullable List<String> classes) { 53e9ee92dc4c0848146e00d5607eb4baa5750361c8JesusFreke@JesusFreke.com 54a6e5671a627284347484db96f40a29a45e4e4ed1JesusFreke@JesusFreke.com //sort the classes, so that if we're on a case-insensitive file system and need to handle classes with file 55a6e5671a627284347484db96f40a29a45e4e4ed1JesusFreke@JesusFreke.com //name collisions, then we'll use the same name for each class, if the dex file goes through multiple 56a6e5671a627284347484db96f40a29a45e4e4ed1JesusFreke@JesusFreke.com //baksmali/smali cycles for some reason. If a class with a colliding name is added or removed, the filenames 57a6e5671a627284347484db96f40a29a45e4e4ed1JesusFreke@JesusFreke.com //may still change of course 5813705697c23cb2d72bea75d661390be26fea50a4Ben Gruver List<? extends ClassDef> classDefs = Ordering.natural().sortedCopy(dexFile.getClasses()); 59987cdc3e0e1ee99c104837192ea1f63e4fa5565aBen Gruver 605a5eafb818cc18baeef8bdae1940401da3735f25Ben Gruver final ClassFileNameHandler fileNameHandler = new ClassFileNameHandler(outputDir, ".smali"); 61a6e5671a627284347484db96f40a29a45e4e4ed1JesusFreke@JesusFreke.com 625a5eafb818cc18baeef8bdae1940401da3735f25Ben Gruver ExecutorService executor = Executors.newFixedThreadPool(jobs); 63dea5f8d544040e506b550116bd7874d27436b99bBen Gruver List<Future<Boolean>> tasks = Lists.newArrayList(); 647e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver 6522e85fc3ff3d85e8a30fc42438ccb942fc5d80dfBen Gruver Set<String> classSet = null; 6622e85fc3ff3d85e8a30fc42438ccb942fc5d80dfBen Gruver if (classes != null) { 6722e85fc3ff3d85e8a30fc42438ccb942fc5d80dfBen Gruver classSet = new HashSet<String>(classes); 6822e85fc3ff3d85e8a30fc42438ccb942fc5d80dfBen Gruver } 6922e85fc3ff3d85e8a30fc42438ccb942fc5d80dfBen Gruver 707e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver for (final ClassDef classDef: classDefs) { 7122e85fc3ff3d85e8a30fc42438ccb942fc5d80dfBen Gruver if (classSet != null && !classSet.contains(classDef.getType())) { 7222e85fc3ff3d85e8a30fc42438ccb942fc5d80dfBen Gruver continue; 7322e85fc3ff3d85e8a30fc42438ccb942fc5d80dfBen Gruver } 74dea5f8d544040e506b550116bd7874d27436b99bBen Gruver tasks.add(executor.submit(new Callable<Boolean>() { 75dea5f8d544040e506b550116bd7874d27436b99bBen Gruver @Override public Boolean call() throws Exception { 76dea5f8d544040e506b550116bd7874d27436b99bBen Gruver return disassembleClass(classDef, fileNameHandler, options); 777e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver } 787e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver })); 797e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver } 807e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver 81dea5f8d544040e506b550116bd7874d27436b99bBen Gruver boolean errorOccurred = false; 82eae0b0edbf3f0feedc289655144c54d27cb2ddccBen Gruver try { 83eae0b0edbf3f0feedc289655144c54d27cb2ddccBen Gruver for (Future<Boolean> task: tasks) { 84eae0b0edbf3f0feedc289655144c54d27cb2ddccBen Gruver while(true) { 85eae0b0edbf3f0feedc289655144c54d27cb2ddccBen Gruver try { 86eae0b0edbf3f0feedc289655144c54d27cb2ddccBen Gruver if (!task.get()) { 87eae0b0edbf3f0feedc289655144c54d27cb2ddccBen Gruver errorOccurred = true; 88eae0b0edbf3f0feedc289655144c54d27cb2ddccBen Gruver } 89eae0b0edbf3f0feedc289655144c54d27cb2ddccBen Gruver } catch (InterruptedException ex) { 90eae0b0edbf3f0feedc289655144c54d27cb2ddccBen Gruver continue; 91eae0b0edbf3f0feedc289655144c54d27cb2ddccBen Gruver } catch (ExecutionException ex) { 92eae0b0edbf3f0feedc289655144c54d27cb2ddccBen Gruver throw new RuntimeException(ex); 93dea5f8d544040e506b550116bd7874d27436b99bBen Gruver } 94eae0b0edbf3f0feedc289655144c54d27cb2ddccBen Gruver break; 957e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver } 967e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver } 97eae0b0edbf3f0feedc289655144c54d27cb2ddccBen Gruver } finally { 98eae0b0edbf3f0feedc289655144c54d27cb2ddccBen Gruver executor.shutdown(); 994b171afedb983fb811990beeec6a15e30a90b455Ben Gruver } 100dea5f8d544040e506b550116bd7874d27436b99bBen Gruver return !errorOccurred; 1014b171afedb983fb811990beeec6a15e30a90b455Ben Gruver } 10281014659d928284a14fafc23bc239e39de836d83JesusFreke@JesusFreke.com 103dea5f8d544040e506b550116bd7874d27436b99bBen Gruver private static boolean disassembleClass(ClassDef classDef, ClassFileNameHandler fileNameHandler, 1045a5eafb818cc18baeef8bdae1940401da3735f25Ben Gruver BaksmaliOptions options) { 1054b171afedb983fb811990beeec6a15e30a90b455Ben Gruver /** 1064b171afedb983fb811990beeec6a15e30a90b455Ben Gruver * The path for the disassembly file is based on the package name 1074b171afedb983fb811990beeec6a15e30a90b455Ben Gruver * The class descriptor will look something like: 1084b171afedb983fb811990beeec6a15e30a90b455Ben Gruver * Ljava/lang/Object; 1094b171afedb983fb811990beeec6a15e30a90b455Ben Gruver * Where the there is leading 'L' and a trailing ';', and the parts of the 1104b171afedb983fb811990beeec6a15e30a90b455Ben Gruver * package name are separated by '/' 1114b171afedb983fb811990beeec6a15e30a90b455Ben Gruver */ 1124b171afedb983fb811990beeec6a15e30a90b455Ben Gruver String classDescriptor = classDef.getType(); 1134b171afedb983fb811990beeec6a15e30a90b455Ben Gruver 1144b171afedb983fb811990beeec6a15e30a90b455Ben Gruver //validate that the descriptor is formatted like we expect 1154b171afedb983fb811990beeec6a15e30a90b455Ben Gruver if (classDescriptor.charAt(0) != 'L' || 1164b171afedb983fb811990beeec6a15e30a90b455Ben Gruver classDescriptor.charAt(classDescriptor.length()-1) != ';') { 1174b171afedb983fb811990beeec6a15e30a90b455Ben Gruver System.err.println("Unrecognized class descriptor - " + classDescriptor + " - skipping class"); 118dea5f8d544040e506b550116bd7874d27436b99bBen Gruver return false; 1194b171afedb983fb811990beeec6a15e30a90b455Ben Gruver } 12081014659d928284a14fafc23bc239e39de836d83JesusFreke@JesusFreke.com 1214b171afedb983fb811990beeec6a15e30a90b455Ben Gruver File smaliFile = fileNameHandler.getUniqueFilenameForClass(classDescriptor); 12236836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com 1234b171afedb983fb811990beeec6a15e30a90b455Ben Gruver //create and initialize the top level string template 1244b171afedb983fb811990beeec6a15e30a90b455Ben Gruver ClassDefinition classDefinition = new ClassDefinition(options, classDef); 1256eae34831fee1f116f3a453bdc5e143d68e05e03JesusFreke@JesusFreke.com 1264b171afedb983fb811990beeec6a15e30a90b455Ben Gruver //write the disassembly 1274b171afedb983fb811990beeec6a15e30a90b455Ben Gruver Writer writer = null; 1284b171afedb983fb811990beeec6a15e30a90b455Ben Gruver try 1294b171afedb983fb811990beeec6a15e30a90b455Ben Gruver { 1304b171afedb983fb811990beeec6a15e30a90b455Ben Gruver File smaliParent = smaliFile.getParentFile(); 1314b171afedb983fb811990beeec6a15e30a90b455Ben Gruver if (!smaliParent.exists()) { 1324b171afedb983fb811990beeec6a15e30a90b455Ben Gruver if (!smaliParent.mkdirs()) { 1337e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver // check again, it's likely it was created in a different thread 1347e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver if (!smaliParent.exists()) { 1357e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver System.err.println("Unable to create directory " + smaliParent.toString() + " - skipping class"); 136dea5f8d544040e506b550116bd7874d27436b99bBen Gruver return false; 1377e25c35df7786c98bc6fa96958e93146ca73367aBen Gruver } 1384b171afedb983fb811990beeec6a15e30a90b455Ben Gruver } 13981014659d928284a14fafc23bc239e39de836d83JesusFreke@JesusFreke.com } 1404b171afedb983fb811990beeec6a15e30a90b455Ben Gruver 1414b171afedb983fb811990beeec6a15e30a90b455Ben Gruver if (!smaliFile.exists()){ 1424b171afedb983fb811990beeec6a15e30a90b455Ben Gruver if (!smaliFile.createNewFile()) { 1434b171afedb983fb811990beeec6a15e30a90b455Ben Gruver System.err.println("Unable to create file " + smaliFile.toString() + " - skipping class"); 144dea5f8d544040e506b550116bd7874d27436b99bBen Gruver return false; 14581014659d928284a14fafc23bc239e39de836d83JesusFreke@JesusFreke.com } 146e9ee92dc4c0848146e00d5607eb4baa5750361c8JesusFreke@JesusFreke.com } 1472371e35aaeaf2ed4d7c571fb3286090eb01b717dJesusFreke@JesusFreke.com 1484b171afedb983fb811990beeec6a15e30a90b455Ben Gruver BufferedWriter bufWriter = new BufferedWriter(new OutputStreamWriter( 1494b171afedb983fb811990beeec6a15e30a90b455Ben Gruver new FileOutputStream(smaliFile), "UTF8")); 1504b171afedb983fb811990beeec6a15e30a90b455Ben Gruver 1514b171afedb983fb811990beeec6a15e30a90b455Ben Gruver writer = new IndentingWriter(bufWriter); 1524b171afedb983fb811990beeec6a15e30a90b455Ben Gruver classDefinition.writeTo((IndentingWriter)writer); 1534b171afedb983fb811990beeec6a15e30a90b455Ben Gruver } catch (Exception ex) { 15468564258a316e41cb014f98d2777b5944bf37de3Ben Gruver System.err.println("\n\nError occurred while disassembling class " + classDescriptor.replace('/', '.') + " - skipping class"); 1554b171afedb983fb811990beeec6a15e30a90b455Ben Gruver ex.printStackTrace(); 1564b171afedb983fb811990beeec6a15e30a90b455Ben Gruver // noinspection ResultOfMethodCallIgnored 1574b171afedb983fb811990beeec6a15e30a90b455Ben Gruver smaliFile.delete(); 158dea5f8d544040e506b550116bd7874d27436b99bBen Gruver return false; 1594b171afedb983fb811990beeec6a15e30a90b455Ben Gruver } 1604b171afedb983fb811990beeec6a15e30a90b455Ben Gruver finally 1614b171afedb983fb811990beeec6a15e30a90b455Ben Gruver { 1624b171afedb983fb811990beeec6a15e30a90b455Ben Gruver if (writer != null) { 1634b171afedb983fb811990beeec6a15e30a90b455Ben Gruver try { 1644b171afedb983fb811990beeec6a15e30a90b455Ben Gruver writer.close(); 1654b171afedb983fb811990beeec6a15e30a90b455Ben Gruver } catch (Throwable ex) { 16668564258a316e41cb014f98d2777b5944bf37de3Ben Gruver System.err.println("\n\nError occurred while closing file " + smaliFile.toString()); 1674b171afedb983fb811990beeec6a15e30a90b455Ben Gruver ex.printStackTrace(); 1684b171afedb983fb811990beeec6a15e30a90b455Ben Gruver } 1692371e35aaeaf2ed4d7c571fb3286090eb01b717dJesusFreke@JesusFreke.com } 170e9ee92dc4c0848146e00d5607eb4baa5750361c8JesusFreke@JesusFreke.com } 171dea5f8d544040e506b550116bd7874d27436b99bBen Gruver return true; 17278bde01ad4bf31ad44ad7bd0279b07fd2696b53cJesusFreke@JesusFreke.com } 17336836121d7ecf72050d3ef065b7ab5fa86548319JesusFreke@JesusFreke.com} 174