mirror of
https://github.com/revanced/smali.git
synced 2025-05-03 16:14:29 +02:00
Allow vtable lookups of Object methods for a class that can't be fully resolved
This commit is contained in:
parent
4ee6056b23
commit
ec1348b46d
@ -59,8 +59,9 @@ public class ClassProto implements TypeProto {
|
|||||||
@Nonnull protected final String type;
|
@Nonnull protected final String type;
|
||||||
@Nullable protected ClassDef classDef;
|
@Nullable protected ClassDef classDef;
|
||||||
@Nullable protected LinkedHashMap<String, ClassDef> interfaces;
|
@Nullable protected LinkedHashMap<String, ClassDef> interfaces;
|
||||||
@Nullable protected Method[] vtable;
|
@Nullable protected List<Method> vtable;
|
||||||
@Nullable protected SparseArray<FieldReference> instanceFields;
|
@Nullable protected SparseArray<FieldReference> instanceFields;
|
||||||
|
protected boolean vtableFullyResolved = true;
|
||||||
protected boolean interfacesFullyResolved = true;
|
protected boolean interfacesFullyResolved = true;
|
||||||
|
|
||||||
public ClassProto(@Nonnull ClassPath classPath, @Nonnull String type) {
|
public ClassProto(@Nonnull ClassPath classPath, @Nonnull String type) {
|
||||||
@ -84,9 +85,9 @@ public class ClassProto implements TypeProto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
Method[] getVtable() {
|
List<Method> getVtable() {
|
||||||
if (vtable == null) {
|
if (vtable == null) {
|
||||||
vtable = loadVtable();
|
loadVtable();
|
||||||
}
|
}
|
||||||
return vtable;
|
return vtable;
|
||||||
}
|
}
|
||||||
@ -346,11 +347,11 @@ public class ClassProto implements TypeProto {
|
|||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public MethodReference getMethodByVtableIndex(int vtableIndex) {
|
public MethodReference getMethodByVtableIndex(int vtableIndex) {
|
||||||
Method[] vtable = getVtable();
|
List<Method> vtable = getVtable();
|
||||||
if (vtableIndex < 0 || vtableIndex >= vtable.length) {
|
if (vtableIndex < 0 || vtableIndex >= vtable.size()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return vtable[vtableIndex];
|
return vtable.get(vtableIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@ -552,66 +553,68 @@ public class ClassProto implements TypeProto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//TODO: check the case when we have a package private method that overrides an interface method
|
//TODO: check the case when we have a package private method that overrides an interface method
|
||||||
@Nonnull
|
private void loadVtable() {
|
||||||
private Method[] loadVtable() {
|
vtable = Lists.newArrayList();
|
||||||
List<Method> virtualMethodList = Lists.newLinkedList();
|
|
||||||
|
|
||||||
//copy the virtual methods from the superclass
|
//copy the virtual methods from the superclass
|
||||||
String superclassType = getSuperclass();
|
String superclassType;
|
||||||
|
try {
|
||||||
|
superclassType = getSuperclass();
|
||||||
|
} catch (UnresolvedClassException ex) {
|
||||||
|
vtable.addAll(((ClassProto)classPath.getClass("Ljava/lang/Object;")).getVtable());
|
||||||
|
vtableFullyResolved = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (superclassType != null) {
|
if (superclassType != null) {
|
||||||
ClassProto superclass = (ClassProto) classPath.getClass(superclassType);
|
ClassProto superclass = (ClassProto) classPath.getClass(superclassType);
|
||||||
for (int i=0; i<superclass.getVtable().length; i++) {
|
vtable.addAll(superclass.getVtable());
|
||||||
virtualMethodList.add(superclass.getVtable()[i]);
|
|
||||||
|
// if the superclass's vtable wasn't fully resolved, then we can't know where the new methods added by this
|
||||||
|
// class should start, so we just propagate what we can from the parent and hope for the best.
|
||||||
|
if (!superclass.interfacesFullyResolved) {
|
||||||
|
vtableFullyResolved = false;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//iterate over the virtual methods in the current class, and only add them when we don't already have the
|
//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)
|
//method (i.e. if it was implemented by the superclass)
|
||||||
if (!isInterface()) {
|
if (!isInterface()) {
|
||||||
addToVtable(getClassDef().getVirtualMethods(), virtualMethodList, true);
|
addToVtable(getClassDef().getVirtualMethods(), vtable, true);
|
||||||
|
|
||||||
for (ClassDef interfaceDef: getDirectInterfaces()) {
|
for (ClassDef interfaceDef: getDirectInterfaces()) {
|
||||||
addToVtable(interfaceDef.getVirtualMethods(), virtualMethodList, false);
|
addToVtable(interfaceDef.getVirtualMethods(), vtable, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Method[] vtable = new Method[virtualMethodList.size()];
|
|
||||||
for (int i=0; i<virtualMethodList.size(); i++) {
|
|
||||||
vtable[i] = virtualMethodList.get(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
return vtable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addToVtable(@Nonnull Iterable<? extends Method> localMethods, @Nonnull List<Method> vtable,
|
private void addToVtable(@Nonnull Iterable<? extends Method> localMethods,
|
||||||
boolean replaceExisting) {
|
@Nonnull List<Method> vtable, boolean replaceExisting) {
|
||||||
List<? extends Method> methods = Lists.newArrayList(localMethods);
|
List<? extends Method> methods = Lists.newArrayList(localMethods);
|
||||||
Collections.sort(methods);
|
Collections.sort(methods);
|
||||||
|
|
||||||
for (Method virtualMethod: methods) {
|
outer: for (Method virtualMethod: methods) {
|
||||||
boolean found = false;
|
|
||||||
for (int i=0; i<vtable.size(); i++) {
|
for (int i=0; i<vtable.size(); i++) {
|
||||||
Method superMethod = vtable.get(i);
|
Method superMethod = vtable.get(i);
|
||||||
if (methodSignaturesMatch(superMethod, virtualMethod)) {
|
if (methodSignaturesMatch(superMethod, virtualMethod)) {
|
||||||
if (classPath.getApi() < 17 || canAccess(superMethod)) {
|
if (classPath.getApi() < 17 || canAccess(superMethod)) {
|
||||||
found = true;
|
|
||||||
if (replaceExisting) {
|
if (replaceExisting) {
|
||||||
vtable.set(i, virtualMethod);
|
vtable.set(i, virtualMethod);
|
||||||
}
|
}
|
||||||
break;
|
continue outer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found) {
|
// we didn't find an equivalent method, so add it as a new entry
|
||||||
vtable.add(virtualMethod);
|
vtable.add(virtualMethod);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean methodSignaturesMatch(@Nonnull Method a, @Nonnull Method b) {
|
private static boolean methodSignaturesMatch(@Nonnull Method a, @Nonnull Method b) {
|
||||||
return (a.getName().equals(b.getName())
|
return (a.getName().equals(b.getName()) &&
|
||||||
&& a.getReturnType().equals(b.getReturnType())
|
a.getReturnType().equals(b.getReturnType()) &&
|
||||||
&& a.getParameters().equals(b.getParameters()));
|
a.getParameters().equals(b.getParameters()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean canAccess(@Nonnull Method virtualMethod) {
|
private boolean canAccess(@Nonnull Method virtualMethod) {
|
||||||
@ -625,7 +628,7 @@ public class ClassProto implements TypeProto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private String getPackage(@Nonnull String classType) {
|
private static String getPackage(@Nonnull String classType) {
|
||||||
int lastSlash = classType.lastIndexOf('/');
|
int lastSlash = classType.lastIndexOf('/');
|
||||||
if (lastSlash < 0) {
|
if (lastSlash < 0) {
|
||||||
return "";
|
return "";
|
||||||
|
@ -112,16 +112,18 @@ public class DumpVtables {
|
|||||||
|
|
||||||
for (ClassDef classDef: dexFile.getClasses()) {
|
for (ClassDef classDef: dexFile.getClasses()) {
|
||||||
ClassProto classProto = (ClassProto) classPath.getClass(classDef);
|
ClassProto classProto = (ClassProto) classPath.getClass(classDef);
|
||||||
Method[] methods = classProto.getVtable();
|
List<Method> methods = classProto.getVtable();
|
||||||
String className = "Class " + classDef.getType() + " extends " + classDef.getSuperclass() + " : " + methods.length + " methods\n";
|
String className = "Class " + classDef.getType() + " extends " + classDef.getSuperclass() + " : " + methods.size() + " methods\n";
|
||||||
outStream.write(className.getBytes());
|
outStream.write(className.getBytes());
|
||||||
for (int i=0;i<methods.length;i++) {
|
for (int i=0;i<methods.size();i++) {
|
||||||
String method = i + ":" + methods[i].getDefiningClass() + "->" + methods[i].getName() + "(";
|
Method method = methods.get(i);
|
||||||
for (CharSequence parameter: methods[i].getParameterTypes()) {
|
|
||||||
method += parameter;
|
String methodString = i + ":" + method.getDefiningClass() + "->" + method.getName() + "(";
|
||||||
|
for (CharSequence parameter: method.getParameterTypes()) {
|
||||||
|
methodString += parameter;
|
||||||
}
|
}
|
||||||
method += ")" + methods[i].getReturnType() + "\n";
|
methodString += ")" + method.getReturnType() + "\n";
|
||||||
outStream.write(method.getBytes());
|
outStream.write(methodString.getBytes());
|
||||||
}
|
}
|
||||||
outStream.write("\n".getBytes());
|
outStream.write("\n".getBytes());
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user