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