mirror of
https://github.com/revanced/smali.git
synced 2025-05-09 19:04:32 +02:00
Take into account defining class information when resolving field offsets
This fixes an issue when deodexing, where a field offset is incorrectly resolved when trying to access a field defined in an ancestor class when the the child class defines a field with the same name Kudos to brightidea @ xda-dev for digging into this issue and figuring out the problem!
This commit is contained in:
parent
c2e3d1a320
commit
a91109b8a7
@ -566,6 +566,18 @@ public class ClassPath {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class FieldDef {
|
||||||
|
public final String definingClass;
|
||||||
|
public final String name;
|
||||||
|
public final String type;
|
||||||
|
|
||||||
|
public FieldDef(String definingClass, String name, String type) {
|
||||||
|
this.definingClass = definingClass;
|
||||||
|
this.name = name;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class ClassDef implements Comparable<ClassDef> {
|
public static class ClassDef implements Comparable<ClassDef> {
|
||||||
private final String classType;
|
private final String classType;
|
||||||
private final ClassDef superclass;
|
private final ClassDef superclass;
|
||||||
@ -583,7 +595,7 @@ public class ClassPath {
|
|||||||
private final String[] vtable;
|
private final String[] vtable;
|
||||||
private final HashMap<String, Integer> virtualMethodLookup;
|
private final HashMap<String, Integer> virtualMethodLookup;
|
||||||
|
|
||||||
private final SparseArray<String> instanceFields;
|
private final SparseArray<FieldDef> instanceFields;
|
||||||
|
|
||||||
public final static int ArrayClassDef = 0;
|
public final static int ArrayClassDef = 0;
|
||||||
public final static int PrimitiveClassDef = 1;
|
public final static int PrimitiveClassDef = 1;
|
||||||
@ -736,7 +748,7 @@ public class ClassPath {
|
|||||||
return virtualMethodLookup.containsKey(method);
|
return virtualMethodLookup.containsKey(method);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getInstanceField(int fieldOffset) {
|
public FieldDef getInstanceField(int fieldOffset) {
|
||||||
return this.instanceFields.get(fieldOffset, null);
|
return this.instanceFields.get(fieldOffset, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -747,12 +759,12 @@ public class ClassPath {
|
|||||||
return this.vtable[vtableIndex];
|
return this.vtable[vtableIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
private void swap(byte[] fieldTypes, String[] fields, int position1, int position2) {
|
private void swap(byte[] fieldTypes, FieldDef[] fields, int position1, int position2) {
|
||||||
byte tempType = fieldTypes[position1];
|
byte tempType = fieldTypes[position1];
|
||||||
fieldTypes[position1] = fieldTypes[position2];
|
fieldTypes[position1] = fieldTypes[position2];
|
||||||
fieldTypes[position2] = tempType;
|
fieldTypes[position2] = tempType;
|
||||||
|
|
||||||
String tempField = fields[position1];
|
FieldDef tempField = fields[position1];
|
||||||
fields[position1] = fields[position2];
|
fields[position1] = fields[position2];
|
||||||
fields[position2] = tempField;
|
fields[position2] = tempField;
|
||||||
}
|
}
|
||||||
@ -915,11 +927,9 @@ public class ClassPath {
|
|||||||
|
|
||||||
int lastItemIndex = instanceFields.size()-1;
|
int lastItemIndex = instanceFields.size()-1;
|
||||||
int fieldOffset = instanceFields.keyAt(lastItemIndex);
|
int fieldOffset = instanceFields.keyAt(lastItemIndex);
|
||||||
String lastField = instanceFields.valueAt(lastItemIndex);
|
FieldDef lastField = instanceFields.valueAt(lastItemIndex);
|
||||||
|
|
||||||
int fieldTypeIndex = lastField.indexOf(":") + 1;
|
switch (lastField.type.charAt(0)) {
|
||||||
|
|
||||||
switch (lastField.charAt(fieldTypeIndex)) {
|
|
||||||
case 'J':
|
case 'J':
|
||||||
case 'D':
|
case 'D':
|
||||||
return fieldOffset + 8;
|
return fieldOffset + 8;
|
||||||
@ -928,7 +938,7 @@ public class ClassPath {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SparseArray<String> loadFields(TempClassInfo classInfo) {
|
private SparseArray<FieldDef> loadFields(TempClassInfo classInfo) {
|
||||||
//This is a bit of an "involved" operation. We need to follow the same algorithm that dalvik uses to
|
//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).
|
//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()
|
//See mydroid/dalvik/vm/oo/Class.c - computeFieldOffsets()
|
||||||
@ -937,12 +947,12 @@ public class ClassPath {
|
|||||||
final byte WIDE = 1;
|
final byte WIDE = 1;
|
||||||
final byte OTHER = 2;
|
final byte OTHER = 2;
|
||||||
|
|
||||||
String[] fields = null;
|
FieldDef[] fields = null;
|
||||||
//the "type" for each field in fields. 0=reference,1=wide,2=other
|
//the "type" for each field in fields. 0=reference,1=wide,2=other
|
||||||
byte[] fieldTypes = null;
|
byte[] fieldTypes = null;
|
||||||
|
|
||||||
if (classInfo.instanceFields != null) {
|
if (classInfo.instanceFields != null) {
|
||||||
fields = new String[classInfo.instanceFields.length];
|
fields = new FieldDef[classInfo.instanceFields.length];
|
||||||
fieldTypes = new byte[fields.length];
|
fieldTypes = new byte[fields.length];
|
||||||
|
|
||||||
for (int i=0; i<fields.length; i++) {
|
for (int i=0; i<fields.length; i++) {
|
||||||
@ -951,14 +961,13 @@ public class ClassPath {
|
|||||||
String fieldName = fieldInfo[0];
|
String fieldName = fieldInfo[0];
|
||||||
String fieldType = fieldInfo[1];
|
String fieldType = fieldInfo[1];
|
||||||
|
|
||||||
String field = String.format("%s:%s", fieldName, fieldType);
|
|
||||||
fieldTypes[i] = getFieldType(fieldType);
|
fieldTypes[i] = getFieldType(fieldType);
|
||||||
fields[i] = field;
|
fields[i] = new FieldDef(classInfo.classType, fieldName, fieldType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fields == null) {
|
if (fields == null) {
|
||||||
fields = new String[0];
|
fields = new FieldDef[0];
|
||||||
fieldTypes = new byte[0];
|
fieldTypes = new byte[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1041,7 +1050,7 @@ public class ClassPath {
|
|||||||
|
|
||||||
//now the fields are in the correct order. Add them to the SparseArray and lookup, and calculate the offsets
|
//now the fields are in the correct order. Add them to the SparseArray and lookup, and calculate the offsets
|
||||||
int totalFieldCount = superFieldCount + fields.length;
|
int totalFieldCount = superFieldCount + fields.length;
|
||||||
SparseArray<String> instanceFields = new SparseArray<String>(totalFieldCount);
|
SparseArray<FieldDef> instanceFields = new SparseArray<FieldDef>(totalFieldCount);
|
||||||
|
|
||||||
int fieldOffset;
|
int fieldOffset;
|
||||||
|
|
||||||
@ -1052,10 +1061,8 @@ public class ClassPath {
|
|||||||
|
|
||||||
fieldOffset = instanceFields.keyAt(superFieldCount-1);
|
fieldOffset = instanceFields.keyAt(superFieldCount-1);
|
||||||
|
|
||||||
String lastSuperField = superclass.instanceFields.valueAt(superFieldCount-1);
|
FieldDef lastSuperField = superclass.instanceFields.valueAt(superFieldCount-1);
|
||||||
assert lastSuperField.indexOf(':') >= 0;
|
char fieldType = lastSuperField.type.charAt(0);
|
||||||
assert lastSuperField.indexOf(':') < lastSuperField.length()-1; //the ':' shouldn't be the last char
|
|
||||||
char fieldType = lastSuperField.charAt(lastSuperField.indexOf(':') + 1);
|
|
||||||
if (fieldType == 'J' || fieldType == 'D') {
|
if (fieldType == 'J' || fieldType == 'D') {
|
||||||
fieldOffset += 8;
|
fieldOffset += 8;
|
||||||
} else {
|
} else {
|
||||||
@ -1068,7 +1075,7 @@ public class ClassPath {
|
|||||||
|
|
||||||
boolean gotDouble = false;
|
boolean gotDouble = false;
|
||||||
for (int i=0; i<fields.length; i++) {
|
for (int i=0; i<fields.length; i++) {
|
||||||
String field = fields[i];
|
FieldDef field = fields[i];
|
||||||
|
|
||||||
//add padding to align the wide fields, if needed
|
//add padding to align the wide fields, if needed
|
||||||
if (fieldTypes[i] == WIDE && !gotDouble) {
|
if (fieldTypes[i] == WIDE && !gotDouble) {
|
||||||
|
@ -30,6 +30,7 @@ package org.jf.dexlib.Code.Analysis;
|
|||||||
|
|
||||||
import org.jf.dexlib.*;
|
import org.jf.dexlib.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@ -59,7 +60,7 @@ public class DeodexUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public FieldIdItem lookupField(ClassPath.ClassDef classDef, int fieldOffset) {
|
public FieldIdItem lookupField(ClassPath.ClassDef classDef, int fieldOffset) {
|
||||||
String field = classDef.getInstanceField(fieldOffset);
|
ClassPath.FieldDef field = classDef.getInstanceField(fieldOffset);
|
||||||
if (field == null) {
|
if (field == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -204,15 +205,10 @@ public class DeodexUtil {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private FieldIdItem parseAndResolveField(ClassPath.ClassDef classDef, String field) {
|
private FieldIdItem parseAndResolveField(ClassPath.ClassDef classDef, ClassPath.FieldDef field) {
|
||||||
//expecting a string like someField:Lfield/type;
|
String definingClass = field.definingClass;
|
||||||
String[] parts = field.split(":");
|
String fieldName = field.name;
|
||||||
if (parts.length != 2) {
|
String fieldType = field.type;
|
||||||
throw new RuntimeException("Invalid field descriptor " + field);
|
|
||||||
}
|
|
||||||
|
|
||||||
String fieldName = parts[0];
|
|
||||||
String fieldType = parts[1];
|
|
||||||
|
|
||||||
StringIdItem fieldNameItem = StringIdItem.lookupStringIdItem(dexFile, fieldName);
|
StringIdItem fieldNameItem = StringIdItem.lookupStringIdItem(dexFile, fieldName);
|
||||||
if (fieldNameItem == null) {
|
if (fieldNameItem == null) {
|
||||||
@ -226,7 +222,17 @@ public class DeodexUtil {
|
|||||||
|
|
||||||
ClassPath.ClassDef fieldClass = classDef;
|
ClassPath.ClassDef fieldClass = classDef;
|
||||||
|
|
||||||
do {
|
ArrayList<ClassPath.ClassDef> parents = new ArrayList<ClassPath.ClassDef>();
|
||||||
|
parents.add(fieldClass);
|
||||||
|
|
||||||
|
while (fieldClass != null && !fieldClass.getClassType().equals(definingClass)) {
|
||||||
|
fieldClass = fieldClass.getSuperclass();
|
||||||
|
parents.add(fieldClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=parents.size()-1; i>=0; i--) {
|
||||||
|
fieldClass = parents.get(i);
|
||||||
|
|
||||||
TypeIdItem classTypeItem = TypeIdItem.lookupTypeIdItem(dexFile, fieldClass.getClassType());
|
TypeIdItem classTypeItem = TypeIdItem.lookupTypeIdItem(dexFile, fieldClass.getClassType());
|
||||||
if (classTypeItem == null) {
|
if (classTypeItem == null) {
|
||||||
continue;
|
continue;
|
||||||
@ -236,10 +242,7 @@ public class DeodexUtil {
|
|||||||
if (fieldIdItem != null) {
|
if (fieldIdItem != null) {
|
||||||
return fieldIdItem;
|
return fieldIdItem;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fieldClass = fieldClass.getSuperclass();
|
|
||||||
} while (fieldClass != null);
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user