diff --git a/baksmali/src/main/java/org/jf/baksmali/DumpCommand.java b/baksmali/src/main/java/org/jf/baksmali/DumpCommand.java index 92a4510f..e1d9da24 100644 --- a/baksmali/src/main/java/org/jf/baksmali/DumpCommand.java +++ b/baksmali/src/main/java/org/jf/baksmali/DumpCommand.java @@ -53,21 +53,21 @@ public class DumpCommand implements Command { @Parameter(names = {"-h", "-?", "--help"}, help = true, description = "Show usage information for this command.") - public boolean help; + private boolean help; @Parameter(names = {"-a", "--api"}, description = "The numeric api level of the file being disassembled.") - public int apiLevel = 15; + private int apiLevel = 15; @Parameter(names = "--experimental", description = "Enable experimental opcodes to be disassembled, even if they aren't necessarily " + "supported in the Android runtime yet.") - public boolean experimentalOpcodes = false; + private boolean experimentalOpcodes = false; @Parameter(description = " - 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 " + "colon. E.g. \"something.apk:classes2.dex\"") - public String input; + private String input; public DumpCommand(@Nonnull JCommander jc) { this.jc = jc; diff --git a/dexlib2/build.gradle b/dexlib2/build.gradle index 8fbe5ffe..952d0100 100644 --- a/dexlib2/build.gradle +++ b/dexlib2/build.gradle @@ -49,6 +49,7 @@ dependencies { compile project(':util') compile depends.findbugs compile depends.guava + compile depends.commons_cli testCompile depends.junit diff --git a/smali/build.gradle b/smali/build.gradle index 75001d73..857812a1 100644 --- a/smali/build.gradle +++ b/smali/build.gradle @@ -80,8 +80,8 @@ dependencies { compile project(':util') compile project(':dexlib2') compile depends.antlr_runtime + compile depends.jcommander compile depends.stringtemplate - compile depends.commons_cli testCompile depends.junit @@ -100,7 +100,7 @@ task fatJar(type: Jar, dependsOn: jar) { classifier = 'fat' manifest { - attributes('Main-Class': 'org.jf.smali.main') + attributes('Main-Class': 'org.jf.smali.Main') } doLast { diff --git a/smali/src/main/java/org/jf/smali/AssembleCommand.java b/smali/src/main/java/org/jf/smali/AssembleCommand.java new file mode 100644 index 00000000..e3cf4a13 --- /dev/null +++ b/smali/src/main/java/org/jf/smali/AssembleCommand.java @@ -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 = "[|]+ - Assembles the given files. If a directory is specified, it will be " + + "recursively searched for any file with a .smali prefix") + private List 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; + } +} diff --git a/smali/src/main/java/org/jf/smali/Command.java b/smali/src/main/java/org/jf/smali/Command.java new file mode 100644 index 00000000..c3af4d16 --- /dev/null +++ b/smali/src/main/java/org/jf/smali/Command.java @@ -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(); +} \ No newline at end of file diff --git a/smali/src/main/java/org/jf/smali/HelpCommand.java b/smali/src/main/java/org/jf/smali/HelpCommand.java new file mode 100644 index 00000000..28085a3c --- /dev/null +++ b/smali/src/main/java/org/jf/smali/HelpCommand.java @@ -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 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); + } + } +} diff --git a/smali/src/main/java/org/jf/smali/Main.java b/smali/src/main/java/org/jf/smali/Main.java new file mode 100644 index 00000000..63639781 --- /dev/null +++ b/smali/src/main/java/org/jf/smali/Main.java @@ -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; + } +} diff --git a/smali/src/main/java/org/jf/smali/Smali.java b/smali/src/main/java/org/jf/smali/Smali.java new file mode 100644 index 00000000..178a3c52 --- /dev/null +++ b/smali/src/main/java/org/jf/smali/Smali.java @@ -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 input) throws IOException { + LinkedHashSet filesToProcessSet = new LinkedHashSet(); + + 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> tasks = Lists.newArrayList(); + + for (final File file: filesToProcessSet) { + tasks.add(executor.submit(new Callable() { + @Override public Boolean call() throws Exception { + return assembleSmaliFile(file, dexBuilder, options); + } + })); + } + + for (Future 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 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 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; + } +} diff --git a/smali/src/main/java/org/jf/smali/SmaliOptions.java b/smali/src/main/java/org/jf/smali/SmaliOptions.java index 165c3a89..91fadf10 100644 --- a/smali/src/main/java/org/jf/smali/SmaliOptions.java +++ b/smali/src/main/java/org/jf/smali/SmaliOptions.java @@ -36,17 +36,8 @@ public class SmaliOptions { public String outputDexFile = "out.dex"; public int jobs = Runtime.getRuntime().availableProcessors(); - public boolean allowOdex = false; + public boolean allowOdexOpcodes = false; public boolean verboseErrors = false; public boolean printTokens = false; - public boolean experimental = 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; + public boolean experimentalOpcodes = false; } diff --git a/smali/src/main/java/org/jf/smali/main.java b/smali/src/main/java/org/jf/smali/main.java deleted file mode 100644 index e5562808..00000000 --- a/smali/src/main/java/org/jf/smali/main.java +++ /dev/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 filesToProcessSet = new LinkedHashSet(); - - 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> tasks = Lists.newArrayList(); - - for (final File file: filesToProcessSet) { - tasks.add(executor.submit(new Callable() { - @Override public Boolean call() throws Exception { - return assembleSmaliFile(file, dexBuilder, options); - } - })); - } - - for (Future 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 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 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 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] [--] [|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" + - " (.methods by default)") - .hasOptionalArg() - .withArgName("FILE") - .create("m"); - - Option listFieldsOption = OptionBuilder.withLongOpt("list-fields") - .withDescription("Lists all the field references to FILE" + - " (.fields by default)") - .hasOptionalArg() - .withArgName("FILE") - .create("f"); - - Option listClassesOption = OptionBuilder.withLongOpt("list-types") - .withDescription("Lists all the type references to FILE" + - " (.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); - } - } -} \ No newline at end of file