Change the way of how dexparser works
This commit is contained in:
parent
a92a9cfb6b
commit
8cec9fe29a
|
|
@ -58,7 +58,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import de.robv.android.xposed.callbacks.XC_InitPackageResources;
|
import de.robv.android.xposed.callbacks.XC_InitPackageResources;
|
||||||
import de.robv.android.xposed.callbacks.XCallback;
|
import de.robv.android.xposed.callbacks.XCallback;
|
||||||
import hidden.HiddenApiBridge;
|
import hidden.HiddenApiBridge;
|
||||||
import io.github.libxposed.XposedModuleInterface;
|
|
||||||
|
|
||||||
public final class XposedInit {
|
public final class XposedInit {
|
||||||
private static final String TAG = XposedBridge.TAG;
|
private static final String TAG = XposedBridge.TAG;
|
||||||
|
|
|
||||||
|
|
@ -11,13 +11,15 @@ import java.nio.ByteBuffer;
|
||||||
import io.github.libxposed.utils.DexParser;
|
import io.github.libxposed.utils.DexParser;
|
||||||
|
|
||||||
public class LSPosedDexParser implements DexParser {
|
public class LSPosedDexParser implements DexParser {
|
||||||
ByteBuffer data;
|
long cookie;
|
||||||
StringId[] strings;
|
|
||||||
TypeId[] typeIds;
|
final ByteBuffer data;
|
||||||
ProtoId[] protoIds;
|
final StringId[] strings;
|
||||||
FieldId[] fieldIds;
|
final TypeId[] typeIds;
|
||||||
MethodId[] methodIds;
|
final ProtoId[] protoIds;
|
||||||
ClassDef[] classDefs;
|
final FieldId[] fieldIds;
|
||||||
|
final MethodId[] methodIds;
|
||||||
|
final Annotation[] annotations;
|
||||||
|
|
||||||
public LSPosedDexParser(ByteBuffer buffer) throws IOException {
|
public LSPosedDexParser(ByteBuffer buffer) throws IOException {
|
||||||
if (!buffer.isDirect() || !buffer.asReadOnlyBuffer().hasArray()) {
|
if (!buffer.isDirect() || !buffer.asReadOnlyBuffer().hasArray()) {
|
||||||
|
|
@ -27,14 +29,15 @@ public class LSPosedDexParser implements DexParser {
|
||||||
data = buffer;
|
data = buffer;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
var out = (Object[]) DexParserBridge.parseDex(buffer);
|
long[] args = new long[1];
|
||||||
|
var out = (Object[]) DexParserBridge.openDex(buffer, args);
|
||||||
|
cookie = args[0];
|
||||||
// out[0]: String[]
|
// out[0]: String[]
|
||||||
// out[1]: int[]
|
// out[1]: int[]
|
||||||
// out[2]: int[][]
|
// out[2]: int[][]
|
||||||
// out[3]: int[]
|
// out[3]: int[]
|
||||||
// out[4]: int[][3]
|
// out[4]: int[]
|
||||||
// out[5]: int[][]
|
// out[6]: Object[]
|
||||||
// out[6]: Object[][][][]
|
|
||||||
var strings = (Object[]) out[0];
|
var strings = (Object[]) out[0];
|
||||||
this.strings = new StringId[strings.length];
|
this.strings = new StringId[strings.length];
|
||||||
for (int i = 0; i < strings.length; ++i) {
|
for (int i = 0; i < strings.length; ++i) {
|
||||||
|
|
@ -54,28 +57,40 @@ public class LSPosedDexParser implements DexParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
var fieldIds = (int[]) out[3];
|
var fieldIds = (int[]) out[3];
|
||||||
this.fieldIds = new FieldId[fieldIds.length];
|
this.fieldIds = new FieldId[fieldIds.length / 3];
|
||||||
for (int i = 0; i < fieldIds.length; ++i) {
|
for (int i = 0; i < this.fieldIds.length; ++i) {
|
||||||
this.fieldIds[i] = new LSPosedFieldId(i, fieldIds[3 * i], fieldIds[3 * i + 1], fieldIds[3 * i + 2]);
|
this.fieldIds[i] = new LSPosedFieldId(i, fieldIds[3 * i], fieldIds[3 * i + 1], fieldIds[3 * i + 2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
var methodIds = (int[]) out[4];
|
var methodIds = (int[]) out[4];
|
||||||
this.methodIds = new MethodId[methodIds.length];
|
this.methodIds = new MethodId[methodIds.length / 3];
|
||||||
for (int i = 0; i < methodIds.length; ++i) {
|
for (int i = 0; i < this.methodIds.length / 3; ++i) {
|
||||||
this.methodIds[i] = new LSPosedMethodId(i, methodIds[3 * i], methodIds[3 * i + 1], methodIds[3 * i + 2]);
|
this.methodIds[i] = new LSPosedMethodId(i, methodIds[3 * i], methodIds[3 * i + 1], methodIds[3 * i + 2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
var classDefs = (int[][]) out[5];
|
if (out[5] != null && out[6] != null) {
|
||||||
this.classDefs = new ClassDef[classDefs.length];
|
var a = (int[]) out[5];
|
||||||
var annotations = (Object[][]) out[6];
|
var b = (Object[]) out[6];
|
||||||
for (int i = 0; i < classDefs.length; ++i) {
|
this.annotations = new Annotation[a.length / 2];
|
||||||
this.classDefs[i] = new LSPosedClassDef(classDefs[i], annotations[4 * i], annotations[4 * i + 1], annotations[4 * i + 2], annotations[4 * i + 3]);
|
for (int i = 0; i < this.annotations.length; ++i) {
|
||||||
|
this.annotations[i] = new LSPosedAnnotation(a[2 * i], a[2 * i + 1], (int[]) b[2 * i], (Object[]) b[2 * i + 1]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.annotations = new Annotation[0];
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
throw new IOException("Invalid dex file", e);
|
throw new IOException("Invalid dex file", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
synchronized public void close() {
|
||||||
|
if (cookie != 0) {
|
||||||
|
DexParserBridge.closeDex(cookie);
|
||||||
|
cookie = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static class LSPosedId implements Id {
|
static class LSPosedId implements Id {
|
||||||
final int id;
|
final int id;
|
||||||
|
|
||||||
|
|
@ -230,137 +245,6 @@ public class LSPosedDexParser implements DexParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class LSPosedEncodedField implements EncodedField {
|
|
||||||
@NonNull
|
|
||||||
final FieldId field;
|
|
||||||
final int accessFlags;
|
|
||||||
|
|
||||||
LSPosedEncodedField(int field, int accessFlags) {
|
|
||||||
this.field = fieldIds[field];
|
|
||||||
this.accessFlags = accessFlags;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public FieldId getField() {
|
|
||||||
return field;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getAccessFlags() {
|
|
||||||
return accessFlags;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LSPosedEncodedMethod implements EncodedMethod {
|
|
||||||
@NonNull
|
|
||||||
final MethodId method;
|
|
||||||
final int accessFlags;
|
|
||||||
final int code;
|
|
||||||
MethodId[] invokedMethods = null;
|
|
||||||
FieldId[] accessedFields = null;
|
|
||||||
FieldId[] assignedFields = null;
|
|
||||||
StringId[] referredStrings = null;
|
|
||||||
byte[] opcodes = null;
|
|
||||||
|
|
||||||
LSPosedEncodedMethod(int method, int accessFlags, int code) {
|
|
||||||
this.method = methodIds[method];
|
|
||||||
this.accessFlags = accessFlags;
|
|
||||||
this.code = code;
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized void parseMethod() {
|
|
||||||
if (invokedMethods != null) return;
|
|
||||||
var out = (Object[]) DexParserBridge.parseMethod(data, code);
|
|
||||||
if (out == null) {
|
|
||||||
invokedMethods = new MethodId[0];
|
|
||||||
accessedFields = new FieldId[0];
|
|
||||||
assignedFields = new FieldId[0];
|
|
||||||
referredStrings = new StringId[0];
|
|
||||||
opcodes = new byte[0];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var invokedMethods = (int[]) out[0];
|
|
||||||
this.invokedMethods = new MethodId[invokedMethods.length];
|
|
||||||
for (int i = 0; i < invokedMethods.length; ++i) {
|
|
||||||
this.invokedMethods[i] = methodIds[invokedMethods[i]];
|
|
||||||
}
|
|
||||||
var accessedFields = (int[]) out[1];
|
|
||||||
this.accessedFields = new FieldId[accessedFields.length];
|
|
||||||
for (int i = 0; i < accessedFields.length; ++i) {
|
|
||||||
this.accessedFields[i] = fieldIds[accessedFields[i]];
|
|
||||||
}
|
|
||||||
var assignedFields = (int[]) out[2];
|
|
||||||
this.assignedFields = new FieldId[assignedFields.length];
|
|
||||||
for (int i = 0; i < assignedFields.length; ++i) {
|
|
||||||
this.assignedFields[i] = fieldIds[assignedFields[i]];
|
|
||||||
}
|
|
||||||
var referredStrings = (int[]) out[3];
|
|
||||||
this.referredStrings = new StringId[referredStrings.length];
|
|
||||||
for (int i = 0; i < referredStrings.length; ++i) {
|
|
||||||
this.referredStrings[i] = strings[referredStrings[i]];
|
|
||||||
}
|
|
||||||
opcodes = (byte[]) out[4];
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public MethodId getMethod() {
|
|
||||||
return method;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getAccessFlags() {
|
|
||||||
return accessFlags;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public MethodId[] getInvokedMethods() {
|
|
||||||
if (invokedMethods == null) {
|
|
||||||
parseMethod();
|
|
||||||
}
|
|
||||||
return invokedMethods;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public FieldId[] getAccessedFields() {
|
|
||||||
if (accessedFields == null) {
|
|
||||||
parseMethod();
|
|
||||||
}
|
|
||||||
return accessedFields;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public FieldId[] getAssignedFields() {
|
|
||||||
if (assignedFields == null) {
|
|
||||||
parseMethod();
|
|
||||||
}
|
|
||||||
return assignedFields;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public byte[] getOpcodes() {
|
|
||||||
if (opcodes == null) {
|
|
||||||
parseMethod();
|
|
||||||
}
|
|
||||||
return opcodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public StringId[] getReferredString() {
|
|
||||||
if (referredStrings == null) {
|
|
||||||
parseMethod();
|
|
||||||
}
|
|
||||||
return referredStrings;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class LSPosedAnnotation implements Annotation {
|
class LSPosedAnnotation implements Annotation {
|
||||||
int visibility;
|
int visibility;
|
||||||
@NonNull
|
@NonNull
|
||||||
|
|
@ -368,12 +252,12 @@ public class LSPosedDexParser implements DexParser {
|
||||||
@Nullable
|
@Nullable
|
||||||
final AnnotationElement[] elements;
|
final AnnotationElement[] elements;
|
||||||
|
|
||||||
LSPosedAnnotation(int visibility, int type, @NonNull int[] elements, @NonNull ByteBuffer[] elementValues) {
|
LSPosedAnnotation(int visibility, int type, @NonNull int[] elements, @NonNull Object[] elementValues) {
|
||||||
this.visibility = visibility;
|
this.visibility = visibility;
|
||||||
this.type = typeIds[type];
|
this.type = typeIds[type];
|
||||||
this.elements = new AnnotationElement[elementValues.length];
|
this.elements = new AnnotationElement[elementValues.length];
|
||||||
for (int i = 0; i < elementValues.length; ++i) {
|
for (int i = 0; i < elementValues.length; ++i) {
|
||||||
this.elements[i] = new LSPosedAnnotationElement(elements[i * 2], elements[i * 2 + 1], elementValues[i]);
|
this.elements[i] = new LSPosedAnnotationElement(elements[i * 2], elements[i * 2 + 1], (ByteBuffer) elementValues[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -406,7 +290,8 @@ public class LSPosedDexParser implements DexParser {
|
||||||
this.name = strings[name];
|
this.name = strings[name];
|
||||||
this.valueType = valueType;
|
this.valueType = valueType;
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
this.value = value.array();
|
this.value = new byte[value.remaining()];
|
||||||
|
value.get(this.value);
|
||||||
} else {
|
} else {
|
||||||
this.value = null;
|
this.value = null;
|
||||||
}
|
}
|
||||||
|
|
@ -430,285 +315,6 @@ public class LSPosedDexParser implements DexParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class LSPosedFieldAnnotation implements FieldAnnotation {
|
|
||||||
@NonNull
|
|
||||||
FieldId field;
|
|
||||||
@NonNull
|
|
||||||
Annotation[] annotations;
|
|
||||||
|
|
||||||
LSPosedFieldAnnotation(int field, @NonNull Object[] annotations) {
|
|
||||||
this.field = fieldIds[field];
|
|
||||||
var a = (int[]) annotations[0];
|
|
||||||
var b = (Object[]) annotations[1];
|
|
||||||
this.annotations = new Annotation[b.length];
|
|
||||||
for (int i = 0; i < b.length; ++i) {
|
|
||||||
this.annotations[i] = new LSPosedAnnotation(a[2 * i], a[2 * i + 1], (int[]) b[2 * i], (ByteBuffer[]) b[2 * i + 1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public FieldId getField() {
|
|
||||||
return field;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Annotation[] getAnnotations() {
|
|
||||||
return annotations;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LSPosedMethodAnnotation implements MethodAnnotation {
|
|
||||||
@NonNull
|
|
||||||
MethodId method;
|
|
||||||
@NonNull
|
|
||||||
Annotation[] annotations;
|
|
||||||
|
|
||||||
LSPosedMethodAnnotation(int method, @NonNull Object[] annotations) {
|
|
||||||
this.method = methodIds[method];
|
|
||||||
var a = (int[]) annotations[0];
|
|
||||||
var b = (Object[]) annotations[1];
|
|
||||||
this.annotations = new Annotation[b.length];
|
|
||||||
for (int i = 0; i < b.length; ++i) {
|
|
||||||
this.annotations[i] = new LSPosedAnnotation(a[2 * i], a[2 * i + 1], (int[]) b[2 * i], (ByteBuffer[]) b[2 * i + 1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public MethodId getMethod() {
|
|
||||||
return method;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Annotation[] getAnnotations() {
|
|
||||||
return annotations;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LSPosedParameterAnnotation implements ParameterAnnotation {
|
|
||||||
@NonNull
|
|
||||||
MethodId method;
|
|
||||||
@NonNull
|
|
||||||
Annotation[][] annotations;
|
|
||||||
|
|
||||||
LSPosedParameterAnnotation(int method, @NonNull Object[][] annotations) {
|
|
||||||
this.method = methodIds[method];
|
|
||||||
this.annotations = new Annotation[annotations.length][];
|
|
||||||
for (int i = 0; i < annotations.length; ++i) {
|
|
||||||
var a = (int[]) annotations[i][0];
|
|
||||||
var b = (Object[]) annotations[i][1];
|
|
||||||
this.annotations[i] = new Annotation[b.length];
|
|
||||||
for (int j = 0; j < b.length; ++j) {
|
|
||||||
this.annotations[i][j] = new LSPosedAnnotation(a[2 * j], a[2 * j + 1], (int[]) b[2 * j], (ByteBuffer[]) b[2 * j + 1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public MethodId getMethod() {
|
|
||||||
return method;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Annotation[][] getAnnotations() {
|
|
||||||
return annotations;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LSPosedClassDef implements ClassDef {
|
|
||||||
@NonNull
|
|
||||||
final TypeId type;
|
|
||||||
final int accessFlags;
|
|
||||||
@Nullable
|
|
||||||
final TypeId superClass;
|
|
||||||
@Nullable
|
|
||||||
final TypeId[] interfaces;
|
|
||||||
@Nullable
|
|
||||||
final StringId sourceFile;
|
|
||||||
@NonNull
|
|
||||||
final EncodedField[] staticFields;
|
|
||||||
@NonNull
|
|
||||||
final EncodedField[] instanceFields;
|
|
||||||
@NonNull
|
|
||||||
final EncodedMethod[] directMethods;
|
|
||||||
@NonNull
|
|
||||||
final EncodedMethod[] virtualMethods;
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
final Annotation[] classAnnotations;
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
final FieldAnnotation[] fieldAnnotations;
|
|
||||||
@NonNull
|
|
||||||
final MethodAnnotation[] methodAnnotations;
|
|
||||||
@NonNull
|
|
||||||
final ParameterAnnotation[] parameterAnnotations;
|
|
||||||
|
|
||||||
LSPosedClassDef(int[] classDef, Object[] classAnnotations, Object[] fieldAnnotations, Object[] methodAnnotations, Object[] parameterAnnotations) {
|
|
||||||
var iter = 0;
|
|
||||||
type = typeIds[classDef[iter++]];
|
|
||||||
accessFlags = classDef[iter++];
|
|
||||||
var superClass = classDef[iter++];
|
|
||||||
this.superClass = superClass == NO_INDEX ? null : typeIds[superClass];
|
|
||||||
var sourceFile = classDef[iter++];
|
|
||||||
this.sourceFile = sourceFile == NO_INDEX ? null : strings[sourceFile];
|
|
||||||
var num_interfaces = classDef[iter++];
|
|
||||||
interfaces = new TypeId[num_interfaces];
|
|
||||||
for (int i = 0; i < num_interfaces; ++i) {
|
|
||||||
interfaces[i] = typeIds[classDef[iter++]];
|
|
||||||
}
|
|
||||||
var num_static_fields = classDef[iter++];
|
|
||||||
staticFields = new EncodedField[num_static_fields];
|
|
||||||
for (int i = 0; i < num_static_fields; ++i) {
|
|
||||||
var field = classDef[iter++];
|
|
||||||
var accessFlags = classDef[iter++];
|
|
||||||
staticFields[i] = new LSPosedEncodedField(field, accessFlags);
|
|
||||||
}
|
|
||||||
var num_instance_fields = classDef[iter++];
|
|
||||||
instanceFields = new EncodedField[num_instance_fields];
|
|
||||||
for (int i = 0; i < num_instance_fields; ++i) {
|
|
||||||
var field = classDef[iter++];
|
|
||||||
var accessFlags = classDef[iter++];
|
|
||||||
instanceFields[i] = new LSPosedEncodedField(field, accessFlags);
|
|
||||||
}
|
|
||||||
var num_direct_methods = classDef[iter++];
|
|
||||||
directMethods = new EncodedMethod[num_direct_methods];
|
|
||||||
for (int i = 0; i < num_direct_methods; ++i) {
|
|
||||||
var method = classDef[iter++];
|
|
||||||
var accessFlags = classDef[iter++];
|
|
||||||
var code = classDef[iter++];
|
|
||||||
directMethods[i] = new LSPosedEncodedMethod(method, accessFlags, code);
|
|
||||||
}
|
|
||||||
var num_virtual_methods = classDef[iter++];
|
|
||||||
virtualMethods = new EncodedMethod[num_virtual_methods];
|
|
||||||
for (int i = 0; i < num_virtual_methods; ++i) {
|
|
||||||
var method = classDef[iter++];
|
|
||||||
var accessFlags = classDef[iter++];
|
|
||||||
var code = classDef[iter++];
|
|
||||||
virtualMethods[i] = new LSPosedEncodedMethod(method, accessFlags, code);
|
|
||||||
}
|
|
||||||
|
|
||||||
var num_class_annotations = classDef[iter++];
|
|
||||||
this.classAnnotations = new LSPosedAnnotation[num_class_annotations];
|
|
||||||
if (num_class_annotations > 0) {
|
|
||||||
var a = (int[]) classAnnotations[0];
|
|
||||||
var b = (Object[]) classAnnotations[1];
|
|
||||||
for (int i = 0; i < num_class_annotations; ++i) {
|
|
||||||
this.classAnnotations[i] = new LSPosedAnnotation(a[2 * i], a[2 * i + 1], (int[]) b[2 * i], (ByteBuffer[]) b[2 * i + 1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var num_field_annotations = classDef[iter++];
|
|
||||||
this.fieldAnnotations = new FieldAnnotation[num_field_annotations];
|
|
||||||
for (int i = 0; i < num_field_annotations; ++i) {
|
|
||||||
var field = classDef[iter++];
|
|
||||||
this.fieldAnnotations[i] = new LSPosedFieldAnnotation(field, (Object[]) fieldAnnotations[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
var num_method_annotations = classDef[iter++];
|
|
||||||
this.methodAnnotations = new MethodAnnotation[num_method_annotations];
|
|
||||||
for (int i = 0; i < num_method_annotations; ++i) {
|
|
||||||
var method = classDef[iter++];
|
|
||||||
this.methodAnnotations[i] = new LSPosedMethodAnnotation(method, (Object[]) methodAnnotations[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
var num_parameter_annotations = classDef[iter++];
|
|
||||||
this.parameterAnnotations = new ParameterAnnotation[num_parameter_annotations];
|
|
||||||
for (int i = 0; i < num_parameter_annotations; ++i) {
|
|
||||||
var method = classDef[iter++];
|
|
||||||
this.parameterAnnotations[i] = new LSPosedParameterAnnotation(method, (Object[][]) parameterAnnotations[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public TypeId getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getAccessFlags() {
|
|
||||||
return accessFlags;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public TypeId getSuperClass() {
|
|
||||||
return superClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public TypeId[] getInterfaces() {
|
|
||||||
return interfaces;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public StringId getSourceFile() {
|
|
||||||
return sourceFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public EncodedField[] getStaticFields() {
|
|
||||||
return staticFields;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public EncodedField[] getInstanceFields() {
|
|
||||||
return instanceFields;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public EncodedMethod[] getDirectMethods() {
|
|
||||||
return directMethods;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public EncodedMethod[] getVirtualMethods() {
|
|
||||||
return virtualMethods;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public Annotation[] getClassAnnotations() {
|
|
||||||
return classAnnotations;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public FieldAnnotation[] getFieldAnnotations() {
|
|
||||||
return fieldAnnotations;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public MethodAnnotation[] getMethodAnnotations() {
|
|
||||||
return methodAnnotations;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public ParameterAnnotation[] getParameterAnnotations() {
|
|
||||||
return parameterAnnotations;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public ClassDef[] getClassDef() {
|
|
||||||
return classDefs;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public StringId[] getStringId() {
|
public StringId[] getStringId() {
|
||||||
|
|
@ -738,4 +344,24 @@ public class LSPosedDexParser implements DexParser {
|
||||||
public ProtoId[] getProtoId() {
|
public ProtoId[] getProtoId() {
|
||||||
return protoIds;
|
return protoIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Annotation[] getAnnotations() {
|
||||||
|
return annotations;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
synchronized public void visitDefinedClasses(@NonNull ClassVisitor visitor) {
|
||||||
|
if (cookie == 0) {
|
||||||
|
throw new IllegalStateException("Closed");
|
||||||
|
}
|
||||||
|
var classVisitMethod = ClassVisitor.class.getDeclaredMethods()[0];
|
||||||
|
var fieldVisitMethod = FieldVisitor.class.getDeclaredMethods()[0];
|
||||||
|
var methodVisitMethod = MethodVisitor.class.getDeclaredMethods()[0];
|
||||||
|
var methodBodyVisitMethod = MethodBodyVisitor.class.getDeclaredMethods()[0];
|
||||||
|
var stopMethod = EarlyStopVisitor.class.getDeclaredMethods()[0];
|
||||||
|
|
||||||
|
DexParserBridge.visitClass(cookie, visitor, FieldVisitor.class, MethodVisitor.class, classVisitMethod, fieldVisitMethod, methodVisitMethod, methodBodyVisitMethod, stopMethod);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,19 @@
|
||||||
package org.lsposed.lspd.nativebridge;
|
package org.lsposed.lspd.nativebridge;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import dalvik.annotation.optimization.FastNative;
|
import dalvik.annotation.optimization.FastNative;
|
||||||
|
import io.github.libxposed.utils.DexParser;
|
||||||
|
|
||||||
public class DexParserBridge {
|
public class DexParserBridge {
|
||||||
public static native Object parseDex(ByteBuffer dex) throws IOException;
|
@FastNative
|
||||||
|
public static native Object openDex(ByteBuffer data, long[] args) throws IOException;
|
||||||
|
|
||||||
@FastNative
|
@FastNative
|
||||||
public static native Object parseMethod(ByteBuffer dex, int codeOffset);
|
public static native void closeDex(long cookie);
|
||||||
|
|
||||||
|
@FastNative
|
||||||
|
public static native void visitClass(long cookie, DexParser.ClassVisitor visitor, Class<DexParser.FieldVisitor> fieldVisitorClass, Class<DexParser.MethodVisitor> methodVisitorClass, Method classVisitMethod, Method fieldVisitMethod, Method methodVisitMethod, Method methodBodyVisitMethod, Method stopMethod);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,38 +21,39 @@
|
||||||
#include "native_util.h"
|
#include "native_util.h"
|
||||||
#include "slicer/reader.h"
|
#include "slicer/reader.h"
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <absl/container/flat_hash_map.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
jobject ParseAnnotation(JNIEnv *env, dex::Reader &dex, jclass object_class,
|
using AnnotationList = std::vector<std::tuple<jint/*vis*/, jint /*type*/, std::vector<std::tuple<jint /*name*/, jint/*value_type*/, std::string_view/*value*/>>>>;
|
||||||
|
|
||||||
|
void ParseAnnotation(JNIEnv *env, dex::Reader &dex, AnnotationList &annotation_list,
|
||||||
|
std::vector<jint> indices,
|
||||||
const dex::AnnotationSetItem *annotation) {
|
const dex::AnnotationSetItem *annotation) {
|
||||||
if (annotation == nullptr) {
|
if (annotation == nullptr) {
|
||||||
return nullptr;
|
return;
|
||||||
}
|
}
|
||||||
auto a = env->NewIntArray(static_cast<jint>(2 * annotation->size));
|
|
||||||
auto *a_ptr = env->GetIntArrayElements(a, nullptr);
|
|
||||||
auto b = env->NewObjectArray(static_cast<jint>(2 * annotation->size), object_class,
|
|
||||||
nullptr);
|
|
||||||
for (size_t i = 0; i < annotation->size; ++i) {
|
for (size_t i = 0; i < annotation->size; ++i) {
|
||||||
auto *item = dex.dataPtr<dex::AnnotationItem>(annotation->entries[i]);
|
auto *item = dex.dataPtr<dex::AnnotationItem>(annotation->entries[i]);
|
||||||
a_ptr[2 * i] = item->visibility;
|
|
||||||
auto *annotation_data = item->annotation;
|
auto *annotation_data = item->annotation;
|
||||||
a_ptr[2 * i + 1] = static_cast<jint>(dex::ReadULeb128(&annotation_data));
|
indices.emplace_back(annotation_list.size());
|
||||||
|
auto &[visibility, type, items] = annotation_list.emplace_back(item->visibility,
|
||||||
|
dex::ReadULeb128(
|
||||||
|
&annotation_data),
|
||||||
|
std::vector<std::tuple<jint, jint, std::string_view>>());
|
||||||
auto size = dex::ReadULeb128(&annotation_data);
|
auto size = dex::ReadULeb128(&annotation_data);
|
||||||
auto b2i0 = env->NewIntArray(static_cast<jint>(2 * size));
|
items.resize(size);
|
||||||
auto *b2i0_ptr = env->GetIntArrayElements(b2i0, nullptr);
|
|
||||||
auto b2i1 = env->NewObjectArray(static_cast<jint>(size), object_class, nullptr);
|
|
||||||
for (size_t j = 0; j < size; ++j) {
|
for (size_t j = 0; j < size; ++j) {
|
||||||
b2i0_ptr[2 * j] = static_cast<jint>(dex::ReadULeb128(&annotation_data));
|
auto &[name, value_type, value] = items[j];
|
||||||
|
name = static_cast<jint>(dex::ReadULeb128(&annotation_data));
|
||||||
auto arg_and_type = *annotation_data++;
|
auto arg_and_type = *annotation_data++;
|
||||||
auto value_type = arg_and_type & 0x1f;
|
value_type = arg_and_type & 0x1f;
|
||||||
b2i0_ptr[2 * j + 1] = value_type;
|
|
||||||
auto value_arg = arg_and_type >> 5;
|
auto value_arg = arg_and_type >> 5;
|
||||||
jobject value = nullptr;
|
|
||||||
switch (value_type) {
|
switch (value_type) {
|
||||||
case 0x00: // byte
|
case 0x00: // byte
|
||||||
case 0x1f: // boolean
|
case 0x1f: // boolean
|
||||||
value = env->NewDirectByteBuffer(
|
value = {reinterpret_cast<char *>(const_cast<dex::u1 *>(annotation_data)),
|
||||||
reinterpret_cast<void *>(const_cast<dex::u1 *>(annotation_data)),
|
1};
|
||||||
1);
|
|
||||||
break;
|
break;
|
||||||
case 0x02: // short
|
case 0x02: // short
|
||||||
case 0x03: // char
|
case 0x03: // char
|
||||||
|
|
@ -65,38 +66,60 @@ namespace {
|
||||||
case 0x19: // field
|
case 0x19: // field
|
||||||
case 0x1a: // method
|
case 0x1a: // method
|
||||||
case 0x1b: // enum
|
case 0x1b: // enum
|
||||||
value = env->NewDirectByteBuffer(
|
value = {
|
||||||
reinterpret_cast<void *>(const_cast<dex::u1 *>(annotation_data)),
|
reinterpret_cast<char *>(const_cast<dex::u1 *>(annotation_data)),
|
||||||
value_arg);
|
static_cast<size_t>(value_arg)};
|
||||||
break;
|
break;
|
||||||
case 0x1c: // array
|
case 0x1c: // array
|
||||||
case 0x1d: // annotation
|
case 0x1d: // annotation
|
||||||
case 0x1e: // null
|
case 0x1e: // null
|
||||||
// not supported
|
|
||||||
value = nullptr;
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
|
// not supported
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
env->SetObjectArrayElement(b2i1, static_cast<jint>(j), value);
|
|
||||||
env->DeleteLocalRef(value);
|
|
||||||
}
|
}
|
||||||
env->SetObjectArrayElement(b, static_cast<jint>(2 * i), b2i0);
|
|
||||||
env->SetObjectArrayElement(b, static_cast<jint>(2 * i + 1), b2i1);
|
|
||||||
env->DeleteLocalRef(b2i0);
|
|
||||||
env->DeleteLocalRef(b2i1);
|
|
||||||
}
|
}
|
||||||
env->ReleaseIntArrayElements(a, a_ptr, 0);
|
|
||||||
auto res = env->NewObjectArray(2, object_class, nullptr);
|
|
||||||
env->SetObjectArrayElement(res, 0, a);
|
|
||||||
env->SetObjectArrayElement(res, 1, b);
|
|
||||||
env->DeleteLocalRef(a);
|
|
||||||
env->DeleteLocalRef(b);
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DexParser : public dex::Reader {
|
||||||
|
public:
|
||||||
|
DexParser(const dex::u1 *data, size_t size) : dex::Reader(data, size, nullptr, 0) {}
|
||||||
|
|
||||||
|
struct ClassData {
|
||||||
|
std::vector<jint> interfaces;
|
||||||
|
std::vector<jint> static_fields;
|
||||||
|
std::vector<jint> static_fields_access_flags;
|
||||||
|
std::vector<jint> instance_fields;
|
||||||
|
std::vector<jint> instance_fields_access_flags;
|
||||||
|
std::vector<jint> direct_methods;
|
||||||
|
std::vector<jint> direct_methods_access_flags;
|
||||||
|
std::vector<const dex::Code *> direct_methods_code;
|
||||||
|
std::vector<jint> virtual_methods;
|
||||||
|
std::vector<jint> virtual_methods_access_flags;
|
||||||
|
std::vector<const dex::Code *> virtual_methods_code;
|
||||||
|
std::vector<jint> annotations;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MethodBody {
|
||||||
|
bool loaded;
|
||||||
|
std::vector<jint> referred_strings;
|
||||||
|
std::vector<jint> accessed_fields;
|
||||||
|
std::vector<jint> assigned_fields;
|
||||||
|
std::vector<jint> invoked_methods;
|
||||||
|
std::vector<jbyte> opcodes;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<ClassData> class_data;
|
||||||
|
absl::flat_hash_map<jint, std::vector<jint>> field_annotations;
|
||||||
|
absl::flat_hash_map<jint, std::vector<jint>> method_annotations;
|
||||||
|
absl::flat_hash_map<jint, std::vector<jint>> parameter_annotations;
|
||||||
|
|
||||||
|
absl::flat_hash_map<jint, MethodBody> method_bodies;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace lspd {
|
namespace lspd {
|
||||||
LSP_DEF_NATIVE_METHOD(jobject, DexParserBridge, parseDex, jobject data) {
|
LSP_DEF_NATIVE_METHOD(jobject, DexParserBridge, openDex, jobject data, jlongArray args) {
|
||||||
auto dex_size = env->GetDirectBufferCapacity(data);
|
auto dex_size = env->GetDirectBufferCapacity(data);
|
||||||
if (dex_size == -1) {
|
if (dex_size == -1) {
|
||||||
env->ThrowNew(env->FindClass("java/io/IOException"), "Invalid dex data");
|
env->ThrowNew(env->FindClass("java/io/IOException"), "Invalid dex data");
|
||||||
|
|
@ -104,7 +127,9 @@ namespace lspd {
|
||||||
}
|
}
|
||||||
auto *dex_data = env->GetDirectBufferAddress(data);
|
auto *dex_data = env->GetDirectBufferAddress(data);
|
||||||
|
|
||||||
dex::Reader dex(reinterpret_cast<dex::u1 *>(dex_data), dex_size, nullptr, 0);
|
auto *dex_reader = new DexParser(reinterpret_cast<dex::u1 *>(dex_data), dex_size);
|
||||||
|
env->SetLongArrayRegion(args, 0, 1, reinterpret_cast<const jlong *>(&dex_reader));
|
||||||
|
auto &dex = *dex_reader;
|
||||||
if (dex.IsCompact()) {
|
if (dex.IsCompact()) {
|
||||||
env->ThrowNew(env->FindClass("java/io/IOException"), "Compact dex is not supported");
|
env->ThrowNew(env->FindClass("java/io/IOException"), "Compact dex is not supported");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
@ -118,7 +143,7 @@ namespace lspd {
|
||||||
auto strings = dex.StringIds();
|
auto strings = dex.StringIds();
|
||||||
for (size_t i = 0; i < strings.size(); ++i) {
|
for (size_t i = 0; i < strings.size(); ++i) {
|
||||||
const auto *ptr = dex.dataPtr<dex::u1>(strings[i].string_data_off);
|
const auto *ptr = dex.dataPtr<dex::u1>(strings[i].string_data_off);
|
||||||
size_t len = dex::ReadULeb128(&ptr);
|
[[maybe_unused]] size_t len = dex::ReadULeb128(&ptr);
|
||||||
auto str = env->NewStringUTF(reinterpret_cast<const char *>(ptr));
|
auto str = env->NewStringUTF(reinterpret_cast<const char *>(ptr));
|
||||||
env->SetObjectArrayElement(out0, static_cast<jint>(i), str);
|
env->SetObjectArrayElement(out0, static_cast<jint>(i), str);
|
||||||
env->DeleteLocalRef(str);
|
env->DeleteLocalRef(str);
|
||||||
|
|
@ -185,31 +210,33 @@ namespace lspd {
|
||||||
env->DeleteLocalRef(out4);
|
env->DeleteLocalRef(out4);
|
||||||
|
|
||||||
auto classes = dex.ClassDefs();
|
auto classes = dex.ClassDefs();
|
||||||
auto out5 = env->NewObjectArray(static_cast<jint>(classes.size()),
|
dex.class_data.resize(classes.size());
|
||||||
int_array_class, nullptr);
|
|
||||||
auto out6 = env->NewObjectArray(static_cast<jint>(4 * classes.size()),
|
AnnotationList annotation_list;
|
||||||
object_class, nullptr);
|
|
||||||
for (size_t i = 0; i < classes.size(); ++i) {
|
for (size_t i = 0; i < classes.size(); ++i) {
|
||||||
auto &class_def = classes[i];
|
auto &class_def = classes[i];
|
||||||
auto &interfaces = class_def.interfaces_off ? *dex.dataPtr<dex::TypeList>(
|
|
||||||
class_def.interfaces_off) : empty_type_list;
|
|
||||||
|
|
||||||
dex::u4 static_fields_count = 0;
|
dex::u4 static_fields_count = 0;
|
||||||
dex::u4 instance_fields_count = 0;
|
dex::u4 instance_fields_count = 0;
|
||||||
dex::u4 direct_methods_count = 0;
|
dex::u4 direct_methods_count = 0;
|
||||||
dex::u4 virtual_methods_count = 0;
|
dex::u4 virtual_methods_count = 0;
|
||||||
|
const dex::u1 *class_data_ptr = nullptr;
|
||||||
|
|
||||||
|
const dex::AnnotationsDirectoryItem *annotations = nullptr;
|
||||||
|
const dex::AnnotationSetItem *class_annotation = nullptr;
|
||||||
dex::u4 field_annotations_count = 0;
|
dex::u4 field_annotations_count = 0;
|
||||||
dex::u4 method_annotations_count = 0;
|
dex::u4 method_annotations_count = 0;
|
||||||
dex::u4 parameter_annotations_count = 0;
|
dex::u4 parameter_annotations_count = 0;
|
||||||
const dex::u1 *class_data = nullptr;
|
|
||||||
const dex::AnnotationsDirectoryItem *annotations = nullptr;
|
auto &class_data = dex.class_data[i];
|
||||||
const dex::AnnotationSetItem *class_annotation = nullptr;
|
|
||||||
if (class_def.class_data_off != 0) {
|
if (class_def.interfaces_off) {
|
||||||
class_data = dex.dataPtr<dex::u1>(class_def.class_data_off);
|
auto defined_interfaces = dex.dataPtr<dex::TypeList>(class_def.interfaces_off);
|
||||||
static_fields_count = dex::ReadULeb128(&class_data);
|
class_data.interfaces.resize(defined_interfaces->size);
|
||||||
instance_fields_count = dex::ReadULeb128(&class_data);
|
for (size_t k = 0; k < class_data.interfaces.size(); ++k) {
|
||||||
direct_methods_count = dex::ReadULeb128(&class_data);
|
class_data.interfaces[k] = defined_interfaces->list[k].type_idx;
|
||||||
virtual_methods_count = dex::ReadULeb128(&class_data);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (class_def.annotations_off != 0) {
|
if (class_def.annotations_off != 0) {
|
||||||
|
|
@ -223,131 +250,156 @@ namespace lspd {
|
||||||
parameter_annotations_count = annotations->parameters_size;
|
parameter_annotations_count = annotations->parameters_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto array_size = 4 + 1 + interfaces.size + 1 + 2 * static_fields_count +
|
if (class_def.class_data_off != 0) {
|
||||||
1 + 2 * instance_fields_count + 1 + 3 * direct_methods_count +
|
class_data_ptr = dex.dataPtr<dex::u1>(class_def.class_data_off);
|
||||||
1 + 3 * virtual_methods_count + 1 +
|
static_fields_count = dex::ReadULeb128(&class_data_ptr);
|
||||||
1 + field_annotations_count + 1 + method_annotations_count +
|
instance_fields_count = dex::ReadULeb128(&class_data_ptr);
|
||||||
1 + parameter_annotations_count;
|
direct_methods_count = dex::ReadULeb128(&class_data_ptr);
|
||||||
auto out5i = env->NewIntArray(static_cast<int>(array_size));
|
virtual_methods_count = dex::ReadULeb128(&class_data_ptr);
|
||||||
auto *out5i_ptr = env->GetIntArrayElements(out5i, nullptr);
|
class_data.static_fields.resize(static_fields_count);
|
||||||
size_t j = 0;
|
class_data.static_fields_access_flags.resize(static_fields_count);
|
||||||
out5i_ptr[j++] = static_cast<jint>(class_def.class_idx);
|
class_data.instance_fields.resize(instance_fields_count);
|
||||||
out5i_ptr[j++] = static_cast<jint>(class_def.access_flags);
|
class_data.instance_fields_access_flags.resize(instance_fields_count);
|
||||||
out5i_ptr[j++] = static_cast<jint>(class_def.superclass_idx);
|
class_data.direct_methods.resize(direct_methods_count);
|
||||||
out5i_ptr[j++] = static_cast<jint>(class_def.source_file_idx);
|
class_data.direct_methods_access_flags.resize(direct_methods_count);
|
||||||
out5i_ptr[j++] = static_cast<jint>(interfaces.size);
|
class_data.direct_methods_code.resize(direct_methods_count);
|
||||||
for (size_t k = 0; k < interfaces.size; ++k) {
|
class_data.virtual_methods.resize(virtual_methods_count);
|
||||||
out5i_ptr[j++] = static_cast<jint>(interfaces.list[k].type_idx);
|
class_data.virtual_methods_access_flags.resize(virtual_methods_count);
|
||||||
|
class_data.virtual_methods_code.resize(virtual_methods_count);
|
||||||
}
|
}
|
||||||
out5i_ptr[j++] = static_cast<jint>(static_fields_count);
|
|
||||||
|
if (class_data_ptr) {
|
||||||
for (size_t k = 0, field_idx = 0; k < static_fields_count; ++k) {
|
for (size_t k = 0, field_idx = 0; k < static_fields_count; ++k) {
|
||||||
out5i_ptr[j++] = static_cast<jint>(field_idx += dex::ReadULeb128(&class_data));
|
class_data.static_fields[k] = static_cast<jint>(field_idx += dex::ReadULeb128(
|
||||||
out5i_ptr[j++] = static_cast<jint>(dex::ReadULeb128(&class_data));
|
&class_data_ptr));
|
||||||
|
class_data.static_fields_access_flags[k] = static_cast<jint>(dex::ReadULeb128(
|
||||||
|
&class_data_ptr));
|
||||||
}
|
}
|
||||||
out5i_ptr[j++] = static_cast<jint>(instance_fields_count);
|
|
||||||
for (size_t k = 0, field_idx = 0; k < instance_fields_count; ++k) {
|
for (size_t k = 0, field_idx = 0; k < instance_fields_count; ++k) {
|
||||||
out5i_ptr[j++] = static_cast<jint>(field_idx += dex::ReadULeb128(&class_data));
|
class_data.instance_fields[k] = static_cast<jint>(field_idx += dex::ReadULeb128(
|
||||||
out5i_ptr[j++] = static_cast<jint>(dex::ReadULeb128(&class_data));
|
&class_data_ptr));
|
||||||
|
class_data.instance_fields_access_flags[k] = static_cast<jint>(dex::ReadULeb128(
|
||||||
|
&class_data_ptr));
|
||||||
}
|
}
|
||||||
out5i_ptr[j++] = static_cast<jint>(direct_methods_count);
|
|
||||||
for (size_t k = 0, method_idx = 0; k < direct_methods_count; ++k) {
|
for (size_t k = 0, method_idx = 0; k < direct_methods_count; ++k) {
|
||||||
out5i_ptr[j++] = static_cast<jint>(method_idx += dex::ReadULeb128(&class_data));
|
class_data.direct_methods[k] = static_cast<jint>(method_idx += dex::ReadULeb128(
|
||||||
out5i_ptr[j++] = static_cast<jint>(dex::ReadULeb128(&class_data));
|
&class_data_ptr));
|
||||||
out5i_ptr[j++] = static_cast<jint>(dex::ReadULeb128(&class_data));
|
class_data.direct_methods_access_flags[k] = static_cast<jint>(dex::ReadULeb128(
|
||||||
|
&class_data_ptr));
|
||||||
|
auto code_off = dex::ReadULeb128(&class_data_ptr);
|
||||||
|
class_data.direct_methods_code[k] = code_off ? dex.dataPtr<dex::Code>(code_off)
|
||||||
|
: nullptr;
|
||||||
}
|
}
|
||||||
out5i_ptr[j++] = static_cast<jint>(virtual_methods_count);
|
|
||||||
for (size_t k = 0, method_idx = 0; k < virtual_methods_count; ++k) {
|
for (size_t k = 0, method_idx = 0; k < virtual_methods_count; ++k) {
|
||||||
out5i_ptr[j++] = static_cast<jint>(method_idx += dex::ReadULeb128(&class_data));
|
class_data.virtual_methods[k] = static_cast<jint>(method_idx += dex::ReadULeb128(
|
||||||
out5i_ptr[j++] = static_cast<jint>(dex::ReadULeb128(&class_data));
|
&class_data_ptr));
|
||||||
out5i_ptr[j++] = static_cast<jint>(dex::ReadULeb128(&class_data));
|
class_data.virtual_methods_access_flags[k] = static_cast<jint>(dex::ReadULeb128(
|
||||||
|
&class_data_ptr));
|
||||||
|
auto code_off = dex::ReadULeb128(&class_data_ptr);
|
||||||
|
class_data.virtual_methods_code[k] = code_off ? dex.dataPtr<dex::Code>(code_off)
|
||||||
|
: nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto out6i0 = ParseAnnotation(env, dex, object_class, class_annotation);
|
ParseAnnotation(env, dex, annotation_list, class_data.annotations, class_annotation);
|
||||||
env->SetObjectArrayElement(out6, static_cast<jint>(4 * i), out6i0);
|
|
||||||
env->DeleteLocalRef(out6i0);
|
|
||||||
|
|
||||||
out5i_ptr[j++] = static_cast<jint>(class_annotation ? class_annotation->size : 0);
|
|
||||||
|
|
||||||
out5i_ptr[j++] = static_cast<jint>(field_annotations_count);
|
|
||||||
auto *field_annotations = annotations
|
auto *field_annotations = annotations
|
||||||
? reinterpret_cast<const dex::FieldAnnotationsItem *>(
|
? reinterpret_cast<const dex::FieldAnnotationsItem *>(
|
||||||
annotations + 1) : nullptr;
|
annotations + 1) : nullptr;
|
||||||
auto out6i1 = env->NewObjectArray(static_cast<jint>(field_annotations_count),
|
|
||||||
object_class, nullptr);
|
|
||||||
for (size_t k = 0; k < field_annotations_count; ++k) {
|
for (size_t k = 0; k < field_annotations_count; ++k) {
|
||||||
out5i_ptr[j++] = static_cast<jint>(field_annotations[k].field_idx);
|
|
||||||
auto *field_annotation = dex.dataPtr<dex::AnnotationSetItem>(
|
auto *field_annotation = dex.dataPtr<dex::AnnotationSetItem>(
|
||||||
field_annotations[k].annotations_off);
|
field_annotations[k].annotations_off);
|
||||||
auto out6i1i = ParseAnnotation(env, dex, object_class, field_annotation);
|
ParseAnnotation(env, dex, annotation_list,
|
||||||
env->SetObjectArrayElement(out6i1, static_cast<jint>(k), out6i1i);
|
dex.field_annotations[static_cast<jint>(field_annotations[k].field_idx)],
|
||||||
env->DeleteLocalRef(out6i1i);
|
field_annotation);
|
||||||
}
|
}
|
||||||
env->SetObjectArrayElement(out6, static_cast<jint>(4 * i + 1), out6i1);
|
|
||||||
env->DeleteLocalRef(out6i1);
|
|
||||||
|
|
||||||
out5i_ptr[j++] = static_cast<jint>(method_annotations_count);
|
|
||||||
auto *method_annotations = field_annotations
|
auto *method_annotations = field_annotations
|
||||||
? reinterpret_cast<const dex::MethodAnnotationsItem *>(
|
? reinterpret_cast<const dex::MethodAnnotationsItem *>(
|
||||||
field_annotations + field_annotations_count)
|
field_annotations + field_annotations_count)
|
||||||
: nullptr;
|
: nullptr;
|
||||||
auto out6i2 = env->NewObjectArray(static_cast<jint>(method_annotations_count),
|
|
||||||
object_class, nullptr);
|
|
||||||
for (size_t k = 0; k < method_annotations_count; ++k) {
|
for (size_t k = 0; k < method_annotations_count; ++k) {
|
||||||
out5i_ptr[j++] = static_cast<jint>(method_annotations[k].method_idx);
|
|
||||||
auto *method_annotation = dex.dataPtr<dex::AnnotationSetItem>(
|
auto *method_annotation = dex.dataPtr<dex::AnnotationSetItem>(
|
||||||
method_annotations[k].annotations_off);
|
method_annotations[k].annotations_off);
|
||||||
auto out6i2i = ParseAnnotation(env, dex, object_class, method_annotation);
|
ParseAnnotation(env, dex, annotation_list,
|
||||||
env->SetObjectArrayElement(out6i2, static_cast<jint>(k), out6i2i);
|
dex.method_annotations[static_cast<jint>(method_annotations[k].method_idx)],
|
||||||
env->DeleteLocalRef(out6i2i);
|
method_annotation);
|
||||||
}
|
}
|
||||||
env->SetObjectArrayElement(out6, static_cast<jint>(4 * i + 2), out6i2);
|
|
||||||
|
|
||||||
out5i_ptr[j++] = static_cast<jint>(parameter_annotations_count);
|
|
||||||
auto *parameter_annotations = method_annotations
|
auto *parameter_annotations = method_annotations
|
||||||
? reinterpret_cast<const dex::ParameterAnnotationsItem *>(
|
? reinterpret_cast<const dex::ParameterAnnotationsItem *>(
|
||||||
method_annotations + method_annotations_count)
|
method_annotations + method_annotations_count)
|
||||||
: nullptr;
|
: nullptr;
|
||||||
auto out6i3 = env->NewObjectArray(static_cast<jint>(parameter_annotations_count),
|
|
||||||
object_class, nullptr);
|
|
||||||
for (size_t k = 0; k < parameter_annotations_count; ++k) {
|
for (size_t k = 0; k < parameter_annotations_count; ++k) {
|
||||||
out5i_ptr[j++] = static_cast<jint>(parameter_annotations[k].method_idx);
|
|
||||||
auto *parameter_annotation = dex.dataPtr<dex::AnnotationSetRefList>(
|
auto *parameter_annotation = dex.dataPtr<dex::AnnotationSetRefList>(
|
||||||
parameter_annotations[k].annotations_off);
|
parameter_annotations[k].annotations_off);
|
||||||
auto out6i3i = env->NewObjectArray(
|
auto &indices = dex.parameter_annotations[static_cast<jint>(parameter_annotations[k].method_idx)];
|
||||||
static_cast<jint>(parameter_annotation->size), object_class, nullptr);
|
|
||||||
for (size_t l = 0; l < parameter_annotation->size; ++l) {
|
for (size_t l = 0; l < parameter_annotation->size; ++l) {
|
||||||
if (parameter_annotation->list[l].annotations_off != 0) {
|
if (parameter_annotation->list[l].annotations_off != 0) {
|
||||||
auto *parameter_annotation_item = dex.dataPtr<dex::AnnotationSetItem>(
|
auto *parameter_annotation_item = dex.dataPtr<dex::AnnotationSetItem>(
|
||||||
parameter_annotation->list[l].annotations_off);
|
parameter_annotation->list[l].annotations_off);
|
||||||
auto out6i3ii = ParseAnnotation(env, dex, object_class,
|
ParseAnnotation(env, dex, annotation_list, indices,
|
||||||
parameter_annotation_item);
|
parameter_annotation_item);
|
||||||
env->SetObjectArrayElement(out6i3i, static_cast<jint>(l), out6i3ii);
|
}
|
||||||
env->DeleteLocalRef(out6i3ii);
|
indices.emplace_back(dex::kNoIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
env->SetObjectArrayElement(out6i3, static_cast<jint>(k), out6i3i);
|
|
||||||
env->DeleteLocalRef(out6i3i);
|
|
||||||
}
|
}
|
||||||
env->SetObjectArrayElement(out6, static_cast<jint>(4 * i + 3), out6i3);
|
return out;
|
||||||
env->DeleteLocalRef(out6i3);
|
auto out5 = env->NewIntArray(static_cast<jint>(2 * annotation_list.size()));
|
||||||
|
auto out6 = env->NewObjectArray(static_cast<jint>(2 * annotation_list.size()), object_class,
|
||||||
env->ReleaseIntArrayElements(out5i, out5i_ptr, 0);
|
nullptr);
|
||||||
env->SetObjectArrayElement(out5, static_cast<jint>(i), out5i);
|
auto out5_ptr = env->GetIntArrayElements(out5, nullptr);
|
||||||
env->DeleteLocalRef(out5i);
|
size_t i = 0;
|
||||||
|
for (auto &[visibility, type, items]: annotation_list) {
|
||||||
|
auto out6i0 = env->NewIntArray(static_cast<jint>(2 * items.size()));
|
||||||
|
auto out6i0_ptr = env->GetIntArrayElements(out6i0, nullptr);
|
||||||
|
auto out6i1 = env->NewObjectArray(static_cast<jint>(items.size()), object_class,
|
||||||
|
nullptr);
|
||||||
|
size_t j = 0;
|
||||||
|
for (auto&[name, value_type, value]: items) {
|
||||||
|
auto java_value = env->NewDirectByteBuffer(const_cast<char *>(value.data()),
|
||||||
|
value.size());
|
||||||
|
env->SetObjectArrayElement(out6i1, static_cast<jint>(j), java_value);
|
||||||
|
out6i0_ptr[2 * j] = name;
|
||||||
|
out6i0_ptr[2 * j + 1] = value_type;
|
||||||
|
env->DeleteLocalRef(java_value);
|
||||||
|
++j;
|
||||||
}
|
}
|
||||||
|
env->ReleaseIntArrayElements(out6i0, out6i0_ptr, 0);
|
||||||
|
env->SetObjectArrayElement(out6, static_cast<jint>(2 * i), out6i0);
|
||||||
|
env->SetObjectArrayElement(out6, static_cast<jint>(2 * i + 1), out6i1);
|
||||||
|
out5_ptr[2 * i] = visibility;
|
||||||
|
out5_ptr[2 * i + 1] = type;
|
||||||
|
env->DeleteLocalRef(out6i0);
|
||||||
|
env->DeleteLocalRef(out6i1);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
env->ReleaseIntArrayElements(out5, out5_ptr, 0);
|
||||||
|
env->SetObjectArrayElement(out, 5, out5);
|
||||||
|
env->SetObjectArrayElement(out, 6, out6);
|
||||||
|
env->DeleteLocalRef(out5);
|
||||||
|
env->DeleteLocalRef(out6);
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
LSP_DEF_NATIVE_METHOD(jobject, DexParserBridge, parseMethod, jobject data, jint code_offset) {
|
LSP_DEF_NATIVE_METHOD(void, DexParserBridge, closeDex, jlong cookie) {
|
||||||
auto dex_size = env->GetDirectBufferCapacity(data);
|
if (cookie != 0)
|
||||||
auto *dex_data = env->GetDirectBufferAddress(data);
|
delete reinterpret_cast<DexParser *>(cookie);
|
||||||
if (dex_size < 0 || dex_data == nullptr || code_offset >= dex_size) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
auto *code = reinterpret_cast<dex::Code *>(reinterpret_cast<dex::u1 *>(dex_data) +
|
|
||||||
code_offset);
|
|
||||||
|
|
||||||
|
LSP_DEF_NATIVE_METHOD(void, DexParserBridge, visitClass, jlong cookie, jobject visitor,
|
||||||
|
jclass field_visitor_class,
|
||||||
|
jclass method_visitor_class,
|
||||||
|
jobject class_visit_method,
|
||||||
|
jobject field_visit_method,
|
||||||
|
jobject method_visit_method,
|
||||||
|
jobject method_body_visit_method,
|
||||||
|
jobject stop_method) {
|
||||||
static constexpr dex::u1 kOpcodeMask = 0xff;
|
static constexpr dex::u1 kOpcodeMask = 0xff;
|
||||||
static constexpr dex::u1 kOpcodeNoOp = 0x00;
|
static constexpr dex::u1 kOpcodeNoOp = 0x00;
|
||||||
static constexpr dex::u1 kOpcodeConstString = 0x1a;
|
static constexpr dex::u1 kOpcodeConstString = 0x1a;
|
||||||
|
|
@ -367,38 +419,191 @@ namespace lspd {
|
||||||
static constexpr dex::u2 kInstPackedSwitchPlayLoad = 0x0100;
|
static constexpr dex::u2 kInstPackedSwitchPlayLoad = 0x0100;
|
||||||
static constexpr dex::u2 kInstSparseSwitchPlayLoad = 0x0200;
|
static constexpr dex::u2 kInstSparseSwitchPlayLoad = 0x0200;
|
||||||
static constexpr dex::u2 kInstFillArrayDataPlayLoad = 0x0300;
|
static constexpr dex::u2 kInstFillArrayDataPlayLoad = 0x0300;
|
||||||
|
|
||||||
|
if (cookie == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto &dex = *reinterpret_cast<DexParser *>(cookie);
|
||||||
|
auto *visit_class = env->FromReflectedMethod(class_visit_method);
|
||||||
|
auto *visit_field = env->FromReflectedMethod(field_visit_method);
|
||||||
|
auto *visit_method = env->FromReflectedMethod(method_visit_method);
|
||||||
|
auto *visit_method_body = env->FromReflectedMethod(method_body_visit_method);
|
||||||
|
auto *stop = env->FromReflectedMethod(stop_method);
|
||||||
|
|
||||||
|
auto classes = dex.ClassDefs();
|
||||||
|
|
||||||
|
|
||||||
|
for (size_t i = 0; i < classes.size(); ++i) {
|
||||||
|
auto &class_def = classes[i];
|
||||||
|
auto &class_data = dex.class_data[i];
|
||||||
|
auto interfaces = env->NewIntArray(
|
||||||
|
static_cast<jint>(class_data.interfaces.size()));
|
||||||
|
env->SetIntArrayRegion(interfaces, 0,
|
||||||
|
static_cast<jint>(class_data.interfaces.size()),
|
||||||
|
class_data.interfaces.data());
|
||||||
|
auto static_fields = env->NewIntArray(
|
||||||
|
static_cast<jint>(class_data.static_fields.size()));
|
||||||
|
env->SetIntArrayRegion(static_fields, 0,
|
||||||
|
static_cast<jint>(class_data.static_fields.size()),
|
||||||
|
class_data.static_fields.data());
|
||||||
|
auto static_fields_access_flags = env->NewIntArray(
|
||||||
|
static_cast<jint>(class_data.static_fields_access_flags.size()));
|
||||||
|
env->SetIntArrayRegion(static_fields_access_flags, 0,
|
||||||
|
static_cast<jint>(
|
||||||
|
class_data.static_fields_access_flags.size()),
|
||||||
|
class_data.static_fields_access_flags.data());
|
||||||
|
auto instance_fields = env->NewIntArray(
|
||||||
|
static_cast<jint>(class_data.instance_fields.size()));
|
||||||
|
env->SetIntArrayRegion(instance_fields, 0,
|
||||||
|
static_cast<jint>(class_data.instance_fields.size()),
|
||||||
|
class_data.instance_fields.data());
|
||||||
|
auto instance_fields_access_flags = env->NewIntArray(
|
||||||
|
static_cast<jint>(class_data.instance_fields_access_flags.size()));
|
||||||
|
env->SetIntArrayRegion(instance_fields_access_flags, 0,
|
||||||
|
static_cast<jint>(
|
||||||
|
class_data.instance_fields_access_flags.size()),
|
||||||
|
class_data.instance_fields_access_flags.data());
|
||||||
|
auto direct_methods = env->NewIntArray(
|
||||||
|
static_cast<jint>(class_data.direct_methods.size()));
|
||||||
|
env->SetIntArrayRegion(direct_methods, 0,
|
||||||
|
static_cast<jint>(class_data.direct_methods.size()),
|
||||||
|
class_data.direct_methods.data());
|
||||||
|
auto direct_methods_access_flags = env->NewIntArray(
|
||||||
|
static_cast<jint>(class_data.direct_methods_access_flags.size()));
|
||||||
|
env->SetIntArrayRegion(direct_methods_access_flags, 0,
|
||||||
|
static_cast<jint>(
|
||||||
|
class_data.direct_methods_access_flags.size()),
|
||||||
|
class_data.direct_methods_access_flags.data());
|
||||||
|
auto virtual_methods = env->NewIntArray(
|
||||||
|
static_cast<jint>(class_data.virtual_methods.size()));
|
||||||
|
env->SetIntArrayRegion(virtual_methods, 0,
|
||||||
|
static_cast<jint>(class_data.virtual_methods.size()),
|
||||||
|
class_data.virtual_methods.data());
|
||||||
|
auto virtual_methods_access_flags = env->NewIntArray(
|
||||||
|
static_cast<jint>(class_data.virtual_methods_access_flags.size()));
|
||||||
|
env->SetIntArrayRegion(virtual_methods_access_flags, 0,
|
||||||
|
static_cast<jint>(
|
||||||
|
class_data.virtual_methods_access_flags.size()),
|
||||||
|
class_data.virtual_methods_access_flags.data());
|
||||||
|
auto class_annotations = env->NewIntArray(
|
||||||
|
static_cast<jint>(class_data.annotations.size()));
|
||||||
|
env->SetIntArrayRegion(class_annotations, 0,
|
||||||
|
static_cast<jint>(class_data.annotations.size()),
|
||||||
|
class_data.annotations.data());
|
||||||
|
jobject member_visitor = env->CallObjectMethod(visitor, visit_class,
|
||||||
|
static_cast<jint>(class_def.class_idx),
|
||||||
|
static_cast<jint>(class_def.access_flags),
|
||||||
|
static_cast<jint>(class_def.superclass_idx),
|
||||||
|
interfaces,
|
||||||
|
static_cast<jint>(class_def.source_file_idx),
|
||||||
|
static_fields,
|
||||||
|
static_fields_access_flags,
|
||||||
|
instance_fields,
|
||||||
|
instance_fields_access_flags,
|
||||||
|
direct_methods,
|
||||||
|
direct_methods_access_flags,
|
||||||
|
virtual_methods,
|
||||||
|
virtual_methods_access_flags,
|
||||||
|
class_annotations
|
||||||
|
);
|
||||||
|
env->DeleteLocalRef(interfaces);
|
||||||
|
env->DeleteLocalRef(static_fields);
|
||||||
|
env->DeleteLocalRef(static_fields_access_flags);
|
||||||
|
env->DeleteLocalRef(instance_fields);
|
||||||
|
env->DeleteLocalRef(instance_fields_access_flags);
|
||||||
|
env->DeleteLocalRef(direct_methods);
|
||||||
|
env->DeleteLocalRef(direct_methods_access_flags);
|
||||||
|
env->DeleteLocalRef(virtual_methods);
|
||||||
|
env->DeleteLocalRef(virtual_methods_access_flags);
|
||||||
|
env->DeleteLocalRef(class_annotations);
|
||||||
|
if (member_visitor && env->IsInstanceOf(member_visitor, field_visitor_class)) {
|
||||||
|
jboolean stopped = JNI_FALSE;
|
||||||
|
for (auto &[fields, fields_access_flags]: {
|
||||||
|
std::make_tuple(class_data.static_fields,
|
||||||
|
class_data.static_fields_access_flags),
|
||||||
|
std::make_tuple(class_data.instance_fields,
|
||||||
|
class_data.instance_fields_access_flags)}) {
|
||||||
|
for (size_t j = 0; j < fields.size(); j++) {
|
||||||
|
auto field_idx = fields[j];
|
||||||
|
auto access_flags = fields_access_flags[j];
|
||||||
|
auto &field_annotations = dex.field_annotations[field_idx];
|
||||||
|
auto annotations = env->NewIntArray(
|
||||||
|
static_cast<jint>(field_annotations.size()));
|
||||||
|
env->SetIntArrayRegion(annotations, 0,
|
||||||
|
static_cast<jint>(field_annotations.size()),
|
||||||
|
field_annotations.data());
|
||||||
|
stopped = env->CallBooleanMethod(member_visitor, visit_field, field_idx,
|
||||||
|
access_flags, annotations);
|
||||||
|
env->DeleteLocalRef(annotations);
|
||||||
|
if (stopped == JNI_TRUE) break;
|
||||||
|
}
|
||||||
|
if (stopped == JNI_TRUE) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (member_visitor && env->IsInstanceOf(member_visitor, method_visitor_class)) {
|
||||||
|
jboolean stopped = JNI_FALSE;
|
||||||
|
for (auto &[methods, methods_access_flags, methods_code]: {
|
||||||
|
std::make_tuple(class_data.direct_methods,
|
||||||
|
class_data.direct_methods_access_flags,
|
||||||
|
class_data.direct_methods_code),
|
||||||
|
std::make_tuple(class_data.virtual_methods,
|
||||||
|
class_data.virtual_methods_access_flags,
|
||||||
|
class_data.virtual_methods_code)}) {
|
||||||
|
for (size_t j = 0; j < methods.size(); j++) {
|
||||||
|
auto method_idx = methods[j];
|
||||||
|
auto access_flags = methods_access_flags[j];
|
||||||
|
auto code = methods_code[j];
|
||||||
|
auto method_annotation = dex.method_annotations[method_idx];
|
||||||
|
auto method_annotations = env->NewIntArray(
|
||||||
|
static_cast<jint>(method_annotation.size()));
|
||||||
|
env->SetIntArrayRegion(method_annotations, 0,
|
||||||
|
static_cast<jint>(method_annotation.size()),
|
||||||
|
method_annotation.data());
|
||||||
|
auto parameter_annotation = dex.parameter_annotations[method_idx];
|
||||||
|
auto parameter_annotations = env->NewIntArray(
|
||||||
|
static_cast<jint>(parameter_annotation.size()));
|
||||||
|
env->SetIntArrayRegion(parameter_annotations, 0,
|
||||||
|
static_cast<jint>(parameter_annotation.size()),
|
||||||
|
parameter_annotation.data());
|
||||||
|
auto body_visitor = env->CallObjectMethod(member_visitor, visit_method,
|
||||||
|
method_idx,
|
||||||
|
access_flags, code != nullptr,
|
||||||
|
method_annotations,
|
||||||
|
parameter_annotations);
|
||||||
|
env->DeleteLocalRef(method_annotations);
|
||||||
|
env->DeleteLocalRef(parameter_annotations);
|
||||||
|
if (body_visitor && code != nullptr) {
|
||||||
|
auto body = dex.method_bodies[method_idx];
|
||||||
|
if (!body.loaded) {
|
||||||
const dex::u2 *inst = code->insns;
|
const dex::u2 *inst = code->insns;
|
||||||
const dex::u2 *end = inst + code->insns_size;
|
const dex::u2 *end = inst + code->insns_size;
|
||||||
std::vector<jint> invoked_methods;
|
|
||||||
std::vector<jint> referred_strings;
|
|
||||||
std::vector<jint> accessed_fields;
|
|
||||||
std::vector<jint> assigned_fields;
|
|
||||||
std::vector<jbyte> opcodes;
|
|
||||||
while (inst < end) {
|
while (inst < end) {
|
||||||
dex::u1 opcode = *inst & kOpcodeMask;
|
dex::u1 opcode = *inst & kOpcodeMask;
|
||||||
opcodes.push_back(static_cast<jbyte>(opcode));
|
body.opcodes.push_back(static_cast<jbyte>(opcode));
|
||||||
if (opcode == kOpcodeConstString) {
|
if (opcode == kOpcodeConstString) {
|
||||||
auto str_idx = inst[1];
|
auto str_idx = inst[1];
|
||||||
referred_strings.push_back(str_idx);
|
body.referred_strings.push_back(str_idx);
|
||||||
}
|
}
|
||||||
if (opcode == kOpcodeConstStringJumbo) {
|
if (opcode == kOpcodeConstStringJumbo) {
|
||||||
auto str_idx = *reinterpret_cast<const dex::u4 *>(&inst[1]);
|
auto str_idx = *reinterpret_cast<const dex::u4 *>(&inst[1]);
|
||||||
referred_strings.push_back(static_cast<jint>(str_idx));
|
body.referred_strings.push_back(static_cast<jint>(str_idx));
|
||||||
}
|
}
|
||||||
if ((opcode >= kOpcodeIGetStart && opcode <= kOpcodeIGetEnd) ||
|
if ((opcode >= kOpcodeIGetStart && opcode <= kOpcodeIGetEnd) ||
|
||||||
(opcode >= kOpcodeSGetStart && opcode <= kOpcodeSGetEnd)) {
|
(opcode >= kOpcodeSGetStart && opcode <= kOpcodeSGetEnd)) {
|
||||||
auto field_idx = inst[1];
|
auto field_idx = inst[1];
|
||||||
accessed_fields.push_back(field_idx);
|
body.accessed_fields.push_back(field_idx);
|
||||||
}
|
}
|
||||||
if ((opcode >= kOpcodeIPutStart && opcode <= kOpcodeIPutEnd) ||
|
if ((opcode >= kOpcodeIPutStart && opcode <= kOpcodeIPutEnd) ||
|
||||||
(opcode >= kOpcodeSPutStart && opcode <= kOpcodeSPutEnd)) {
|
(opcode >= kOpcodeSPutStart && opcode <= kOpcodeSPutEnd)) {
|
||||||
auto field_idx = inst[1];
|
auto field_idx = inst[1];
|
||||||
assigned_fields.push_back(field_idx);
|
body.assigned_fields.push_back(field_idx);
|
||||||
}
|
}
|
||||||
if ((opcode >= kOpcodeInvokeStart && opcode <= kOpcodeInvokeEnd) ||
|
if ((opcode >= kOpcodeInvokeStart &&
|
||||||
(opcode >= kOpcodeInvokeRangeStart && opcode <= kOpcodeInvokeRangeEnd)) {
|
opcode <= kOpcodeInvokeEnd) ||
|
||||||
|
(opcode >= kOpcodeInvokeRangeStart &&
|
||||||
|
opcode <= kOpcodeInvokeRangeEnd)) {
|
||||||
auto callee = inst[1];
|
auto callee = inst[1];
|
||||||
invoked_methods.push_back(callee);
|
body.invoked_methods.push_back(callee);
|
||||||
}
|
}
|
||||||
if (opcode == kOpcodeNoOp) {
|
if (opcode == kOpcodeNoOp) {
|
||||||
if (*inst == kInstPackedSwitchPlayLoad) {
|
if (*inst == kInstPackedSwitchPlayLoad) {
|
||||||
|
|
@ -406,51 +611,59 @@ namespace lspd {
|
||||||
} else if (*inst == kInstSparseSwitchPlayLoad) {
|
} else if (*inst == kInstSparseSwitchPlayLoad) {
|
||||||
inst += inst[1] * 4 + 1;
|
inst += inst[1] * 4 + 1;
|
||||||
} else if (*inst == kInstFillArrayDataPlayLoad) {
|
} else if (*inst == kInstFillArrayDataPlayLoad) {
|
||||||
inst += (*reinterpret_cast<const dex::u4 *>(&inst[2]) * inst[1] + 1) / 2 + 3;
|
inst += (*reinterpret_cast<const dex::u4 *>(&inst[2]) *
|
||||||
|
inst[1] + 1) /
|
||||||
|
2 + 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inst += dex::opcode_len[opcode];
|
inst += dex::opcode_len[opcode];
|
||||||
}
|
}
|
||||||
auto res = env->NewObjectArray(5, env->FindClass("java/lang/Object"), nullptr);
|
body.loaded = true;
|
||||||
auto res0 = env->NewIntArray(static_cast<jint>(invoked_methods.size()));
|
}
|
||||||
auto res0_ptr = env->GetIntArrayElements(res0, nullptr);
|
auto referred_strings = env->NewIntArray(
|
||||||
memcpy(res0_ptr, invoked_methods.data(), invoked_methods.size() * sizeof(jint));
|
static_cast<jint>(body.referred_strings.size()));
|
||||||
env->ReleaseIntArrayElements(res0, res0_ptr, 0);
|
env->SetIntArrayRegion(referred_strings, 0,
|
||||||
env->SetObjectArrayElement(res, 0, res0);
|
static_cast<jint>(body.referred_strings.size()),
|
||||||
env->DeleteLocalRef(res0);
|
body.referred_strings.data());
|
||||||
auto res1 = env->NewIntArray(static_cast<jint>(accessed_fields.size()));
|
auto accessed_fields = env->NewIntArray(
|
||||||
auto res1_ptr = env->GetIntArrayElements(res1, nullptr);
|
static_cast<jint>(body.accessed_fields.size()));
|
||||||
memcpy(res1_ptr, accessed_fields.data(), accessed_fields.size() * sizeof(jint));
|
env->SetIntArrayRegion(accessed_fields, 0,
|
||||||
env->ReleaseIntArrayElements(res1, res1_ptr, 0);
|
static_cast<jint>(body.accessed_fields.size()),
|
||||||
env->SetObjectArrayElement(res, 1, res1);
|
body.accessed_fields.data());
|
||||||
env->DeleteLocalRef(res1);
|
auto assigned_fields = env->NewIntArray(
|
||||||
auto res2 = env->NewIntArray(static_cast<jint>(assigned_fields.size()));
|
static_cast<jint>(body.assigned_fields.size()));
|
||||||
auto res2_ptr = env->GetIntArrayElements(res2, nullptr);
|
env->SetIntArrayRegion(assigned_fields, 0,
|
||||||
memcpy(res2_ptr, assigned_fields.data(), assigned_fields.size() * sizeof(jint));
|
static_cast<jint>(body.assigned_fields.size()),
|
||||||
env->ReleaseIntArrayElements(res2, res2_ptr, 0);
|
body.assigned_fields.data());
|
||||||
env->SetObjectArrayElement(res, 2, res2);
|
auto invoked_methods = env->NewIntArray(
|
||||||
env->DeleteLocalRef(res2);
|
static_cast<jint>(body.invoked_methods.size()));
|
||||||
auto res3 = env->NewIntArray(static_cast<jint>(referred_strings.size()));
|
env->SetIntArrayRegion(invoked_methods, 0,
|
||||||
auto res3_ptr = env->GetIntArrayElements(res3, nullptr);
|
static_cast<jint>(body.invoked_methods.size()),
|
||||||
memcpy(res3_ptr, referred_strings.data(), referred_strings.size() * sizeof(jint));
|
body.invoked_methods.data());
|
||||||
env->ReleaseIntArrayElements(res3, res3_ptr, 0);
|
auto opcodes = env->NewByteArray(
|
||||||
env->SetObjectArrayElement(res, 3, res3);
|
static_cast<jint>(body.opcodes.size()));
|
||||||
env->DeleteLocalRef(res3);
|
env->SetByteArrayRegion(opcodes, 0,
|
||||||
auto res4 = env->NewByteArray(static_cast<jint>(opcodes.size()));
|
static_cast<jint>(body.opcodes.size()),
|
||||||
auto res4_ptr = env->GetByteArrayElements(res4, nullptr);
|
body.opcodes.data());
|
||||||
memcpy(res4_ptr, opcodes.data(), opcodes.size() * sizeof(jbyte));
|
env->CallVoidMethod(body_visitor, visit_method_body,
|
||||||
env->ReleaseByteArrayElements(res4, res4_ptr, 0);
|
referred_strings,
|
||||||
env->SetObjectArrayElement(res, 4, res4);
|
invoked_methods,
|
||||||
env->DeleteLocalRef(res4);
|
accessed_fields, assigned_fields, opcodes);
|
||||||
|
}
|
||||||
return res;
|
stopped = env->CallBooleanMethod(member_visitor, stop);
|
||||||
|
if (stopped == JNI_TRUE) break;
|
||||||
|
}
|
||||||
|
if (stopped == JNI_TRUE) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (env->CallBooleanMethod(visitor, stop) == JNI_TRUE) break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static JNINativeMethod gMethods[] = {
|
static JNINativeMethod gMethods[] = {
|
||||||
LSP_NATIVE_METHOD(DexParserBridge, parseDex,
|
LSP_NATIVE_METHOD(DexParserBridge, openDex,
|
||||||
"(Ljava/nio/buffer/ByteBuffer;)Ljava/lang/Object;"),
|
"(Ljava/nio/buffer/ByteBuffer;[J)Ljava/lang/Object;"),
|
||||||
LSP_NATIVE_METHOD(DexParserBridge, parseDex,
|
LSP_NATIVE_METHOD(DexParserBridge, closeDex, "(J)V;"),
|
||||||
"(Ljava/nio/buffer/ByteBuffer;I)Ljava/lang/Object;"),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,73 +3,11 @@ package io.github.libxposed.utils;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
public interface DexParser {
|
import java.io.Closeable;
|
||||||
|
|
||||||
|
public interface DexParser extends Closeable {
|
||||||
int NO_INDEX = 0xffffffff;
|
int NO_INDEX = 0xffffffff;
|
||||||
|
|
||||||
interface ClassDef {
|
|
||||||
@NonNull
|
|
||||||
TypeId getType();
|
|
||||||
|
|
||||||
int getAccessFlags();
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
TypeId getSuperClass();
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
TypeId[] getInterfaces();
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
StringId getSourceFile();
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
EncodedField[] getStaticFields();
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
EncodedField[] getInstanceFields();
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
EncodedMethod[] getDirectMethods();
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
EncodedMethod[] getVirtualMethods();
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
Annotation[] getClassAnnotations();
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
FieldAnnotation[] getFieldAnnotations();
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
MethodAnnotation[] getMethodAnnotations();
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
ParameterAnnotation[] getParameterAnnotations();
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FieldAnnotation {
|
|
||||||
@NonNull
|
|
||||||
FieldId getField();
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
Annotation[] getAnnotations();
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MethodAnnotation {
|
|
||||||
@NonNull
|
|
||||||
MethodId getMethod();
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
Annotation[] getAnnotations();
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ParameterAnnotation {
|
|
||||||
@NonNull
|
|
||||||
MethodId getMethod();
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
Annotation[][] getAnnotations();
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Annotation {
|
interface Annotation {
|
||||||
int getVisibility();
|
int getVisibility();
|
||||||
|
|
||||||
|
|
@ -95,35 +33,6 @@ public interface DexParser {
|
||||||
StringId getDescriptor();
|
StringId getDescriptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EncodedField {
|
|
||||||
@NonNull
|
|
||||||
FieldId getField();
|
|
||||||
|
|
||||||
int getAccessFlags();
|
|
||||||
}
|
|
||||||
|
|
||||||
interface EncodedMethod {
|
|
||||||
@NonNull
|
|
||||||
MethodId getMethod();
|
|
||||||
|
|
||||||
int getAccessFlags();
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
MethodId[] getInvokedMethods();
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
FieldId[] getAccessedFields();
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
FieldId[] getAssignedFields();
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
byte[] getOpcodes();
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
StringId[] getReferredString();
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Id {
|
interface Id {
|
||||||
int getId();
|
int getId();
|
||||||
}
|
}
|
||||||
|
|
@ -166,9 +75,6 @@ public interface DexParser {
|
||||||
TypeId[] getParameters();
|
TypeId[] getParameters();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
|
||||||
ClassDef[] getClassDef();
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
StringId[] getStringId();
|
StringId[] getStringId();
|
||||||
|
|
||||||
|
|
@ -183,4 +89,34 @@ public interface DexParser {
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
ProtoId[] getProtoId();
|
ProtoId[] getProtoId();
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
Annotation[] getAnnotations();
|
||||||
|
|
||||||
|
interface EarlyStopVisitor {
|
||||||
|
void stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MemberVisitor extends EarlyStopVisitor {
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ClassVisitor {
|
||||||
|
@Nullable
|
||||||
|
MemberVisitor visit(int clazz, int accessFlags, int superClass, @NonNull int[] interfaces, int sourceFile, @NonNull int[] staticFields, @NonNull int[] staticFieldsAccessFlags, @NonNull int[] instanceFields, @NonNull int[] instanceFieldsAccessFlags, @NonNull int[] directMethods, @NonNull int[] directMethodsAccessFlags, @NonNull int[] virtualMethods, @NonNull int[] virtualMethodsAccessFlags, @NonNull int[] annotations);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FieldVisitor extends MemberVisitor {
|
||||||
|
boolean visit(int field, int accessFlags, @NonNull int[] annotations);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MethodVisitor extends MemberVisitor {
|
||||||
|
@Nullable
|
||||||
|
MethodBodyVisitor visit(int method, int accessFlags, boolean hasBody, @NonNull int[] annotations, @NonNull int[] parameterAnnotations);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MethodBodyVisitor extends MemberVisitor {
|
||||||
|
boolean visit(int method, int accessFlags, @NonNull int[] referredStrings, @NonNull int[] invokedMethods, @NonNull int[] accessedFields, @NonNull int[] assignedFields, @NonNull byte[] opcodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visitDefinedClasses(@NonNull ClassVisitor visitor) throws IllegalStateException;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue