main.java revision c9be5e13034da9827b5598a6257376164745b827
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.jf.baksmali;
18
19import org.apache.commons.cli.*;
20import org.jf.baksmali.Deodex.Deodexerant;
21import org.jf.dexlib.DexFile;
22
23import java.io.File;
24import java.io.InputStream;
25import java.io.IOException;
26import java.util.Properties;
27
28public class main {
29
30    public static final String VERSION;
31
32    private static final Options options;
33
34    static {
35        options = new Options();
36        buildOptions();
37
38        InputStream templateStream = baksmali.class.getClassLoader().getResourceAsStream("baksmali.properties");
39        Properties properties = new Properties();
40        String version = "(unknown)";
41        try {
42            properties.load(templateStream);
43            version = properties.getProperty("application.version");
44        } catch (IOException ex) {
45        }
46        VERSION = version;
47    }
48
49    /**
50     * This class is uninstantiable.
51     */
52    private main() {
53    }
54
55    /**
56     * Run!
57     */
58    public static void main(String[] args) {
59        CommandLineParser parser = new PosixParser();
60        CommandLine commandLine;
61
62        try {
63            commandLine = parser.parse(options, args);
64        } catch (ParseException ex) {
65            usage();
66            return;
67        }
68
69        boolean disassemble = true;
70        boolean doDump = false;
71        boolean write = false;
72        boolean sort = false;
73        boolean fixRegisters = false;
74        boolean noParameterRegisters = false;
75        boolean useLocalsDirective = false;
76        boolean useSequentialLabels = false;
77        boolean outputDebugInfo = true;
78        boolean verboseRegisterInfo = false;
79
80        String outputDirectory = "out";
81        String dumpFileName = null;
82        String outputDexFileName = null;
83        String inputDexFileName = null;
84        String deodexerantHost = null;
85        String bootClassPath = "core.jar:ext.jar:framework.jar:android.policy.jar:services.jar";
86        int deodexerantPort = 0;
87
88        String[] remainingArgs = commandLine.getArgs();
89
90        if (commandLine.hasOption("v")) {
91            version();
92            return;
93        }
94
95        if (commandLine.hasOption("?")) {
96            usage();
97            return;
98        }
99
100        if (remainingArgs.length != 1) {
101            usage();
102            return;
103        }
104
105        inputDexFileName = remainingArgs[0];
106
107        if (commandLine.hasOption("n")) {
108            disassemble = false;
109        }
110
111        if (commandLine.hasOption("d")) {
112            doDump = true;
113            dumpFileName = commandLine.getOptionValue("d", inputDexFileName + ".dump");
114        }
115
116        if (commandLine.hasOption("w")) {
117            write = true;
118            outputDexFileName = commandLine.getOptionValue("w");
119        }
120
121        if (commandLine.hasOption("o")) {
122            outputDirectory = commandLine.getOptionValue("o");
123        }
124
125        if (commandLine.hasOption("s")) {
126            sort = true;
127        }
128
129        if (commandLine.hasOption("f")) {
130            fixRegisters = true;
131        }
132
133        if (commandLine.hasOption("p")) {
134            noParameterRegisters = true;
135        }
136
137        if (commandLine.hasOption("l")) {
138            useLocalsDirective = true;
139        }
140
141        if (commandLine.hasOption("i")) {
142            useSequentialLabels = true;
143        }
144
145        if (commandLine.hasOption("b")) {
146            outputDebugInfo = false;
147        }
148
149        if (commandLine.hasOption("r")) {
150            verboseRegisterInfo = true;
151        }
152
153        if (commandLine.hasOption("c")) {
154            bootClassPath = commandLine.getOptionValue("c");
155        }
156
157        if (commandLine.hasOption("x")) {
158            String deodexerantAddress = commandLine.getOptionValue("x");
159            String[] parts = deodexerantAddress.split(":");
160            if (parts.length != 2) {
161                System.err.println("Invalid deodexerant address. Expecting :<port> or <host>:<port>");
162                System.exit(1);
163            }
164
165            deodexerantHost = parts[0];
166            if (deodexerantHost.length() == 0) {
167                deodexerantHost = "localhost";
168            }
169            try {
170                deodexerantPort = Integer.parseInt(parts[1]);
171            } catch (NumberFormatException ex) {
172                System.err.println("Invalid port \"" + deodexerantPort + "\" for deodexerant address");
173                System.exit(1);
174            }
175        }
176
177        try {
178            File dexFileFile = new File(inputDexFileName);
179            if (!dexFileFile.exists()) {
180                System.err.println("Can't find the file " + inputDexFileName);
181                System.exit(1);
182            }
183
184            //Read in and parse the dex file
185            DexFile dexFile = new DexFile(dexFileFile, !fixRegisters);
186
187            Deodexerant deodexerant = null;
188
189
190            if (deodexerantHost != null) {
191                if (!dexFile.isOdex()) {
192                    System.err.println("-x cannot be used with a normal dex file. Ignoring -x");
193                }
194                deodexerant = new Deodexerant(dexFile, deodexerantHost, deodexerantPort);
195            }
196
197            if (dexFile.isOdex()) {
198                if (doDump) {
199                    System.err.println("-d cannot be used with on odex file. Ignoring -d");
200                }
201                if (write) {
202                    System.err.println("-w cannot be used with an odex file. Ignoring -w");
203                }
204                if (deodexerant == null) {
205                    System.err.println("Warning: You are disassembling an odex file without deodexing it. You");
206                    System.err.println("won't be able to re-assemble the results unless you use deodexerant, and");
207                    System.err.println("the -x option for baksmali");
208                }
209            }
210
211            if (disassemble) {
212                baksmali.disassembleDexFile(dexFile, deodexerant, outputDirectory, bootClassPath, noParameterRegisters,
213                        useLocalsDirective, useSequentialLabels, outputDebugInfo, verboseRegisterInfo);
214            }
215
216            if ((doDump || write) && !dexFile.isOdex()) {
217                try
218                {
219                    dump.dump(dexFile, dumpFileName, outputDexFileName, sort);
220                }catch (IOException ex) {
221                    System.err.println("Error occured while writing dump file");
222                    ex.printStackTrace();
223                }
224            }
225        } catch (RuntimeException ex) {
226            System.err.println("\n\nUNEXPECTED TOP-LEVEL EXCEPTION:");
227            ex.printStackTrace();
228            System.exit(1);
229        } catch (Throwable ex) {
230            System.err.println("\n\nUNEXPECTED TOP-LEVEL ERROR:");
231            ex.printStackTrace();
232            System.exit(1);
233        }
234    }
235
236    /**
237     * Prints the usage message.
238     */
239    private static void usage() {
240        HelpFormatter formatter = new HelpFormatter();
241        formatter.printHelp("java -jar baksmali.jar [options] <dex-file>",
242                "disassembles and/or dumps a dex file", options, "");
243    }
244
245    /**
246     * Prints the version message.
247     */
248    private static void version() {
249        System.out.println("baksmali " + VERSION + " (http://smali.googlecode.com)");
250        System.out.println("Copyright (C) 2009 Ben Gruver");
251        System.out.println("BSD license (http://www.opensource.org/licenses/bsd-license.php)");
252        System.exit(0);
253    }
254
255    private static void buildOptions() {
256        Option versionOption = OptionBuilder.withLongOpt("version")
257                .withDescription("prints the version then exits")
258                .create("v");
259
260        Option helpOption = OptionBuilder.withLongOpt("help")
261                .withDescription("prints the help message then exits")
262                .create("?");
263
264        Option noDisassemblyOption = OptionBuilder.withLongOpt("no-disassembly")
265                .withDescription("suppresses the output of the disassembly")
266                .create("n");
267
268        Option dumpOption = OptionBuilder.withLongOpt("dump-to")
269                .withDescription("dumps the given dex file into a single annotated dump file named FILE" +
270                        " (<dexfile>.dump by default), along with the normal disassembly.")
271                .hasOptionalArg()
272                .withArgName("FILE")
273                .create("d");
274
275        Option writeDexOption = OptionBuilder.withLongOpt("write-dex")
276                .withDescription("additionally rewrites the input dex file to FILE")
277                .hasArg()
278                .withArgName("FILE")
279                .create("w");
280
281        Option outputDirOption = OptionBuilder.withLongOpt("output")
282                .withDescription("the directory where the disassembled files will be placed. The default is out")
283                .hasArg()
284                .withArgName("DIR")
285                .create("o");
286
287        Option sortOption = OptionBuilder.withLongOpt("sort")
288                .withDescription("sort the items in the dex file into a canonical order before dumping/writing")
289                .create("s");
290
291        Option fixSignedRegisterOption = OptionBuilder.withLongOpt("fix-signed-registers")
292                .withDescription("when dumping or rewriting, fix any registers in the debug info that are encoded as" +
293                        " a signed value")
294                .create("f");
295
296        Option noParameterRegistersOption = OptionBuilder.withLongOpt("no-parameter-registers")
297                .withDescription("use the v<n> syntax instead of the p<n> syntax for registers mapped to method" +
298                        " parameters")
299                .create("p");
300
301        Option deodexerantOption = OptionBuilder.withLongOpt("deodexerant")
302                .withDescription("connect to deodexerant on the specified HOST:PORT, and deodex the input odex"
303                        + " file. This option is ignored if the input file is a dex file instead of an odex file")
304                .hasArg()
305                .withArgName("HOST:PORT")
306                .create("x");
307
308        Option useLocalsOption = OptionBuilder.withLongOpt("use-locals")
309                .withDescription("output the .locals directive with the number of non-parameter registers, rather" +
310                        " than the .register directive with the total number of register")
311                .create("l");
312
313        Option sequentialLabelsOption = OptionBuilder.withLongOpt("sequential-labels")
314                .withDescription("create label names using a sequential numbering scheme per label type, rather than " +
315                        "using the bytecode address")
316                .create("q");
317
318        Option noDebugInfoOption = OptionBuilder.withLongOpt("no-debug-info")
319                .withDescription("don't write out debug info (.local, .param, .line, etc.)")
320                .create("b");
321
322        Option verboseRegisterInfoOption = OptionBuilder.withLongOpt("verbose-registers")
323                .withDescription("print verbose register type information for each instruction")
324                .create("r");
325
326        Option classPathOption = OptionBuilder.withLongOpt("bootclasspath")
327                .withDescription("the bootclasspath jars to use, for analysis")
328                .hasOptionalArg()
329                .withArgName("BOOTCLASSPATH")
330                .create("c");
331
332        options.addOption(versionOption);
333        options.addOption(helpOption);
334        options.addOption(dumpOption);
335        options.addOption(noDisassemblyOption);
336        options.addOption(writeDexOption);
337        options.addOption(outputDirOption);
338        options.addOption(sortOption);
339        options.addOption(fixSignedRegisterOption);
340        options.addOption(noParameterRegistersOption);
341        options.addOption(deodexerantOption);
342        options.addOption(useLocalsOption);
343        options.addOption(sequentialLabelsOption);
344        options.addOption(noDebugInfoOption);
345        options.addOption(verboseRegisterInfoOption);
346        options.addOption(classPathOption);
347    }
348}