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