Getting instance fields by offset and methods by vtable index.

- Dump utilities
This commit is contained in:
Izzat Bahadirov 2013-04-19 00:29:30 -04:00
parent f939e912b5
commit b2ce899471
8 changed files with 1041 additions and 10 deletions

View File

@ -724,6 +724,14 @@ public class ClassPath {
return superclass;
}
VirtualMethod[] getVtable() {
return vtable;
}
SparseArray<FieldDef> getInstanceFields() {
return instanceFields;
}
public int getClassDepth() {
return classDepth;
}
@ -1207,7 +1215,7 @@ public class ClassPath {
}
}
private static class VirtualMethod {
static class VirtualMethod {
public String containingClass;
public String method;
public boolean isPackagePrivate;

View File

@ -0,0 +1,160 @@
/*
* 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.dexlib.Code.Analysis;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import org.apache.commons.cli.*;
import org.jf.dexlib.ClassDefItem;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Util.SparseArray;
import org.jf.util.ConsoleUtil;
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";
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;
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 {
DexFile dexFile = new DexFile(dexFileFile);
String[] bootClassPaths = new String[bootClassPathDirs.size()];
int j = 0;
for (String bootClassPathDir: bootClassPathDirs) {
bootClassPaths[j++] = bootClassPathDir;
}
ClassPath.InitializeClassPathFromOdex(bootClassPaths, null, inputDexFileName, dexFile, false);
FileOutputStream outStream = new FileOutputStream(outFile);
for (ClassDefItem classDefItem: dexFile.ClassDefsSection.getItems()) {
ClassPath.ClassDef classDef = ClassPath.getClassDef(classDefItem.getClassType());
SparseArray<ClassPath.FieldDef> fields = classDef.getInstanceFields();
String className = "Class " + classDef.getClassType() + " : " + 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).type + " " + fields.valueAt(i).name + "\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.dexlib.Code.Analysis.DumpFields -d path/to/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");
options.addOption(classPathDirOption);
options.addOption(outputFileOption);
}
}

View File

@ -0,0 +1,159 @@
/*
* 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.dexlib.Code.Analysis;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import org.apache.commons.cli.*;
import org.jf.dexlib.ClassDefItem;
import org.jf.dexlib.DexFile;
import org.jf.util.ConsoleUtil;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
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";
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;
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 {
DexFile dexFile = new DexFile(dexFileFile);
String[] bootClassPaths = new String[bootClassPathDirs.size()];
int j = 0;
for (String bootClassPathDir: bootClassPathDirs) {
bootClassPaths[j++] = bootClassPathDir;
}
ClassPath.InitializeClassPathFromOdex(bootClassPaths, null, inputDexFileName, dexFile, false);
FileOutputStream outStream = new FileOutputStream(outFile);
for (ClassDefItem classDefItem: dexFile.ClassDefsSection.getItems()) {
ClassPath.ClassDef classDef = ClassPath.getClassDef(classDefItem.getClassType());
ClassPath.VirtualMethod[] methods = classDef.getVtable();
String className = "Class " + classDef.getClassType() + " extends " + classDef.getSuperclass().getClassType() + " : " + methods.length + " methods\n";
outStream.write(className.getBytes());
for (int i=0;i<methods.length;i++) {
String method = i + ":" + methods[i].containingClass + "->" + methods[i].method + "\n";
outStream.write(method.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.dexlib.Code.Analysis.DumpVtables -d path/to/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");
options.addOption(classPathDirOption);
options.addOption(outputFileOption);
}
}

View File

@ -34,6 +34,7 @@ package org.jf.dexlib2.analysis;
import com.google.common.base.Strings;
import org.jf.dexlib2.iface.reference.FieldReference;
import org.jf.dexlib2.iface.reference.MethodReference;
import org.jf.dexlib2.immutable.reference.ImmutableFieldReference;
import org.jf.dexlib2.util.TypeUtils;
import org.jf.util.ExceptionWithContext;
@ -151,12 +152,15 @@ public class ArrayProto implements TypeProto {
@Override
@Nullable
public FieldReference getFieldByOffset(int fieldOffset) {
return classPath.getClass("Ljava/lang/Array;").getFieldByOffset(fieldOffset);
if (fieldOffset==8) {
return new ImmutableFieldReference(getType(), "length", "int");
}
return null;
}
@Override
@Nullable
public MethodReference getMethodByVtableIndex(int vtableIndex) {
return classPath.getClass("Ljava/lang/Array;").getMethodByVtableIndex(vtableIndex);
return classPath.getClass("Ljava/lang/Object;").getMethodByVtableIndex(vtableIndex);
}
}

View File

@ -33,18 +33,23 @@ package org.jf.dexlib2.analysis;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.jf.dexlib2.AccessFlags;
import org.jf.dexlib2.analysis.util.TypeProtoUtils;
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.dexlib2.iface.reference.MethodReference;
import org.jf.dexlib2.util.FieldUtil;
import org.jf.dexlib2.util.MethodUtil;
import org.jf.util.ExceptionWithContext;
import org.jf.util.SparseArray;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Set;
import java.util.*;
/**
* A class "prototype". This contains things like the interfaces, the superclass, the vtable and the instance fields
@ -55,6 +60,9 @@ public class ClassProto implements TypeProto {
@Nonnull protected final String type;
@Nullable protected ClassDef classDef;
@Nullable protected Set<String> interfaces;
@Nullable protected LinkedHashMap<String, ClassDef> interfaceTable;
@Nullable protected Method[] vtable;
@Nullable protected SparseArray<FieldReference> instanceFields;
protected boolean interfacesFullyResolved = true;
public ClassProto(@Nonnull ClassPath classPath, @Nonnull String type) {
@ -77,6 +85,27 @@ public class ClassProto implements TypeProto {
return classDef;
}
Method[] getVtable() {
if (vtable == null) {
vtable = loadVtable();
}
return vtable;
}
SparseArray<FieldReference> getInstanceFields() {
if (instanceFields == null) {
instanceFields = loadFields();
}
return instanceFields;
}
LinkedHashMap<String, ClassDef> getInterfaceTable() {
if (interfaceTable == null) {
interfaceTable = loadInterfaceTable();
}
return interfaceTable;
}
/**
* Returns true if this class is an interface.
*
@ -274,14 +303,343 @@ public class ClassProto implements TypeProto {
@Override
@Nullable
public FieldReference getFieldByOffset(int fieldOffset) {
// TODO: implement this
return null;
if (getInstanceFields() == null) {
return null;
}
return getInstanceFields().get(fieldOffset);
}
@Override
@Nullable
public MethodReference getMethodByVtableIndex(int vtableIndex) {
// TODO: implement this
return null;
if (getVtable() == null
|| vtableIndex < 0
|| vtableIndex >= getVtable().length) {
return null;
}
return getVtable()[vtableIndex];
}
private SparseArray<FieldReference> loadFields() {
//This is a bit of an "involved" operation. We need to follow the same algorithm that dalvik uses to
//arrange fields, so that we end up with the same field offsets (which is needed for deodexing).
//See mydroid/dalvik/vm/oo/Class.c - computeFieldOffsets()
final byte REFERENCE = 0;
final byte WIDE = 1;
final byte OTHER = 2;
ArrayList<Field> loadedFields = getInstanceFields(getClassDef());
Field[] fields = new Field[loadedFields.size()];
//the "type" for each field in fields. 0=reference,1=wide,2=other
byte[] fieldTypes = new byte[fields.length];
for (int i=0;i<fields.length;i++) {
fields[i] = loadedFields.get(i);
fieldTypes[i] = getFieldType(fields[i].getType());
}
//The first operation is to move all of the reference fields to the front. To do this, find the first
//non-reference field, then find the last reference field, swap them and repeat
int back = fields.length - 1;
int front;
for (front = 0; front<fields.length; front++) {
if (fieldTypes[front] != REFERENCE) {
while (back > front) {
if (fieldTypes[back] == REFERENCE) {
swap(fieldTypes, fields, front, back--);
break;
}
back--;
}
}
if (fieldTypes[front] != REFERENCE) {
break;
}
}
int startFieldOffset = 8;
String superclassType = getSuperclass();
ClassProto superclass = null;
if (superclassType != null) {
superclass = (ClassProto) classPath.getClass(superclassType);
if (superclass != null) {
startFieldOffset = superclass.getNextFieldOffset();
}
}
int fieldIndexMod;
if ((startFieldOffset % 8) == 0) {
fieldIndexMod = 0;
} else {
fieldIndexMod = 1;
}
//next, we need to group all the wide fields after the reference fields. But the wide fields have to be
//8-byte aligned. If we're on an odd field index, we need to insert a 32-bit field. If the next field
//is already a 32-bit field, use that. Otherwise, find the first 32-bit field from the end and swap it in.
//If there are no 32-bit fields, do nothing for now. We'll add padding when calculating the field offsets
if (front < fields.length && (front % 2) != fieldIndexMod) {
if (fieldTypes[front] == WIDE) {
//we need to swap in a 32-bit field, so the wide fields will be correctly aligned
back = fields.length - 1;
while (back > front) {
if (fieldTypes[back] == OTHER) {
swap(fieldTypes, fields, front++, back);
break;
}
back--;
}
} else {
//there's already a 32-bit field here that we can use
front++;
}
}
//do the swap thing for wide fields
back = fields.length - 1;
for (; front<fields.length; front++) {
if (fieldTypes[front] != WIDE) {
while (back > front) {
if (fieldTypes[back] == WIDE) {
swap(fieldTypes, fields, front, back--);
break;
}
back--;
}
}
if (fieldTypes[front] != WIDE) {
break;
}
}
int superFieldCount = 0;
if (superclass != null) {
superFieldCount = superclass.instanceFields.size();
}
//now the fields are in the correct order. Add them to the SparseArray and lookup, and calculate the offsets
int totalFieldCount = superFieldCount + fields.length;
SparseArray<FieldReference> instanceFields = new SparseArray<FieldReference>(totalFieldCount);
int fieldOffset;
if (superclass != null && superFieldCount > 0) {
for (int i=0; i<superFieldCount; i++) {
instanceFields.append(superclass.instanceFields.keyAt(i), superclass.instanceFields.valueAt(i));
}
fieldOffset = instanceFields.keyAt(superFieldCount-1);
FieldReference lastSuperField = superclass.instanceFields.valueAt(superFieldCount-1);
char fieldType = lastSuperField.getType().charAt(0);
if (fieldType == 'J' || fieldType == 'D') {
fieldOffset += 8;
} else {
fieldOffset += 4;
}
} else {
//the field values start at 8 bytes into the DataObject dalvik structure
fieldOffset = 8;
}
boolean gotDouble = false;
for (int i=0; i<fields.length; i++) {
FieldReference field = fields[i];
//add padding to align the wide fields, if needed
if (fieldTypes[i] == WIDE && !gotDouble) {
if (!gotDouble) {
if (fieldOffset % 8 != 0) {
assert fieldOffset % 8 == 4;
fieldOffset += 4;
}
gotDouble = true;
}
}
instanceFields.append(fieldOffset, field);
if (fieldTypes[i] == WIDE) {
fieldOffset += 8;
} else {
fieldOffset += 4;
}
}
return instanceFields;
}
private ArrayList<Field> getInstanceFields(ClassDef classDef) {
ArrayList<Field> instanceFields = Lists.newArrayList();
for (Field field: classDef.getFields()) {
if (!FieldUtil.isStatic(field)) {
instanceFields.add(field);
}
}
return instanceFields;
}
private byte getFieldType(String fieldType) {
switch (fieldType.charAt(0)) {
case '[':
case 'L':
return 0; //REFERENCE
case 'J':
case 'D':
return 1; //WIDE
default:
return 2; //OTHER
}
}
private void swap(byte[] fieldTypes, FieldReference[] fields, int position1, int position2) {
byte tempType = fieldTypes[position1];
fieldTypes[position1] = fieldTypes[position2];
fieldTypes[position2] = tempType;
FieldReference tempField = fields[position1];
fields[position1] = fields[position2];
fields[position2] = tempField;
}
private int getNextFieldOffset() {
SparseArray<FieldReference> instanceFields = getInstanceFields();
if (instanceFields == null || instanceFields.size() == 0) {
return 8;
}
int lastItemIndex = instanceFields.size()-1;
int fieldOffset = instanceFields.keyAt(lastItemIndex);
FieldReference lastField = instanceFields.valueAt(lastItemIndex);
switch (lastField.getType().charAt(0)) {
case 'J':
case 'D':
return fieldOffset + 8;
default:
return fieldOffset + 4;
}
}
//TODO: check the case when we have a package private method that overrides an interface method
private Method[] loadVtable() {
//TODO: it might be useful to keep track of which class's implementation is used for each virtual method. In other words, associate the implementing class type with each vtable entry
List<Method> virtualMethodList = Lists.newLinkedList();
//copy the virtual methods from the superclass
String superclassType = getSuperclass();
if (superclassType != null) {
ClassProto superclass = (ClassProto) classPath.getClass(superclassType);
for (int i=0; i<superclass.getVtable().length; i++) {
virtualMethodList.add(superclass.getVtable()[i]);
}
}
//iterate over the virtual methods in the current class, and only add them when we don't already have the
//method (i.e. if it was implemented by the superclass)
if (!isInterface()) {
addToVtable(getVirtualMethods(getClassDef()), virtualMethodList);
LinkedHashMap<String, ClassDef> interfaceTable = loadInterfaceTable();
if (interfaceTable != null) {
for (ClassDef interfaceDef: interfaceTable.values()) {
addToVtable(getVirtualMethods(interfaceDef), virtualMethodList);
}
}
}
Method[] vtable = new Method[virtualMethodList.size()];
for (int i=0; i<virtualMethodList.size(); i++) {
vtable[i] = virtualMethodList.get(i);
}
return vtable;
}
private LinkedHashMap<String, ClassDef> loadInterfaceTable() {
LinkedHashMap<String, ClassDef> interfaceTable = Maps.newLinkedHashMap();
for (String interfaceType: getClassDef().getInterfaces()) {
if (!interfaceTable.containsKey(interfaceType)) {
ClassDef interfaceDef;
try {
interfaceDef = classPath.getClassDef(interfaceType);
} catch (Exception ex) {
throw ExceptionWithContext.withContext(ex,
String.format("Could not find interface %s", interfaceType));
}
interfaceTable.put(interfaceType, interfaceDef);
ClassProto interfaceProto = (ClassProto) classPath.getClass(interfaceType);
for (ClassDef superInterface: interfaceProto.getInterfaceTable().values()) {
if (!interfaceTable.containsKey(superInterface.getType())) {
interfaceTable.put(superInterface.getType(), superInterface);
}
}
}
}
return interfaceTable;
}
private LinkedList<Method> getVirtualMethods(ClassDef classDef) {
LinkedList<Method> virtualMethods = Lists.newLinkedList();
for (Method method: classDef.getMethods()) {
if (!MethodUtil.isDirect(method)) {
virtualMethods.add(method);
}
}
return virtualMethods;
}
private void addToVtable(List<Method> localMethods, List<Method> vtable) {
for (Method virtualMethod: localMethods) {
boolean found = false;
for (int i=0; i<vtable.size(); i++) {
Method superMethod = vtable.get(i);
if (methodSignaturesMatch(superMethod, virtualMethod)) {
if (canAccess(superMethod)) {
found = true;
vtable.set(i, virtualMethod);
break;
}
}
}
if (!found) {
vtable.add(virtualMethod);
}
}
}
private boolean methodSignaturesMatch(Method a, Method b) {
return (a.getName().equals(b.getName())
&& a.getReturnType().equals(b.getReturnType())
&& a.getParameters().equals(b.getParameters()));
}
private boolean canAccess(Method virtualMethod) {
if (!methodIsPackagePrivate(virtualMethod.getAccessFlags())) {
return true;
}
String otherPackage = getPackage(virtualMethod.getDefiningClass());
String ourPackage = getPackage(getClassDef().getType());
return otherPackage.equals(ourPackage);
}
private String getPackage(String classType) {
int lastSlash = classType.lastIndexOf('/');
if (lastSlash < 0) {
return "";
}
return classType.substring(1, lastSlash);
}
private static boolean methodIsPackagePrivate(int accessFlags) {
return (accessFlags & (AccessFlags.PRIVATE.getValue() |
AccessFlags.PROTECTED.getValue() |
AccessFlags.PUBLIC.getValue())) == 0;
}
}

View File

@ -0,0 +1,170 @@
/*
* 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;
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;
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);
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);
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/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");
options.addOption(classPathDirOption);
options.addOption(outputFileOption);
options.addOption(apiLevelOption);
}
}

View File

@ -0,0 +1,172 @@
/*
* 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;
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;
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);
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);
FileOutputStream outStream = new FileOutputStream(outFile);
for (ClassDef classDef: dexFile.getClasses()) {
ClassProto classProto = (ClassProto) classPath.getClass(classDef);
Method[] methods = classProto.getVtable();
String className = "Class " + classDef.getType() + " extends " + classDef.getSuperclass() + " : " + methods.length + " methods\n";
outStream.write(className.getBytes());
for (int i=0;i<methods.length;i++) {
String method = i + ":" + methods[i].getDefiningClass() + "->" + methods[i].getName() + "(";
for (CharSequence parameter: methods[i].getParameterTypes()) {
method += parameter;
}
method += ")" + methods[i].getReturnType() + "\n";
outStream.write(method.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/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");
options.addOption(classPathDirOption);
options.addOption(outputFileOption);
options.addOption(apiLevelOption);
}
}

View File

@ -51,7 +51,7 @@ public class OdexHeaderItem {
public static final int AUX_LENGTH_OFFSET = 28;
public static final int FLAGS_OFFSET = 32;
private static int getVersion(byte[] magic) {
public static int getVersion(byte[] magic) {
if (magic.length < 8) {
return 0;
}