main.java revision 3f7739cd09932129a51badaca9e172aaa47f50f4
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    public static final int ALL = 1;
35    public static final int ALLPRE = 2;
36    public static final int ALLPOST = 4;
37    public static final int ARGS = 8;
38    public static final int DEST = 16;
39    public static final int MERGE = 32;
40    public static final int FULLMERGE = 64;
41
42    static {
43        options = new Options();
44        buildOptions();
45
46        InputStream templateStream = baksmali.class.getClassLoader().getResourceAsStream("baksmali.properties");
47        Properties properties = new Properties();
48        String version = "(unknown)";
49        try {
50            properties.load(templateStream);
51            version = properties.getProperty("application.version");
52        } catch (IOException ex) {
53        }
54        VERSION = version;
55    }
56
57    /**
58     * This class is uninstantiable.
59     */
60    private main() {
61    }
62
63    /**
64     * Run!
65     */
66    public static void main(String[] args) {
67        CommandLineParser parser = new PosixParser();
68        CommandLine commandLine;
69
70        try {
71            commandLine = parser.parse(options, args);
72        } catch (ParseException ex) {
73            usage();
74            return;
75        }
76
77        boolean disassemble = true;
78        boolean doDump = false;
79        boolean write = false;
80        boolean sort = false;
81        boolean fixRegisters = false;
82        boolean noParameterRegisters = false;
83        boolean useLocalsDirective = false;
84        boolean useSequentialLabels = false;
85        boolean outputDebugInfo = true;
86
87        int registerInfo = 0;
88
89        String outputDirectory = "out";
90        String dumpFileName = null;
91        String outputDexFileName = null;
92        String inputDexFileName = null;
93        String deodexerantHost = null;
94        String bootClassPath = "core.jar:ext.jar:framework.jar:android.policy.jar:services.jar";
95        String bootClassPathDir = ".";
96        int deodexerantPort = 0;
97
98        String[] remainingArgs = commandLine.getArgs();
99
100        if (commandLine.hasOption("v")) {
101            version();
102            return;
103        }
104
105        if (commandLine.hasOption("?")) {
106            usage();
107            return;
108        }
109
110        if (remainingArgs.length != 1) {
111            usage();
112            return;
113        }
114
115        inputDexFileName = remainingArgs[0];
116
117        if (commandLine.hasOption("n")) {
118            disassemble = false;
119        }
120
121        if (commandLine.hasOption("d")) {
122            doDump = true;
123            dumpFileName = commandLine.getOptionValue("d", inputDexFileName + ".dump");
124        }
125
126        if (commandLine.hasOption("w")) {
127            write = true;
128            outputDexFileName = commandLine.getOptionValue("w");
129        }
130
131        if (commandLine.hasOption("o")) {
132            outputDirectory = commandLine.getOptionValue("o");
133        }
134
135        if (commandLine.hasOption("s")) {
136            sort = true;
137        }
138
139        if (commandLine.hasOption("f")) {
140            fixRegisters = true;
141        }
142
143        if (commandLine.hasOption("p")) {
144            noParameterRegisters = true;
145        }
146
147        if (commandLine.hasOption("l")) {
148            useLocalsDirective = true;
149        }
150
151        if (commandLine.hasOption("i")) {
152            useSequentialLabels = true;
153        }
154
155        if (commandLine.hasOption("b")) {
156            outputDebugInfo = false;
157        }
158
159        if (commandLine.hasOption("r")) {
160            String[] values = commandLine.getOptionValues('r');
161
162            if (values == null || values.length == 0) {
163                registerInfo = ARGS | DEST | MERGE;
164            } else {
165                for (String value: values) {
166                    if (value.equalsIgnoreCase("ALL")) {
167                        registerInfo |= ALL;
168                    } else if (value.equalsIgnoreCase("ALLPRE")) {
169                        registerInfo |= ALLPRE;
170                    } else if (value.equalsIgnoreCase("ALLPOST")) {
171                        registerInfo |= ALLPOST;
172                    } else if (value.equalsIgnoreCase("ARGS")) {
173                        registerInfo |= ARGS;
174                    } else if (value.equalsIgnoreCase("DEST")) {
175                        registerInfo |= DEST;
176                    } else if (value.equalsIgnoreCase("MERGE")) {
177                        registerInfo |= MERGE;
178                    } else if (value.equalsIgnoreCase("FULLMERGE")) {
179                        registerInfo |= FULLMERGE;
180                    } else {
181                        usage();
182                        return;
183                    }
184                }
185
186                if ((registerInfo & FULLMERGE) != 0) {
187                    registerInfo &= ~MERGE;
188                }
189            }
190        }
191
192        if (commandLine.hasOption("c")) {
193            bootClassPath = commandLine.getOptionValue("c");
194        }
195
196        if (commandLine.hasOption("C")) {
197            bootClassPathDir = commandLine.getOptionValue("C");
198        }
199
200        if (commandLine.hasOption("x")) {
201            String deodexerantAddress = commandLine.getOptionValue("x");
202            String[] parts = deodexerantAddress.split(":");
203            if (parts.length != 2) {
204                System.err.println("Invalid deodexerant address. Expecting :<port> or <host>:<port>");
205                System.exit(1);
206            }
207
208            deodexerantHost = parts[0];
209            if (deodexerantHost.length() == 0) {
210                deodexerantHost = "localhost";
211            }
212            try {
213                deodexerantPort = Integer.parseInt(parts[1]);
214            } catch (NumberFormatException ex) {
215                System.err.println("Invalid port \"" + deodexerantPort + "\" for deodexerant address");
216                System.exit(1);
217            }
218        }
219
220        try {
221            File dexFileFile = new File(inputDexFileName);
222            if (!dexFileFile.exists()) {
223                System.err.println("Can't find the file " + inputDexFileName);
224                System.exit(1);
225            }
226
227            //Read in and parse the dex file
228            DexFile dexFile = new DexFile(dexFileFile, !fixRegisters, false);
229
230            Deodexerant deodexerant = null;
231
232
233            if (deodexerantHost != null) {
234                if (!dexFile.isOdex()) {
235                    System.err.println("-x cannot be used with a normal dex file. Ignoring -x");
236                }
237                deodexerant = new Deodexerant(dexFile, deodexerantHost, deodexerantPort);
238            }
239
240            if (dexFile.isOdex()) {
241                if (doDump) {
242                    System.err.println("-d cannot be used with on odex file. Ignoring -d");
243                }
244                if (write) {
245                    System.err.println("-w cannot be used with an odex file. Ignoring -w");
246                }
247                if (deodexerant == null) {
248                    System.err.println("Warning: You are disassembling an odex file without deodexing it. You");
249                    System.err.println("won't be able to re-assemble the results unless you use deodexerant, and");
250                    System.err.println("the -x option for baksmali");
251                }
252            }
253
254            if (disassemble) {
255                baksmali.disassembleDexFile(dexFile, deodexerant, outputDirectory, bootClassPathDir, bootClassPath,
256                        noParameterRegisters, useLocalsDirective, useSequentialLabels, outputDebugInfo, registerInfo);
257            }
258
259            if ((doDump || write) && !dexFile.isOdex()) {
260                try
261                {
262                    dump.dump(dexFile, dumpFileName, outputDexFileName, sort);
263                }catch (IOException ex) {
264                    System.err.println("Error occured while writing dump file");
265                    ex.printStackTrace();
266                }
267            }
268        } catch (RuntimeException ex) {
269            System.err.println("\n\nUNEXPECTED TOP-LEVEL EXCEPTION:");
270            ex.printStackTrace();
271            System.exit(1);
272        } catch (Throwable ex) {
273            System.err.println("\n\nUNEXPECTED TOP-LEVEL ERROR:");
274            ex.printStackTrace();
275            System.exit(1);
276        }
277    }
278
279    /**
280     * Prints the usage message.
281     */
282    private static void usage() {
283        HelpFormatter formatter = new HelpFormatter();
284        formatter.setWidth(100);
285        formatter.printHelp("java -jar baksmali.jar [options] <dex-file>",
286                "disassembles and/or dumps a dex file", options, "");
287    }
288
289    /**
290     * Prints the version message.
291     */
292    private static void version() {
293        System.out.println("baksmali " + VERSION + " (http://smali.googlecode.com)");
294        System.out.println("Copyright (C) 2009 Ben Gruver");
295        System.out.println("BSD license (http://www.opensource.org/licenses/bsd-license.php)");
296        System.exit(0);
297    }
298
299    private static void buildOptions() {
300        Option versionOption = OptionBuilder.withLongOpt("version")
301                .withDescription("prints the version then exits")
302                .create("v");
303
304        Option helpOption = OptionBuilder.withLongOpt("help")
305                .withDescription("prints the help message then exits")
306                .create("?");
307
308        Option noDisassemblyOption = OptionBuilder.withLongOpt("no-disassembly")
309                .withDescription("suppresses the output of the disassembly")
310                .create("n");
311
312        Option dumpOption = OptionBuilder.withLongOpt("dump-to")
313                .withDescription("dumps the given dex file into a single annotated dump file named FILE" +
314                        " (<dexfile>.dump by default), along with the normal disassembly.")
315                .hasOptionalArg()
316                .withArgName("FILE")
317                .create("d");
318
319        Option writeDexOption = OptionBuilder.withLongOpt("write-dex")
320                .withDescription("additionally rewrites the input dex file to FILE")
321                .hasArg()
322                .withArgName("FILE")
323                .create("w");
324
325        Option outputDirOption = OptionBuilder.withLongOpt("output")
326                .withDescription("the directory where the disassembled files will be placed. The default is out")
327                .hasArg()
328                .withArgName("DIR")
329                .create("o");
330
331        Option sortOption = OptionBuilder.withLongOpt("sort")
332                .withDescription("sort the items in the dex file into a canonical order before dumping/writing")
333                .create("s");
334
335        Option fixSignedRegisterOption = OptionBuilder.withLongOpt("fix-signed-registers")
336                .withDescription("when dumping or rewriting, fix any registers in the debug info that are encoded as" +
337                        " a signed value")
338                .create("f");
339
340        Option noParameterRegistersOption = OptionBuilder.withLongOpt("no-parameter-registers")
341                .withDescription("use the v<n> syntax instead of the p<n> syntax for registers mapped to method" +
342                        " parameters")
343                .create("p");
344
345        Option deodexerantOption = OptionBuilder.withLongOpt("deodexerant")
346                .withDescription("connect to deodexerant on the specified HOST:PORT, and deodex the input odex"
347                        + " file. This option is ignored if the input file is a dex file instead of an odex file")
348                .hasArg()
349                .withArgName("HOST:PORT")
350                .create("x");
351
352        Option useLocalsOption = OptionBuilder.withLongOpt("use-locals")
353                .withDescription("output the .locals directive with the number of non-parameter registers, rather" +
354                        " than the .register directive with the total number of register")
355                .create("l");
356
357        Option sequentialLabelsOption = OptionBuilder.withLongOpt("sequential-labels")
358                .withDescription("create label names using a sequential numbering scheme per label type, rather than " +
359                        "using the bytecode address")
360                .create("q");
361
362        Option noDebugInfoOption = OptionBuilder.withLongOpt("no-debug-info")
363                .withDescription("don't write out debug info (.local, .param, .line, etc.)")
364                .create("b");
365
366        Option registerInfoOption = OptionBuilder.withLongOpt("register-info")
367                .hasOptionalArgs()
368                .withArgName("REGISTER_INFO_TYPES")
369                .withValueSeparator(',')
370                .withDescription("print the specificed type(s) of register information for each instruction. " +
371                        "\"ARGS,DEST,MERGE\" is the default if no types are specified.\nValid values are:\nALL: all " +
372                        "pre- and post-instruction registers.\nALLPRE: all pre-instruction registers\nALLPOST: all " +
373                        "post-instruction registers\nARGS: any pre-instruction registers used as arguments to the " +
374                        "instruction\nDEST: the post-instruction destination register, if any\nMERGE: Any " +
375                        "pre-instruction register has been merged from more than 1 different post-instruction " +
376                        "register from its predecessors\nFULLMERGE: For each register that would be printed by " +
377                        "MERGE, also show the incoming register types that were merged")
378                .create("r");
379
380        Option classPathOption = OptionBuilder.withLongOpt("bootclasspath")
381                .withDescription("the bootclasspath jars to use, for analysis. Defaults to " +
382                        "core.jar:ext.jar:framework.jar:android.policy.jar:services.jar")
383                .hasOptionalArg()
384                .withArgName("BOOTCLASSPATH")
385                .create("c");
386
387        Option classPathDirOption = OptionBuilder.withLongOpt("bootclasspath-dir")
388                .withDescription("the base folder to look for the bootclasspath files in. Defaults to the current " +
389                        "directory")
390                .hasArg()
391                .withArgName("DIR")
392                .create("C");
393
394        options.addOption(versionOption);
395        options.addOption(helpOption);
396        options.addOption(dumpOption);
397        options.addOption(noDisassemblyOption);
398        options.addOption(writeDexOption);
399        options.addOption(outputDirOption);
400        options.addOption(sortOption);
401        options.addOption(fixSignedRegisterOption);
402        options.addOption(noParameterRegistersOption);
403        options.addOption(deodexerantOption);
404        options.addOption(useLocalsOption);
405        options.addOption(sequentialLabelsOption);
406        options.addOption(noDebugInfoOption);
407        options.addOption(registerInfoOption);
408        options.addOption(classPathOption);
409        options.addOption(classPathDirOption);
410    }
411}