Ensure class permissions are valid when resolving a virtual method while deodexing
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java
index 2e398f6..189a252 100644
--- a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java
@@ -593,6 +593,10 @@
private final int classDepth;
+ // classes can only be public or package-private. Internally, any private/protected inner class is actually
+ // package-private.
+ private final boolean isPublic;
+
private final VirtualMethod[] vtable;
//this maps a method name of the form method(III)Ljava/lang/String; to an integer
@@ -635,6 +639,7 @@
implementedInterfaces.add(ClassPath.getClassDef("Ljava/lang/Cloneable;"));
implementedInterfaces.add(ClassPath.getClassDef("Ljava/io/Serializable;"));
isInterface = false;
+ isPublic = true;
vtable = superclass.vtable;
methodLookup = superclass.methodLookup;
@@ -652,6 +657,7 @@
this.superclass = null;
implementedInterfaces = null;
isInterface = false;
+ isPublic = true;
vtable = null;
methodLookup = null;
instanceFields = null;
@@ -665,6 +671,7 @@
this.superclass = ClassPath.getClassDef("Ljava/lang/Object;");
implementedInterfaces = new TreeSet<ClassDef>();
isInterface = false;
+ isPublic = true;
vtable = superclass.vtable;
methodLookup = superclass.methodLookup;
@@ -679,6 +686,7 @@
protected ClassDef(UnresolvedClassInfo classInfo) {
classType = classInfo.classType;
+ isPublic = classInfo.isPublic;
isInterface = classInfo.isInterface;
superclass = loadSuperclass(classInfo);
@@ -732,6 +740,10 @@
return this.isInterface;
}
+ public boolean isPublic() {
+ return this.isPublic;
+ }
+
public boolean extendsClass(ClassDef superclassDef) {
if (superclassDef == null) {
return false;
@@ -1220,6 +1232,7 @@
private static class UnresolvedClassInfo {
public final String dexFilePath;
public final String classType;
+ public final boolean isPublic;
public final boolean isInterface;
public final String superclassType;
public final String[] interfaces;
@@ -1233,6 +1246,7 @@
classType = classDefItem.getClassType().getTypeDescriptor();
+ isPublic = (classDefItem.getAccessFlags() & AccessFlags.PUBLIC.getValue()) != 0;
isInterface = (classDefItem.getAccessFlags() & AccessFlags.INTERFACE.getValue()) != 0;
TypeIdItem superclassType = classDefItem.getSuperclass();
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DeodexUtil.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DeodexUtil.java
index be689cc..f31ec56 100644
--- a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DeodexUtil.java
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DeodexUtil.java
@@ -75,8 +75,9 @@
private static final Pattern shortMethodPattern = Pattern.compile("([^(]+)\\(([^)]*)\\)(.+)");
- public MethodIdItem lookupVirtualMethod(ClassPath.ClassDef classDef, int methodIndex) {
- String method = classDef.getVirtualMethod(methodIndex);
+ public MethodIdItem lookupVirtualMethod(ClassPath.ClassDef accessingClass, ClassPath.ClassDef definingClass,
+ int methodIndex) {
+ String method = definingClass.getVirtualMethod(methodIndex);
if (method == null) {
return null;
}
@@ -91,20 +92,20 @@
String methodParams = m.group(2);
String methodRet = m.group(3);
- if (classDef instanceof ClassPath.UnresolvedClassDef) {
+ if (definingClass instanceof ClassPath.UnresolvedClassDef) {
//if this is an unresolved class, the only way getVirtualMethod could have found a method is if the virtual
//method being looked up was a method on java.lang.Object.
- classDef = ClassPath.getClassDef("Ljava/lang/Object;");
- } else if (classDef.isInterface()) {
- classDef = classDef.getSuperclass();
- assert classDef != null;
+ definingClass = ClassPath.getClassDef("Ljava/lang/Object;");
+ } else if (definingClass.isInterface()) {
+ definingClass = definingClass.getSuperclass();
+ assert definingClass != null;
}
- return parseAndResolveMethod(classDef, methodName, methodParams, methodRet);
+ return parseAndResolveMethod(accessingClass, definingClass, methodName, methodParams, methodRet);
}
- private MethodIdItem parseAndResolveMethod(ClassPath.ClassDef classDef, String methodName, String methodParams,
- String methodRet) {
+ private MethodIdItem parseAndResolveMethod(ClassPath.ClassDef accessingClass, ClassPath.ClassDef definingClass,
+ String methodName, String methodParams, String methodRet) {
StringIdItem methodNameItem = StringIdItem.lookupStringIdItem(dexFile, methodName);
if (methodNameItem == null) {
return null;
@@ -197,14 +198,15 @@
return null;
}
- ClassPath.ClassDef methodClassDef = classDef;
+ ClassPath.ClassDef methodClassDef = definingClass;
do {
TypeIdItem classTypeItem = TypeIdItem.lookupTypeIdItem(dexFile, methodClassDef.getClassType());
+
if (classTypeItem != null) {
MethodIdItem methodIdItem = MethodIdItem.lookupMethodIdItem(dexFile, classTypeItem, protoItem, methodNameItem);
- if (methodIdItem != null) {
+ if (methodIdItem != null && checkClassAccess(accessingClass, methodClassDef)) {
return methodIdItem;
}
}
@@ -214,6 +216,19 @@
return null;
}
+ private static boolean checkClassAccess(ClassPath.ClassDef accessingClass, ClassPath.ClassDef definingClass) {
+ return definingClass.isPublic() ||
+ getPackage(accessingClass.getClassType()).equals(getPackage(definingClass.getClassType()));
+ }
+
+ private static String getPackage(String classRef) {
+ int lastSlash = classRef.lastIndexOf('/');
+ if (lastSlash < 0) {
+ return "";
+ }
+ return classRef.substring(1, lastSlash);
+ }
+
private FieldIdItem parseAndResolveField(ClassPath.ClassDef classDef, ClassPath.FieldDef field) {
String definingClass = field.definingClass;
String fieldName = field.name;
@@ -283,7 +298,8 @@
private void loadMethod(DeodexUtil deodexUtil) {
ClassPath.ClassDef classDef = ClassPath.getClassDef(classType);
- this.methodIdItem = deodexUtil.parseAndResolveMethod(classDef, methodName, parameters, returnType);
+ this.methodIdItem = deodexUtil.parseAndResolveMethod(classDef, classDef, methodName, parameters,
+ returnType);
}
public String getMethodString() {
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java
index 17bf86a..64f8ab6 100644
--- a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java
@@ -3621,12 +3621,16 @@
}
MethodIdItem methodIdItem = null;
+ ClassPath.ClassDef accessingClass =
+ ClassPath.getClassDef(this.encodedMethod.method.getContainingClass(), false);
+ if (accessingClass == null) {
+ throw new ExceptionWithContext(String.format("Could not find ClassDef for current class: %s",
+ this.encodedMethod.method.getContainingClass()));
+ }
if (isSuper) {
- ClassPath.ClassDef classDef = ClassPath.getClassDef(this.encodedMethod.method.getContainingClass(), false);
- assert classDef != null;
-
- if (classDef.getSuperclass() != null) {
- methodIdItem = deodexUtil.lookupVirtualMethod(classDef.getSuperclass(), methodIndex);
+ if (accessingClass.getSuperclass() != null) {
+ methodIdItem = deodexUtil.lookupVirtualMethod(accessingClass, accessingClass.getSuperclass(),
+ methodIndex);
}
if (methodIdItem == null) {
@@ -3634,10 +3638,10 @@
//of from the superclass (although the superclass method is still what would actually be called).
//And so the MethodIdItem for the superclass method may not be in the dex file. Let's try to get the
//MethodIdItem for the method in the current class instead
- methodIdItem = deodexUtil.lookupVirtualMethod(classDef, methodIndex);
+ methodIdItem = deodexUtil.lookupVirtualMethod(accessingClass, accessingClass, methodIndex);
}
} else{
- methodIdItem = deodexUtil.lookupVirtualMethod(objectRegisterType.type, methodIndex);
+ methodIdItem = deodexUtil.lookupVirtualMethod(accessingClass, objectRegisterType.type, methodIndex);
}
if (methodIdItem == null) {