Misc baksmali cleanup

This commit is contained in:
Ben Gruver 2013-05-11 13:37:19 -07:00
parent 0e103007d1
commit 4b171afedb
7 changed files with 226 additions and 268 deletions

View File

@ -62,7 +62,7 @@ public class ArrayDataMethodItem extends InstructionMethodItem<ArrayPayload> {
break;
}
for (Number number: instruction.getArrayElements()) {
for (Number number: elements) {
LongRenderer.writeSignedIntOrLongTo(writer, number.longValue());
writer.write(suffix);
writer.write("\n");

View File

@ -176,9 +176,7 @@ public class MethodDefinition {
writer.write('\n');
List<MethodItem> methodItems = getMethodItems();
int size = methodItems.size();
for (int i=0; i<size; i++) {
MethodItem methodItem = methodItems.get(i);
for (MethodItem methodItem: methodItems) {
if (methodItem.writeTo(writer)) {
writer.write('\n');
}

View File

@ -28,14 +28,10 @@
package org.jf.baksmali;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import org.jf.baksmali.Adaptors.ClassDefinition;
import org.jf.dexlib2.analysis.ClassPath;
import org.jf.dexlib2.analysis.CustomInlineMethodResolver;
import org.jf.dexlib2.analysis.InlineMethodResolver;
import org.jf.dexlib2.dexbacked.DexBackedOdexFile;
import org.jf.dexlib2.iface.ClassDef;
import org.jf.dexlib2.iface.DexFile;
import org.jf.dexlib2.util.SyntheticAccessorResolver;
@ -44,57 +40,22 @@ import org.jf.util.IndentingWriter;
import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class baksmali {
public static void disassembleDexFile(String dexFilePath, DexFile dexFile, int apiLevel, boolean deodex,
String outputDirectory, String[] classPathDirs, String bootClassPath,
String extraBootClassPath, boolean noParameterRegisters,
boolean useLocalsDirective, boolean useSequentialLabels,
boolean outputDebugInfo, boolean addCodeOffsets, boolean noAccessorComments,
int registerInfo, boolean ignoreErrors, String inlineTable,
boolean checkPackagePrivateAccess)
{
baksmaliOptions options = new baksmaliOptions();
options.noParameterRegisters = noParameterRegisters;
options.useLocalsDirective = useLocalsDirective;
options.useSequentialLabels = useSequentialLabels;
options.outputDebugInfo = outputDebugInfo;
options.addCodeOffsets = addCodeOffsets;
options.noAccessorComments = noAccessorComments;
options.deodex = deodex;
options.registerInfo = registerInfo;
options.bootClassPath = bootClassPath;
if (registerInfo != 0 || deodex) {
public static void disassembleDexFile(DexFile dexFile, baksmaliOptions options) {
if (options.registerInfo != 0 || options.deodex) {
try {
Iterable<String> extraBootClassPaths = null;
if (extraBootClassPath != null && extraBootClassPath.length() > 0) {
assert extraBootClassPath.charAt(0) == ':';
extraBootClassPaths = Splitter.on(':').split(extraBootClassPath.substring(1));
Iterable<String> extraClassPathEntries;
if (options.extraClassPathEntries != null) {
extraClassPathEntries = options.extraClassPathEntries;
} else {
extraBootClassPaths = ImmutableList.of();
extraClassPathEntries = ImmutableList.of();
}
Iterable<String> bootClassPaths = null;
if (bootClassPath != null) {
bootClassPaths = Splitter.on(':').split(bootClassPath);
} else if (dexFile instanceof DexBackedOdexFile) {
bootClassPaths = ((DexBackedOdexFile)dexFile).getDependencies();
}else {
bootClassPaths = ImmutableList.of();
}
options.classPath = ClassPath.fromClassPath(Arrays.asList(classPathDirs),
Iterables.concat(bootClassPaths, extraBootClassPaths), dexFile, apiLevel);
if (inlineTable != null) {
options.inlineResolver = new CustomInlineMethodResolver(options.classPath, new File(inlineTable));
} else if (dexFile instanceof DexBackedOdexFile) {
options.inlineResolver = InlineMethodResolver.createInlineMethodResolver(((DexBackedOdexFile) dexFile).getOdexVersion());
}
options.classPath = ClassPath.fromClassPath(options.bootClassPathDirs,
Iterables.concat(options.bootClassPathEntries, extraClassPathEntries), dexFile,
options.apiLevel);
} catch (Exception ex) {
System.err.println("\n\nError occured while loading boot class path files. Aborting.");
ex.printStackTrace(System.err);
@ -102,10 +63,10 @@ public class baksmali {
}
}
File outputDirectoryFile = new File(outputDirectory);
File outputDirectoryFile = new File(options.outputDirectory);
if (!outputDirectoryFile.exists()) {
if (!outputDirectoryFile.mkdirs()) {
System.err.println("Can't create the output directory " + outputDirectory);
System.err.println("Can't create the output directory " + options.outputDirectory);
System.exit(1);
}
}
@ -122,13 +83,19 @@ public class baksmali {
});
classDefs = ImmutableList.copyOf(classDefs);
if (!noAccessorComments) {
if (!options.noAccessorComments) {
options.syntheticAccessorResolver = new SyntheticAccessorResolver(classDefs);
}
ClassFileNameHandler fileNameHandler = new ClassFileNameHandler(outputDirectoryFile, ".smali");
for (ClassDef classDef: classDefs) {
disassembleClass(classDef, fileNameHandler, options);
}
}
private static void disassembleClass(ClassDef classDef, ClassFileNameHandler fileNameHandler,
baksmaliOptions options) {
/**
* The path for the disassembly file is based on the package name
* The class descriptor will look something like:
@ -136,14 +103,13 @@ public class baksmali {
* Where the there is leading 'L' and a trailing ';', and the parts of the
* package name are separated by '/'
*/
String classDescriptor = classDef.getType();
//validate that the descriptor is formatted like we expect
if (classDescriptor.charAt(0) != 'L' ||
classDescriptor.charAt(classDescriptor.length()-1) != ';') {
System.err.println("Unrecognized class descriptor - " + classDescriptor + " - skipping class");
continue;
return;
}
File smaliFile = fileNameHandler.getUniqueFilenameForClass(classDescriptor);
@ -159,14 +125,14 @@ public class baksmali {
if (!smaliParent.exists()) {
if (!smaliParent.mkdirs()) {
System.err.println("Unable to create directory " + smaliParent.toString() + " - skipping class");
continue;
return;
}
}
if (!smaliFile.exists()){
if (!smaliFile.createNewFile()) {
System.err.println("Unable to create file " + smaliFile.toString() + " - skipping class");
continue;
return;
}
}
@ -178,6 +144,7 @@ public class baksmali {
} catch (Exception ex) {
System.err.println("\n\nError occured while disassembling class " + classDescriptor.replace('/', '.') + " - skipping class");
ex.printStackTrace();
// noinspection ResultOfMethodCallIgnored
smaliFile.delete();
}
finally
@ -192,15 +159,8 @@ public class baksmali {
}
}
if (!ignoreErrors && classDefinition.hadValidationErrors()) {
if (!options.ignoreErrors && classDefinition.hadValidationErrors()) {
System.exit(1);
}
}
}
private static final Pattern extJarPattern = Pattern.compile("(?:^|\\\\|/)ext.(?:jar|odex)$");
private static boolean isExtJar(String dexFilePath) {
Matcher m = extJarPattern.matcher(dexFilePath);
return m.find();
}
}

View File

@ -31,10 +31,14 @@
package org.jf.baksmali;
import com.google.common.collect.Lists;
import org.jf.dexlib2.analysis.ClassPath;
import org.jf.dexlib2.analysis.InlineMethodResolver;
import org.jf.dexlib2.util.SyntheticAccessorResolver;
import java.util.Arrays;
import java.util.List;
public class baksmaliOptions {
// register info values
public static final int ALL = 1;
@ -45,6 +49,13 @@ public class baksmaliOptions {
public static final int MERGE = 32;
public static final int FULLMERGE = 64;
public int apiLevel = 15;
public String outputDirectory = "out";
public List<String> bootClassPathDirs = Lists.newArrayList();
public List<String> bootClassPathEntries = Lists.newArrayList();
public List<String> extraClassPathEntries = Lists.newArrayList();
public boolean noParameterRegisters = false;
public boolean useLocalsDirective = false;
public boolean useSequentialLabels = false;
@ -52,10 +63,22 @@ public class baksmaliOptions {
public boolean addCodeOffsets = false;
public boolean noAccessorComments = false;
public boolean deodex = false;
public boolean ignoreErrors = false;
public boolean checkPackagePrivateAccess = false;
public InlineMethodResolver inlineResolver = null;
public int registerInfo = 0;
public String bootClassPath;
public ClassPath classPath = null;
public SyntheticAccessorResolver syntheticAccessorResolver = null;
public void setBootClassPath(String bootClassPath) {
bootClassPathEntries = Lists.newArrayList(bootClassPath.split(":"));
}
public void addExtraClassPath(String extraClassPath) {
if (extraClassPath.startsWith(":")) {
extraClassPath = extraClassPath.substring(1);
}
extraClassPathEntries.addAll(Arrays.asList(extraClassPath.split(":")));
}
}

View File

@ -40,9 +40,7 @@ import java.io.IOException;
import java.io.Writer;
public class dump {
public static void dump(DexBackedDexFile dexFile, String dumpFileName, int apiLevel)
throws IOException {
public static void dump(DexBackedDexFile dexFile, String dumpFileName, int apiLevel) throws IOException {
if (dumpFileName != null) {
Writer writer = null;

View File

@ -28,16 +28,19 @@
package org.jf.baksmali;
import com.google.common.collect.Lists;
import org.apache.commons.cli.*;
import org.jf.dexlib2.DexFileFactory;
import org.jf.dexlib2.analysis.CustomInlineMethodResolver;
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
import org.jf.dexlib2.dexbacked.DexBackedOdexFile;
import org.jf.util.ConsoleUtil;
import org.jf.util.SmaliHelpFormatter;
import javax.annotation.Nonnull;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
@ -63,6 +66,7 @@ public class main {
properties.load(templateStream);
version = properties.getProperty("application.version");
} catch (IOException ex) {
// ignore
}
VERSION = version;
}
@ -76,7 +80,7 @@ public class main {
/**
* Run!
*/
public static void main(String[] args) {
public static void main(String[] args) throws IOException {
Locale locale = new Locale("en", "US");
Locale.setDefault(locale);
@ -90,37 +94,18 @@ public class main {
return;
}
baksmaliOptions options = new baksmaliOptions();
boolean disassemble = true;
boolean doDump = false;
boolean noParameterRegisters = false;
boolean useLocalsDirective = false;
boolean useSequentialLabels = false;
boolean outputDebugInfo = true;
boolean addCodeOffsets = false;
boolean noAccessorComments = false;
boolean deodex = false;
boolean ignoreErrors = false;
boolean checkPackagePrivateAccess = false;
int apiLevel = 15;
int registerInfo = 0;
String outputDirectory = "out";
String dumpFileName = null;
String inputDexFileName = null;
String bootClassPath = null;
StringBuffer extraBootClassPathEntries = new StringBuffer();
List<String> bootClassPathDirs = new ArrayList<String>();
bootClassPathDirs.add(".");
String inlineTable = null;
String[] remainingArgs = commandLine.getArgs();
Option[] options = commandLine.getOptions();
Option[] clOptions = commandLine.getOptions();
for (int i=0; i<options.length; i++) {
Option option = options[i];
for (int i=0; i<clOptions.length; i++) {
Option option = clOptions[i];
String opt = option.getOpt();
switch (opt.charAt(0)) {
@ -128,8 +113,8 @@ public class main {
version();
return;
case '?':
while (++i < options.length) {
if (options[i].getOpt().charAt(0) == '?') {
while (++i < clOptions.length) {
if (clOptions[i].getOpt().charAt(0) == '?') {
usage(true);
return;
}
@ -137,28 +122,29 @@ public class main {
usage(false);
return;
case 'o':
outputDirectory = commandLine.getOptionValue("o");
options.outputDirectory = commandLine.getOptionValue("o");
break;
case 'p':
noParameterRegisters = true;
options.noParameterRegisters = true;
break;
case 'l':
useLocalsDirective = true;
options.useLocalsDirective = true;
break;
case 's':
useSequentialLabels = true;
options.useSequentialLabels = true;
break;
case 'b':
outputDebugInfo = false;
options.outputDebugInfo = false;
break;
case 'd':
bootClassPathDirs.add(option.getValue());
options.bootClassPathDirs.add(option.getValue());
break;
case 'f':
addCodeOffsets = true;
options.addCodeOffsets = true;
break;
case 'r':
String[] values = commandLine.getOptionValues('r');
int registerInfo = 0;
if (values == null || values.length == 0) {
registerInfo = baksmaliOptions.ARGS | baksmaliOptions.DEST;
@ -188,39 +174,40 @@ public class main {
registerInfo &= ~baksmaliOptions.MERGE;
}
}
options.registerInfo = registerInfo;
break;
case 'c':
String bcp = commandLine.getOptionValue("c");
if (bcp != null && bcp.charAt(0) == ':') {
extraBootClassPathEntries.append(bcp);
options.addExtraClassPath(bcp);
} else {
bootClassPath = bcp;
options.setBootClassPath(bcp);
}
break;
case 'x':
deodex = true;
options.deodex = true;
break;
case 'm':
noAccessorComments = true;
options.noAccessorComments = true;
break;
case 'a':
apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
options.apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
break;
case 'N':
disassemble = false;
break;
case 'D':
doDump = true;
dumpFileName = commandLine.getOptionValue("D", inputDexFileName + ".dump");
dumpFileName = commandLine.getOptionValue("D");
break;
case 'I':
ignoreErrors = true;
options.ignoreErrors = true;
break;
case 'T':
inlineTable = commandLine.getOptionValue("T");
options.inlineResolver = new CustomInlineMethodResolver(options.classPath, new File(commandLine.getOptionValue("T")));
break;
case 'K':
checkPackagePrivateAccess = true;
options.checkPackagePrivateAccess = true;
break;
default:
assert false;
@ -232,9 +219,8 @@ public class main {
return;
}
inputDexFileName = remainingArgs[0];
String inputDexFileName = remainingArgs[0];
try {
File dexFileFile = new File(inputDexFileName);
if (!dexFileFile.exists()) {
System.err.println("Can't find the file " + inputDexFileName);
@ -242,50 +228,36 @@ public class main {
}
//Read in and parse the dex file
DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, apiLevel);
DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, options.apiLevel);
if (dexFile.isOdexFile()) {
if (!deodex) {
if (!options.deodex) {
System.err.println("Warning: You are disassembling an odex file without deodexing it. You");
System.err.println("won't be able to re-assemble the results unless you deodex it with the -x");
System.err.println("option");
}
} else {
deodex = false;
options.deodex = false;
}
if (bootClassPath == null) {
bootClassPath = "core.jar:ext.jar:framework.jar:android.policy.jar:services.jar";
if ((options.deodex || options.registerInfo != 0) && options.bootClassPathEntries == null) {
if (dexFile instanceof DexBackedOdexFile) {
((DexBackedOdexFile)dexFile).getDependencies();
} else {
options.bootClassPathEntries = getDefaultBootClassPathForApi(options.apiLevel);
}
}
if (disassemble) {
String[] bootClassPathDirsArray = new String[bootClassPathDirs.size()];
for (int i=0; i<bootClassPathDirsArray.length; i++) {
bootClassPathDirsArray[i] = bootClassPathDirs.get(i);
}
baksmali.disassembleDexFile(dexFileFile.getPath(), dexFile, apiLevel, deodex, outputDirectory,
bootClassPathDirsArray, bootClassPath, extraBootClassPathEntries.toString(),
noParameterRegisters, useLocalsDirective, useSequentialLabels, outputDebugInfo, addCodeOffsets,
noAccessorComments, registerInfo, ignoreErrors, inlineTable, checkPackagePrivateAccess);
baksmali.disassembleDexFile(dexFile, options);
}
if (doDump) {
try {
dump.dump(dexFile, dumpFileName, apiLevel);
}catch (IOException ex) {
System.err.println("Error occured while writing dump file");
ex.printStackTrace();
if (dumpFileName == null) {
dumpFileName = commandLine.getOptionValue(inputDexFileName + ".dump");
}
}
} catch (RuntimeException ex) {
System.err.println("\n\nUNEXPECTED TOP-LEVEL EXCEPTION:");
ex.printStackTrace();
System.exit(1);
} catch (Throwable ex) {
System.err.println("\n\nUNEXPECTED TOP-LEVEL ERROR:");
ex.printStackTrace();
System.exit(1);
dump.dump(dexFile, dumpFileName, options.apiLevel);
}
}
@ -319,6 +291,7 @@ public class main {
System.exit(0);
}
@SuppressWarnings("AccessStaticViaInstance")
private static void buildOptions() {
Option versionOption = OptionBuilder.withLongOpt("version")
.withDescription("prints the version then exits")
@ -459,4 +432,60 @@ public class main {
options.addOption((Option)option);
}
}
@Nonnull
private static List<String> getDefaultBootClassPathForApi(int apiLevel) {
if (apiLevel < 9) {
return Lists.newArrayList(
"/system/framework/core.jar",
"/system/framework/ext.jar",
"/system/framework/framework.jar",
"/system/framework/android.policy.jar",
"/system/framework/services.jar");
} else if (apiLevel < 12) {
return Lists.newArrayList(
"/system/framework/core.jar",
"/system/framework/bouncycastle.jar",
"/system/framework/ext.jar",
"/system/framework/framework.jar",
"/system/framework/android.policy.jar",
"/system/framework/services.jar",
"/system/framework/core-junit.jar");
} else if (apiLevel < 14) {
return Lists.newArrayList(
"/system/framework/core.jar",
"/system/framework/apache-xml.jar",
"/system/framework/bouncycastle.jar",
"/system/framework/ext.jar",
"/system/framework/framework.jar",
"/system/framework/android.policy.jar",
"/system/framework/services.jar",
"/system/framework/core-junit.jar");
} else if (apiLevel < 16) {
return Lists.newArrayList(
"/system/framework/core.jar",
"/system/framework/core-junit.jar",
"/system/framework/bouncycastle.jar",
"/system/framework/ext.jar",
"/system/framework/framework.jar",
"/system/framework/android.policy.jar",
"/system/framework/services.jar",
"/system/framework/apache-xml.jar",
"/system/framework/filterfw.jar");
} else {
// this is correct as of api 17/4.2.2
return Lists.newArrayList(
"/system/framework/core.jar",
"/system/framework/core-junit.jar",
"/system/framework/bouncycastle.jar",
"/system/framework/ext.jar",
"/system/framework/framework.jar",
"/system/framework/telephony-common.jar",
"/system/framework/mms-common.jar",
"/system/framework/android.policy.jar",
"/system/framework/services.jar",
"/system/framework/apache-xml.jar");
}
}
}

View File

@ -1,50 +0,0 @@
/*
* Copyright 2013, 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.baksmali;
import org.jf.dexlib2.dexbacked.BaseDexBuffer;
public class temp {
public static void main(String[] args) {
BaseDexBuffer bdb = new BaseDexBuffer(new byte[]{1,2,3,4,5,6,7,8});
long start = System.nanoTime();
for (long i=0; i<10000000000l; i++) {
bdb.readLong(0);
}
long end = System.nanoTime();
System.out.println((end-start)/1E9d);
}
}