mirror of
https://github.com/revanced/smali.git
synced 2025-05-29 20:20:12 +02:00
Implement a new command line interface for smali
This commit is contained in:
parent
5a5eafb818
commit
735746595f
@ -53,21 +53,21 @@ public class DumpCommand implements Command {
|
|||||||
|
|
||||||
@Parameter(names = {"-h", "-?", "--help"}, help = true,
|
@Parameter(names = {"-h", "-?", "--help"}, help = true,
|
||||||
description = "Show usage information for this command.")
|
description = "Show usage information for this command.")
|
||||||
public boolean help;
|
private boolean help;
|
||||||
|
|
||||||
@Parameter(names = {"-a", "--api"},
|
@Parameter(names = {"-a", "--api"},
|
||||||
description = "The numeric api level of the file being disassembled.")
|
description = "The numeric api level of the file being disassembled.")
|
||||||
public int apiLevel = 15;
|
private int apiLevel = 15;
|
||||||
|
|
||||||
@Parameter(names = "--experimental",
|
@Parameter(names = "--experimental",
|
||||||
description = "Enable experimental opcodes to be disassembled, even if they aren't necessarily " +
|
description = "Enable experimental opcodes to be disassembled, even if they aren't necessarily " +
|
||||||
"supported in the Android runtime yet.")
|
"supported in the Android runtime yet.")
|
||||||
public boolean experimentalOpcodes = false;
|
private boolean experimentalOpcodes = false;
|
||||||
|
|
||||||
@Parameter(description = "<file> - A dex/apk/oat/odex file. For apk or oat files that contain multiple dex " +
|
@Parameter(description = "<file> - A dex/apk/oat/odex file. For apk or oat files that contain multiple dex " +
|
||||||
"files, you can specify which dex file to disassemble by appending the name of the dex file with a " +
|
"files, you can specify which dex file to disassemble by appending the name of the dex file with a " +
|
||||||
"colon. E.g. \"something.apk:classes2.dex\"")
|
"colon. E.g. \"something.apk:classes2.dex\"")
|
||||||
public String input;
|
private String input;
|
||||||
|
|
||||||
public DumpCommand(@Nonnull JCommander jc) {
|
public DumpCommand(@Nonnull JCommander jc) {
|
||||||
this.jc = jc;
|
this.jc = jc;
|
||||||
|
@ -49,6 +49,7 @@ dependencies {
|
|||||||
compile project(':util')
|
compile project(':util')
|
||||||
compile depends.findbugs
|
compile depends.findbugs
|
||||||
compile depends.guava
|
compile depends.guava
|
||||||
|
compile depends.commons_cli
|
||||||
|
|
||||||
testCompile depends.junit
|
testCompile depends.junit
|
||||||
|
|
||||||
|
@ -80,8 +80,8 @@ dependencies {
|
|||||||
compile project(':util')
|
compile project(':util')
|
||||||
compile project(':dexlib2')
|
compile project(':dexlib2')
|
||||||
compile depends.antlr_runtime
|
compile depends.antlr_runtime
|
||||||
|
compile depends.jcommander
|
||||||
compile depends.stringtemplate
|
compile depends.stringtemplate
|
||||||
compile depends.commons_cli
|
|
||||||
|
|
||||||
testCompile depends.junit
|
testCompile depends.junit
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ task fatJar(type: Jar, dependsOn: jar) {
|
|||||||
classifier = 'fat'
|
classifier = 'fat'
|
||||||
|
|
||||||
manifest {
|
manifest {
|
||||||
attributes('Main-Class': 'org.jf.smali.main')
|
attributes('Main-Class': 'org.jf.smali.Main')
|
||||||
}
|
}
|
||||||
|
|
||||||
doLast {
|
doLast {
|
||||||
|
111
smali/src/main/java/org/jf/smali/AssembleCommand.java
Normal file
111
smali/src/main/java/org/jf/smali/AssembleCommand.java
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 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.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "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 COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS 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 com.beust.jcommander.JCommander;
|
||||||
|
import com.beust.jcommander.Parameter;
|
||||||
|
import com.beust.jcommander.Parameters;
|
||||||
|
import com.beust.jcommander.validators.PositiveInteger;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Parameters(commandDescription = "Assembles smali files into a dex file.")
|
||||||
|
public class AssembleCommand implements Command {
|
||||||
|
|
||||||
|
@Nonnull private final JCommander jc;
|
||||||
|
|
||||||
|
@Parameter(names = {"-h", "-?", "--help"}, help = true,
|
||||||
|
description = "Show usage information for this command.")
|
||||||
|
private boolean help;
|
||||||
|
|
||||||
|
@Parameter(names = {"-j", "--jobs"},
|
||||||
|
description = "The number of threads to use. Defaults to the number of cores available.",
|
||||||
|
validateWith = PositiveInteger.class)
|
||||||
|
private int jobs = Runtime.getRuntime().availableProcessors();
|
||||||
|
|
||||||
|
@Parameter(names = {"-a", "--api"},
|
||||||
|
description = "The numeric api level to use while assembling.")
|
||||||
|
private int apiLevel = 15;
|
||||||
|
|
||||||
|
@Parameter(names = {"-o", "--output"},
|
||||||
|
description = "The location of the dex file to write.")
|
||||||
|
private String output = "out.dex";
|
||||||
|
|
||||||
|
@Parameter(names = "--experimental",
|
||||||
|
description = "Enable experimental opcodes to be assembled, even if they aren't necessarily " +
|
||||||
|
"supported in the Android runtime yet.")
|
||||||
|
private boolean experimentalOpcodes = false;
|
||||||
|
|
||||||
|
@Parameter(names = "--verbose",
|
||||||
|
description = "Generate verbose error messages.")
|
||||||
|
private boolean verbose = false;
|
||||||
|
|
||||||
|
@Parameter(names = "--allow-odex-opcodes",
|
||||||
|
description = "Allows the odex opcodes that dalvik doesn't reject to be assembled.")
|
||||||
|
private boolean allowOdexOpcodes;
|
||||||
|
|
||||||
|
@Parameter(description = "[<file>|<dir>]+ - Assembles the given files. If a directory is specified, it will be " +
|
||||||
|
"recursively searched for any file with a .smali prefix")
|
||||||
|
private List<String> input;
|
||||||
|
|
||||||
|
public AssembleCommand(@Nonnull JCommander jc) {
|
||||||
|
this.jc = jc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void run() {
|
||||||
|
if (help || input == null || input.isEmpty()) {
|
||||||
|
jc.usage(jc.getParsedCommand());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Smali.assemble(getOptions(), input);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SmaliOptions getOptions() {
|
||||||
|
SmaliOptions options = new SmaliOptions();
|
||||||
|
|
||||||
|
options.jobs = jobs;
|
||||||
|
options.apiLevel = apiLevel;
|
||||||
|
options.outputDexFile = output;
|
||||||
|
options.experimentalOpcodes = experimentalOpcodes;
|
||||||
|
options.allowOdexOpcodes = allowOdexOpcodes;
|
||||||
|
options.verboseErrors = verbose;
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
}
|
36
smali/src/main/java/org/jf/smali/Command.java
Normal file
36
smali/src/main/java/org/jf/smali/Command.java
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * 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.
|
||||||
|
* * Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "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 COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS 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;
|
||||||
|
|
||||||
|
public interface Command {
|
||||||
|
void run();
|
||||||
|
}
|
68
smali/src/main/java/org/jf/smali/HelpCommand.java
Normal file
68
smali/src/main/java/org/jf/smali/HelpCommand.java
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 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.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "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 COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS 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 com.beust.jcommander.JCommander;
|
||||||
|
import com.beust.jcommander.Parameter;
|
||||||
|
import com.beust.jcommander.Parameters;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Parameters(commandDescription = "Shows usage information")
|
||||||
|
public class HelpCommand implements Command {
|
||||||
|
@Nonnull private final JCommander jc;
|
||||||
|
|
||||||
|
public HelpCommand(@Nonnull JCommander jc) {
|
||||||
|
this.jc = jc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parameter(description = "If specified, only show the usage information for the given commands")
|
||||||
|
private List<String> commands;
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
if (commands == null || commands.isEmpty()) {
|
||||||
|
jc.usage();
|
||||||
|
} else {
|
||||||
|
for (String cmd : commands) {
|
||||||
|
jc.usage(cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parameters(hidden = true)
|
||||||
|
public static class HlepCommand extends HelpCommand {
|
||||||
|
public HlepCommand(@Nonnull JCommander jc) {
|
||||||
|
super(jc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
99
smali/src/main/java/org/jf/smali/Main.java
Normal file
99
smali/src/main/java/org/jf/smali/Main.java
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 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.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "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 COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS 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 com.beust.jcommander.JCommander;
|
||||||
|
import com.beust.jcommander.Parameter;
|
||||||
|
import org.jf.smali.HelpCommand.HlepCommand;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static final String VERSION = loadVersion();
|
||||||
|
|
||||||
|
@Parameter(names = {"-h", "-?", "--help"}, help = true,
|
||||||
|
description = "Show usage information")
|
||||||
|
private boolean help;
|
||||||
|
|
||||||
|
@Parameter(names = {"-v", "--version"}, help = true,
|
||||||
|
description = "Print the version of baksmali and then exit")
|
||||||
|
public boolean version;
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Main main = new Main();
|
||||||
|
|
||||||
|
JCommander jc = new JCommander(main);
|
||||||
|
|
||||||
|
jc.addCommand("assemble", new AssembleCommand(jc), "a", "as");
|
||||||
|
jc.addCommand("help", new HelpCommand(jc), "h");
|
||||||
|
jc.addCommand("hlep", new HlepCommand(jc));
|
||||||
|
|
||||||
|
jc.parse(args);
|
||||||
|
|
||||||
|
if (jc.getParsedCommand() == null || main.help) {
|
||||||
|
jc.usage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (main.version) {
|
||||||
|
version();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Command command = (Command)jc.getCommands().get(jc.getParsedCommand()).getObjects().get(0);
|
||||||
|
command.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void version() {
|
||||||
|
System.out.println("smali " + VERSION + " (http://smali.googlecode.com)");
|
||||||
|
System.out.println("Copyright (C) 2010 Ben Gruver (JesusFreke@JesusFreke.com)");
|
||||||
|
System.out.println("BSD license (http://www.opensource.org/licenses/bsd-license.php)");
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String loadVersion() {
|
||||||
|
InputStream propertiesStream = Main.class.getClassLoader().getResourceAsStream("smali.properties");
|
||||||
|
String version = "[unknown version]";
|
||||||
|
if (propertiesStream != null) {
|
||||||
|
Properties properties = new Properties();
|
||||||
|
try {
|
||||||
|
properties.load(propertiesStream);
|
||||||
|
version = properties.getProperty("application.version");
|
||||||
|
} catch (IOException ex) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
}
|
206
smali/src/main/java/org/jf/smali/Smali.java
Normal file
206
smali/src/main/java/org/jf/smali/Smali.java
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 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.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "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 COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS 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 com.google.common.collect.Lists;
|
||||||
|
import org.antlr.runtime.CommonTokenStream;
|
||||||
|
import org.antlr.runtime.Token;
|
||||||
|
import org.antlr.runtime.TokenSource;
|
||||||
|
import org.antlr.runtime.tree.CommonTree;
|
||||||
|
import org.antlr.runtime.tree.CommonTreeNodeStream;
|
||||||
|
import org.jf.dexlib2.Opcodes;
|
||||||
|
import org.jf.dexlib2.writer.builder.DexBuilder;
|
||||||
|
import org.jf.dexlib2.writer.io.FileDataStore;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
|
public class Smali {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assemble the specified files, using the given options
|
||||||
|
*
|
||||||
|
* @param options a SmaliOptions object with the options to run smali with
|
||||||
|
* @param input The files/directories to process
|
||||||
|
* @return true if assembly completed with no errors, or false if errors were encountered
|
||||||
|
*/
|
||||||
|
public static boolean assemble(final SmaliOptions options, String... input) throws IOException {
|
||||||
|
return assemble(options, Arrays.asList(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assemble the specified files, using the given options
|
||||||
|
*
|
||||||
|
* @param options a SmaliOptions object with the options to run smali with
|
||||||
|
* @param input The files/directories to process
|
||||||
|
* @return true if assembly completed with no errors, or false if errors were encountered
|
||||||
|
*/
|
||||||
|
public static boolean assemble(final SmaliOptions options, List<String> input) throws IOException {
|
||||||
|
LinkedHashSet<File> filesToProcessSet = new LinkedHashSet<File>();
|
||||||
|
|
||||||
|
for (String fileToProcess: input) {
|
||||||
|
File argFile = new File(fileToProcess);
|
||||||
|
|
||||||
|
if (!argFile.exists()) {
|
||||||
|
throw new IllegalArgumentException("Cannot find file or directory \"" + fileToProcess + "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argFile.isDirectory()) {
|
||||||
|
getSmaliFilesInDir(argFile, filesToProcessSet);
|
||||||
|
} else if (argFile.isFile()) {
|
||||||
|
filesToProcessSet.add(argFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean errors = false;
|
||||||
|
|
||||||
|
final DexBuilder dexBuilder = DexBuilder.makeDexBuilder(
|
||||||
|
Opcodes.forApi(options.apiLevel, options.experimentalOpcodes));
|
||||||
|
|
||||||
|
ExecutorService executor = Executors.newFixedThreadPool(options.jobs);
|
||||||
|
List<Future<Boolean>> tasks = Lists.newArrayList();
|
||||||
|
|
||||||
|
for (final File file: filesToProcessSet) {
|
||||||
|
tasks.add(executor.submit(new Callable<Boolean>() {
|
||||||
|
@Override public Boolean call() throws Exception {
|
||||||
|
return assembleSmaliFile(file, dexBuilder, options);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Future<Boolean> task: tasks) {
|
||||||
|
while(true) {
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
if (!task.get()) {
|
||||||
|
errors = true;
|
||||||
|
}
|
||||||
|
} catch (ExecutionException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
executor.shutdown();
|
||||||
|
|
||||||
|
if (errors) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
dexBuilder.writeTo(new FileDataStore(new File(options.outputDexFile)));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void getSmaliFilesInDir(@Nonnull File dir, @Nonnull Set<File> smaliFiles) {
|
||||||
|
File[] files = dir.listFiles();
|
||||||
|
if (files != null) {
|
||||||
|
for(File file: files) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
getSmaliFilesInDir(file, smaliFiles);
|
||||||
|
} else if (file.getName().endsWith(".smali")) {
|
||||||
|
smaliFiles.add(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean assembleSmaliFile(File smaliFile, DexBuilder dexBuilder, SmaliOptions options)
|
||||||
|
throws Exception {
|
||||||
|
CommonTokenStream tokens;
|
||||||
|
|
||||||
|
LexerErrorInterface lexer;
|
||||||
|
|
||||||
|
FileInputStream fis = new FileInputStream(smaliFile);
|
||||||
|
InputStreamReader reader = new InputStreamReader(fis, "UTF-8");
|
||||||
|
|
||||||
|
lexer = new smaliFlexLexer(reader);
|
||||||
|
((smaliFlexLexer)lexer).setSourceFile(smaliFile);
|
||||||
|
tokens = new CommonTokenStream((TokenSource)lexer);
|
||||||
|
|
||||||
|
if (options.printTokens) {
|
||||||
|
tokens.getTokens();
|
||||||
|
|
||||||
|
for (int i=0; i<tokens.size(); i++) {
|
||||||
|
Token token = tokens.get(i);
|
||||||
|
if (token.getChannel() == smaliParser.HIDDEN) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println(smaliParser.tokenNames[token.getType()] + ": " + token.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
smaliParser parser = new smaliParser(tokens);
|
||||||
|
parser.setVerboseErrors(options.verboseErrors);
|
||||||
|
parser.setAllowOdex(options.allowOdexOpcodes);
|
||||||
|
parser.setApiLevel(options.apiLevel, options.experimentalOpcodes);
|
||||||
|
|
||||||
|
smaliParser.smali_file_return result = parser.smali_file();
|
||||||
|
|
||||||
|
if (parser.getNumberOfSyntaxErrors() > 0 || lexer.getNumberOfSyntaxErrors() > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommonTree t = result.getTree();
|
||||||
|
|
||||||
|
CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t);
|
||||||
|
treeStream.setTokenStream(tokens);
|
||||||
|
|
||||||
|
if (options.printTokens) {
|
||||||
|
System.out.println(t.toStringTree());
|
||||||
|
}
|
||||||
|
|
||||||
|
smaliTreeWalker dexGen = new smaliTreeWalker(treeStream);
|
||||||
|
dexGen.setApiLevel(options.apiLevel, options.experimentalOpcodes);
|
||||||
|
|
||||||
|
dexGen.setVerboseErrors(options.verboseErrors);
|
||||||
|
dexGen.setDexBuilder(dexBuilder);
|
||||||
|
dexGen.smali_file();
|
||||||
|
|
||||||
|
return dexGen.getNumberOfSyntaxErrors() == 0;
|
||||||
|
}
|
||||||
|
}
|
@ -36,17 +36,8 @@ public class SmaliOptions {
|
|||||||
public String outputDexFile = "out.dex";
|
public String outputDexFile = "out.dex";
|
||||||
|
|
||||||
public int jobs = Runtime.getRuntime().availableProcessors();
|
public int jobs = Runtime.getRuntime().availableProcessors();
|
||||||
public boolean allowOdex = false;
|
public boolean allowOdexOpcodes = false;
|
||||||
public boolean verboseErrors = false;
|
public boolean verboseErrors = false;
|
||||||
public boolean printTokens = false;
|
public boolean printTokens = false;
|
||||||
public boolean experimental = false;
|
public boolean experimentalOpcodes = false;
|
||||||
|
|
||||||
public boolean listMethods = false;
|
|
||||||
public String methodListFilename = null;
|
|
||||||
|
|
||||||
public boolean listFields = false;
|
|
||||||
public String fieldListFilename = null;
|
|
||||||
|
|
||||||
public boolean listTypes = false;
|
|
||||||
public String typeListFilename = null;
|
|
||||||
}
|
}
|
||||||
|
@ -1,491 +0,0 @@
|
|||||||
/*
|
|
||||||
* [The "BSD licence"]
|
|
||||||
* Copyright (c) 2010 Ben Gruver (JesusFreke)
|
|
||||||
* 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 com.google.common.base.Strings;
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.google.common.collect.Ordering;
|
|
||||||
import org.antlr.runtime.CommonTokenStream;
|
|
||||||
import org.antlr.runtime.Token;
|
|
||||||
import org.antlr.runtime.TokenSource;
|
|
||||||
import org.antlr.runtime.tree.CommonTree;
|
|
||||||
import org.antlr.runtime.tree.CommonTreeNodeStream;
|
|
||||||
import org.apache.commons.cli.*;
|
|
||||||
import org.jf.dexlib2.Opcodes;
|
|
||||||
import org.jf.dexlib2.writer.builder.DexBuilder;
|
|
||||||
import org.jf.dexlib2.writer.io.FileDataStore;
|
|
||||||
import org.jf.util.ConsoleUtil;
|
|
||||||
import org.jf.util.SmaliHelpFormatter;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.io.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
private final static Options basicOptions;
|
|
||||||
private final static Options debugOptions;
|
|
||||||
private final static Options options;
|
|
||||||
|
|
||||||
static {
|
|
||||||
basicOptions = new Options();
|
|
||||||
debugOptions = new Options();
|
|
||||||
options = new Options();
|
|
||||||
buildOptions();
|
|
||||||
|
|
||||||
InputStream templateStream = main.class.getClassLoader().getResourceAsStream("smali.properties");
|
|
||||||
if (templateStream != null) {
|
|
||||||
Properties properties = new Properties();
|
|
||||||
String version = "(unknown)";
|
|
||||||
try {
|
|
||||||
properties.load(templateStream);
|
|
||||||
version = properties.getProperty("application.version");
|
|
||||||
} catch (IOException ex) {
|
|
||||||
// just eat it
|
|
||||||
}
|
|
||||||
VERSION = version;
|
|
||||||
} else {
|
|
||||||
VERSION = "[unknown version]";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class is uninstantiable.
|
|
||||||
*/
|
|
||||||
private main() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A more programmatic-friendly entry point for smali
|
|
||||||
*
|
|
||||||
* @param options a SmaliOptions object with the options to run smali with
|
|
||||||
* @param input The files/directories to process
|
|
||||||
* @return true if assembly completed with no errors, or false if errors were encountered
|
|
||||||
*/
|
|
||||||
public static boolean run(final SmaliOptions options, String... input) throws IOException {
|
|
||||||
LinkedHashSet<File> filesToProcessSet = new LinkedHashSet<File>();
|
|
||||||
|
|
||||||
for (String fileToProcess: input) {
|
|
||||||
File argFile = new File(fileToProcess);
|
|
||||||
|
|
||||||
if (!argFile.exists()) {
|
|
||||||
throw new IllegalArgumentException("Cannot find file or directory \"" + fileToProcess + "\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argFile.isDirectory()) {
|
|
||||||
getSmaliFilesInDir(argFile, filesToProcessSet);
|
|
||||||
} else if (argFile.isFile()) {
|
|
||||||
filesToProcessSet.add(argFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean errors = false;
|
|
||||||
|
|
||||||
final DexBuilder dexBuilder = DexBuilder.makeDexBuilder(
|
|
||||||
Opcodes.forApi(options.apiLevel, options.experimental));
|
|
||||||
|
|
||||||
ExecutorService executor = Executors.newFixedThreadPool(options.jobs);
|
|
||||||
List<Future<Boolean>> tasks = Lists.newArrayList();
|
|
||||||
|
|
||||||
for (final File file: filesToProcessSet) {
|
|
||||||
tasks.add(executor.submit(new Callable<Boolean>() {
|
|
||||||
@Override public Boolean call() throws Exception {
|
|
||||||
return assembleSmaliFile(file, dexBuilder, options);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Future<Boolean> task: tasks) {
|
|
||||||
while(true) {
|
|
||||||
try {
|
|
||||||
try {
|
|
||||||
if (!task.get()) {
|
|
||||||
errors = true;
|
|
||||||
}
|
|
||||||
} catch (ExecutionException ex) {
|
|
||||||
throw new RuntimeException(ex);
|
|
||||||
}
|
|
||||||
} catch (InterruptedException ex) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
executor.shutdown();
|
|
||||||
|
|
||||||
if (errors) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.listMethods) {
|
|
||||||
if (Strings.isNullOrEmpty(options.methodListFilename)) {
|
|
||||||
options.methodListFilename = options.outputDexFile + ".methods";
|
|
||||||
}
|
|
||||||
writeReferences(dexBuilder.getMethodReferences(), options.methodListFilename);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.listFields) {
|
|
||||||
if (Strings.isNullOrEmpty(options.fieldListFilename)) {
|
|
||||||
options.fieldListFilename = options.outputDexFile + ".fields";
|
|
||||||
}
|
|
||||||
writeReferences(dexBuilder.getFieldReferences(), options.fieldListFilename);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.listTypes) {
|
|
||||||
if (Strings.isNullOrEmpty(options.typeListFilename)) {
|
|
||||||
options.typeListFilename = options.outputDexFile + ".types";
|
|
||||||
}
|
|
||||||
writeReferences(dexBuilder.getTypeReferences(), options.typeListFilename);
|
|
||||||
}
|
|
||||||
|
|
||||||
dexBuilder.writeTo(new FileDataStore(new File(options.outputDexFile)));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run!
|
|
||||||
*/
|
|
||||||
public static void main(String[] args) {
|
|
||||||
Locale locale = new Locale("en", "US");
|
|
||||||
Locale.setDefault(locale);
|
|
||||||
|
|
||||||
CommandLineParser parser = new PosixParser();
|
|
||||||
CommandLine commandLine;
|
|
||||||
|
|
||||||
try {
|
|
||||||
commandLine = parser.parse(options, args);
|
|
||||||
} catch (ParseException ex) {
|
|
||||||
usage();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SmaliOptions smaliOptions = new SmaliOptions();
|
|
||||||
|
|
||||||
String[] remainingArgs = commandLine.getArgs();
|
|
||||||
|
|
||||||
Option[] options = commandLine.getOptions();
|
|
||||||
|
|
||||||
for (int i=0; i<options.length; i++) {
|
|
||||||
Option option = options[i];
|
|
||||||
String opt = option.getOpt();
|
|
||||||
|
|
||||||
switch (opt.charAt(0)) {
|
|
||||||
case 'v':
|
|
||||||
version();
|
|
||||||
return;
|
|
||||||
case '?':
|
|
||||||
while (++i < options.length) {
|
|
||||||
if (options[i].getOpt().charAt(0) == '?') {
|
|
||||||
usage(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
usage(false);
|
|
||||||
return;
|
|
||||||
case 'o':
|
|
||||||
smaliOptions.outputDexFile = commandLine.getOptionValue("o");
|
|
||||||
break;
|
|
||||||
case 'x':
|
|
||||||
smaliOptions.allowOdex = true;
|
|
||||||
break;
|
|
||||||
case 'X':
|
|
||||||
smaliOptions.experimental = true;
|
|
||||||
break;
|
|
||||||
case 'a':
|
|
||||||
smaliOptions.apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
|
|
||||||
break;
|
|
||||||
case 'j':
|
|
||||||
smaliOptions.jobs = Integer.parseInt(commandLine.getOptionValue("j"));
|
|
||||||
break;
|
|
||||||
case 'm':
|
|
||||||
smaliOptions.listMethods = true;
|
|
||||||
smaliOptions.methodListFilename = commandLine.getOptionValue("m");
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
smaliOptions.listFields = true;
|
|
||||||
smaliOptions.fieldListFilename = commandLine.getOptionValue("f");
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
smaliOptions.listTypes = true;
|
|
||||||
smaliOptions.typeListFilename = commandLine.getOptionValue("t");
|
|
||||||
break;
|
|
||||||
case 'V':
|
|
||||||
smaliOptions.verboseErrors = true;
|
|
||||||
break;
|
|
||||||
case 'T':
|
|
||||||
smaliOptions.printTokens = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remainingArgs.length == 0) {
|
|
||||||
usage();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!run(smaliOptions, remainingArgs)) {
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
} catch (RuntimeException ex) {
|
|
||||||
System.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:");
|
|
||||||
ex.printStackTrace();
|
|
||||||
System.exit(2);
|
|
||||||
} catch (Throwable ex) {
|
|
||||||
System.err.println("\nUNEXPECTED TOP-LEVEL ERROR:");
|
|
||||||
ex.printStackTrace();
|
|
||||||
System.exit(3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void writeReferences(List<String> references, String filename) {
|
|
||||||
PrintWriter writer = null;
|
|
||||||
try {
|
|
||||||
writer = new PrintWriter(new BufferedWriter(new FileWriter(filename)));
|
|
||||||
|
|
||||||
for (String reference: Ordering.natural().sortedCopy(references)) {
|
|
||||||
writer.println(reference);
|
|
||||||
}
|
|
||||||
} catch (IOException ex) {
|
|
||||||
throw new RuntimeException(ex);
|
|
||||||
} finally {
|
|
||||||
if (writer != null) {
|
|
||||||
writer.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void getSmaliFilesInDir(@Nonnull File dir, @Nonnull Set<File> smaliFiles) {
|
|
||||||
File[] files = dir.listFiles();
|
|
||||||
if (files != null) {
|
|
||||||
for(File file: files) {
|
|
||||||
if (file.isDirectory()) {
|
|
||||||
getSmaliFilesInDir(file, smaliFiles);
|
|
||||||
} else if (file.getName().endsWith(".smali")) {
|
|
||||||
smaliFiles.add(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean assembleSmaliFile(File smaliFile, DexBuilder dexBuilder, SmaliOptions options)
|
|
||||||
throws Exception {
|
|
||||||
CommonTokenStream tokens;
|
|
||||||
|
|
||||||
LexerErrorInterface lexer;
|
|
||||||
|
|
||||||
FileInputStream fis = new FileInputStream(smaliFile);
|
|
||||||
InputStreamReader reader = new InputStreamReader(fis, "UTF-8");
|
|
||||||
|
|
||||||
lexer = new smaliFlexLexer(reader);
|
|
||||||
((smaliFlexLexer)lexer).setSourceFile(smaliFile);
|
|
||||||
tokens = new CommonTokenStream((TokenSource)lexer);
|
|
||||||
|
|
||||||
if (options.printTokens) {
|
|
||||||
tokens.getTokens();
|
|
||||||
|
|
||||||
for (int i=0; i<tokens.size(); i++) {
|
|
||||||
Token token = tokens.get(i);
|
|
||||||
if (token.getChannel() == smaliParser.HIDDEN) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println(smaliParser.tokenNames[token.getType()] + ": " + token.getText());
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
smaliParser parser = new smaliParser(tokens);
|
|
||||||
parser.setVerboseErrors(options.verboseErrors);
|
|
||||||
parser.setAllowOdex(options.allowOdex);
|
|
||||||
parser.setApiLevel(options.apiLevel, options.experimental);
|
|
||||||
|
|
||||||
smaliParser.smali_file_return result = parser.smali_file();
|
|
||||||
|
|
||||||
if (parser.getNumberOfSyntaxErrors() > 0 || lexer.getNumberOfSyntaxErrors() > 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
CommonTree t = result.getTree();
|
|
||||||
|
|
||||||
CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t);
|
|
||||||
treeStream.setTokenStream(tokens);
|
|
||||||
|
|
||||||
if (options.printTokens) {
|
|
||||||
System.out.println(t.toStringTree());
|
|
||||||
}
|
|
||||||
|
|
||||||
smaliTreeWalker dexGen = new smaliTreeWalker(treeStream);
|
|
||||||
dexGen.setApiLevel(options.apiLevel, options.experimental);
|
|
||||||
|
|
||||||
dexGen.setVerboseErrors(options.verboseErrors);
|
|
||||||
dexGen.setDexBuilder(dexBuilder);
|
|
||||||
dexGen.smali_file();
|
|
||||||
|
|
||||||
return dexGen.getNumberOfSyntaxErrors() == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prints the usage message.
|
|
||||||
*/
|
|
||||||
private static void usage(boolean printDebugOptions) {
|
|
||||||
SmaliHelpFormatter formatter = new SmaliHelpFormatter();
|
|
||||||
|
|
||||||
int consoleWidth = ConsoleUtil.getConsoleWidth();
|
|
||||||
if (consoleWidth <= 0) {
|
|
||||||
consoleWidth = 80;
|
|
||||||
}
|
|
||||||
|
|
||||||
formatter.setWidth(consoleWidth);
|
|
||||||
|
|
||||||
formatter.printHelp("java -jar smali.jar [options] [--] [<smali-file>|folder]*",
|
|
||||||
"assembles a set of smali files into a dex file", basicOptions, printDebugOptions?debugOptions:null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void usage() {
|
|
||||||
usage(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prints the version message.
|
|
||||||
*/
|
|
||||||
private static void version() {
|
|
||||||
System.out.println("smali " + VERSION + " (http://smali.googlecode.com)");
|
|
||||||
System.out.println("Copyright (C) 2010 Ben Gruver (JesusFreke@JesusFreke.com)");
|
|
||||||
System.out.println("BSD license (http://www.opensource.org/licenses/bsd-license.php)");
|
|
||||||
System.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("AccessStaticViaInstance")
|
|
||||||
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. Specify twice for debug options")
|
|
||||||
.create("?");
|
|
||||||
|
|
||||||
Option outputOption = OptionBuilder.withLongOpt("output")
|
|
||||||
.withDescription("the name of the dex file that will be written. The default is out.dex")
|
|
||||||
.hasArg()
|
|
||||||
.withArgName("FILE")
|
|
||||||
.create("o");
|
|
||||||
|
|
||||||
Option allowOdexOption = OptionBuilder.withLongOpt("allow-odex-instructions")
|
|
||||||
.withDescription("allow odex instructions to be compiled into the dex file. Only a few" +
|
|
||||||
" instructions are supported - the ones that can exist in a dead code path and not" +
|
|
||||||
" cause dalvik to reject the class")
|
|
||||||
.create("x");
|
|
||||||
|
|
||||||
Option apiLevelOption = OptionBuilder.withLongOpt("api-level")
|
|
||||||
.withDescription("The numeric api-level of the file to generate, e.g. 14 for ICS. If not " +
|
|
||||||
"specified, it defaults to 15 (ICS).")
|
|
||||||
.hasArg()
|
|
||||||
.withArgName("API_LEVEL")
|
|
||||||
.create("a");
|
|
||||||
|
|
||||||
Option listMethodsOption = OptionBuilder.withLongOpt("list-methods")
|
|
||||||
.withDescription("Lists all the method references to FILE" +
|
|
||||||
" (<output_dex_filename>.methods by default)")
|
|
||||||
.hasOptionalArg()
|
|
||||||
.withArgName("FILE")
|
|
||||||
.create("m");
|
|
||||||
|
|
||||||
Option listFieldsOption = OptionBuilder.withLongOpt("list-fields")
|
|
||||||
.withDescription("Lists all the field references to FILE" +
|
|
||||||
" (<output_dex_filename>.fields by default)")
|
|
||||||
.hasOptionalArg()
|
|
||||||
.withArgName("FILE")
|
|
||||||
.create("f");
|
|
||||||
|
|
||||||
Option listClassesOption = OptionBuilder.withLongOpt("list-types")
|
|
||||||
.withDescription("Lists all the type references to FILE" +
|
|
||||||
" (<output_dex_filename>.types by default)")
|
|
||||||
.hasOptionalArg()
|
|
||||||
.withArgName("FILE")
|
|
||||||
.create("t");
|
|
||||||
|
|
||||||
Option experimentalOption = OptionBuilder.withLongOpt("experimental")
|
|
||||||
.withDescription("enable experimental opcodes to be assembled, even if they " +
|
|
||||||
" aren't necessarily supported by the Android runtime yet")
|
|
||||||
.create("X");
|
|
||||||
|
|
||||||
Option jobsOption = OptionBuilder.withLongOpt("jobs")
|
|
||||||
.withDescription("The number of threads to use. Defaults to the number of cores available, up to a " +
|
|
||||||
"maximum of 6")
|
|
||||||
.hasArg()
|
|
||||||
.withArgName("NUM_THREADS")
|
|
||||||
.create("j");
|
|
||||||
|
|
||||||
Option verboseErrorsOption = OptionBuilder.withLongOpt("verbose-errors")
|
|
||||||
.withDescription("Generate verbose error messages")
|
|
||||||
.create("V");
|
|
||||||
|
|
||||||
Option printTokensOption = OptionBuilder.withLongOpt("print-tokens")
|
|
||||||
.withDescription("Print the name and text of each token")
|
|
||||||
.create("T");
|
|
||||||
|
|
||||||
basicOptions.addOption(versionOption);
|
|
||||||
basicOptions.addOption(helpOption);
|
|
||||||
basicOptions.addOption(outputOption);
|
|
||||||
basicOptions.addOption(allowOdexOption);
|
|
||||||
basicOptions.addOption(apiLevelOption);
|
|
||||||
basicOptions.addOption(experimentalOption);
|
|
||||||
basicOptions.addOption(jobsOption);
|
|
||||||
basicOptions.addOption(listMethodsOption);
|
|
||||||
basicOptions.addOption(listFieldsOption);
|
|
||||||
basicOptions.addOption(listClassesOption);
|
|
||||||
|
|
||||||
debugOptions.addOption(verboseErrorsOption);
|
|
||||||
debugOptions.addOption(printTokensOption);
|
|
||||||
|
|
||||||
for (Object option: basicOptions.getOptions()) {
|
|
||||||
options.addOption((Option)option);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Object option: debugOptions.getOptions()) {
|
|
||||||
options.addOption((Option)option);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user