baksmali.java revision 6eae34831fee1f116f3a453bdc5e143d68e05e03
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.baksmali.Renderers.*; 33import org.jf.dexlib.Code.Analysis.ClassPath; 34import org.jf.dexlib.DexFile; 35import org.jf.dexlib.ClassDefItem; 36import org.jf.dexlib.StringIdItem; 37 38import java.io.*; 39import java.util.regex.Matcher; 40import java.util.regex.Pattern; 41 42public class baksmali { 43 public static boolean noParameterRegisters = false; 44 public static boolean useLocalsDirective = false; 45 public static boolean useSequentialLabels = false; 46 public static boolean outputDebugInfo = true; 47 public static boolean addCodeOffsets = false; 48 public static boolean deodex = false; 49 public static boolean verify = false; 50 public static int registerInfo = 0; 51 public static String bootClassPath; 52 53 public static void disassembleDexFile(String dexFilePath, DexFile dexFile, boolean deodex, String outputDirectory, 54 String[] classPathDirs, String bootClassPath, String extraBootClassPath, 55 boolean noParameterRegisters, boolean useLocalsDirective, 56 boolean useSequentialLabels, boolean outputDebugInfo, boolean addCodeOffsets, 57 int registerInfo, boolean verify) 58 { 59 baksmali.noParameterRegisters = noParameterRegisters; 60 baksmali.useLocalsDirective = useLocalsDirective; 61 baksmali.useSequentialLabels = useSequentialLabels; 62 baksmali.outputDebugInfo = outputDebugInfo; 63 baksmali.addCodeOffsets = addCodeOffsets; 64 baksmali.deodex = deodex; 65 baksmali.registerInfo = registerInfo; 66 baksmali.bootClassPath = bootClassPath; 67 baksmali.verify = verify; 68 69 if (registerInfo != 0 || deodex || verify) { 70 try { 71 String[] extraBootClassPathArray = null; 72 if (extraBootClassPath != null && extraBootClassPath.length() > 0) { 73 assert extraBootClassPath.charAt(0) == ':'; 74 extraBootClassPathArray = extraBootClassPath.substring(1).split(":"); 75 } 76 77 if (dexFile.isOdex() && bootClassPath == null) { 78 //ext.jar is a special case - it is typically the 2nd jar in the boot class path, but it also 79 //depends on classes in framework.jar. If the user didn't specify a -c option, we should add 80 //framework.jar to the boot class path by default, so that it "just works" 81 if (extraBootClassPathArray == null && isExtJar(dexFilePath)) { 82 extraBootClassPath = ":framework.jar"; 83 } 84 ClassPath.InitializeClassPathFromOdex(classPathDirs, extraBootClassPathArray, dexFilePath, dexFile); 85 } else { 86 String[] bootClassPathArray = null; 87 if (bootClassPath != null) { 88 bootClassPathArray = bootClassPath.split(":"); 89 } 90 ClassPath.InitializeClassPath(classPathDirs, bootClassPathArray, extraBootClassPathArray, 91 dexFilePath, dexFile); 92 } 93 } catch (Exception ex) { 94 System.err.println("\n\nError occured while loading boot class path files. Aborting."); 95 ex.printStackTrace(System.err); 96 System.exit(1); 97 } 98 } 99 100 File outputDirectoryFile = new File(outputDirectory); 101 if (!outputDirectoryFile.exists()) { 102 if (!outputDirectoryFile.mkdirs()) { 103 System.err.println("Can't create the output directory " + outputDirectory); 104 System.exit(1); 105 } 106 } 107 108 for (ClassDefItem classDefItem: dexFile.ClassDefsSection.getItems()) { 109 /** 110 * The path for the disassembly file is based on the package name 111 * The class descriptor will look something like: 112 * Ljava/lang/Object; 113 * Where the there is leading 'L' and a trailing ';', and the parts of the 114 * package name are separated by '/' 115 */ 116 117 String classDescriptor = classDefItem.getClassType().getTypeDescriptor(); 118 119 //validate that the descriptor is formatted like we expect 120 if (classDescriptor.charAt(0) != 'L' || 121 classDescriptor.charAt(classDescriptor.length()-1) != ';') { 122 System.err.println("Unrecognized class descriptor - " + classDescriptor + " - skipping class"); 123 continue; 124 } 125 126 //trim off the leading L and trailing ; 127 classDescriptor = classDescriptor.substring(1, classDescriptor.length()-1); 128 129 //trim off the leading 'L' and trailing ';', and get the individual package elements 130 String[] pathElements = classDescriptor.split("/"); 131 132 //build the path to the smali file to generate for this class 133 StringBuilder smaliPath = new StringBuilder(outputDirectory); 134 for (String pathElement: pathElements) { 135 smaliPath.append(File.separatorChar); 136 smaliPath.append(pathElement); 137 } 138 smaliPath.append(".smali"); 139 140 File smaliFile = new File(smaliPath.toString()); 141 142 //create and initialize the top level string template 143 ClassDefinition classDefinition = new ClassDefinition(classDefItem); 144 145 //write the disassembly 146 Writer writer = null; 147 try 148 { 149 File smaliParent = smaliFile.getParentFile(); 150 if (!smaliParent.exists()) { 151 if (!smaliParent.mkdirs()) { 152 System.err.println("Unable to create directory " + smaliParent.toString() + " - skipping class"); 153 continue; 154 } 155 } 156 157 if (!smaliFile.exists()){ 158 if (!smaliFile.createNewFile()) { 159 System.err.println("Unable to create file " + smaliFile.toString() + " - skipping class"); 160 continue; 161 } 162 } 163 164 BufferedWriter bufWriter = new BufferedWriter(new FileWriter(smaliFile)); 165 166 writer = new IndentingPrintWriter(new IndentingWriter(bufWriter)); 167 classDefinition.writeTo((IndentingPrintWriter)writer); 168 } catch (Exception ex) { 169 System.err.println("\n\nError occured while disassembling class " + classDescriptor.replace('/', '.') + " - skipping class"); 170 ex.printStackTrace(); 171 } 172 finally 173 { 174 if (writer != null) { 175 try { 176 writer.close(); 177 } catch (Throwable ex) { 178 System.err.println("\n\nError occured while closing file " + smaliFile.toString()); 179 ex.printStackTrace(); 180 } 181 } 182 } 183 184 //TODO: GROT 185 if (classDefinition.hadValidationErrors()) { 186 System.exit(1); 187 } 188 } 189 } 190 191 private static final Pattern extJarPattern = Pattern.compile("(?:^|\\\\|/)ext.(?:jar|odex)$"); 192 private static boolean isExtJar(String dexFilePath) { 193 Matcher m = extJarPattern.matcher(dexFilePath); 194 return m.find(); 195 } 196} 197