baksmali.java revision d166b746b91c114cd8d8fe4b054069083c33170b
1/*
2 * [The "BSD licence"]
3 * Copyright (c) 2009 Ben Gruver
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.antlr.stringtemplate.StringTemplate;
32import org.antlr.stringtemplate.StringTemplateGroup;
33import org.jf.baksmali.Adaptors.ClassDefinition;
34import org.jf.baksmali.Renderers.*;
35import org.jf.dexlib.DexFile;
36import org.jf.dexlib.ClassDefItem;
37import org.apache.commons.cli.*;
38
39import java.io.*;
40import java.net.URL;
41
42public class baksmali {
43    public static void main(String[] args) throws Exception
44    {
45        Options options = new Options();
46
47        Option helpOption = OptionBuilder.withLongOpt("help")
48                                         .withDescription("prints the usage information for the -dis command")
49                                         .create("?");
50
51        Option outputDirOption = OptionBuilder.withLongOpt("output")
52                                              .withDescription("the directory where the disassembled files will be placed. The default is out")
53                                              .hasArg()
54                                              .withArgName("DIR")
55                                              .create("out");
56
57        options.addOption(helpOption);
58        options.addOption(outputDirOption);
59
60        CommandLineParser parser = new PosixParser();
61        CommandLine commandLine;
62
63        try {
64            commandLine = parser.parse(options, args);
65        } catch (ParseException ex) {
66            printHelp(options);
67            return;
68        }
69
70        if (commandLine.hasOption("?")) {
71            printHelp(options);
72            return;
73        }
74
75
76        String[] leftover = commandLine.getArgs();
77
78        if (leftover.length != 1) {
79            printHelp(options);
80            return;
81        }
82
83        String dexFileName = leftover[0];
84        String outputDirName = commandLine.getOptionValue("out", "out");
85
86        File dexFileFile = new File(dexFileName);
87        if (!dexFileFile.exists()) {
88            System.out.println("Can't find the file " + dexFileFile.toString());
89            System.exit(1);
90        }
91
92        File outputDir = new File(outputDirName);
93        if (!outputDir.exists()) {
94            if (!outputDir.mkdirs()) {
95                System.out.println("Can't create the output directory " + outputDir.toString());
96                System.exit(1);
97            }
98        }
99
100        DexFile dexFile = new DexFile(dexFileFile);
101
102        InputStream templateStream = baksmali.class.getClassLoader().getResourceAsStream("templates/baksmali.stg");
103        StringTemplateGroup templates = new StringTemplateGroup(new InputStreamReader(templateStream));
104
105        templates.registerRenderer(Long.class, new LongRenderer());
106        templates.registerRenderer(Integer.class,  new IntegerRenderer());
107        templates.registerRenderer(Short.class, new ShortRenderer());
108        templates.registerRenderer(Byte.class, new ByteRenderer());
109        templates.registerRenderer(Float.class, new FloatRenderer());
110        templates.registerRenderer(Character.class, new CharRenderer());
111
112        int classCount = dexFile.ClassDefsSection.size();
113        for (int i=0; i<classCount; i++) {
114            ClassDefItem classDef = dexFile.ClassDefsSection.getByIndex(i);
115
116            String classDescriptor = classDef.getClassType().getTypeDescriptor();
117            if (classDescriptor.charAt(0) != 'L' ||
118                classDescriptor.charAt(classDescriptor.length()-1) != ';') {
119                System.out.println("Unrecognized class descriptor - " + classDescriptor + " - skipping class");
120                continue;
121            }
122            //trim off the leading L and trailing ;
123            classDescriptor = classDescriptor.substring(1, classDescriptor.length()-1);
124            String[] pathElements = classDescriptor.split("/");
125
126            //build the path to the smali file to generate for this class
127            StringBuilder smaliPath = new StringBuilder(outputDir.getPath());
128            for (String pathElement: pathElements) {
129                smaliPath.append(File.separatorChar);
130                smaliPath.append(pathElement);
131            }
132            smaliPath.append(".smali");
133
134            File smaliFile = new File(smaliPath.toString());
135
136            StringTemplate smaliFileST = templates.getInstanceOf("smaliFile");
137            smaliFileST.setAttribute("classDef", new ClassDefinition(classDef));
138
139            String output = smaliFileST.toString();
140
141            FileWriter writer = null;
142            try
143            {
144                if (!smaliFile.getParentFile().exists()) {
145                    if (!smaliFile.getParentFile().mkdirs()) {
146                        System.out.println("Unable to create directory " + smaliFile.getParentFile().toString() + " - skipping class");
147                        continue;
148                    }
149                }
150                if (!smaliFile.exists()){
151                    if (!smaliFile.createNewFile()) {
152                        System.out.println("Unable to create file " + smaliFile.toString() + " - skipping class");
153                        continue;
154                    }
155                }
156
157                writer = new FileWriter(smaliFile);
158                writer.write(output);
159            }finally
160            {
161                if (writer != null)
162                    writer.close();
163            }
164        }
165    }
166
167    /**
168     * Prints the usage message.
169     */
170    private static void printHelp(Options options) {
171        HelpFormatter formatter = new HelpFormatter();
172        formatter.printHelp("java -jar baksmali.jar -dis [-out <DIR>] <dexfile>",
173                "Disassembles the given dex file", options, "");
174    }
175}
176