- Redid the option parsing logic to use the apache commons cli library

- Added options to the dump command, to help with dumping and comparing 2 dex files


git-svn-id: https://smali.googlecode.com/svn/trunk@210 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
This commit is contained in:
JesusFreke@JesusFreke.com 2009-06-22 08:03:46 +00:00
parent ec857fcecd
commit d166b746b9
6 changed files with 224 additions and 100 deletions

View File

@ -12,7 +12,7 @@
</parent> </parent>
<build> <build>
<plugins> <plugins>
<plugin> <!--<plugin>
<groupId>org.jf</groupId> <groupId>org.jf</groupId>
<artifactId>maven-smali-plugin</artifactId> <artifactId>maven-smali-plugin</artifactId>
<executions> <executions>
@ -48,12 +48,12 @@
<argument>-classpath</argument> <argument>-classpath</argument>
<classpath/> <classpath/>
<argument>org.jf.baksmali.main</argument> <argument>org.jf.baksmali.main</argument>
<argument>--disassemble</argument> <argument>-dis</argument>
<argument>${project.build.directory}/test/classes.dex</argument> <argument>${project.build.directory}/test/classes.dex</argument>
<argument>${project.build.directory}/test/out</argument> <argument>${project.build.directory}/test/out</argument>
</arguments> </arguments>
</configuration> </configuration>
</plugin> </plugin>-->
<plugin> <plugin>
<artifactId>maven-assembly-plugin</artifactId> <artifactId>maven-assembly-plugin</artifactId>
<configuration> <configuration>
@ -80,5 +80,10 @@
<artifactId>dexlib</artifactId> <artifactId>dexlib</artifactId>
<version>0.91-SNAPSHOT</version> <version>0.91-SNAPSHOT</version>
</dependency> </dependency>
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
<version>1.2</version>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -36,7 +36,7 @@ public class LongRenderer implements AttributeRenderer {
if (l < 0) { if (l < 0) {
return "-0x" + Long.toHexString(-1 * l) + "L"; 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) { public String toString(Object o, String s) {

View File

@ -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.
}

View File

@ -34,6 +34,7 @@ import org.jf.baksmali.Adaptors.ClassDefinition;
import org.jf.baksmali.Renderers.*; import org.jf.baksmali.Renderers.*;
import org.jf.dexlib.DexFile; import org.jf.dexlib.DexFile;
import org.jf.dexlib.ClassDefItem; import org.jf.dexlib.ClassDefItem;
import org.apache.commons.cli.*;
import java.io.*; import java.io.*;
import java.net.URL; import java.net.URL;
@ -41,12 +42,46 @@ import java.net.URL;
public class baksmali { public class baksmali {
public static void main(String[] args) throws Exception public static void main(String[] args) throws Exception
{ {
if (args.length < 2) { Options options = new Options();
throw new UsageException();
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); File dexFileFile = new File(dexFileName);
if (!dexFileFile.exists()) { 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 <DIR>] <dexfile>",
"Disassembles the given dex file", options, "");
}
} }

View File

@ -30,21 +30,89 @@ package org.jf.baksmali;
import org.jf.dexlib.DexFile; import org.jf.dexlib.DexFile;
import org.jf.dexlib.Util.ByteArrayAnnotatedOutput; import org.jf.dexlib.Util.ByteArrayAnnotatedOutput;
import org.apache.commons.cli.*;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.FileOutputStream;
public class dump { public class dump {
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
Options options = new Options();
if (args.length < 2) { Option helpOption = OptionBuilder.withLongOpt("help")
throw new UsageException(); .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 <dexfile>.dump, where <dexfile> 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]; if (commandLine.hasOption("?")) {
String outFile = args[1]; 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); File dexFileFile = new File(dexFileName);
if (!dexFileFile.exists()) { if (!dexFileFile.exists()) {
@ -52,20 +120,35 @@ public class dump {
System.exit(1); System.exit(1);
} }
DexFile dexFile = new DexFile(new File(dexFileName), !unsignedRegisters);
DexFile dexFile = new DexFile(new File(dexFileName), true);
ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(); ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
out.enableAnnotations(120, true); out.enableAnnotations(120, true);
if (sortDex) {
dexFile.place(true);
}
dexFile.writeTo(out); dexFile.writeTo(out);
out.finishAnnotating(); out.finishAnnotating();
FileWriter writer = null; FileWriter writer = null;
try { try {
writer = new FileWriter(outFile); writer = new FileWriter(outputDumpName);
out.writeAnnotationsTo(writer); out.writeAnnotationsTo(writer);
writer.close(); 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) { }catch (IOException ex) {
if (writer != null) { if (writer != null) {
writer.close(); writer.close();
@ -73,4 +156,13 @@ public class dump {
throw ex; throw ex;
} }
} }
/**
* Prints the usage message.
*/
private static void printHelp(Options options) {
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("java -jar baksmali.jar -dump [options] <dexfile>",
"Dumps the given dex file", options, "");
}
} }

View File

@ -16,6 +16,8 @@
package org.jf.baksmali; package org.jf.baksmali;
import org.apache.commons.cli.*;
/** /**
* Main class for baksmali. It recognizes enough options to be able to dispatch * Main class for baksmali. It recognizes enough options to be able to dispatch
* to the right "actual" main. * to the right "actual" main.
@ -25,20 +27,6 @@ public class main {
public static final String VERSION = "0.91"; public static final String VERSION = "0.91";
private static String USAGE_MESSAGE =
"usage:\n" +
" java -jar baksmali.jar --disassemble <.dex file> <output folder>\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> <dump 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. * This class is uninstantiable.
*/ */
@ -50,73 +38,93 @@ public class main {
* Run! * Run!
*/ */
public static void main(String[] args) { public static void main(String[] args) {
boolean gotCmd = false; Options options = new Options();
boolean showUsage = false;
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 { try {
for (int i = 0; i < args.length; i++) { parser.parse(options, new String[]{args[0]});
String arg = args[i]; } catch (ParseException ex) {
if (arg.equals("--") || !arg.startsWith("--")) { printHelp(options);
gotCmd = false; return;
showUsage = true; }
break;
}
gotCmd = true; try
if (arg.equals("--disassemble")) { {
baksmali.main(without(args, i));
break; String command = mainCommand.getSelected();
} else if (arg.equals("--dump")) { if (command.equals("?")) {
dump.main(without(args, i)); printHelp(options);
break; return;
} else if (arg.equals("--version")) { }
version();
break; if (command.equals("v")) {
} else if (arg.equals("--help")) { version();
showUsage = true; return;
break; }
} else {
gotCmd = false; 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) { } catch (RuntimeException ex) {
System.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:"); System.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:");
ex.printStackTrace(); ex.printStackTrace();
System.exit(2); System.exit(1);
} catch (Throwable ex) { } catch (Throwable ex) {
System.err.println("\nUNEXPECTED TOP-LEVEL ERROR:"); System.err.println("\nUNEXPECTED TOP-LEVEL ERROR:");
ex.printStackTrace(); ex.printStackTrace();
System.exit(3); System.exit(2);
} }
}
if (!gotCmd) { /**
System.err.println("error: no command specified"); * Prints the usage message.
showUsage = true; */
} private static void printHelp(Options options) {
HelpFormatter formatter = new HelpFormatter();
if (showUsage) { formatter.printHelp("java -jar baksmali.jar <command> [command-args]",
usage(); "use <command> --help to see the options each command accepts", options, "");
System.exit(1);
}
} }
/** /**
* Prints the version message. * Prints the version message.
*/ */
private static void version() { private static void version() {
System.err.println("baksmali version " + VERSION); System.err.println("baksmali v" + VERSION);
System.exit(0); 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 * Returns a copy of the given args array, but without the indicated
* element. * element.