baksmali.java revision 8b1508ee58f4918835d8c01483725b508d21be29
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.ClassDefItem; 33import org.jf.dexlib.Code.Analysis.*; 34import org.jf.dexlib2.iface.ClassDef; 35import org.jf.dexlib2.iface.DexFile; 36import org.jf.util.ClassFileNameHandler; 37import org.jf.util.IndentingWriter; 38 39import java.io.*; 40import java.util.ArrayList; 41import java.util.Collections; 42import java.util.Comparator; 43import java.util.regex.Matcher; 44import java.util.regex.Pattern; 45 46public class baksmali { 47 public static boolean noParameterRegisters = false; 48 public static boolean useLocalsDirective = false; 49 public static boolean useSequentialLabels = false; 50 public static boolean outputDebugInfo = true; 51 public static boolean addCodeOffsets = false; 52 public static boolean noAccessorComments = false; 53 public static boolean deodex = false; 54 public static boolean verify = false; 55 public static InlineMethodResolver inlineResolver = null; 56 public static int registerInfo = 0; 57 public static String bootClassPath; 58 59 public static SyntheticAccessorResolver syntheticAccessorResolver = null; 60 61 public static void disassembleDexFile(String dexFilePath, DexFile dexFile, boolean deodex, String outputDirectory, 62 String[] classPathDirs, String bootClassPath, String extraBootClassPath, 63 boolean noParameterRegisters, boolean useLocalsDirective, 64 boolean useSequentialLabels, boolean outputDebugInfo, boolean addCodeOffsets, 65 boolean noAccessorComments, int registerInfo, boolean verify, 66 boolean ignoreErrors, String inlineTable, boolean checkPackagePrivateAccess) 67 { 68 baksmali.noParameterRegisters = noParameterRegisters; 69 baksmali.useLocalsDirective = useLocalsDirective; 70 baksmali.useSequentialLabels = useSequentialLabels; 71 baksmali.outputDebugInfo = outputDebugInfo; 72 baksmali.addCodeOffsets = addCodeOffsets; 73 baksmali.noAccessorComments = noAccessorComments; 74 baksmali.deodex = deodex; 75 baksmali.registerInfo = registerInfo; 76 baksmali.bootClassPath = bootClassPath; 77 baksmali.verify = verify; 78 79 //TODO: uncomment 80 /*if (registerInfo != 0 || deodex || verify) { 81 try { 82 String[] extraBootClassPathArray = null; 83 if (extraBootClassPath != null && extraBootClassPath.length() > 0) { 84 assert extraBootClassPath.charAt(0) == ':'; 85 extraBootClassPathArray = extraBootClassPath.substring(1).split(":"); 86 } 87 88 if (dexFile.isOdex() && bootClassPath == null) { 89 //ext.jar is a special case - it is typically the 2nd jar in the boot class path, but it also 90 //depends on classes in framework.jar (typically the 3rd jar in the BCP). If the user didn't 91 //specify a -c option, we should add framework.jar to the boot class path by default, so that it 92 //"just works" 93 if (extraBootClassPathArray == null && isExtJar(dexFilePath)) { 94 extraBootClassPathArray = new String[] {"framework.jar"}; 95 } 96 ClassPath.InitializeClassPathFromOdex(classPathDirs, extraBootClassPathArray, dexFilePath, dexFile, 97 checkPackagePrivateAccess); 98 } else { 99 String[] bootClassPathArray = null; 100 if (bootClassPath != null) { 101 bootClassPathArray = bootClassPath.split(":"); 102 } 103 ClassPath.InitializeClassPath(classPathDirs, bootClassPathArray, extraBootClassPathArray, 104 dexFilePath, dexFile, checkPackagePrivateAccess); 105 } 106 107 if (inlineTable != null) { 108 inlineResolver = new CustomInlineMethodResolver(inlineTable); 109 } 110 } catch (Exception ex) { 111 System.err.println("\n\nError occured while loading boot class path files. Aborting."); 112 ex.printStackTrace(System.err); 113 System.exit(1); 114 } 115 }*/ 116 117 File outputDirectoryFile = new File(outputDirectory); 118 if (!outputDirectoryFile.exists()) { 119 if (!outputDirectoryFile.mkdirs()) { 120 System.err.println("Can't create the output directory " + outputDirectory); 121 System.exit(1); 122 } 123 } 124 125 //TODO: uncomment 126 /*if (!noAccessorComments) { 127 syntheticAccessorResolver = new SyntheticAccessorResolver(dexFile); 128 }*/ 129 130 //sort the classes, so that if we're on a case-insensitive file system and need to handle classes with file 131 //name collisions, then we'll use the same name for each class, if the dex file goes through multiple 132 //baksmali/smali cycles for some reason. If a class with a colliding name is added or removed, the filenames 133 //may still change of course 134 // TODO: aren't classes already sorted? Why do we need to sort here? 135 /*ArrayList<ClassDefItem> classDefItems = new ArrayList<ClassDefItem>(dexFile.ClassDefsSection.getItems()); 136 Collections.sort(classDefItems, new Comparator<ClassDefItem>() { 137 public int compare(ClassDefItem classDefItem1, ClassDefItem classDefItem2) { 138 return classDefItem1.getClassType().getTypeDescriptor().compareTo(classDefItem1.getClassType().getTypeDescriptor()); 139 } 140 });*/ 141 142 ClassFileNameHandler fileNameHandler = new ClassFileNameHandler(outputDirectoryFile, ".smali"); 143 144 for (ClassDef classDef: dexFile.getClasses()) { 145 /** 146 * The path for the disassembly file is based on the package name 147 * The class descriptor will look something like: 148 * Ljava/lang/Object; 149 * Where the there is leading 'L' and a trailing ';', and the parts of the 150 * package name are separated by '/' 151 */ 152 153 String classDescriptor = classDef.getName(); 154 155 //validate that the descriptor is formatted like we expect 156 if (classDescriptor.charAt(0) != 'L' || 157 classDescriptor.charAt(classDescriptor.length()-1) != ';') { 158 System.err.println("Unrecognized class descriptor - " + classDescriptor + " - skipping class"); 159 continue; 160 } 161 162 File smaliFile = fileNameHandler.getUniqueFilenameForClass(classDescriptor); 163 164 //create and initialize the top level string template 165 ClassDefinition classDefinition = new ClassDefinition(classDef); 166 167 //write the disassembly 168 Writer writer = null; 169 try 170 { 171 File smaliParent = smaliFile.getParentFile(); 172 if (!smaliParent.exists()) { 173 if (!smaliParent.mkdirs()) { 174 System.err.println("Unable to create directory " + smaliParent.toString() + " - skipping class"); 175 continue; 176 } 177 } 178 179 if (!smaliFile.exists()){ 180 if (!smaliFile.createNewFile()) { 181 System.err.println("Unable to create file " + smaliFile.toString() + " - skipping class"); 182 continue; 183 } 184 } 185 186 BufferedWriter bufWriter = new BufferedWriter(new OutputStreamWriter( 187 new FileOutputStream(smaliFile), "UTF8")); 188 189 writer = new IndentingWriter(bufWriter); 190 classDefinition.writeTo((IndentingWriter)writer); 191 } catch (Exception ex) { 192 System.err.println("\n\nError occured while disassembling class " + classDescriptor.replace('/', '.') + " - skipping class"); 193 ex.printStackTrace(); 194 smaliFile.delete(); 195 } 196 finally 197 { 198 if (writer != null) { 199 try { 200 writer.close(); 201 } catch (Throwable ex) { 202 System.err.println("\n\nError occured while closing file " + smaliFile.toString()); 203 ex.printStackTrace(); 204 } 205 } 206 } 207 208 if (!ignoreErrors && classDefinition.hadValidationErrors()) { 209 System.exit(1); 210 } 211 } 212 } 213 214 private static final Pattern extJarPattern = Pattern.compile("(?:^|\\\\|/)ext.(?:jar|odex)$"); 215 private static boolean isExtJar(String dexFilePath) { 216 Matcher m = extJarPattern.matcher(dexFilePath); 217 return m.find(); 218 } 219} 220