mirror of
https://github.com/revanced/smali.git
synced 2025-05-29 12:20:11 +02:00
Add support for calculating field offsets for art
This commit is contained in:
parent
64785d79a3
commit
c8c70ac58e
@ -31,6 +31,8 @@
|
|||||||
|
|
||||||
package org.jf.dexlib2.analysis;
|
package org.jf.dexlib2.analysis;
|
||||||
|
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
|
import com.google.common.base.Suppliers;
|
||||||
import com.google.common.cache.CacheBuilder;
|
import com.google.common.cache.CacheBuilder;
|
||||||
import com.google.common.cache.CacheLoader;
|
import com.google.common.cache.CacheLoader;
|
||||||
import com.google.common.cache.LoadingCache;
|
import com.google.common.cache.LoadingCache;
|
||||||
@ -205,7 +207,7 @@ public class ClassPath {
|
|||||||
dexFiles.addAll(classPathDexFiles);
|
dexFiles.addAll(classPathDexFiles);
|
||||||
}
|
}
|
||||||
dexFiles.add(dexFile);
|
dexFiles.add(dexFile);
|
||||||
return new ClassPath(dexFiles, checkPackagePrivateAccess);
|
return new ClassPath(dexFiles, checkPackagePrivateAccess, isArt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@ -215,9 +217,7 @@ public class ClassPath {
|
|||||||
ArrayList<DexFile> dexFiles = Lists.newArrayList();
|
ArrayList<DexFile> dexFiles = Lists.newArrayList();
|
||||||
|
|
||||||
for (String classPathEntry: classPath) {
|
for (String classPathEntry: classPath) {
|
||||||
try {
|
dexFiles.addAll(loadClassPathEntry(classPathDirs, classPathEntry, api, experimental));
|
||||||
dexFiles.addAll(loadClassPathEntry(classPathDirs, classPathEntry, api, experimental));
|
|
||||||
} catch (ExceptionWithContext e){}
|
|
||||||
}
|
}
|
||||||
dexFiles.add(dexFile);
|
dexFiles.add(dexFile);
|
||||||
return new ClassPath(dexFiles, checkPackagePrivateAccess, isArt);
|
return new ClassPath(dexFiles, checkPackagePrivateAccess, isArt);
|
||||||
@ -286,4 +286,16 @@ public class ClassPath {
|
|||||||
}
|
}
|
||||||
throw new ExceptionWithContext("Cannot locate boot class path file %s", bootClassPathEntry);
|
throw new ExceptionWithContext("Cannot locate boot class path file %s", bootClassPathEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final Supplier<OdexedFieldInstructionMapper> fieldInstructionMapperSupplier = Suppliers.memoize(
|
||||||
|
new Supplier<OdexedFieldInstructionMapper>() {
|
||||||
|
@Override public OdexedFieldInstructionMapper get() {
|
||||||
|
return new OdexedFieldInstructionMapper(isArt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public OdexedFieldInstructionMapper getFieldInstructionMapper() {
|
||||||
|
return fieldInstructionMapperSupplier.get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,10 +34,8 @@ package org.jf.dexlib2.analysis;
|
|||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import com.google.common.base.Supplier;
|
import com.google.common.base.Supplier;
|
||||||
import com.google.common.base.Suppliers;
|
import com.google.common.base.Suppliers;
|
||||||
import com.google.common.collect.FluentIterable;
|
import com.google.common.collect.*;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.primitives.Ints;
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import org.jf.dexlib2.AccessFlags;
|
import org.jf.dexlib2.AccessFlags;
|
||||||
import org.jf.dexlib2.analysis.util.TypeProtoUtils;
|
import org.jf.dexlib2.analysis.util.TypeProtoUtils;
|
||||||
import org.jf.dexlib2.iface.ClassDef;
|
import org.jf.dexlib2.iface.ClassDef;
|
||||||
@ -46,21 +44,23 @@ import org.jf.dexlib2.iface.Method;
|
|||||||
import org.jf.dexlib2.iface.reference.FieldReference;
|
import org.jf.dexlib2.iface.reference.FieldReference;
|
||||||
import org.jf.dexlib2.iface.reference.MethodReference;
|
import org.jf.dexlib2.iface.reference.MethodReference;
|
||||||
import org.jf.dexlib2.immutable.ImmutableMethod;
|
import org.jf.dexlib2.immutable.ImmutableMethod;
|
||||||
|
import org.jf.util.AlignmentUtils;
|
||||||
import org.jf.util.ExceptionWithContext;
|
import org.jf.util.ExceptionWithContext;
|
||||||
import org.jf.util.SparseArray;
|
import org.jf.util.SparseArray;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class "prototype". This contains things like the interfaces, the superclass, the vtable and the instance fields
|
* A class "prototype". This contains things like the interfaces, the superclass, the vtable and the instance fields
|
||||||
* and their offsets.
|
* and their offsets.
|
||||||
*/
|
*/
|
||||||
public class ClassProto implements TypeProto {
|
public class ClassProto implements TypeProto {
|
||||||
|
private static final byte REFERENCE = 0;
|
||||||
|
private static final byte WIDE = 1;
|
||||||
|
private static final byte OTHER = 2;
|
||||||
|
|
||||||
@Nonnull protected final ClassPath classPath;
|
@Nonnull protected final ClassPath classPath;
|
||||||
@Nonnull protected final String type;
|
@Nonnull protected final String type;
|
||||||
|
|
||||||
@ -356,20 +356,20 @@ public class ClassProto implements TypeProto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull SparseArray<FieldReference> getInstanceFields() {
|
@Nonnull SparseArray<FieldReference> getInstanceFields() {
|
||||||
return instanceFieldsSupplier.get();
|
if (classPath.isArt) {
|
||||||
|
return artInstanceFieldsSupplier.get();
|
||||||
|
} else {
|
||||||
|
return dalvikInstanceFieldsSupplier.get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull private final Supplier<SparseArray<FieldReference>> instanceFieldsSupplier =
|
@Nonnull private final Supplier<SparseArray<FieldReference>> dalvikInstanceFieldsSupplier =
|
||||||
Suppliers.memoize(new Supplier<SparseArray<FieldReference>>() {
|
Suppliers.memoize(new Supplier<SparseArray<FieldReference>>() {
|
||||||
@Override public SparseArray<FieldReference> get() {
|
@Override public SparseArray<FieldReference> get() {
|
||||||
//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()
|
||||||
|
|
||||||
final byte REFERENCE = 0;
|
|
||||||
final byte WIDE = 1;
|
|
||||||
final byte OTHER = 2;
|
|
||||||
|
|
||||||
ArrayList<Field> fields = getSortedInstanceFields(getClassDef());
|
ArrayList<Field> fields = getSortedInstanceFields(getClassDef());
|
||||||
final int fieldCount = fields.size();
|
final int fieldCount = fields.size();
|
||||||
//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
|
||||||
@ -520,19 +520,6 @@ public class ClassProto implements TypeProto {
|
|||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte getFieldType(@Nonnull FieldReference field) {
|
|
||||||
switch (field.getType().charAt(0)) {
|
|
||||||
case '[':
|
|
||||||
case 'L':
|
|
||||||
return 0; //REFERENCE
|
|
||||||
case 'J':
|
|
||||||
case 'D':
|
|
||||||
return 1; //WIDE
|
|
||||||
default:
|
|
||||||
return 2; //OTHER
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void swap(byte[] fieldTypes, List<Field> fields, int position1, int position2) {
|
private void swap(byte[] fieldTypes, List<Field> fields, int position1, int position2) {
|
||||||
byte tempType = fieldTypes[position1];
|
byte tempType = fieldTypes[position1];
|
||||||
fieldTypes[position1] = fieldTypes[position2];
|
fieldTypes[position1] = fieldTypes[position2];
|
||||||
@ -543,23 +530,199 @@ public class ClassProto implements TypeProto {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
private static class FieldGap implements Comparable<FieldGap> {
|
||||||
|
public final int offset;
|
||||||
|
public final int size;
|
||||||
|
|
||||||
|
public FieldGap(int offset, int size) {
|
||||||
|
this.offset = offset;
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public int compareTo(@Nonnull FieldGap o) {
|
||||||
|
int result = Ints.compare(o.size, size);
|
||||||
|
if (result != 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return Ints.compare(o.offset, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull private final Supplier<SparseArray<FieldReference>> artInstanceFieldsSupplier =
|
||||||
|
Suppliers.memoize(new Supplier<SparseArray<FieldReference>>() {
|
||||||
|
|
||||||
|
@Override public SparseArray<FieldReference> get() {
|
||||||
|
// We need to follow the same algorithm that art uses to arrange fields, so that we end up with the
|
||||||
|
// same field offsets, which is needed for deodexing.
|
||||||
|
// See LinkFields() in art/runtime/class_linker.cc
|
||||||
|
|
||||||
|
PriorityQueue<FieldGap> gaps = new PriorityQueue<FieldGap>();
|
||||||
|
|
||||||
|
SparseArray<FieldReference> linkedFields = new SparseArray<FieldReference>();
|
||||||
|
ArrayList<Field> fields = getSortedInstanceFields(getClassDef());
|
||||||
|
|
||||||
|
int fieldOffset = 0;
|
||||||
|
String superclassType = getSuperclass();
|
||||||
|
if (superclassType != null) {
|
||||||
|
// TODO: what to do if superclass doesn't exist?
|
||||||
|
ClassProto superclass = (ClassProto) classPath.getClass(superclassType);
|
||||||
|
SparseArray<FieldReference> superFields = superclass.getInstanceFields();
|
||||||
|
FieldReference field = null;
|
||||||
|
int lastOffset = 0;
|
||||||
|
for (int i=0; i<superFields.size(); i++) {
|
||||||
|
int offset = superFields.keyAt(i);
|
||||||
|
field = superFields.valueAt(i);
|
||||||
|
linkedFields.put(offset, field);
|
||||||
|
lastOffset = offset;
|
||||||
|
}
|
||||||
|
if (field != null) {
|
||||||
|
fieldOffset = lastOffset + getFieldSize(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Field field: fields) {
|
||||||
|
int fieldSize = getFieldSize(field);
|
||||||
|
|
||||||
|
if (!AlignmentUtils.isAligned(fieldOffset, fieldSize)) {
|
||||||
|
int oldOffset = fieldOffset;
|
||||||
|
fieldOffset = AlignmentUtils.alignOffset(fieldOffset, fieldSize);
|
||||||
|
addFieldGap(oldOffset, fieldOffset, gaps);
|
||||||
|
}
|
||||||
|
|
||||||
|
FieldGap gap = gaps.peek();
|
||||||
|
if (gap != null && gap.size >= fieldSize) {
|
||||||
|
gaps.poll();
|
||||||
|
linkedFields.put(gap.offset, field);
|
||||||
|
if (gap.size > fieldSize) {
|
||||||
|
addFieldGap(gap.offset + fieldSize, gap.offset + gap.size, gaps);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
linkedFields.append(fieldOffset, field);
|
||||||
|
fieldOffset += fieldSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return linkedFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addFieldGap(int gapStart, int gapEnd, @Nonnull PriorityQueue<FieldGap> gaps) {
|
||||||
|
int offset = gapStart;
|
||||||
|
|
||||||
|
while (offset < gapEnd) {
|
||||||
|
int remaining = gapEnd - offset;
|
||||||
|
|
||||||
|
if ((remaining >= 4) && (offset % 4 == 0)) {
|
||||||
|
gaps.add(new FieldGap(offset, 4));
|
||||||
|
offset += 4;
|
||||||
|
} else if (remaining >= 2 && (offset % 2 == 0)) {
|
||||||
|
gaps.add(new FieldGap(offset, 2));
|
||||||
|
offset += 2;
|
||||||
|
} else {
|
||||||
|
gaps.add(new FieldGap(offset, 1));
|
||||||
|
offset += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private ArrayList<Field> getSortedInstanceFields(@Nonnull ClassDef classDef) {
|
||||||
|
ArrayList<Field> fields = Lists.newArrayList(classDef.getInstanceFields());
|
||||||
|
Collections.sort(fields, new Comparator<Field>() {
|
||||||
|
@Override public int compare(Field field1, Field field2) {
|
||||||
|
int result = Ints.compare(getFieldSortOrder(field1), getFieldSortOrder(field2));
|
||||||
|
if (result != 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = field1.getName().compareTo(field2.getName());
|
||||||
|
if (result != 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return field1.getType().compareTo(field2.getType());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getFieldSortOrder(@Nonnull FieldReference field) {
|
||||||
|
// The sort order is based on type size (except references are first), and then based on the
|
||||||
|
// enum value of the primitive type for types of equal size. See: Primitive::Type enum
|
||||||
|
// in art/runtime/primitive.h
|
||||||
|
switch (field.getType().charAt(0)) {
|
||||||
|
/* reference */
|
||||||
|
case '[':
|
||||||
|
case 'L':
|
||||||
|
return 0;
|
||||||
|
/* 64 bit */
|
||||||
|
case 'J':
|
||||||
|
return 1;
|
||||||
|
case 'D':
|
||||||
|
return 2;
|
||||||
|
/* 32 bit */
|
||||||
|
case 'I':
|
||||||
|
return 3;
|
||||||
|
case 'F':
|
||||||
|
return 4;
|
||||||
|
/* 16 bit */
|
||||||
|
case 'C':
|
||||||
|
return 5;
|
||||||
|
case 'S':
|
||||||
|
return 6;
|
||||||
|
/* 8 bit */
|
||||||
|
case 'Z':
|
||||||
|
return 7;
|
||||||
|
case 'B':
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
throw new ExceptionWithContext("Invalid field type: %s", field.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getFieldSize(@Nonnull FieldReference field) {
|
||||||
|
return getTypeSize(field.getType().charAt(0));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
private int getNextFieldOffset() {
|
private int getNextFieldOffset() {
|
||||||
SparseArray<FieldReference> instanceFields = getInstanceFields();
|
SparseArray<FieldReference> instanceFields = getInstanceFields();
|
||||||
if (instanceFields.size() == 0) {
|
if (instanceFields.size() == 0) {
|
||||||
return 8;
|
return classPath.isArt ? 0 : 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lastItemIndex = instanceFields.size()-1;
|
int lastItemIndex = instanceFields.size()-1;
|
||||||
int fieldOffset = instanceFields.keyAt(lastItemIndex);
|
int fieldOffset = instanceFields.keyAt(lastItemIndex);
|
||||||
FieldReference lastField = instanceFields.valueAt(lastItemIndex);
|
FieldReference lastField = instanceFields.valueAt(lastItemIndex);
|
||||||
|
|
||||||
switch (lastField.getType().charAt(0)) {
|
if (classPath.isArt) {
|
||||||
|
return fieldOffset + getTypeSize(lastField.getType().charAt(0));
|
||||||
|
} else {
|
||||||
|
switch (lastField.getType().charAt(0)) {
|
||||||
|
case 'J':
|
||||||
|
case 'D':
|
||||||
|
return fieldOffset + 8;
|
||||||
|
default:
|
||||||
|
return fieldOffset + 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getTypeSize(char type) {
|
||||||
|
switch (type) {
|
||||||
case 'J':
|
case 'J':
|
||||||
case 'D':
|
case 'D':
|
||||||
return fieldOffset + 8;
|
return 8;
|
||||||
default:
|
case '[':
|
||||||
return fieldOffset + 4;
|
case 'L':
|
||||||
|
case 'I':
|
||||||
|
case 'F':
|
||||||
|
return 4;
|
||||||
|
case 'C':
|
||||||
|
case 'S':
|
||||||
|
return 2;
|
||||||
|
case 'B':
|
||||||
|
case 'Z':
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
throw new ExceptionWithContext("Invalid type: %s", type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull List<Method> getVtable() {
|
@Nonnull List<Method> getVtable() {
|
||||||
@ -672,4 +835,17 @@ public class ClassProto implements TypeProto {
|
|||||||
AccessFlags.PUBLIC.getValue())) == 0;
|
AccessFlags.PUBLIC.getValue())) == 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
private static byte getFieldType(@Nonnull FieldReference field) {
|
||||||
|
switch (field.getType().charAt(0)) {
|
||||||
|
case '[':
|
||||||
|
case 'L':
|
||||||
|
return 0; //REFERENCE
|
||||||
|
case 'J':
|
||||||
|
case 'D':
|
||||||
|
return 1; //WIDE
|
||||||
|
default:
|
||||||
|
return 2; //OTHER
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1573,8 +1573,8 @@ public class MethodAnalyzer {
|
|||||||
|
|
||||||
String fieldType = resolvedField.getType();
|
String fieldType = resolvedField.getType();
|
||||||
|
|
||||||
Opcode opcode = OdexedFieldInstructionMapper.getAndCheckDeodexedOpcodeForOdexedOpcode(fieldType,
|
Opcode opcode = classPath.getFieldInstructionMapper().getAndCheckDeodexedOpcode(
|
||||||
instruction.getOpcode());
|
fieldType, instruction.getOpcode());
|
||||||
|
|
||||||
Instruction22c deodexedInstruction = new ImmutableInstruction22c(opcode, (byte)instruction.getRegisterA(),
|
Instruction22c deodexedInstruction = new ImmutableInstruction22c(opcode, (byte)instruction.getRegisterA(),
|
||||||
(byte)instruction.getRegisterB(), resolvedField);
|
(byte)instruction.getRegisterB(), resolvedField);
|
||||||
@ -1726,8 +1726,8 @@ public class MethodAnalyzer {
|
|||||||
|
|
||||||
Opcode originalOpcode = analyzedInstruction.instruction.getOpcode();
|
Opcode originalOpcode = analyzedInstruction.instruction.getOpcode();
|
||||||
|
|
||||||
Opcode opcode = OdexedFieldInstructionMapper.getAndCheckDeodexedOpcodeForOdexedOpcode(fieldType,
|
Opcode opcode = classPath.getFieldInstructionMapper().getAndCheckDeodexedOpcode(
|
||||||
originalOpcode);
|
fieldType, originalOpcode);
|
||||||
|
|
||||||
Instruction deodexedInstruction;
|
Instruction deodexedInstruction;
|
||||||
|
|
||||||
|
@ -34,155 +34,111 @@ package org.jf.dexlib2.analysis;
|
|||||||
import org.jf.dexlib2.Opcode;
|
import org.jf.dexlib2.Opcode;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class OdexedFieldInstructionMapper {
|
public class OdexedFieldInstructionMapper {
|
||||||
private static Opcode[][][][] opcodeMap = new Opcode[][][][] {
|
|
||||||
//get opcodes
|
private static final int GET = 0;
|
||||||
new Opcode[][][] {
|
private static final int PUT = 1;
|
||||||
//iget quick
|
|
||||||
new Opcode[][] {
|
private static final int PRIMITIVE = 0;
|
||||||
//odexed
|
private static final int WIDE = 1;
|
||||||
new Opcode[] {
|
private static final int REFERENCE = 2;
|
||||||
/*Z*/ Opcode.IGET_QUICK,
|
|
||||||
/*B*/ Opcode.IGET_QUICK,
|
private static class FieldOpcode {
|
||||||
/*S*/ Opcode.IGET_QUICK,
|
public final char type;
|
||||||
/*C*/ Opcode.IGET_QUICK,
|
@Nonnull public final Opcode normalOpcode;
|
||||||
/*I,F*/ Opcode.IGET_QUICK,
|
@Nonnull public final Opcode quickOpcode;
|
||||||
/*J,D*/ Opcode.IGET_WIDE_QUICK,
|
@Nullable public final Opcode volatileOpcode;
|
||||||
/*L,[*/ Opcode.IGET_OBJECT_QUICK
|
|
||||||
},
|
public FieldOpcode(char type, @Nonnull Opcode normalOpcode, @Nonnull Opcode quickOpcode,
|
||||||
//deodexed
|
@Nullable Opcode volatileOpcode) {
|
||||||
new Opcode[] {
|
this.type = type;
|
||||||
/*Z*/ Opcode.IGET_BOOLEAN,
|
this.normalOpcode = normalOpcode;
|
||||||
/*B*/ Opcode.IGET_BYTE,
|
this.quickOpcode = quickOpcode;
|
||||||
/*S*/ Opcode.IGET_SHORT,
|
this.volatileOpcode = volatileOpcode;
|
||||||
/*C*/ Opcode.IGET_CHAR,
|
}
|
||||||
/*I,F*/ Opcode.IGET,
|
|
||||||
/*J,D*/ Opcode.IGET_WIDE,
|
public FieldOpcode(char type, @Nonnull Opcode normalOpcode, @Nonnull Opcode quickOpcode) {
|
||||||
/*L,[*/ Opcode.IGET_OBJECT
|
this.type = type;
|
||||||
}
|
this.normalOpcode = normalOpcode;
|
||||||
},
|
this.quickOpcode = quickOpcode;
|
||||||
//iget volatile
|
this.volatileOpcode = null;
|
||||||
new Opcode[][] {
|
}
|
||||||
//odexed
|
}
|
||||||
new Opcode[] {
|
|
||||||
/*Z*/ Opcode.IGET_VOLATILE,
|
private static final FieldOpcode[] dalvikFieldOpcodes = new FieldOpcode[] {
|
||||||
/*B*/ Opcode.IGET_VOLATILE,
|
new FieldOpcode('Z', Opcode.IGET_BOOLEAN, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
|
||||||
/*S*/ Opcode.IGET_VOLATILE,
|
new FieldOpcode('B', Opcode.IGET_BYTE, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
|
||||||
/*C*/ Opcode.IGET_VOLATILE,
|
new FieldOpcode('S', Opcode.IGET_SHORT, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
|
||||||
/*I,F*/ Opcode.IGET_VOLATILE,
|
new FieldOpcode('C', Opcode.IGET_CHAR, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
|
||||||
/*J,D*/ Opcode.IGET_WIDE_VOLATILE,
|
new FieldOpcode('I', Opcode.IGET, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
|
||||||
/*L,[*/ Opcode.IGET_OBJECT_VOLATILE
|
new FieldOpcode('F', Opcode.IGET, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
|
||||||
},
|
new FieldOpcode('J', Opcode.IGET_WIDE, Opcode.IGET_WIDE_QUICK, Opcode.IGET_WIDE_VOLATILE),
|
||||||
//deodexed
|
new FieldOpcode('D', Opcode.IGET_WIDE, Opcode.IGET_WIDE_QUICK, Opcode.IGET_WIDE_VOLATILE),
|
||||||
new Opcode[] {
|
new FieldOpcode('L', Opcode.IGET_OBJECT, Opcode.IGET_OBJECT_QUICK, Opcode.IGET_OBJECT_VOLATILE),
|
||||||
/*Z*/ Opcode.IGET_BOOLEAN,
|
new FieldOpcode('[', Opcode.IGET_OBJECT, Opcode.IGET_OBJECT_QUICK, Opcode.IGET_OBJECT_VOLATILE),
|
||||||
/*B*/ Opcode.IGET_BYTE,
|
|
||||||
/*S*/ Opcode.IGET_SHORT,
|
new FieldOpcode('Z', Opcode.IPUT_BOOLEAN, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
|
||||||
/*C*/ Opcode.IGET_CHAR,
|
new FieldOpcode('B', Opcode.IPUT_BYTE, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
|
||||||
/*I,F*/ Opcode.IGET,
|
new FieldOpcode('S', Opcode.IPUT_SHORT, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
|
||||||
/*J,D*/ Opcode.IGET_WIDE,
|
new FieldOpcode('C', Opcode.IPUT_CHAR, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
|
||||||
/*L,[*/ Opcode.IGET_OBJECT
|
new FieldOpcode('I', Opcode.IPUT, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
|
||||||
}
|
new FieldOpcode('F', Opcode.IPUT, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
|
||||||
},
|
new FieldOpcode('J', Opcode.IPUT_WIDE, Opcode.IPUT_WIDE_QUICK, Opcode.IPUT_WIDE_VOLATILE),
|
||||||
//sget volatile
|
new FieldOpcode('D', Opcode.IPUT_WIDE, Opcode.IPUT_WIDE_QUICK, Opcode.IPUT_WIDE_VOLATILE),
|
||||||
new Opcode[][] {
|
new FieldOpcode('L', Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_QUICK, Opcode.IPUT_OBJECT_VOLATILE),
|
||||||
//odexed
|
new FieldOpcode('[', Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_QUICK, Opcode.IPUT_OBJECT_VOLATILE),
|
||||||
new Opcode[] {
|
|
||||||
/*Z*/ Opcode.SGET_VOLATILE,
|
|
||||||
/*B*/ Opcode.SGET_VOLATILE,
|
|
||||||
/*S*/ Opcode.SGET_VOLATILE,
|
|
||||||
/*C*/ Opcode.SGET_VOLATILE,
|
|
||||||
/*I,F*/ Opcode.SGET_VOLATILE,
|
|
||||||
/*J,D*/ Opcode.SGET_WIDE_VOLATILE,
|
|
||||||
/*L,[*/ Opcode.SGET_OBJECT_VOLATILE
|
|
||||||
},
|
|
||||||
//deodexed
|
|
||||||
new Opcode[] {
|
|
||||||
/*Z*/ Opcode.SGET_BOOLEAN,
|
|
||||||
/*B*/ Opcode.SGET_BYTE,
|
|
||||||
/*S*/ Opcode.SGET_SHORT,
|
|
||||||
/*C*/ Opcode.SGET_CHAR,
|
|
||||||
/*I,F*/ Opcode.SGET,
|
|
||||||
/*J,D*/ Opcode.SGET_WIDE,
|
|
||||||
/*L,[*/ Opcode.SGET_OBJECT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
//put opcodes
|
|
||||||
new Opcode[][][] {
|
|
||||||
//iput quick
|
|
||||||
new Opcode[][] {
|
|
||||||
//odexed
|
|
||||||
new Opcode[] {
|
|
||||||
/*Z*/ Opcode.IPUT_QUICK,
|
|
||||||
/*B*/ Opcode.IPUT_QUICK,
|
|
||||||
/*S*/ Opcode.IPUT_QUICK,
|
|
||||||
/*C*/ Opcode.IPUT_QUICK,
|
|
||||||
/*I,F*/ Opcode.IPUT_QUICK,
|
|
||||||
/*J,D*/ Opcode.IPUT_WIDE_QUICK,
|
|
||||||
/*L,[*/ Opcode.IPUT_OBJECT_QUICK
|
|
||||||
},
|
|
||||||
//deodexed
|
|
||||||
new Opcode[] {
|
|
||||||
/*Z*/ Opcode.IPUT_BOOLEAN,
|
|
||||||
/*B*/ Opcode.IPUT_BYTE,
|
|
||||||
/*S*/ Opcode.IPUT_SHORT,
|
|
||||||
/*C*/ Opcode.IPUT_CHAR,
|
|
||||||
/*I,F*/ Opcode.IPUT,
|
|
||||||
/*J,D*/ Opcode.IPUT_WIDE,
|
|
||||||
/*L,[*/ Opcode.IPUT_OBJECT
|
|
||||||
}
|
|
||||||
},
|
|
||||||
//iput volatile
|
|
||||||
new Opcode[][] {
|
|
||||||
//odexed
|
|
||||||
new Opcode[] {
|
|
||||||
/*Z*/ Opcode.IPUT_VOLATILE,
|
|
||||||
/*B*/ Opcode.IPUT_VOLATILE,
|
|
||||||
/*S*/ Opcode.IPUT_VOLATILE,
|
|
||||||
/*C*/ Opcode.IPUT_VOLATILE,
|
|
||||||
/*I,F*/ Opcode.IPUT_VOLATILE,
|
|
||||||
/*J,D*/ Opcode.IPUT_WIDE_VOLATILE,
|
|
||||||
/*L,[*/ Opcode.IPUT_OBJECT_VOLATILE
|
|
||||||
},
|
|
||||||
//deodexed
|
|
||||||
new Opcode[] {
|
|
||||||
/*Z*/ Opcode.IPUT_BOOLEAN,
|
|
||||||
/*B*/ Opcode.IPUT_BYTE,
|
|
||||||
/*S*/ Opcode.IPUT_SHORT,
|
|
||||||
/*C*/ Opcode.IPUT_CHAR,
|
|
||||||
/*I,F*/ Opcode.IPUT,
|
|
||||||
/*J,D*/ Opcode.IPUT_WIDE,
|
|
||||||
/*L,[*/ Opcode.IPUT_OBJECT
|
|
||||||
}
|
|
||||||
},
|
|
||||||
//sput volatile
|
|
||||||
new Opcode[][] {
|
|
||||||
//odexed
|
|
||||||
new Opcode[] {
|
|
||||||
/*Z*/ Opcode.SPUT_VOLATILE,
|
|
||||||
/*B*/ Opcode.SPUT_VOLATILE,
|
|
||||||
/*S*/ Opcode.SPUT_VOLATILE,
|
|
||||||
/*C*/ Opcode.SPUT_VOLATILE,
|
|
||||||
/*I,F*/ Opcode.SPUT_VOLATILE,
|
|
||||||
/*J,D*/ Opcode.SPUT_WIDE_VOLATILE,
|
|
||||||
/*L,[*/ Opcode.SPUT_OBJECT_VOLATILE
|
|
||||||
},
|
|
||||||
//deodexed
|
|
||||||
new Opcode[] {
|
|
||||||
/*Z*/ Opcode.SPUT_BOOLEAN,
|
|
||||||
/*B*/ Opcode.SPUT_BYTE,
|
|
||||||
/*S*/ Opcode.SPUT_SHORT,
|
|
||||||
/*C*/ Opcode.SPUT_CHAR,
|
|
||||||
/*I,F*/ Opcode.SPUT,
|
|
||||||
/*J,D*/ Opcode.SPUT_WIDE,
|
|
||||||
/*L,[*/ Opcode.SPUT_OBJECT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static final FieldOpcode[] artFieldOpcodes = new FieldOpcode[] {
|
||||||
|
new FieldOpcode('Z', Opcode.IGET_BOOLEAN, Opcode.IGET_BOOLEAN_QUICK),
|
||||||
|
new FieldOpcode('B', Opcode.IGET_BYTE, Opcode.IGET_BYTE_QUICK),
|
||||||
|
new FieldOpcode('S', Opcode.IGET_SHORT, Opcode.IGET_SHORT_QUICK),
|
||||||
|
new FieldOpcode('C', Opcode.IGET_CHAR, Opcode.IGET_CHAR_QUICK),
|
||||||
|
new FieldOpcode('I', Opcode.IGET, Opcode.IGET_QUICK),
|
||||||
|
new FieldOpcode('F', Opcode.IGET, Opcode.IGET_QUICK),
|
||||||
|
new FieldOpcode('J', Opcode.IGET_WIDE, Opcode.IGET_WIDE_QUICK),
|
||||||
|
new FieldOpcode('D', Opcode.IGET_WIDE, Opcode.IGET_WIDE_QUICK),
|
||||||
|
new FieldOpcode('L', Opcode.IGET_OBJECT, Opcode.IGET_OBJECT_QUICK),
|
||||||
|
new FieldOpcode('[', Opcode.IGET_OBJECT, Opcode.IGET_OBJECT_QUICK),
|
||||||
|
|
||||||
|
new FieldOpcode('Z', Opcode.IPUT_BOOLEAN, Opcode.IPUT_BOOLEAN_QUICK),
|
||||||
|
new FieldOpcode('B', Opcode.IPUT_BYTE, Opcode.IPUT_BYTE_QUICK),
|
||||||
|
new FieldOpcode('S', Opcode.IPUT_SHORT, Opcode.IPUT_SHORT_QUICK),
|
||||||
|
new FieldOpcode('C', Opcode.IPUT_CHAR, Opcode.IPUT_CHAR_QUICK),
|
||||||
|
new FieldOpcode('I', Opcode.IPUT, Opcode.IPUT_QUICK),
|
||||||
|
new FieldOpcode('F', Opcode.IPUT, Opcode.IPUT_QUICK),
|
||||||
|
new FieldOpcode('J', Opcode.IPUT_WIDE, Opcode.IPUT_WIDE_QUICK),
|
||||||
|
new FieldOpcode('D', Opcode.IPUT_WIDE, Opcode.IPUT_WIDE_QUICK),
|
||||||
|
new FieldOpcode('L', Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_QUICK),
|
||||||
|
new FieldOpcode('[', Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_QUICK)
|
||||||
|
};
|
||||||
|
|
||||||
|
private final FieldOpcode[][] opcodeMap = new FieldOpcode[2][10];
|
||||||
|
private final Map<Opcode, Integer> opcodeValueTypeMap = new HashMap<Opcode, Integer>(30);
|
||||||
|
|
||||||
|
private static int getValueType(char type) {
|
||||||
|
switch (type) {
|
||||||
|
case 'Z':
|
||||||
|
case 'B':
|
||||||
|
case 'S':
|
||||||
|
case 'C':
|
||||||
|
case 'I':
|
||||||
|
case 'F':
|
||||||
|
return PRIMITIVE;
|
||||||
|
case 'J':
|
||||||
|
case 'D':
|
||||||
|
return WIDE;
|
||||||
|
case 'L':
|
||||||
|
case '[':
|
||||||
|
return REFERENCE;
|
||||||
|
}
|
||||||
|
throw new RuntimeException(String.format("Unknown type %s: ", type));
|
||||||
|
}
|
||||||
|
|
||||||
private static int getTypeIndex(char type) {
|
private static int getTypeIndex(char type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'Z':
|
case 'Z':
|
||||||
@ -194,47 +150,61 @@ public class OdexedFieldInstructionMapper {
|
|||||||
case 'C':
|
case 'C':
|
||||||
return 3;
|
return 3;
|
||||||
case 'I':
|
case 'I':
|
||||||
case 'F':
|
|
||||||
return 4;
|
return 4;
|
||||||
case 'J':
|
case 'F':
|
||||||
case 'D':
|
|
||||||
return 5;
|
return 5;
|
||||||
case 'L':
|
case 'J':
|
||||||
case '[':
|
|
||||||
return 6;
|
return 6;
|
||||||
default:
|
case 'D':
|
||||||
|
return 7;
|
||||||
|
case 'L':
|
||||||
|
return 8;
|
||||||
|
case '[':
|
||||||
|
return 9;
|
||||||
}
|
}
|
||||||
throw new RuntimeException(String.format("Unknown type %s: ", type));
|
throw new RuntimeException(String.format("Unknown type %s: ", type));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getOpcodeSubtype(@Nonnull Opcode opcode) {
|
private static boolean isGet(@Nonnull Opcode opcode) {
|
||||||
if (opcode.isOdexedInstanceQuick()) {
|
return (opcode.flags & Opcode.SETS_REGISTER) != 0;
|
||||||
return 0;
|
}
|
||||||
} else if (opcode.isOdexedInstanceVolatile()) {
|
|
||||||
return 1;
|
public OdexedFieldInstructionMapper(boolean isArt) {
|
||||||
} else if (opcode.isOdexedStaticVolatile()) {
|
FieldOpcode[] opcodes;
|
||||||
return 2;
|
if (isArt) {
|
||||||
|
opcodes = artFieldOpcodes;
|
||||||
|
} else {
|
||||||
|
opcodes = dalvikFieldOpcodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (FieldOpcode fieldOpcode: opcodes) {
|
||||||
|
opcodeMap[isGet(fieldOpcode.normalOpcode)?GET:PUT][getTypeIndex(fieldOpcode.type)] = fieldOpcode;
|
||||||
|
|
||||||
|
opcodeValueTypeMap.put(fieldOpcode.quickOpcode, getValueType(fieldOpcode.type));
|
||||||
|
if (fieldOpcode.volatileOpcode != null) {
|
||||||
|
opcodeValueTypeMap.put(fieldOpcode.volatileOpcode, getValueType(fieldOpcode.type));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
throw new RuntimeException(String.format("Not an odexed field access opcode: %s", opcode.name));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
static Opcode getAndCheckDeodexedOpcodeForOdexedOpcode(@Nonnull String fieldType, @Nonnull Opcode odexedOpcode) {
|
public Opcode getAndCheckDeodexedOpcode(@Nonnull String fieldType, @Nonnull Opcode odexedOpcode) {
|
||||||
int opcodeType = odexedOpcode.setsRegister()?0:1;
|
FieldOpcode fieldOpcode = opcodeMap[isGet(odexedOpcode)?GET:PUT][getTypeIndex(fieldType.charAt(0))];
|
||||||
int opcodeSubType = getOpcodeSubtype(odexedOpcode);
|
|
||||||
int typeIndex = getTypeIndex(fieldType.charAt(0));
|
|
||||||
|
|
||||||
Opcode correctOdexedOpcode, deodexedOpcode;
|
if (!isCompatible(odexedOpcode, fieldOpcode.type)) {
|
||||||
|
|
||||||
correctOdexedOpcode = opcodeMap[opcodeType][opcodeSubType][0][typeIndex];
|
|
||||||
deodexedOpcode = opcodeMap[opcodeType][opcodeSubType][1][typeIndex];
|
|
||||||
|
|
||||||
if (correctOdexedOpcode != odexedOpcode) {
|
|
||||||
throw new AnalysisException(String.format("Incorrect field type \"%s\" for %s", fieldType,
|
throw new AnalysisException(String.format("Incorrect field type \"%s\" for %s", fieldType,
|
||||||
odexedOpcode.name));
|
odexedOpcode.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
return deodexedOpcode;
|
return fieldOpcode.normalOpcode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCompatible(Opcode opcode, char type) {
|
||||||
|
Integer valueType = opcodeValueTypeMap.get(opcode);
|
||||||
|
if (valueType == null) {
|
||||||
|
throw new RuntimeException("Unexpected opcode: " + opcode.name);
|
||||||
|
}
|
||||||
|
return valueType == getValueType(type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user