diff --git a/baksmali/pom.xml b/baksmali/pom.xml index b1b5282d..54eb22b5 100644 --- a/baksmali/pom.xml +++ b/baksmali/pom.xml @@ -12,7 +12,7 @@ - + maven-assembly-plugin @@ -80,5 +80,10 @@ dexlib 0.91-SNAPSHOT + + commons-cli + commons-cli + 1.2 + \ No newline at end of file diff --git a/baksmali/src/main/java/org/jf/baksmali/Renderers/LongRenderer.java b/baksmali/src/main/java/org/jf/baksmali/Renderers/LongRenderer.java index 70f46ede..330a432d 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Renderers/LongRenderer.java +++ b/baksmali/src/main/java/org/jf/baksmali/Renderers/LongRenderer.java @@ -36,7 +36,7 @@ public class LongRenderer implements AttributeRenderer { if (l < 0) { return "-0x" + Long.toHexString(-1 * l) + "L"; } - return "0x" + Long.toHexString((Long)o) + "L"; + return "0x" + Long.toHexString(l) + "L"; } public String toString(Object o, String s) { diff --git a/baksmali/src/main/java/org/jf/baksmali/UsageException.java b/baksmali/src/main/java/org/jf/baksmali/UsageException.java deleted file mode 100644 index aa95dd37..00000000 --- a/baksmali/src/main/java/org/jf/baksmali/UsageException.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jf.baksmali; - -/** - * Simple exception class used to communicate that the command-line tool - * should print the usage message. - */ -public class UsageException extends RuntimeException { - // This space intentionally left blank. -} diff --git a/baksmali/src/main/java/org/jf/baksmali/baksmali.java b/baksmali/src/main/java/org/jf/baksmali/baksmali.java index 82dfd7fc..241b14cf 100644 --- a/baksmali/src/main/java/org/jf/baksmali/baksmali.java +++ b/baksmali/src/main/java/org/jf/baksmali/baksmali.java @@ -34,6 +34,7 @@ import org.jf.baksmali.Adaptors.ClassDefinition; import org.jf.baksmali.Renderers.*; import org.jf.dexlib.DexFile; import org.jf.dexlib.ClassDefItem; +import org.apache.commons.cli.*; import java.io.*; import java.net.URL; @@ -41,12 +42,46 @@ import java.net.URL; public class baksmali { public static void main(String[] args) throws Exception { - if (args.length < 2) { - throw new UsageException(); + Options options = new Options(); + + Option helpOption = OptionBuilder.withLongOpt("help") + .withDescription("prints the usage information for the -dis command") + .create("?"); + + Option outputDirOption = OptionBuilder.withLongOpt("output") + .withDescription("the directory where the disassembled files will be placed. The default is out") + .hasArg() + .withArgName("DIR") + .create("out"); + + options.addOption(helpOption); + options.addOption(outputDirOption); + + CommandLineParser parser = new PosixParser(); + CommandLine commandLine; + + try { + commandLine = parser.parse(options, args); + } catch (ParseException ex) { + printHelp(options); + return; + } + + if (commandLine.hasOption("?")) { + printHelp(options); + return; } - String dexFileName = args[0]; - String outputDirName = args[1]; + + String[] leftover = commandLine.getArgs(); + + if (leftover.length != 1) { + printHelp(options); + return; + } + + String dexFileName = leftover[0]; + String outputDirName = commandLine.getOptionValue("out", "out"); File dexFileFile = new File(dexFileName); if (!dexFileFile.exists()) { @@ -128,4 +163,13 @@ public class baksmali { } } } + + /** + * Prints the usage message. + */ + private static void printHelp(Options options) { + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp("java -jar baksmali.jar -dis [-out ] ", + "Disassembles the given dex file", options, ""); + } } diff --git a/baksmali/src/main/java/org/jf/baksmali/dump.java b/baksmali/src/main/java/org/jf/baksmali/dump.java index 450659d2..9d2661d3 100644 --- a/baksmali/src/main/java/org/jf/baksmali/dump.java +++ b/baksmali/src/main/java/org/jf/baksmali/dump.java @@ -30,21 +30,89 @@ package org.jf.baksmali; import org.jf.dexlib.DexFile; import org.jf.dexlib.Util.ByteArrayAnnotatedOutput; +import org.apache.commons.cli.*; import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.io.FileOutputStream; public class dump { public static void main(String[] args) throws IOException { + Options options = new Options(); - if (args.length < 2) { - throw new UsageException(); + Option helpOption = OptionBuilder.withLongOpt("help") + .withDescription("prints the usage information for the --dump command") + .create("?"); + + Option outputFileOption = OptionBuilder.withLongOpt("output") + .withDescription("the file where the dump will be written. The default is .dump, where is the name of the given dex file") + .hasArg() + .withArgName("FILE") + .create("out"); + + Option writeDexFileOption = OptionBuilder.withLongOpt("write-dex-file") + .withDescription("optionally re-write out the dex file to the given file") + .hasArg() + .withArgName("FILE") + .create("writedex"); + + Option sortDexFileOption = OptionBuilder.withLongOpt("sort-dex-file") + .withDescription("optionally sorts the items in the dex file before dumping/writing it") + .create("sortdex"); + + Option unsignedRegisterOption = OptionBuilder.withLongOpt("unsigned-registers") + .withDescription("always write the registers in the debug info as unsigned. By default we keep the same signed/unsigned format as what was in the original file") + .create("unsigned"); + + options.addOption(helpOption); + options.addOption(outputFileOption); + options.addOption(writeDexFileOption); + options.addOption(sortDexFileOption); + options.addOption(unsignedRegisterOption); + + CommandLineParser parser = new PosixParser(); + CommandLine commandLine; + + try { + commandLine = parser.parse(options, args); + } catch (ParseException ex) { + printHelp(options); + return; } - String dexFileName = args[0]; - String outFile = args[1]; + if (commandLine.hasOption("?")) { + printHelp(options); + return; + } + + String[] leftover = commandLine.getArgs(); + + if (leftover.length != 1) { + printHelp(options); + return; + } + + String dexFileName = leftover[0]; + String outputDumpName = commandLine.getOptionValue("out", dexFileName + ".dump"); + + boolean writeDex = false; + String outputDexName = null; + if (commandLine.hasOption("writedex")) { + writeDex = true; + outputDexName = commandLine.getOptionValue("writedex"); + } + + boolean sortDex = false; + if (commandLine.hasOption("sortdex")) { + sortDex = true; + } + + boolean unsignedRegisters = false; + if (commandLine.hasOption("unsigned")) { + unsignedRegisters = true; + } File dexFileFile = new File(dexFileName); if (!dexFileFile.exists()) { @@ -52,20 +120,35 @@ public class dump { System.exit(1); } - - DexFile dexFile = new DexFile(new File(dexFileName), true); + DexFile dexFile = new DexFile(new File(dexFileName), !unsignedRegisters); ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(); out.enableAnnotations(120, true); + + if (sortDex) { + dexFile.place(true); + } dexFile.writeTo(out); + out.finishAnnotating(); FileWriter writer = null; try { - writer = new FileWriter(outFile); + writer = new FileWriter(outputDumpName); out.writeAnnotationsTo(writer); writer.close(); + + if (writeDex) { + byte[] bytes = out.toByteArray(); + + DexFile.calcSignature(bytes); + DexFile.calcChecksum(bytes); + + FileOutputStream fileOutputStream = new FileOutputStream(outputDexName); + fileOutputStream.write(bytes); + fileOutputStream.close(); + } }catch (IOException ex) { if (writer != null) { writer.close(); @@ -73,4 +156,13 @@ public class dump { throw ex; } } + + /** + * Prints the usage message. + */ + private static void printHelp(Options options) { + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp("java -jar baksmali.jar -dump [options] ", + "Dumps the given dex file", options, ""); + } } diff --git a/baksmali/src/main/java/org/jf/baksmali/main.java b/baksmali/src/main/java/org/jf/baksmali/main.java index bcdfa343..e0ad762e 100644 --- a/baksmali/src/main/java/org/jf/baksmali/main.java +++ b/baksmali/src/main/java/org/jf/baksmali/main.java @@ -16,6 +16,8 @@ package org.jf.baksmali; +import org.apache.commons.cli.*; + /** * Main class for baksmali. It recognizes enough options to be able to dispatch * to the right "actual" main. @@ -25,20 +27,6 @@ public class main { public static final String VERSION = "0.91"; - private static String USAGE_MESSAGE = - "usage:\n" + - " java -jar baksmali.jar --disassemble <.dex file> \n" + - " disassembles the given dex file into a set of .smali files\n" + - " in the given folder\n" + - " java -jar baksmali.jar --dump <.dex file> \n" + - " dumps the given dex file to a single annotated dump file\n" + - " that follows the order and structure of the dex file.\n" + - " java -jar baksmali.jar --version\n" + - " Print the version of this tool (" + VERSION + - ").\n" + - " java -jar baksmali.jar --help\n" + - " Print this message."; - /** * This class is uninstantiable. */ @@ -50,73 +38,93 @@ public class main { * Run! */ public static void main(String[] args) { - boolean gotCmd = false; - boolean showUsage = false; + Options options = new Options(); + + + Option versionOption = OptionBuilder.withLongOpt("version") + .withDescription("prints the version") + .create("v"); + + Option helpOption = OptionBuilder.withLongOpt("help") + .withDescription("prints the help message") + .create("?"); + + Option disassembleOption = OptionBuilder.withLongOpt("disassemble") + .withDescription("disassembles a dex file into individual files for each class that are placed into a folder structure that matches the package structure of the classes.") + .create("dis"); + + Option dumpOption = OptionBuilder.withLongOpt("dump") + .withDescription("Dumps a dex file into a single annotated dump file named FILE") + .create("dump"); + + OptionGroup mainCommand = new OptionGroup(); + mainCommand.addOption(versionOption); + mainCommand.addOption(helpOption); + mainCommand.addOption(disassembleOption); + mainCommand.addOption(dumpOption); + mainCommand.setRequired(true); + + options.addOptionGroup(mainCommand); + + CommandLineParser parser = new PosixParser(); try { - for (int i = 0; i < args.length; i++) { - String arg = args[i]; - if (arg.equals("--") || !arg.startsWith("--")) { - gotCmd = false; - showUsage = true; - break; - } + parser.parse(options, new String[]{args[0]}); + } catch (ParseException ex) { + printHelp(options); + return; + } - gotCmd = true; - if (arg.equals("--disassemble")) { - baksmali.main(without(args, i)); - break; - } else if (arg.equals("--dump")) { - dump.main(without(args, i)); - break; - } else if (arg.equals("--version")) { - version(); - break; - } else if (arg.equals("--help")) { - showUsage = true; - break; - } else { - gotCmd = false; - } + try + { + + String command = mainCommand.getSelected(); + if (command.equals("?")) { + printHelp(options); + return; + } + + if (command.equals("v")) { + version(); + return; + } + + if (command.equals("dis")) { + baksmali.main(without(args, 0)); + return; + } + + if (command.equals("dump")) { + dump.main(without(args, 0)); } - } catch (UsageException ex) { - showUsage = true; } catch (RuntimeException ex) { System.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:"); ex.printStackTrace(); - System.exit(2); + System.exit(1); } catch (Throwable ex) { System.err.println("\nUNEXPECTED TOP-LEVEL ERROR:"); ex.printStackTrace(); - System.exit(3); + System.exit(2); } + } - if (!gotCmd) { - System.err.println("error: no command specified"); - showUsage = true; - } - - if (showUsage) { - usage(); - System.exit(1); - } + /** + * Prints the usage message. + */ + private static void printHelp(Options options) { + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp("java -jar baksmali.jar [command-args]", + "use --help to see the options each command accepts", options, ""); } /** * Prints the version message. */ private static void version() { - System.err.println("baksmali version " + VERSION); + System.err.println("baksmali v" + VERSION); System.exit(0); } - /** - * Prints the usage message. - */ - private static void usage() { - System.err.println(USAGE_MESSAGE); - } - /** * Returns a copy of the given args array, but without the indicated * element.