mirror of
https://github.com/revanced/smali.git
synced 2025-05-28 20:00:13 +02:00
Significant rewrite of the command line interface, using apache commons CLI
git-svn-id: https://smali.googlecode.com/svn/trunk@216 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
This commit is contained in:
parent
44682fe235
commit
2cd9246976
@ -78,5 +78,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>
|
@ -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.smali;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
}
|
@ -16,71 +16,158 @@
|
||||
|
||||
package org.jf.smali;
|
||||
|
||||
import org.apache.commons.cli.*;
|
||||
import org.jf.dexlib.DexFile;
|
||||
import org.jf.dexlib.Util.ByteArrayAnnotatedOutput;
|
||||
import org.antlr.runtime.ANTLRInputStream;
|
||||
import org.antlr.runtime.CommonTokenStream;
|
||||
import org.antlr.runtime.tree.CommonTree;
|
||||
import org.antlr.runtime.tree.CommonTreeNodeStream;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.util.Set;
|
||||
import java.util.LinkedHashSet;
|
||||
|
||||
/**
|
||||
* Main class for smali. It recognizes enough options to be able to dispatch
|
||||
* to the right "actual" main.
|
||||
*/
|
||||
public class main {
|
||||
|
||||
public static final String VERSION = "0.5b";
|
||||
public static final String VERSION = "0.91";
|
||||
|
||||
private final static Options options;
|
||||
|
||||
static {
|
||||
options = new Options();
|
||||
buildOptions();
|
||||
}
|
||||
|
||||
|
||||
private static String USAGE_MESSAGE =
|
||||
"usage:\n" +
|
||||
" java -jar smali.jar --dex [--output=<file>]\n" +
|
||||
" [--dump-to=<file>] [--dump-width=<width>]\n" +
|
||||
" [--] [<file> | <dir> | -]+\n" +
|
||||
" Convert a set of smali assembly files into a dex file.\n" +
|
||||
" <file> - assemble the specified smali file\n" +
|
||||
" <dir> - recusively find and assemble all *.smali files in\n" +
|
||||
" the specified directory\n"+
|
||||
" - - additionally read a list of files and directories\n" +
|
||||
" from stdin\n" +
|
||||
" java -jar smali.jar --version\n" +
|
||||
" Print the version of this tool (" + VERSION +
|
||||
").\n" +
|
||||
" java -jar smali.jar --help\n" +
|
||||
" Print this message.";
|
||||
|
||||
/**
|
||||
* This class is uninstantiable.
|
||||
*/
|
||||
private main() {
|
||||
// This space intentionally left blank.
|
||||
}
|
||||
|
||||
/**
|
||||
* Run!
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
boolean gotCmd = false;
|
||||
boolean showUsage = false;
|
||||
CommandLineParser parser = new PosixParser();
|
||||
CommandLine commandLine;
|
||||
|
||||
try {
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
String arg = args[i];
|
||||
if (arg.equals("--") || !arg.startsWith("--")) {
|
||||
gotCmd = false;
|
||||
showUsage = true;
|
||||
break;
|
||||
}
|
||||
commandLine = parser.parse(options, args);
|
||||
} catch (ParseException ex) {
|
||||
usage();
|
||||
return;
|
||||
}
|
||||
|
||||
gotCmd = true;
|
||||
if (arg.equals("--dex")) {
|
||||
smali.main(without(args, i));
|
||||
break;
|
||||
} else if (arg.equals("--version")) {
|
||||
version();
|
||||
break;
|
||||
} else if (arg.equals("--help")) {
|
||||
showUsage = true;
|
||||
break;
|
||||
} else {
|
||||
gotCmd = false;
|
||||
boolean doDump = false;
|
||||
boolean sort = false;
|
||||
|
||||
String outputDexFile = "out.dex";
|
||||
String dumpFileName = null;
|
||||
|
||||
String[] remainingArgs = commandLine.getArgs();
|
||||
|
||||
if (commandLine.hasOption("v")) {
|
||||
version();
|
||||
return;
|
||||
}
|
||||
|
||||
if (commandLine.hasOption("?")) {
|
||||
usage();
|
||||
return;
|
||||
}
|
||||
|
||||
if (remainingArgs.length == 0) {
|
||||
usage();
|
||||
return;
|
||||
}
|
||||
|
||||
if (commandLine.hasOption("o")) {
|
||||
outputDexFile = commandLine.getOptionValue("o");
|
||||
}
|
||||
|
||||
if (commandLine.hasOption("d")) {
|
||||
doDump = true;
|
||||
dumpFileName = commandLine.getOptionValue("d", outputDexFile + ".dump");
|
||||
}
|
||||
|
||||
if (commandLine.hasOption("s")) {
|
||||
sort = true;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
|
||||
LinkedHashSet<File> filesToProcess = new LinkedHashSet<File>();
|
||||
|
||||
for (String arg: remainingArgs) {
|
||||
File argFile = new File(arg);
|
||||
|
||||
if (!argFile.exists()) {
|
||||
throw new RuntimeException("Cannot find file or directory \"" + arg + "\"");
|
||||
}
|
||||
|
||||
if (argFile.isDirectory()) {
|
||||
getSmaliFilesInDir(argFile, filesToProcess);
|
||||
} else if (argFile.isFile()) {
|
||||
filesToProcess.add(argFile);
|
||||
}
|
||||
}
|
||||
|
||||
DexFile dexFile = new DexFile();
|
||||
|
||||
boolean errors = false;
|
||||
|
||||
for (File file: filesToProcess) {
|
||||
if (!assembleSmaliFile(file, dexFile)) {
|
||||
errors = true;
|
||||
}
|
||||
}
|
||||
} catch (UsageException ex) {
|
||||
showUsage = true;
|
||||
|
||||
if (errors) {
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
|
||||
if (sort) {
|
||||
dexFile.setSortAllItems(true);
|
||||
}
|
||||
|
||||
dexFile.place();
|
||||
|
||||
ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
|
||||
|
||||
if (dumpFileName != null) {
|
||||
out.enableAnnotations(120, true);
|
||||
}
|
||||
|
||||
dexFile.writeTo(out);
|
||||
|
||||
byte[] bytes = out.toByteArray();
|
||||
|
||||
DexFile.calcSignature(bytes);
|
||||
DexFile.calcChecksum(bytes);
|
||||
|
||||
if (dumpFileName != null) {
|
||||
out.finishAnnotating();
|
||||
|
||||
FileWriter fileWriter = new FileWriter(dumpFileName);
|
||||
out.writeAnnotationsTo(fileWriter);
|
||||
fileWriter.close();
|
||||
}
|
||||
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(outputDexFile);
|
||||
|
||||
fileOutputStream.write(bytes);
|
||||
fileOutputStream.close();
|
||||
} catch (RuntimeException ex) {
|
||||
System.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:");
|
||||
ex.printStackTrace();
|
||||
@ -90,46 +177,104 @@ public class main {
|
||||
ex.printStackTrace();
|
||||
System.exit(3);
|
||||
}
|
||||
}
|
||||
|
||||
if (!gotCmd) {
|
||||
System.err.println("error: no command specified");
|
||||
showUsage = true;
|
||||
|
||||
private static void getSmaliFilesInDir(File dir, Set<File> smaliFiles) {
|
||||
for(File file: dir.listFiles()) {
|
||||
if (file.isDirectory()) {
|
||||
getSmaliFilesInDir(file, smaliFiles);
|
||||
} else if (file.getName().endsWith(".smali")) {
|
||||
smaliFiles.add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean assembleSmaliFile(File smaliFile, DexFile dexFile)
|
||||
throws Exception {
|
||||
ANTLRInputStream input = new ANTLRInputStream(new FileInputStream(smaliFile));
|
||||
input.name = smaliFile.getAbsolutePath();
|
||||
|
||||
smaliLexer lexer = new smaliLexer(input);
|
||||
|
||||
CommonTokenStream tokens = new CommonTokenStream(lexer);
|
||||
smaliParser parser = new smaliParser(tokens);
|
||||
|
||||
smaliParser.smali_file_return result = parser.smali_file();
|
||||
|
||||
if (parser.getNumberOfSyntaxErrors() > 0 || lexer.getNumberOfLexerErrors() > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (showUsage) {
|
||||
usage();
|
||||
System.exit(1);
|
||||
CommonTree t = (CommonTree) result.getTree();
|
||||
|
||||
CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t);
|
||||
treeStream.setTokenStream(tokens);
|
||||
|
||||
smaliTreeWalker dexGen = new smaliTreeWalker(treeStream);
|
||||
|
||||
dexGen.dexFile = dexFile;
|
||||
dexGen.smali_file();
|
||||
|
||||
if (dexGen.getNumberOfSyntaxErrors() > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dexFile.ClassDefsSection.intern(dexGen.classDefItem);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prints the usage message.
|
||||
*/
|
||||
private static void usage() {
|
||||
HelpFormatter formatter = new HelpFormatter();
|
||||
formatter.printHelp("java -jar smali.jar [options] [--] [<smali-file>|folder]*",
|
||||
"assembles a set of smali files into a dex file, and optionally generats an annotated dump of the output file", options, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the version message.
|
||||
*/
|
||||
private static void version() {
|
||||
System.err.println("smali version " + VERSION);
|
||||
System.out.println("smali " + VERSION + " (http://smali.googlecode.com)");
|
||||
System.out.println("Copyright (C) 2009 Ben Gruver");
|
||||
System.out.println("BSD license (http://www.opensource.org/licenses/bsd-license.php)");
|
||||
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.
|
||||
*
|
||||
* @param orig non-null; original array
|
||||
* @param n which element to omit
|
||||
* @return non-null; new array
|
||||
*/
|
||||
private static String[] without(String[] orig, int n) {
|
||||
int len = orig.length - 1;
|
||||
String[] newa = new String[len];
|
||||
System.arraycopy(orig, 0, newa, 0, n);
|
||||
System.arraycopy(orig, n + 1, newa, n, len - n);
|
||||
return newa;
|
||||
private static void buildOptions() {
|
||||
Option versionOption = OptionBuilder.withLongOpt("version")
|
||||
.withDescription("prints the version then exits")
|
||||
.create("v");
|
||||
|
||||
Option helpOption = OptionBuilder.withLongOpt("help")
|
||||
.withDescription("prints the help message then exits")
|
||||
.create("?");
|
||||
|
||||
Option dumpOption = OptionBuilder.withLongOpt("dump-to")
|
||||
.withDescription("additionally writes a dump of written dex file to FILE (<dexfile>.dump by default)")
|
||||
.hasOptionalArg()
|
||||
.withArgName("FILE")
|
||||
.create("d");
|
||||
|
||||
Option outputOption = OptionBuilder.withLongOpt("output")
|
||||
.withDescription("the directory where the disassembled files will be placed. The default is out.dex")
|
||||
.hasArg()
|
||||
.withArgName("FILE")
|
||||
.create("o");
|
||||
|
||||
Option sortOption = OptionBuilder.withLongOpt("sort")
|
||||
.withDescription("sort the items in the dex file into a canonical order before writing")
|
||||
.create("s");
|
||||
|
||||
options.addOption(versionOption);
|
||||
options.addOption(helpOption);
|
||||
options.addOption(dumpOption);
|
||||
options.addOption(outputOption);
|
||||
options.addOption(sortOption);
|
||||
}
|
||||
}
|
@ -1,209 +0,0 @@
|
||||
/*
|
||||
* [The "BSD licence"]
|
||||
* Copyright (c) 2009 Ben Gruver
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.smali;
|
||||
|
||||
import org.antlr.runtime.ANTLRInputStream;
|
||||
import org.antlr.runtime.CommonTokenStream;
|
||||
import org.antlr.runtime.tree.CommonTree;
|
||||
import org.antlr.runtime.tree.CommonTreeNodeStream;
|
||||
import org.jf.dexlib.DexFile;
|
||||
import org.jf.dexlib.AnnotationItem;
|
||||
import org.jf.dexlib.Util.ByteArrayAnnotatedOutput;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.List;
|
||||
|
||||
public class smali
|
||||
{
|
||||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
LinkedHashSet<File> filesToProcess = new LinkedHashSet<File>();
|
||||
|
||||
boolean getFilesFromStdin = false;
|
||||
String outputFilename = "classes.dex";
|
||||
String dumpFilename = null;
|
||||
int dumpWidth = 120;
|
||||
|
||||
int i;
|
||||
|
||||
for (i=0; i<args.length; i++) {
|
||||
String arg = args[i];
|
||||
if (arg.equals("--") || !arg.startsWith("--")) {
|
||||
break;
|
||||
}
|
||||
if (arg.startsWith("--output=")) {
|
||||
outputFilename = arg.substring(arg.indexOf('=') + 1);
|
||||
} else if (arg.startsWith("--dump-to=")) {
|
||||
dumpFilename = arg.substring(arg.indexOf("=") + 1);
|
||||
} else if (arg.startsWith("--dump-width=")) {
|
||||
dumpWidth = Integer.parseInt(arg.substring(arg.indexOf("=") + 1));
|
||||
} else {
|
||||
System.err.println("unknown option: " + arg);
|
||||
throw new UsageException();
|
||||
}
|
||||
}
|
||||
|
||||
for (i=i; i<args.length; i++) {
|
||||
String arg = args[i];
|
||||
|
||||
if (arg.compareTo("-") == 0) {
|
||||
getFilesFromStdin = true;
|
||||
} else {
|
||||
File argFile = new File(arg);
|
||||
|
||||
if (!argFile.exists()) {
|
||||
throw new RuntimeException("Cannot find file or directory \"" + arg + "\"");
|
||||
}
|
||||
|
||||
if (argFile.isDirectory()) {
|
||||
getSmaliFilesInDir(argFile, filesToProcess);
|
||||
} else if (argFile.isFile()) {
|
||||
filesToProcess.add(argFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (getFilesFromStdin) {
|
||||
InputStreamReader isr = new InputStreamReader(System.in);
|
||||
BufferedReader in = new BufferedReader(isr);
|
||||
|
||||
String line = in.readLine();
|
||||
while (line != null) {
|
||||
File file = new File(line);
|
||||
|
||||
if (!file.exists()) {
|
||||
throw new RuntimeException("Cannot find file or directory \"" + line + "\"");
|
||||
}
|
||||
|
||||
if (file.isDirectory()) {
|
||||
getSmaliFilesInDir(file, filesToProcess);
|
||||
} else {
|
||||
filesToProcess.add(file);
|
||||
}
|
||||
|
||||
line = in.readLine();
|
||||
}
|
||||
}
|
||||
|
||||
DexFile dexFile = DexFile.makeBlankDexFile();
|
||||
|
||||
boolean errors = false;
|
||||
|
||||
for (File file: filesToProcess) {
|
||||
if (!assembleSmaliFile(file, dexFile)) {
|
||||
errors = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (errors) {
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
dexFile.place(false);
|
||||
try
|
||||
{
|
||||
ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
|
||||
|
||||
if (dumpFilename != null) {
|
||||
out.enableAnnotations(dumpWidth, true);
|
||||
}
|
||||
|
||||
dexFile.writeTo(out);
|
||||
|
||||
byte[] bytes = out.toByteArray();
|
||||
|
||||
DexFile.calcSignature(bytes);
|
||||
DexFile.calcChecksum(bytes);
|
||||
|
||||
if (dumpFilename != null) {
|
||||
out.finishAnnotating();
|
||||
|
||||
FileWriter fileWriter = new FileWriter(dumpFilename);
|
||||
out.writeAnnotationsTo(fileWriter);
|
||||
fileWriter.close();
|
||||
}
|
||||
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(outputFilename);
|
||||
|
||||
fileOutputStream.write(bytes);
|
||||
fileOutputStream.close();
|
||||
} catch (Exception ex)
|
||||
{
|
||||
System.out.println(ex.toString());
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void getSmaliFilesInDir(File dir, Set<File> smaliFiles) {
|
||||
for(File file: dir.listFiles()) {
|
||||
if (file.isDirectory()) {
|
||||
getSmaliFilesInDir(file, smaliFiles);
|
||||
} else if (file.getName().endsWith(".smali")) {
|
||||
smaliFiles.add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean assembleSmaliFile(File smaliFile, DexFile dexFile)
|
||||
throws Exception {
|
||||
ANTLRInputStream input = new ANTLRInputStream(new FileInputStream(smaliFile));
|
||||
input.name = smaliFile.getAbsolutePath();
|
||||
|
||||
smaliLexer lexer = new smaliLexer(input);
|
||||
|
||||
CommonTokenStream tokens = new CommonTokenStream(lexer);
|
||||
smaliParser parser = new smaliParser(tokens);
|
||||
|
||||
smaliParser.smali_file_return result = parser.smali_file();
|
||||
|
||||
if (parser.getNumberOfSyntaxErrors() > 0 || lexer.getNumberOfLexerErrors() > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
CommonTree t = (CommonTree) result.getTree();
|
||||
|
||||
CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t);
|
||||
treeStream.setTokenStream(tokens);
|
||||
|
||||
smaliTreeWalker dexGen = new smaliTreeWalker(treeStream);
|
||||
|
||||
dexGen.dexFile = dexFile;
|
||||
dexGen.smali_file();
|
||||
|
||||
if (dexGen.getNumberOfSyntaxErrors() > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dexFile.ClassDefsSection.intern(dexFile, dexGen.classDefItem);
|
||||
return true;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user