baksmali.java revision 4b171afedb983fb811990beeec6a15e30a90b455
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 com.google.common.collect.ImmutableList; 32import com.google.common.collect.Iterables; 33import org.jf.baksmali.Adaptors.ClassDefinition; 34import org.jf.dexlib2.analysis.ClassPath; 35import org.jf.dexlib2.iface.ClassDef; 36import org.jf.dexlib2.iface.DexFile; 37import org.jf.dexlib2.util.SyntheticAccessorResolver; 38import org.jf.util.ClassFileNameHandler; 39import org.jf.util.IndentingWriter; 40 41import java.io.*; 42import java.util.*; 43 44public class baksmali { 45 46 public static void disassembleDexFile(DexFile dexFile, baksmaliOptions options) { 47 if (options.registerInfo != 0 || options.deodex) { 48 try { 49 Iterable<String> extraClassPathEntries; 50 if (options.extraClassPathEntries != null) { 51 extraClassPathEntries = options.extraClassPathEntries; 52 } else { 53 extraClassPathEntries = ImmutableList.of(); 54 } 55 56 options.classPath = ClassPath.fromClassPath(options.bootClassPathDirs, 57 Iterables.concat(options.bootClassPathEntries, extraClassPathEntries), dexFile, 58 options.apiLevel); 59 } catch (Exception ex) { 60 System.err.println("\n\nError occured while loading boot class path files. Aborting."); 61 ex.printStackTrace(System.err); 62 System.exit(1); 63 } 64 } 65 66 File outputDirectoryFile = new File(options.outputDirectory); 67 if (!outputDirectoryFile.exists()) { 68 if (!outputDirectoryFile.mkdirs()) { 69 System.err.println("Can't create the output directory " + options.outputDirectory); 70 System.exit(1); 71 } 72 } 73 74 //sort the classes, so that if we're on a case-insensitive file system and need to handle classes with file 75 //name collisions, then we'll use the same name for each class, if the dex file goes through multiple 76 //baksmali/smali cycles for some reason. If a class with a colliding name is added or removed, the filenames 77 //may still change of course 78 List<ClassDef> classDefs = new ArrayList<ClassDef>(dexFile.getClasses()); 79 Collections.sort(classDefs, new Comparator<ClassDef>() { 80 public int compare(ClassDef classDef1, ClassDef classDef2) { 81 return classDef1.getType().compareTo(classDef2.getType()); 82 } 83 }); 84 classDefs = ImmutableList.copyOf(classDefs); 85 86 if (!options.noAccessorComments) { 87 options.syntheticAccessorResolver = new SyntheticAccessorResolver(classDefs); 88 } 89 90 ClassFileNameHandler fileNameHandler = new ClassFileNameHandler(outputDirectoryFile, ".smali"); 91 92 for (ClassDef classDef: classDefs) { 93 disassembleClass(classDef, fileNameHandler, options); 94 } 95 } 96 97 private static void disassembleClass(ClassDef classDef, ClassFileNameHandler fileNameHandler, 98 baksmaliOptions options) { 99 /** 100 * The path for the disassembly file is based on the package name 101 * The class descriptor will look something like: 102 * Ljava/lang/Object; 103 * Where the there is leading 'L' and a trailing ';', and the parts of the 104 * package name are separated by '/' 105 */ 106 String classDescriptor = classDef.getType(); 107 108 //validate that the descriptor is formatted like we expect 109 if (classDescriptor.charAt(0) != 'L' || 110 classDescriptor.charAt(classDescriptor.length()-1) != ';') { 111 System.err.println("Unrecognized class descriptor - " + classDescriptor + " - skipping class"); 112 return; 113 } 114 115 File smaliFile = fileNameHandler.getUniqueFilenameForClass(classDescriptor); 116 117 //create and initialize the top level string template 118 ClassDefinition classDefinition = new ClassDefinition(options, classDef); 119 120 //write the disassembly 121 Writer writer = null; 122 try 123 { 124 File smaliParent = smaliFile.getParentFile(); 125 if (!smaliParent.exists()) { 126 if (!smaliParent.mkdirs()) { 127 System.err.println("Unable to create directory " + smaliParent.toString() + " - skipping class"); 128 return; 129 } 130 } 131 132 if (!smaliFile.exists()){ 133 if (!smaliFile.createNewFile()) { 134 System.err.println("Unable to create file " + smaliFile.toString() + " - skipping class"); 135 return; 136 } 137 } 138 139 BufferedWriter bufWriter = new BufferedWriter(new OutputStreamWriter( 140 new FileOutputStream(smaliFile), "UTF8")); 141 142 writer = new IndentingWriter(bufWriter); 143 classDefinition.writeTo((IndentingWriter)writer); 144 } catch (Exception ex) { 145 System.err.println("\n\nError occured while disassembling class " + classDescriptor.replace('/', '.') + " - skipping class"); 146 ex.printStackTrace(); 147 // noinspection ResultOfMethodCallIgnored 148 smaliFile.delete(); 149 } 150 finally 151 { 152 if (writer != null) { 153 try { 154 writer.close(); 155 } catch (Throwable ex) { 156 System.err.println("\n\nError occured while closing file " + smaliFile.toString()); 157 ex.printStackTrace(); 158 } 159 } 160 } 161 162 if (!options.ignoreErrors && classDefinition.hadValidationErrors()) { 163 System.exit(1); 164 } 165 } 166} 167