mirror of
https://github.com/revanced/smali.git
synced 2025-05-02 23:54:38 +02:00
Add "list field offsets" and "list vtables" commands to baksmali
This is a reimplementation of the "DumpFields" and "DumpVtables" entry points that were previously in dexlib2
This commit is contained in:
parent
75cf7e4c64
commit
bccdc809fa
@ -59,6 +59,8 @@ public class ListCommand implements Command {
|
|||||||
subJc.addCommand("types", new ListTypesCommand(subJc), "type", "t");
|
subJc.addCommand("types", new ListTypesCommand(subJc), "type", "t");
|
||||||
subJc.addCommand("classes", new ListClassesCommand(subJc), "class", "c");
|
subJc.addCommand("classes", new ListClassesCommand(subJc), "class", "c");
|
||||||
subJc.addCommand("dex", new ListDexCommand(subJc), "d");
|
subJc.addCommand("dex", new ListDexCommand(subJc), "d");
|
||||||
|
subJc.addCommand("vtables", new ListVtablesCommand(subJc), "vtable", "v");
|
||||||
|
subJc.addCommand("fieldoffsets", new ListFieldOffsetsCommand(subJc), "fieldoffset", "fo");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void run() {
|
@Override public void run() {
|
||||||
|
@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
* 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.baksmali;
|
||||||
|
|
||||||
|
import com.beust.jcommander.JCommander;
|
||||||
|
import com.beust.jcommander.Parameter;
|
||||||
|
import com.beust.jcommander.Parameters;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import org.jf.dexlib2.analysis.ClassPath;
|
||||||
|
import org.jf.dexlib2.analysis.ClassProto;
|
||||||
|
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
|
||||||
|
import org.jf.dexlib2.iface.ClassDef;
|
||||||
|
import org.jf.dexlib2.iface.DexFile;
|
||||||
|
import org.jf.dexlib2.iface.reference.FieldReference;
|
||||||
|
import org.jf.util.SparseArray;
|
||||||
|
import org.jf.util.jcommander.CommaColonParameterSplitter;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Parameters(commandDescription = "Lists the instance field offsets for classes in a dex file.")
|
||||||
|
public class ListFieldOffsetsCommand extends DexInputCommand {
|
||||||
|
|
||||||
|
@Nonnull private final JCommander jc;
|
||||||
|
|
||||||
|
@Parameter(names = {"-h", "-?", "--help"}, help = true,
|
||||||
|
description = "Show usage information")
|
||||||
|
private boolean help;
|
||||||
|
|
||||||
|
@Parameter(names = {"-a", "--api"},
|
||||||
|
description = "The numeric api level of the file being loaded.")
|
||||||
|
private int apiLevel = 15;
|
||||||
|
|
||||||
|
@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 " +
|
||||||
|
"colon. E.g. \"something.apk:classes2.dex\"")
|
||||||
|
private List<String> inputList = Lists.newArrayList();
|
||||||
|
|
||||||
|
@Parameter(names = {"-b", "--bootclasspath"},
|
||||||
|
description = "A comma/colon separated list of the bootclasspath jar/oat files to include in the " +
|
||||||
|
"classpath when analyzing the dex file. This will override any automatic selection of " +
|
||||||
|
"bootclasspath files that baksmali would otherwise perform. This is analogous to Android's " +
|
||||||
|
"BOOTCLASSPATH environment variable.",
|
||||||
|
splitter = CommaColonParameterSplitter.class)
|
||||||
|
private List<String> bootClassPath = new ArrayList<String>();
|
||||||
|
|
||||||
|
@Parameter(names = {"-c", "--classpath"},
|
||||||
|
description = "A comma/colon separated list of additional jar/oat files to include in the classpath " +
|
||||||
|
"when analyzing the dex file. These will be added to the classpath after any bootclasspath " +
|
||||||
|
"entries.",
|
||||||
|
splitter = CommaColonParameterSplitter.class)
|
||||||
|
private List<String> classPath = new ArrayList<String>();
|
||||||
|
|
||||||
|
@Parameter(names = {"-d", "--classpath-dir"},
|
||||||
|
description = "baksmali will search these directories in order for any classpath entries.")
|
||||||
|
private List<String> classPathDirectories = Lists.newArrayList(".");
|
||||||
|
|
||||||
|
@Parameter(names = "--check-package-private-access",
|
||||||
|
description = "Use the package-private access check when calculating vtable indexes. This should " +
|
||||||
|
"only be needed for 4.2.0 odexes. It was reverted in 4.2.1.")
|
||||||
|
private boolean checkPackagePrivateAccess = false;
|
||||||
|
|
||||||
|
@Parameter(names = "--experimental",
|
||||||
|
description = "Enable experimental opcodes to be disassembled, even if they aren't necessarily " +
|
||||||
|
"supported in the Android runtime yet.")
|
||||||
|
private boolean experimentalOpcodes = false;
|
||||||
|
|
||||||
|
public ListFieldOffsetsCommand(@Nonnull JCommander jc) {
|
||||||
|
this.jc = jc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void run() {
|
||||||
|
if (help || inputList == null || inputList.isEmpty()) {
|
||||||
|
jc.usage(jc.getParsedCommand());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputList.size() > 1) {
|
||||||
|
System.err.println("Too many files specified");
|
||||||
|
jc.usage(jc.getParsedCommand());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String input = inputList.get(0);
|
||||||
|
DexBackedDexFile dexFile = loadDexFile(input, 15, false);
|
||||||
|
BaksmaliOptions options = getOptions(dexFile);
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (ClassDef classDef: dexFile.getClasses()) {
|
||||||
|
ClassProto classProto = (ClassProto) options.classPath.getClass(classDef);
|
||||||
|
SparseArray<FieldReference> fields = classProto.getInstanceFields();
|
||||||
|
String className = "Class " + classDef.getType() + " : " + fields.size() + " instance fields\n";
|
||||||
|
System.out.write(className.getBytes());
|
||||||
|
for (int i=0;i<fields.size();i++) {
|
||||||
|
String field = fields.keyAt(i) + ":" + fields.valueAt(i).getType() + " " + fields.valueAt(i).getName() + "\n";
|
||||||
|
System.out.write(field.getBytes());
|
||||||
|
}
|
||||||
|
System.out.write("\n".getBytes());
|
||||||
|
}
|
||||||
|
System.out.close();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private BaksmaliOptions getOptions(DexFile dexFile) {
|
||||||
|
final BaksmaliOptions options = new BaksmaliOptions();
|
||||||
|
|
||||||
|
options.apiLevel = apiLevel;
|
||||||
|
|
||||||
|
try {
|
||||||
|
options.classPath = ClassPath.fromClassPath(classPathDirectories,
|
||||||
|
Iterables.concat(bootClassPath, classPath), dexFile, apiLevel,
|
||||||
|
checkPackagePrivateAccess, experimentalOpcodes);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
System.err.println("Error occurred while loading class path files.");
|
||||||
|
ex.printStackTrace(System.err);
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
options.experimentalOpcodes = experimentalOpcodes;
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
}
|
165
baksmali/src/main/java/org/jf/baksmali/ListVtablesCommand.java
Normal file
165
baksmali/src/main/java/org/jf/baksmali/ListVtablesCommand.java
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
* 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.baksmali;
|
||||||
|
|
||||||
|
import com.beust.jcommander.JCommander;
|
||||||
|
import com.beust.jcommander.Parameter;
|
||||||
|
import com.beust.jcommander.Parameters;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import org.jf.dexlib2.analysis.ClassPath;
|
||||||
|
import org.jf.dexlib2.analysis.ClassProto;
|
||||||
|
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
|
||||||
|
import org.jf.dexlib2.iface.ClassDef;
|
||||||
|
import org.jf.dexlib2.iface.DexFile;
|
||||||
|
import org.jf.dexlib2.iface.Method;
|
||||||
|
import org.jf.util.jcommander.CommaColonParameterSplitter;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Parameters(commandDescription = "Lists the virtual method tables for classes in a dex file.")
|
||||||
|
public class ListVtablesCommand extends DexInputCommand {
|
||||||
|
|
||||||
|
@Nonnull private final JCommander jc;
|
||||||
|
|
||||||
|
@Parameter(names = {"-h", "-?", "--help"}, help = true,
|
||||||
|
description = "Show usage information")
|
||||||
|
private boolean help;
|
||||||
|
|
||||||
|
@Parameter(names = {"-a", "--api"},
|
||||||
|
description = "The numeric api level of the file being loaded.")
|
||||||
|
public int apiLevel = 15;
|
||||||
|
|
||||||
|
@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 " +
|
||||||
|
"colon. E.g. \"something.apk:classes2.dex\"")
|
||||||
|
private List<String> inputList = Lists.newArrayList();
|
||||||
|
|
||||||
|
@Parameter(names = {"-b", "--bootclasspath"},
|
||||||
|
description = "A comma/colon separated list of the bootclasspath jar/oat files to include in the " +
|
||||||
|
"classpath when analyzing the dex file. This will override any automatic selection of " +
|
||||||
|
"bootclasspath files that baksmali would otherwise perform. This is analogous to Android's " +
|
||||||
|
"BOOTCLASSPATH environment variable.",
|
||||||
|
splitter = CommaColonParameterSplitter.class)
|
||||||
|
private List<String> bootClassPath = new ArrayList<String>();
|
||||||
|
|
||||||
|
@Parameter(names = {"-c", "--classpath"},
|
||||||
|
description = "A comma/colon separated list of additional jar/oat files to include in the classpath " +
|
||||||
|
"when analyzing the dex file. These will be added to the classpath after any bootclasspath " +
|
||||||
|
"entries.",
|
||||||
|
splitter = CommaColonParameterSplitter.class)
|
||||||
|
private List<String> classPath = new ArrayList<String>();
|
||||||
|
|
||||||
|
@Parameter(names = {"-d", "--classpath-dir"},
|
||||||
|
description = "baksmali will search these directories in order for any classpath entries.")
|
||||||
|
private List<String> classPathDirectories = Lists.newArrayList(".");
|
||||||
|
|
||||||
|
@Parameter(names = "--check-package-private-access",
|
||||||
|
description = "Use the package-private access check when calculating vtable indexes. This should " +
|
||||||
|
"only be needed for 4.2.0 odexes. It was reverted in 4.2.1.")
|
||||||
|
private boolean checkPackagePrivateAccess = false;
|
||||||
|
|
||||||
|
@Parameter(names = "--experimental",
|
||||||
|
description = "Enable experimental opcodes to be disassembled, even if they aren't necessarily " +
|
||||||
|
"supported in the Android runtime yet.")
|
||||||
|
private boolean experimentalOpcodes = false;
|
||||||
|
|
||||||
|
public ListVtablesCommand(@Nonnull JCommander jc) {
|
||||||
|
this.jc = jc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void run() {
|
||||||
|
if (help || inputList == null || inputList.isEmpty()) {
|
||||||
|
jc.usage(jc.getParsedCommand());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputList.size() > 1) {
|
||||||
|
System.err.println("Too many files specified");
|
||||||
|
jc.usage(jc.getParsedCommand());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String input = inputList.get(0);
|
||||||
|
DexBackedDexFile dexFile = loadDexFile(input, 15, false);
|
||||||
|
|
||||||
|
BaksmaliOptions options = getOptions(dexFile);
|
||||||
|
if (options == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (ClassDef classDef : dexFile.getClasses()) {
|
||||||
|
ClassProto classProto = (ClassProto) options.classPath.getClass(classDef);
|
||||||
|
List<Method> methods = classProto.getVtable();
|
||||||
|
String className = "Class " + classDef.getType() + " extends " + classDef.getSuperclass() + " : " + methods.size() + " methods\n";
|
||||||
|
System.out.write(className.getBytes());
|
||||||
|
for (int i = 0; i < methods.size(); i++) {
|
||||||
|
Method method = methods.get(i);
|
||||||
|
|
||||||
|
String methodString = i + ":" + method.getDefiningClass() + "->" + method.getName() + "(";
|
||||||
|
for (CharSequence parameter : method.getParameterTypes()) {
|
||||||
|
methodString += parameter;
|
||||||
|
}
|
||||||
|
methodString += ")" + method.getReturnType() + "\n";
|
||||||
|
System.out.write(methodString.getBytes());
|
||||||
|
}
|
||||||
|
System.out.write("\n".getBytes());
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected BaksmaliOptions getOptions(DexFile dexFile) {
|
||||||
|
final BaksmaliOptions options = new BaksmaliOptions();
|
||||||
|
|
||||||
|
options.apiLevel = apiLevel;
|
||||||
|
|
||||||
|
try {
|
||||||
|
options.classPath = ClassPath.fromClassPath(classPathDirectories,
|
||||||
|
Iterables.concat(bootClassPath, classPath), dexFile, apiLevel,
|
||||||
|
checkPackagePrivateAccess, experimentalOpcodes);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
System.err.println("Error occurred while loading class path files.");
|
||||||
|
ex.printStackTrace(System.err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
options.experimentalOpcodes = experimentalOpcodes;
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
}
|
@ -104,7 +104,6 @@ subprojects {
|
|||||||
antlr_runtime: 'org.antlr:antlr-runtime:3.5.2',
|
antlr_runtime: 'org.antlr:antlr-runtime:3.5.2',
|
||||||
antlr: 'org.antlr:antlr:3.5.2',
|
antlr: 'org.antlr:antlr:3.5.2',
|
||||||
stringtemplate: 'org.antlr:stringtemplate:3.2.1',
|
stringtemplate: 'org.antlr:stringtemplate:3.2.1',
|
||||||
commons_cli: 'commons-cli:commons-cli:1.2',
|
|
||||||
jflex: 'de.jflex:jflex:1.4.3',
|
jflex: 'de.jflex:jflex:1.4.3',
|
||||||
jflex_plugin: 'co.tomlee.gradle.plugins:gradle-jflex-plugin:0.0.2',
|
jflex_plugin: 'co.tomlee.gradle.plugins:gradle-jflex-plugin:0.0.2',
|
||||||
proguard_gradle: 'net.sf.proguard:proguard-gradle:5.2.1',
|
proguard_gradle: 'net.sf.proguard:proguard-gradle:5.2.1',
|
||||||
|
@ -49,7 +49,6 @@ 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
|
||||||
|
|
||||||
|
@ -373,7 +373,7 @@ public class ClassProto implements TypeProto {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull SparseArray<FieldReference> getInstanceFields() {
|
@Nonnull public SparseArray<FieldReference> getInstanceFields() {
|
||||||
if (classPath.isArt()) {
|
if (classPath.isArt()) {
|
||||||
return artInstanceFieldsSupplier.get();
|
return artInstanceFieldsSupplier.get();
|
||||||
} else {
|
} else {
|
||||||
@ -759,7 +759,7 @@ public class ClassProto implements TypeProto {
|
|||||||
throw new ExceptionWithContext("Invalid type: %s", type);
|
throw new ExceptionWithContext("Invalid type: %s", type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull List<Method> getVtable() {
|
@Nonnull public List<Method> getVtable() {
|
||||||
return vtableSupplier.get();
|
return vtableSupplier.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,180 +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.dexlib2.analysis;
|
|
||||||
|
|
||||||
import com.google.common.base.Splitter;
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import org.apache.commons.cli.*;
|
|
||||||
import org.jf.dexlib2.DexFileFactory;
|
|
||||||
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
|
|
||||||
import org.jf.dexlib2.iface.ClassDef;
|
|
||||||
import org.jf.dexlib2.iface.Field;
|
|
||||||
import org.jf.dexlib2.iface.Method;
|
|
||||||
import org.jf.dexlib2.iface.reference.FieldReference;
|
|
||||||
import org.jf.util.ConsoleUtil;
|
|
||||||
import org.jf.util.SparseArray;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public class DumpFields {
|
|
||||||
private static final Options options;
|
|
||||||
|
|
||||||
static {
|
|
||||||
options = new Options();
|
|
||||||
buildOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
CommandLineParser parser = new PosixParser();
|
|
||||||
CommandLine commandLine;
|
|
||||||
|
|
||||||
try {
|
|
||||||
commandLine = parser.parse(options, args);
|
|
||||||
} catch (ParseException ex) {
|
|
||||||
usage();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] remainingArgs = commandLine.getArgs();
|
|
||||||
|
|
||||||
Option[] parsedOptions = commandLine.getOptions();
|
|
||||||
ArrayList<String> bootClassPathDirs = Lists.newArrayList();
|
|
||||||
String outFile = "fields.txt";
|
|
||||||
int apiLevel = 15;
|
|
||||||
boolean experimental = false;
|
|
||||||
|
|
||||||
for (int i=0; i<parsedOptions.length; i++) {
|
|
||||||
Option option = parsedOptions[i];
|
|
||||||
String opt = option.getOpt();
|
|
||||||
|
|
||||||
switch (opt.charAt(0)) {
|
|
||||||
case 'd':
|
|
||||||
bootClassPathDirs.add(option.getValue());
|
|
||||||
break;
|
|
||||||
case 'o':
|
|
||||||
outFile = option.getValue();
|
|
||||||
break;
|
|
||||||
case 'a':
|
|
||||||
apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
|
|
||||||
break;
|
|
||||||
case 'X':
|
|
||||||
experimental = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remainingArgs.length != 1) {
|
|
||||||
usage();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String inputDexFileName = remainingArgs[0];
|
|
||||||
|
|
||||||
File dexFileFile = new File(inputDexFileName);
|
|
||||||
if (!dexFileFile.exists()) {
|
|
||||||
System.err.println("Can't find the file " + inputDexFileName);
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, apiLevel, experimental);
|
|
||||||
Iterable<String> bootClassPaths = Splitter.on(":").split("core.jar:ext.jar:framework.jar:android.policy.jar:services.jar");
|
|
||||||
ClassPath classPath = ClassPath.fromClassPath(bootClassPathDirs, bootClassPaths, dexFile, apiLevel, experimental);
|
|
||||||
FileOutputStream outStream = new FileOutputStream(outFile);
|
|
||||||
|
|
||||||
for (ClassDef classDef: dexFile.getClasses()) {
|
|
||||||
ClassProto classProto = (ClassProto) classPath.getClass(classDef);
|
|
||||||
SparseArray<FieldReference> fields = classProto.getInstanceFields();
|
|
||||||
String className = "Class " + classDef.getType() + " : " + fields.size() + " instance fields\n";
|
|
||||||
outStream.write(className.getBytes());
|
|
||||||
for (int i=0;i<fields.size();i++) {
|
|
||||||
String field = fields.keyAt(i) + ":" + fields.valueAt(i).getType() + " " + fields.valueAt(i).getName() + "\n";
|
|
||||||
outStream.write(field.getBytes());
|
|
||||||
}
|
|
||||||
outStream.write("\n".getBytes());
|
|
||||||
}
|
|
||||||
outStream.close();
|
|
||||||
} catch (IOException ex) {
|
|
||||||
System.out.println("IOException thrown when trying to open a dex file or write out vtables: " + ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prints the usage message.
|
|
||||||
*/
|
|
||||||
private static void usage() {
|
|
||||||
int consoleWidth = ConsoleUtil.getConsoleWidth();
|
|
||||||
if (consoleWidth <= 0) {
|
|
||||||
consoleWidth = 80;
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("java -cp baksmali.jar org.jf.dexlib2.analysis.DumpFields -d path/to/framework/jar/files <dex-file>");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void buildOptions() {
|
|
||||||
Option classPathDirOption = OptionBuilder.withLongOpt("bootclasspath-dir")
|
|
||||||
.withDescription("the base folder to look for the bootclasspath files in. Defaults to the current " +
|
|
||||||
"directory")
|
|
||||||
.hasArg()
|
|
||||||
.withArgName("DIR")
|
|
||||||
.create("d");
|
|
||||||
|
|
||||||
Option outputFileOption = OptionBuilder.withLongOpt("out-file")
|
|
||||||
.withDescription("output file")
|
|
||||||
.hasArg()
|
|
||||||
.withArgName("FILE")
|
|
||||||
.create("o");
|
|
||||||
|
|
||||||
Option apiLevelOption = OptionBuilder.withLongOpt("api-level")
|
|
||||||
.withDescription("The numeric api-level of the file being disassembled. If not " +
|
|
||||||
"specified, it defaults to 15 (ICS).")
|
|
||||||
.hasArg()
|
|
||||||
.withArgName("API_LEVEL")
|
|
||||||
.create("a");
|
|
||||||
|
|
||||||
Option experimentalOption = OptionBuilder.withLongOpt("experimental")
|
|
||||||
.withDescription("Enable dumping experimental opcodes, that aren't necessarily " +
|
|
||||||
"supported by the android runtime yet.")
|
|
||||||
.create("X");
|
|
||||||
|
|
||||||
options.addOption(classPathDirOption);
|
|
||||||
options.addOption(outputFileOption);
|
|
||||||
options.addOption(apiLevelOption);
|
|
||||||
options.addOption(experimentalOption);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,184 +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.dexlib2.analysis;
|
|
||||||
|
|
||||||
import com.google.common.base.Splitter;
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import org.apache.commons.cli.*;
|
|
||||||
import org.jf.dexlib2.DexFileFactory;
|
|
||||||
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
|
|
||||||
import org.jf.dexlib2.iface.ClassDef;
|
|
||||||
import org.jf.dexlib2.iface.Method;
|
|
||||||
import org.jf.util.ConsoleUtil;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class DumpVtables {
|
|
||||||
private static final Options options;
|
|
||||||
|
|
||||||
static {
|
|
||||||
options = new Options();
|
|
||||||
buildOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
CommandLineParser parser = new PosixParser();
|
|
||||||
CommandLine commandLine;
|
|
||||||
|
|
||||||
try {
|
|
||||||
commandLine = parser.parse(options, args);
|
|
||||||
} catch (ParseException ex) {
|
|
||||||
usage();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] remainingArgs = commandLine.getArgs();
|
|
||||||
|
|
||||||
Option[] parsedOptions = commandLine.getOptions();
|
|
||||||
ArrayList<String> bootClassPathDirs = Lists.newArrayList();
|
|
||||||
String outFile = "vtables.txt";
|
|
||||||
int apiLevel = 15;
|
|
||||||
boolean experimental = false;
|
|
||||||
|
|
||||||
for (int i=0; i<parsedOptions.length; i++) {
|
|
||||||
Option option = parsedOptions[i];
|
|
||||||
String opt = option.getOpt();
|
|
||||||
|
|
||||||
switch (opt.charAt(0)) {
|
|
||||||
case 'd':
|
|
||||||
bootClassPathDirs.add(option.getValue());
|
|
||||||
break;
|
|
||||||
case 'o':
|
|
||||||
outFile = option.getValue();
|
|
||||||
break;
|
|
||||||
case 'a':
|
|
||||||
apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
|
|
||||||
break;
|
|
||||||
case 'X':
|
|
||||||
experimental = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remainingArgs.length != 1) {
|
|
||||||
usage();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String inputDexFileName = remainingArgs[0];
|
|
||||||
|
|
||||||
File dexFileFile = new File(inputDexFileName);
|
|
||||||
if (!dexFileFile.exists()) {
|
|
||||||
System.err.println("Can't find the file " + inputDexFileName);
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, apiLevel, experimental);
|
|
||||||
Iterable<String> bootClassPaths = Splitter.on(":").split("core.jar:ext.jar:framework.jar:android.policy.jar:services.jar");
|
|
||||||
ClassPath classPath = ClassPath.fromClassPath(bootClassPathDirs, bootClassPaths, dexFile, apiLevel, experimental);
|
|
||||||
FileOutputStream outStream = new FileOutputStream(outFile);
|
|
||||||
|
|
||||||
for (ClassDef classDef: dexFile.getClasses()) {
|
|
||||||
ClassProto classProto = (ClassProto) classPath.getClass(classDef);
|
|
||||||
List<Method> methods = classProto.getVtable();
|
|
||||||
String className = "Class " + classDef.getType() + " extends " + classDef.getSuperclass() + " : " + methods.size() + " methods\n";
|
|
||||||
outStream.write(className.getBytes());
|
|
||||||
for (int i=0;i<methods.size();i++) {
|
|
||||||
Method method = methods.get(i);
|
|
||||||
|
|
||||||
String methodString = i + ":" + method.getDefiningClass() + "->" + method.getName() + "(";
|
|
||||||
for (CharSequence parameter: method.getParameterTypes()) {
|
|
||||||
methodString += parameter;
|
|
||||||
}
|
|
||||||
methodString += ")" + method.getReturnType() + "\n";
|
|
||||||
outStream.write(methodString.getBytes());
|
|
||||||
}
|
|
||||||
outStream.write("\n".getBytes());
|
|
||||||
}
|
|
||||||
outStream.close();
|
|
||||||
} catch (IOException ex) {
|
|
||||||
System.out.println("IOException thrown when trying to open a dex file or write out vtables: " + ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prints the usage message.
|
|
||||||
*/
|
|
||||||
private static void usage() {
|
|
||||||
int consoleWidth = ConsoleUtil.getConsoleWidth();
|
|
||||||
if (consoleWidth <= 0) {
|
|
||||||
consoleWidth = 80;
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("java -cp baksmali.jar org.jf.dexlib2.analysis.DumpVtables -d path/to/framework/jar/files <dex-file>");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void buildOptions() {
|
|
||||||
Option classPathDirOption = OptionBuilder.withLongOpt("bootclasspath-dir")
|
|
||||||
.withDescription("the base folder to look for the bootclasspath files in. Defaults to the current " +
|
|
||||||
"directory")
|
|
||||||
.hasArg()
|
|
||||||
.withArgName("DIR")
|
|
||||||
.create("d");
|
|
||||||
|
|
||||||
Option outputFileOption = OptionBuilder.withLongOpt("out-file")
|
|
||||||
.withDescription("output file")
|
|
||||||
.hasArg()
|
|
||||||
.withArgName("FILE")
|
|
||||||
.create("o");
|
|
||||||
|
|
||||||
Option apiLevelOption = OptionBuilder.withLongOpt("api-level")
|
|
||||||
.withDescription("The numeric api-level of the file being disassembled. If not " +
|
|
||||||
"specified, it defaults to 15 (ICS).")
|
|
||||||
.hasArg()
|
|
||||||
.withArgName("API_LEVEL")
|
|
||||||
.create("a");
|
|
||||||
|
|
||||||
Option experimentalOption = OptionBuilder.withLongOpt("experimental")
|
|
||||||
.withDescription("Enable dumping experimental opcodes, that aren't necessarily " +
|
|
||||||
"supported by the android runtime yet.")
|
|
||||||
.create("X");
|
|
||||||
|
|
||||||
options.addOption(classPathDirOption);
|
|
||||||
options.addOption(outputFileOption);
|
|
||||||
options.addOption(apiLevelOption);
|
|
||||||
options.addOption(experimentalOption);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user