mirror of
https://github.com/revanced/smali.git
synced 2025-05-28 11:50:12 +02:00
Add more programmatic-friendly entry points for smali/baksmali
This adds entry points that are more friendly to programmatic usage. E.g. no calls to System.exit()
This commit is contained in:
parent
a198b46e20
commit
87d10dac27
@ -82,6 +82,9 @@ public class baksmaliOptions {
|
||||
public int registerInfo = 0;
|
||||
public ClassPath classPath = null;
|
||||
public int jobs = Runtime.getRuntime().availableProcessors();
|
||||
public boolean disassemble = true;
|
||||
public boolean dump = false;
|
||||
public String dumpFileName = null;
|
||||
|
||||
public SyntheticAccessorResolver syntheticAccessorResolver = null;
|
||||
|
||||
|
@ -40,7 +40,7 @@ import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
public class dump {
|
||||
public static void dump(DexBackedDexFile dexFile, String dumpFileName, int apiLevel, boolean experimental) throws IOException {
|
||||
public static void dump(DexBackedDexFile dexFile, String dumpFileName, int apiLevel) throws IOException {
|
||||
if (dumpFileName != null) {
|
||||
Writer writer = null;
|
||||
|
||||
|
@ -36,6 +36,7 @@ import org.jf.dexlib2.analysis.InlineMethodResolver;
|
||||
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
|
||||
import org.jf.dexlib2.dexbacked.DexBackedOdexFile;
|
||||
import org.jf.dexlib2.dexbacked.OatFile.OatDexFile;
|
||||
import org.jf.dexlib2.iface.DexFile;
|
||||
import org.jf.util.ConsoleUtil;
|
||||
import org.jf.util.SmaliHelpFormatter;
|
||||
|
||||
@ -83,6 +84,45 @@ public class main {
|
||||
private main() {
|
||||
}
|
||||
|
||||
/**
|
||||
* A more programmatic-friendly entry point for baksmali
|
||||
*
|
||||
* @param options a baksmaliOptions object with the options to run baksmali with
|
||||
* @param inputDexFile The DexFile to disassemble
|
||||
* @return true if disassembly completed with no errors, or false if errors were encountered
|
||||
*/
|
||||
public static boolean run(@Nonnull baksmaliOptions options, @Nonnull DexFile inputDexFile) throws IOException {
|
||||
if (options.bootClassPathEntries.isEmpty() &&
|
||||
(options.deodex || options.registerInfo != 0 || options.normalizeVirtualMethods)) {
|
||||
if (inputDexFile instanceof DexBackedOdexFile) {
|
||||
options.bootClassPathEntries = ((DexBackedOdexFile)inputDexFile).getDependencies();
|
||||
} else {
|
||||
options.bootClassPathEntries = getDefaultBootClassPathForApi(options.apiLevel,
|
||||
options.experimental);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.customInlineDefinitions == null && inputDexFile instanceof DexBackedOdexFile) {
|
||||
options.inlineResolver =
|
||||
InlineMethodResolver.createInlineMethodResolver(
|
||||
((DexBackedOdexFile)inputDexFile).getOdexVersion());
|
||||
}
|
||||
|
||||
boolean errorOccurred = false;
|
||||
if (options.disassemble) {
|
||||
errorOccurred = !baksmali.disassembleDexFile(inputDexFile, options);
|
||||
}
|
||||
|
||||
if (options.dump) {
|
||||
if (!(inputDexFile instanceof DexBackedDexFile)) {
|
||||
throw new IllegalArgumentException("Annotated hex-dumps require a DexBackedDexFile");
|
||||
}
|
||||
dump.dump((DexBackedDexFile)inputDexFile, options.dumpFileName, options.apiLevel);
|
||||
}
|
||||
|
||||
return !errorOccurred;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run!
|
||||
*/
|
||||
@ -102,11 +142,6 @@ public class main {
|
||||
|
||||
baksmaliOptions options = new baksmaliOptions();
|
||||
|
||||
boolean disassemble = true;
|
||||
boolean doDump = false;
|
||||
String dumpFileName = null;
|
||||
boolean setBootClassPath = false;
|
||||
|
||||
String[] remainingArgs = commandLine.getArgs();
|
||||
Option[] clOptions = commandLine.getOptions();
|
||||
|
||||
@ -187,7 +222,6 @@ public class main {
|
||||
if (bcp != null && bcp.charAt(0) == ':') {
|
||||
options.addExtraClassPath(bcp);
|
||||
} else {
|
||||
setBootClassPath = true;
|
||||
options.setBootClassPath(bcp);
|
||||
}
|
||||
break;
|
||||
@ -223,11 +257,11 @@ public class main {
|
||||
options.normalizeVirtualMethods = true;
|
||||
break;
|
||||
case 'N':
|
||||
disassemble = false;
|
||||
options.disassemble = false;
|
||||
break;
|
||||
case 'D':
|
||||
doDump = true;
|
||||
dumpFileName = commandLine.getOptionValue("D");
|
||||
options.dump = true;
|
||||
options.dumpFileName = commandLine.getOptionValue("D");
|
||||
break;
|
||||
case 'I':
|
||||
options.ignoreErrors = true;
|
||||
@ -245,11 +279,10 @@ public class main {
|
||||
return;
|
||||
}
|
||||
|
||||
String inputDexFileName = remainingArgs[0];
|
||||
|
||||
File dexFileFile = new File(inputDexFileName);
|
||||
String inputDexPath = remainingArgs[0];
|
||||
File dexFileFile = new File(inputDexPath);
|
||||
if (!dexFileFile.exists()) {
|
||||
System.err.println("Can't find the file " + inputDexFileName);
|
||||
System.err.println("Can't find the file " + inputDexPath);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
@ -261,6 +294,7 @@ public class main {
|
||||
System.err.println(String.format("%s contains multiple dex files. You must specify which one to " +
|
||||
"disassemble with the -e option", dexFileFile.getName()));
|
||||
System.err.println("Valid entries include:");
|
||||
|
||||
for (OatDexFile oatDexFile: ex.oatFile.getDexFiles()) {
|
||||
System.err.println(oatDexFile.filename);
|
||||
}
|
||||
@ -278,34 +312,18 @@ public class main {
|
||||
options.deodex = false;
|
||||
}
|
||||
|
||||
if (!setBootClassPath && (options.deodex || options.registerInfo != 0 || options.normalizeVirtualMethods)) {
|
||||
if (dexFile instanceof DexBackedOdexFile) {
|
||||
options.bootClassPathEntries = ((DexBackedOdexFile)dexFile).getDependencies();
|
||||
} else {
|
||||
options.bootClassPathEntries = getDefaultBootClassPathForApi(options.apiLevel,
|
||||
options.experimental);
|
||||
if (options.dump) {
|
||||
if (options.dumpFileName == null) {
|
||||
options.dumpFileName = inputDexPath + ".dump";
|
||||
}
|
||||
}
|
||||
|
||||
if (options.customInlineDefinitions == null && dexFile instanceof DexBackedOdexFile) {
|
||||
options.inlineResolver =
|
||||
InlineMethodResolver.createInlineMethodResolver(
|
||||
((DexBackedOdexFile)dexFile).getOdexVersion());
|
||||
}
|
||||
|
||||
boolean errorOccurred = false;
|
||||
if (disassemble) {
|
||||
errorOccurred = !baksmali.disassembleDexFile(dexFile, options);
|
||||
}
|
||||
|
||||
if (doDump) {
|
||||
if (dumpFileName == null) {
|
||||
dumpFileName = commandLine.getOptionValue(inputDexFileName + ".dump");
|
||||
try {
|
||||
if (!run(options, dexFile)) {
|
||||
System.exit(1);
|
||||
}
|
||||
dump.dump(dexFile, dumpFileName, options.apiLevel, options.experimental);
|
||||
}
|
||||
|
||||
if (errorOccurred) {
|
||||
} catch (IllegalArgumentException ex) {
|
||||
System.err.println(ex.getMessage());
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
52
smali/src/main/java/org/jf/smali/SmaliOptions.java
Normal file
52
smali/src/main/java/org/jf/smali/SmaliOptions.java
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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 class SmaliOptions {
|
||||
public int apiLevel = 15;
|
||||
public String outputDexFile = "out.dex";
|
||||
|
||||
public int jobs = Runtime.getRuntime().availableProcessors();
|
||||
public boolean allowOdex = 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;
|
||||
}
|
@ -46,10 +46,7 @@ import org.jf.util.SmaliHelpFormatter;
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* Main class for smali. It recognizes enough options to be able to dispatch
|
||||
@ -92,6 +89,95 @@ public class main {
|
||||
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!
|
||||
*/
|
||||
@ -109,24 +195,7 @@ public class main {
|
||||
return;
|
||||
}
|
||||
|
||||
int jobs = Runtime.getRuntime().availableProcessors();
|
||||
boolean allowOdex = false;
|
||||
boolean verboseErrors = false;
|
||||
boolean printTokens = false;
|
||||
boolean experimental = false;
|
||||
|
||||
boolean listMethods = false;
|
||||
String methodListFilename = null;
|
||||
|
||||
boolean listFields = false;
|
||||
String fieldListFilename = null;
|
||||
|
||||
boolean listTypes = false;
|
||||
String typeListFilename = null;
|
||||
|
||||
int apiLevel = 15;
|
||||
|
||||
String outputDexFile = "out.dex";
|
||||
SmaliOptions smaliOptions = new SmaliOptions();
|
||||
|
||||
String[] remainingArgs = commandLine.getArgs();
|
||||
|
||||
@ -150,37 +219,37 @@ public class main {
|
||||
usage(false);
|
||||
return;
|
||||
case 'o':
|
||||
outputDexFile = commandLine.getOptionValue("o");
|
||||
smaliOptions.outputDexFile = commandLine.getOptionValue("o");
|
||||
break;
|
||||
case 'x':
|
||||
allowOdex = true;
|
||||
smaliOptions.allowOdex = true;
|
||||
break;
|
||||
case 'X':
|
||||
experimental = true;
|
||||
smaliOptions.experimental = true;
|
||||
break;
|
||||
case 'a':
|
||||
apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
|
||||
smaliOptions.apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
|
||||
break;
|
||||
case 'j':
|
||||
jobs = Integer.parseInt(commandLine.getOptionValue("j"));
|
||||
smaliOptions.jobs = Integer.parseInt(commandLine.getOptionValue("j"));
|
||||
break;
|
||||
case 'm':
|
||||
listMethods = true;
|
||||
methodListFilename = commandLine.getOptionValue("m");
|
||||
smaliOptions.listMethods = true;
|
||||
smaliOptions.methodListFilename = commandLine.getOptionValue("m");
|
||||
break;
|
||||
case 'f':
|
||||
listFields = true;
|
||||
fieldListFilename = commandLine.getOptionValue("f");
|
||||
smaliOptions.listFields = true;
|
||||
smaliOptions.fieldListFilename = commandLine.getOptionValue("f");
|
||||
break;
|
||||
case 't':
|
||||
listTypes = true;
|
||||
typeListFilename = commandLine.getOptionValue("t");
|
||||
smaliOptions.listTypes = true;
|
||||
smaliOptions.typeListFilename = commandLine.getOptionValue("t");
|
||||
break;
|
||||
case 'V':
|
||||
verboseErrors = true;
|
||||
smaliOptions.verboseErrors = true;
|
||||
break;
|
||||
case 'T':
|
||||
printTokens = true;
|
||||
smaliOptions.printTokens = true;
|
||||
break;
|
||||
default:
|
||||
assert false;
|
||||
@ -193,84 +262,9 @@ public class main {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
boolean errors = false;
|
||||
|
||||
final DexBuilder dexBuilder = DexBuilder.makeDexBuilder(Opcodes.forApi(apiLevel, experimental));
|
||||
|
||||
ExecutorService executor = Executors.newFixedThreadPool(jobs);
|
||||
List<Future<Boolean>> tasks = Lists.newArrayList();
|
||||
|
||||
final boolean finalVerboseErrors = verboseErrors;
|
||||
final boolean finalPrintTokens = printTokens;
|
||||
final boolean finalAllowOdex = allowOdex;
|
||||
final int finalApiLevel = apiLevel;
|
||||
final boolean finalExperimental = experimental;
|
||||
for (final File file: filesToProcess) {
|
||||
tasks.add(executor.submit(new Callable<Boolean>() {
|
||||
@Override public Boolean call() throws Exception {
|
||||
return assembleSmaliFile(file, dexBuilder, finalVerboseErrors, finalPrintTokens,
|
||||
finalAllowOdex, finalApiLevel, finalExperimental);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
for (Future<Boolean> task: tasks) {
|
||||
while(true) {
|
||||
try {
|
||||
if (!task.get()) {
|
||||
errors = true;
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
executor.shutdown();
|
||||
|
||||
if (errors) {
|
||||
if (!run(smaliOptions, remainingArgs)) {
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
if (listMethods) {
|
||||
if (Strings.isNullOrEmpty(methodListFilename)) {
|
||||
methodListFilename = outputDexFile + ".methods";
|
||||
}
|
||||
writeReferences(dexBuilder.getMethodReferences(), methodListFilename);
|
||||
}
|
||||
|
||||
if (listFields) {
|
||||
if (Strings.isNullOrEmpty(fieldListFilename)) {
|
||||
fieldListFilename = outputDexFile + ".fields";
|
||||
}
|
||||
writeReferences(dexBuilder.getFieldReferences(), fieldListFilename);
|
||||
}
|
||||
|
||||
if (listTypes) {
|
||||
if (Strings.isNullOrEmpty(typeListFilename)) {
|
||||
typeListFilename = outputDexFile + ".types";
|
||||
}
|
||||
writeReferences(dexBuilder.getTypeReferences(), typeListFilename);
|
||||
}
|
||||
|
||||
dexBuilder.writeTo(new FileDataStore(new File(outputDexFile)));
|
||||
} catch (RuntimeException ex) {
|
||||
System.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:");
|
||||
ex.printStackTrace();
|
||||
@ -312,9 +306,7 @@ public class main {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean assembleSmaliFile(File smaliFile, DexBuilder dexBuilder, boolean verboseErrors,
|
||||
boolean printTokens, boolean allowOdex, int apiLevel,
|
||||
boolean experimental)
|
||||
private static boolean assembleSmaliFile(File smaliFile, DexBuilder dexBuilder, SmaliOptions options)
|
||||
throws Exception {
|
||||
CommonTokenStream tokens;
|
||||
|
||||
@ -327,7 +319,7 @@ public class main {
|
||||
((smaliFlexLexer)lexer).setSourceFile(smaliFile);
|
||||
tokens = new CommonTokenStream((TokenSource)lexer);
|
||||
|
||||
if (printTokens) {
|
||||
if (options.printTokens) {
|
||||
tokens.getTokens();
|
||||
|
||||
for (int i=0; i<tokens.size(); i++) {
|
||||
@ -343,9 +335,9 @@ public class main {
|
||||
}
|
||||
|
||||
smaliParser parser = new smaliParser(tokens);
|
||||
parser.setVerboseErrors(verboseErrors);
|
||||
parser.setAllowOdex(allowOdex);
|
||||
parser.setApiLevel(apiLevel, experimental);
|
||||
parser.setVerboseErrors(options.verboseErrors);
|
||||
parser.setAllowOdex(options.allowOdex);
|
||||
parser.setApiLevel(options.apiLevel, options.experimental);
|
||||
|
||||
smaliParser.smali_file_return result = parser.smali_file();
|
||||
|
||||
@ -358,14 +350,14 @@ public class main {
|
||||
CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t);
|
||||
treeStream.setTokenStream(tokens);
|
||||
|
||||
if (printTokens) {
|
||||
if (options.printTokens) {
|
||||
System.out.println(t.toStringTree());
|
||||
}
|
||||
|
||||
smaliTreeWalker dexGen = new smaliTreeWalker(treeStream);
|
||||
dexGen.setApiLevel(apiLevel, experimental);
|
||||
dexGen.setApiLevel(options.apiLevel, options.experimental);
|
||||
|
||||
dexGen.setVerboseErrors(verboseErrors);
|
||||
dexGen.setVerboseErrors(options.verboseErrors);
|
||||
dexGen.setDexBuilder(dexBuilder);
|
||||
dexGen.smali_file();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user