- 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>
<build>
<plugins>
<plugin>
<!--<plugin>
<groupId>org.jf</groupId>
<artifactId>maven-smali-plugin</artifactId>
<executions>
@ -48,12 +48,12 @@
<argument>-classpath</argument>
<classpath/>
<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/out</argument>
</arguments>
</configuration>
</plugin>
</plugin>-->
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
@ -80,5 +80,10 @@
<artifactId>dexlib</artifactId>
<version>0.91-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
</project>

View File

@ -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) {

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.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;
}
String dexFileName = args[0];
String outputDirName = args[1];
if (commandLine.hasOption("?")) {
printHelp(options);
return;
}
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 <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.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 <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];
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] <dexfile>",
"Dumps the given dex file", options, "");
}
}

View File

@ -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> <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.
*/
@ -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")) {
try
{
String command = mainCommand.getSelected();
if (command.equals("?")) {
printHelp(options);
return;
}
if (command.equals("v")) {
version();
break;
} else if (arg.equals("--help")) {
showUsage = true;
break;
} else {
gotCmd = false;
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> [command-args]",
"use <command> --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.