baksmali.java revision 2b8845bb247e3e5ee154966866b53fa9887e2609
1/* 2 * [The "BSD licence"] 3 * Copyright (c) 2010 Ben Gruver (JesusFreke) 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29package org.jf.baksmali; 30 31import org.jf.baksmali.Adaptors.ClassDefinition; 32import org.jf.dexlib.Code.Analysis.InlineMethodResolver; 33import org.jf.dexlib2.iface.ClassDef; 34import org.jf.dexlib2.iface.DexFile; 35import org.jf.dexlib2.util.SyntheticAccessorResolver; 36import org.jf.util.ClassFileNameHandler; 37import org.jf.util.IndentingWriter; 38 39import java.io.*; 40import java.util.regex.Matcher; 41import java.util.regex.Pattern; 42 43public class baksmali { 44 public static boolean noParameterRegisters = false; 45 public static boolean useLocalsDirective = false; 46 public static boolean useSequentialLabels = false; 47 public static boolean outputDebugInfo = true; 48 public static boolean addCodeOffsets = false; 49 public static boolean noAccessorComments = false; 50 public static boolean deodex = false; 51 public static boolean verify = false; 52 public static InlineMethodResolver inlineResolver = null; 53 public static int registerInfo = 0; 54 public static String bootClassPath; 55 56 public static SyntheticAccessorResolver syntheticAccessorResolver = null; 57 58 public static void disassembleDexFile(String dexFilePath, DexFile dexFile, boolean deodex, String outputDirectory, 59 String[] classPathDirs, String bootClassPath, String extraBootClassPath, 60 boolean noParameterRegisters, boolean useLocalsDirective, 61 boolean useSequentialLabels, boolean outputDebugInfo, boolean addCodeOffsets, 62 boolean noAccessorComments, int registerInfo, boolean verify, 63 boolean ignoreErrors, String inlineTable, boolean checkPackagePrivateAccess) 64 { 65 baksmali.noParameterRegisters = noParameterRegisters; 66 baksmali.useLocalsDirective = useLocalsDirective; 67 baksmali.useSequentialLabels = useSequentialLabels; 68 baksmali.outputDebugInfo = outputDebugInfo; 69 baksmali.addCodeOffsets = addCodeOffsets; 70 baksmali.noAccessorComments = noAccessorComments; 71 baksmali.deodex = deodex; 72 baksmali.registerInfo = registerInfo; 73 baksmali.bootClassPath = bootClassPath; 74 baksmali.verify = verify; 75 76 //TODO: uncomment 77 /*if (registerInfo != 0 || deodex || verify) { 78 try { 79 String[] extraBootClassPathArray = null; 80 if (extraBootClassPath != null && extraBootClassPath.length() > 0) { 81 assert extraBootClassPath.charAt(0) == ':'; 82 extraBootClassPathArray = extraBootClassPath.substring(1).split(":"); 83 } 84 85 if (dexFile.isOdex() && bootClassPath == null) { 86 //ext.jar is a special case - it is typically the 2nd jar in the boot class path, but it also 87 //depends on classes in framework.jar (typically the 3rd jar in the BCP). If the user didn't 88 //specify a -c option, we should add framework.jar to the boot class path by default, so that it 89 //"just works" 90 if (extraBootClassPathArray == null && isExtJar(dexFilePath)) { 91 extraBootClassPathArray = new String[] {"framework.jar"}; 92 } 93 ClassPath.InitializeClassPathFromOdex(classPathDirs, extraBootClassPathArray, dexFilePath, dexFile, 94 checkPackagePrivateAccess); 95 } else { 96 String[] bootClassPathArray = null; 97 if (bootClassPath != null) { 98 bootClassPathArray = bootClassPath.split(":"); 99 } 100 ClassPath.InitializeClassPath(classPathDirs, bootClassPathArray, extraBootClassPathArray, 101 dexFilePath, dexFile, checkPackagePrivateAccess); 102 } 103 104 if (inlineTable != null) { 105 inlineResolver = new CustomInlineMethodResolver(inlineTable); 106 } 107 } catch (Exception ex) { 108 System.err.println("\n\nError occured while loading boot class path files. Aborting."); 109 ex.printStackTrace(System.err); 110 System.exit(1); 111 } 112 }*/ 113 114 File outputDirectoryFile = new File(outputDirectory); 115 if (!outputDirectoryFile.exists()) { 116 if (!outputDirectoryFile.mkdirs()) { 117 System.err.println("Can't create the output directory " + outputDirectory); 118 System.exit(1); 119 } 120 } 121 122 if (!noAccessorComments) { 123 syntheticAccessorResolver = new SyntheticAccessorResolver(dexFile.getClasses()); 124 } 125 126 //sort the classes, so that if we're on a case-insensitive file system and need to handle classes with file 127 //name collisions, then we'll use the same name for each class, if the dex file goes through multiple 128 //baksmali/smali cycles for some reason. If a class with a colliding name is added or removed, the filenames 129 //may still change of course 130 // TODO: aren't classes already sorted? Why do we need to sort here? 131 /*ArrayList<ClassDefItem> classDefItems = new ArrayList<ClassDefItem>(dexFile.ClassDefsSection.getItems()); 132 Collections.sort(classDefItems, new Comparator<ClassDefItem>() { 133 public int compare(ClassDefItem classDefItem1, ClassDefItem classDefItem2) { 134 return classDefItem1.getClassType().getTypeDescriptor().compareTo(classDefItem1.getClassType().getTypeDescriptor()); 135 } 136 });*/ 137 138 ClassFileNameHandler fileNameHandler = new ClassFileNameHandler(outputDirectoryFile, ".smali"); 139 140 for (ClassDef classDef: dexFile.getClasses()) { 141 /** 142 * The path for the disassembly file is based on the package name 143 * The class descriptor will look something like: 144 * Ljava/lang/Object; 145 * Where the there is leading 'L' and a trailing ';', and the parts of the 146 * package name are separated by '/' 147 */ 148 149 String classDescriptor = classDef.getType(); 150 151 //validate that the descriptor is formatted like we expect 152 if (classDescriptor.charAt(0) != 'L' || 153 classDescriptor.charAt(classDescriptor.length()-1) != ';') { 154 System.err.println("Unrecognized class descriptor - " + classDescriptor + " - skipping class"); 155 continue; 156 } 157 158 File smaliFile = fileNameHandler.getUniqueFilenameForClass(classDescriptor); 159 160 //create and initialize the top level string template 161 ClassDefinition classDefinition = new ClassDefinition(classDef); 162 163 //write the disassembly 164 Writer writer = null; 165 try 166 { 167 File smaliParent = smaliFile.getParentFile(); 168 if (!smaliParent.exists()) { 169 if (!smaliParent.mkdirs()) { 170 System.err.println("Unable to create directory " + smaliParent.toString() + " - skipping class"); 171 continue; 172 } 173 } 174 175 if (!smaliFile.exists()){ 176 if (!smaliFile.createNewFile()) { 177 System.err.println("Unable to create file " + smaliFile.toString() + " - skipping class"); 178 continue; 179 } 180 } 181 182 BufferedWriter bufWriter = new BufferedWriter(new OutputStreamWriter( 183 new FileOutputStream(smaliFile), "UTF8")); 184 185 writer = new IndentingWriter(bufWriter); 186 classDefinition.writeTo((IndentingWriter)writer); 187 } catch (Exception ex) { 188 System.err.println("\n\nError occured while disassembling class " + classDescriptor.replace('/', '.') + " - skipping class"); 189 ex.printStackTrace(); 190 smaliFile.delete(); 191 } 192 finally 193 { 194 if (writer != null) { 195 try { 196 writer.close(); 197 } catch (Throwable ex) { 198 System.err.println("\n\nError occured while closing file " + smaliFile.toString()); 199 ex.printStackTrace(); 200 } 201 } 202 } 203 204 if (!ignoreErrors && classDefinition.hadValidationErrors()) { 205 System.exit(1); 206 } 207 } 208 } 209 210 private static final Pattern extJarPattern = Pattern.compile("(?:^|\\\\|/)ext.(?:jar|odex)$"); 211 private static boolean isExtJar(String dexFilePath) { 212 Matcher m = extJarPattern.matcher(dexFilePath); 213 return m.find(); 214 } 215} 216