Home | History | Annotate | Download | only in baksmali
      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 com.google.common.collect.Lists;
     32 import com.google.common.collect.Ordering;
     33 import org.jf.baksmali.Adaptors.ClassDefinition;
     34 import org.jf.dexlib2.iface.ClassDef;
     35 import org.jf.dexlib2.iface.DexFile;
     36 import org.jf.util.ClassFileNameHandler;
     37 import org.jf.util.IndentingWriter;
     38 
     39 import javax.annotation.Nullable;
     40 import java.io.*;
     41 import java.util.HashSet;
     42 import java.util.List;
     43 import java.util.Set;
     44 import java.util.concurrent.*;
     45 
     46 public class Baksmali {
     47     public static boolean disassembleDexFile(DexFile dexFile, File outputDir, int jobs, final BaksmaliOptions options) {
     48         return disassembleDexFile(dexFile, outputDir, jobs, options, null);
     49     }
     50 
     51     public static boolean disassembleDexFile(DexFile dexFile, File outputDir, int jobs, final BaksmaliOptions options,
     52                                              @Nullable List<String> classes) {
     53 
     54         //sort the classes, so that if we're on a case-insensitive file system and need to handle classes with file
     55         //name collisions, then we'll use the same name for each class, if the dex file goes through multiple
     56         //baksmali/smali cycles for some reason. If a class with a colliding name is added or removed, the filenames
     57         //may still change of course
     58         List<? extends ClassDef> classDefs = Ordering.natural().sortedCopy(dexFile.getClasses());
     59 
     60         final ClassFileNameHandler fileNameHandler = new ClassFileNameHandler(outputDir, ".smali");
     61 
     62         ExecutorService executor = Executors.newFixedThreadPool(jobs);
     63         List<Future<Boolean>> tasks = Lists.newArrayList();
     64 
     65         Set<String> classSet = null;
     66         if (classes != null) {
     67             classSet = new HashSet<String>(classes);
     68         }
     69 
     70         for (final ClassDef classDef: classDefs) {
     71             if (classSet != null && !classSet.contains(classDef.getType())) {
     72                 continue;
     73             }
     74             tasks.add(executor.submit(new Callable<Boolean>() {
     75                 @Override public Boolean call() throws Exception {
     76                     return disassembleClass(classDef, fileNameHandler, options);
     77                 }
     78             }));
     79         }
     80 
     81         boolean errorOccurred = false;
     82         try {
     83             for (Future<Boolean> task: tasks) {
     84                 while(true) {
     85                     try {
     86                         if (!task.get()) {
     87                             errorOccurred = true;
     88                         }
     89                     } catch (InterruptedException ex) {
     90                         continue;
     91                     } catch (ExecutionException ex) {
     92                         throw new RuntimeException(ex);
     93                     }
     94                     break;
     95                 }
     96             }
     97         } finally {
     98             executor.shutdown();
     99         }
    100         return !errorOccurred;
    101     }
    102 
    103     private static boolean disassembleClass(ClassDef classDef, ClassFileNameHandler fileNameHandler,
    104                                             BaksmaliOptions options) {
    105         /**
    106          * The path for the disassembly file is based on the package name
    107          * The class descriptor will look something like:
    108          * Ljava/lang/Object;
    109          * Where the there is leading 'L' and a trailing ';', and the parts of the
    110          * package name are separated by '/'
    111          */
    112         String classDescriptor = classDef.getType();
    113 
    114         //validate that the descriptor is formatted like we expect
    115         if (classDescriptor.charAt(0) != 'L' ||
    116                 classDescriptor.charAt(classDescriptor.length()-1) != ';') {
    117             System.err.println("Unrecognized class descriptor - " + classDescriptor + " - skipping class");
    118             return false;
    119         }
    120 
    121         File smaliFile = fileNameHandler.getUniqueFilenameForClass(classDescriptor);
    122 
    123         //create and initialize the top level string template
    124         ClassDefinition classDefinition = new ClassDefinition(options, classDef);
    125 
    126         //write the disassembly
    127         Writer writer = null;
    128         try
    129         {
    130             File smaliParent = smaliFile.getParentFile();
    131             if (!smaliParent.exists()) {
    132                 if (!smaliParent.mkdirs()) {
    133                     // check again, it's likely it was created in a different thread
    134                     if (!smaliParent.exists()) {
    135                         System.err.println("Unable to create directory " + smaliParent.toString() + " - skipping class");
    136                         return false;
    137                     }
    138                 }
    139             }
    140 
    141             if (!smaliFile.exists()){
    142                 if (!smaliFile.createNewFile()) {
    143                     System.err.println("Unable to create file " + smaliFile.toString() + " - skipping class");
    144                     return false;
    145                 }
    146             }
    147 
    148             BufferedWriter bufWriter = new BufferedWriter(new OutputStreamWriter(
    149                     new FileOutputStream(smaliFile), "UTF8"));
    150 
    151             writer = new IndentingWriter(bufWriter);
    152             classDefinition.writeTo((IndentingWriter)writer);
    153         } catch (Exception ex) {
    154             System.err.println("\n\nError occurred while disassembling class " + classDescriptor.replace('/', '.') + " - skipping class");
    155             ex.printStackTrace();
    156             // noinspection ResultOfMethodCallIgnored
    157             smaliFile.delete();
    158             return false;
    159         }
    160         finally
    161         {
    162             if (writer != null) {
    163                 try {
    164                     writer.close();
    165                 } catch (Throwable ex) {
    166                     System.err.println("\n\nError occurred while closing file " + smaliFile.toString());
    167                     ex.printStackTrace();
    168                 }
    169             }
    170         }
    171         return true;
    172     }
    173 }
    174