From 8cec9fe29acda81e17bc224f4c09a06e9b763ed1 Mon Sep 17 00:00:00 2001 From: LoveSy Date: Sun, 8 Jan 2023 03:13:35 +0800 Subject: [PATCH] Change the way of how dexparser works --- .../de/robv/android/xposed/XposedInit.java | 1 - .../lspd/impl/utils/LSPosedDexParser.java | 492 ++----------- .../lspd/nativebridge/DexParserBridge.java | 10 +- core/src/main/jni/src/jni/dex_parser.cpp | 649 ++++++++++++------ .../io/github/libxposed/utils/DexParser.java | 130 +--- 5 files changed, 531 insertions(+), 751 deletions(-) diff --git a/core/src/main/java/de/robv/android/xposed/XposedInit.java b/core/src/main/java/de/robv/android/xposed/XposedInit.java index 56a57027..e1a7eaaf 100644 --- a/core/src/main/java/de/robv/android/xposed/XposedInit.java +++ b/core/src/main/java/de/robv/android/xposed/XposedInit.java @@ -58,7 +58,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import de.robv.android.xposed.callbacks.XC_InitPackageResources; import de.robv.android.xposed.callbacks.XCallback; import hidden.HiddenApiBridge; -import io.github.libxposed.XposedModuleInterface; public final class XposedInit { private static final String TAG = XposedBridge.TAG; diff --git a/core/src/main/java/org/lsposed/lspd/impl/utils/LSPosedDexParser.java b/core/src/main/java/org/lsposed/lspd/impl/utils/LSPosedDexParser.java index e199bdcc..f570e527 100644 --- a/core/src/main/java/org/lsposed/lspd/impl/utils/LSPosedDexParser.java +++ b/core/src/main/java/org/lsposed/lspd/impl/utils/LSPosedDexParser.java @@ -11,13 +11,15 @@ import java.nio.ByteBuffer; import io.github.libxposed.utils.DexParser; public class LSPosedDexParser implements DexParser { - ByteBuffer data; - StringId[] strings; - TypeId[] typeIds; - ProtoId[] protoIds; - FieldId[] fieldIds; - MethodId[] methodIds; - ClassDef[] classDefs; + long cookie; + + final ByteBuffer data; + final StringId[] strings; + final TypeId[] typeIds; + final ProtoId[] protoIds; + final FieldId[] fieldIds; + final MethodId[] methodIds; + final Annotation[] annotations; public LSPosedDexParser(ByteBuffer buffer) throws IOException { if (!buffer.isDirect() || !buffer.asReadOnlyBuffer().hasArray()) { @@ -27,14 +29,15 @@ public class LSPosedDexParser implements DexParser { data = buffer; } 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[1]: int[] // out[2]: int[][] // out[3]: int[] - // out[4]: int[][3] - // out[5]: int[][] - // out[6]: Object[][][][] + // out[4]: int[] + // out[6]: Object[] var strings = (Object[]) out[0]; this.strings = new StringId[strings.length]; for (int i = 0; i < strings.length; ++i) { @@ -54,28 +57,40 @@ public class LSPosedDexParser implements DexParser { } var fieldIds = (int[]) out[3]; - this.fieldIds = new FieldId[fieldIds.length]; - for (int i = 0; i < fieldIds.length; ++i) { + this.fieldIds = new FieldId[fieldIds.length / 3]; + 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]); } var methodIds = (int[]) out[4]; - this.methodIds = new MethodId[methodIds.length]; - for (int i = 0; i < methodIds.length; ++i) { + this.methodIds = new MethodId[methodIds.length / 3]; + 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]); } - var classDefs = (int[][]) out[5]; - this.classDefs = new ClassDef[classDefs.length]; - var annotations = (Object[][]) out[6]; - for (int i = 0; i < classDefs.length; ++i) { - this.classDefs[i] = new LSPosedClassDef(classDefs[i], annotations[4 * i], annotations[4 * i + 1], annotations[4 * i + 2], annotations[4 * i + 3]); + if (out[5] != null && out[6] != null) { + var a = (int[]) out[5]; + var b = (Object[]) out[6]; + this.annotations = new Annotation[a.length / 2]; + 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) { 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 { 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 { int visibility; @NonNull @@ -368,12 +252,12 @@ public class LSPosedDexParser implements DexParser { @Nullable 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.type = typeIds[type]; this.elements = new AnnotationElement[elementValues.length]; 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.valueType = valueType; if (value != null) { - this.value = value.array(); + this.value = new byte[value.remaining()]; + value.get(this.value); } else { 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 @Override public StringId[] getStringId() { @@ -738,4 +344,24 @@ public class LSPosedDexParser implements DexParser { public ProtoId[] getProtoId() { 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); + } } diff --git a/core/src/main/java/org/lsposed/lspd/nativebridge/DexParserBridge.java b/core/src/main/java/org/lsposed/lspd/nativebridge/DexParserBridge.java index d7613f16..65d837a9 100644 --- a/core/src/main/java/org/lsposed/lspd/nativebridge/DexParserBridge.java +++ b/core/src/main/java/org/lsposed/lspd/nativebridge/DexParserBridge.java @@ -1,13 +1,19 @@ package org.lsposed.lspd.nativebridge; import java.io.IOException; +import java.lang.reflect.Method; import java.nio.ByteBuffer; import dalvik.annotation.optimization.FastNative; +import io.github.libxposed.utils.DexParser; 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 - 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 fieldVisitorClass, Class methodVisitorClass, Method classVisitMethod, Method fieldVisitMethod, Method methodVisitMethod, Method methodBodyVisitMethod, Method stopMethod); } diff --git a/core/src/main/jni/src/jni/dex_parser.cpp b/core/src/main/jni/src/jni/dex_parser.cpp index d83828e9..264feb2e 100644 --- a/core/src/main/jni/src/jni/dex_parser.cpp +++ b/core/src/main/jni/src/jni/dex_parser.cpp @@ -21,38 +21,39 @@ #include "native_util.h" #include "slicer/reader.h" +#include +#include + namespace { - jobject ParseAnnotation(JNIEnv *env, dex::Reader &dex, jclass object_class, - const dex::AnnotationSetItem *annotation) { + using AnnotationList = std::vector>>>; + + void ParseAnnotation(JNIEnv *env, dex::Reader &dex, AnnotationList &annotation_list, + std::vector indices, + const dex::AnnotationSetItem *annotation) { if (annotation == nullptr) { - return nullptr; + return; } - auto a = env->NewIntArray(static_cast(2 * annotation->size)); - auto *a_ptr = env->GetIntArrayElements(a, nullptr); - auto b = env->NewObjectArray(static_cast(2 * annotation->size), object_class, - nullptr); for (size_t i = 0; i < annotation->size; ++i) { auto *item = dex.dataPtr(annotation->entries[i]); - a_ptr[2 * i] = item->visibility; auto *annotation_data = item->annotation; - a_ptr[2 * i + 1] = static_cast(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>()); auto size = dex::ReadULeb128(&annotation_data); - auto b2i0 = env->NewIntArray(static_cast(2 * size)); - auto *b2i0_ptr = env->GetIntArrayElements(b2i0, nullptr); - auto b2i1 = env->NewObjectArray(static_cast(size), object_class, nullptr); + items.resize(size); for (size_t j = 0; j < size; ++j) { - b2i0_ptr[2 * j] = static_cast(dex::ReadULeb128(&annotation_data)); + auto &[name, value_type, value] = items[j]; + name = static_cast(dex::ReadULeb128(&annotation_data)); auto arg_and_type = *annotation_data++; - auto value_type = arg_and_type & 0x1f; - b2i0_ptr[2 * j + 1] = value_type; + value_type = arg_and_type & 0x1f; auto value_arg = arg_and_type >> 5; - jobject value = nullptr; switch (value_type) { case 0x00: // byte case 0x1f: // boolean - value = env->NewDirectByteBuffer( - reinterpret_cast(const_cast(annotation_data)), - 1); + value = {reinterpret_cast(const_cast(annotation_data)), + 1}; break; case 0x02: // short case 0x03: // char @@ -65,38 +66,60 @@ namespace { case 0x19: // field case 0x1a: // method case 0x1b: // enum - value = env->NewDirectByteBuffer( - reinterpret_cast(const_cast(annotation_data)), - value_arg); + value = { + reinterpret_cast(const_cast(annotation_data)), + static_cast(value_arg)}; break; case 0x1c: // array case 0x1d: // annotation case 0x1e: // null - // not supported - value = nullptr; - break; default: + // not supported break; } - env->SetObjectArrayElement(b2i1, static_cast(j), value); - env->DeleteLocalRef(value); } - env->SetObjectArrayElement(b, static_cast(2 * i), b2i0); - env->SetObjectArrayElement(b, static_cast(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 interfaces; + std::vector static_fields; + std::vector static_fields_access_flags; + std::vector instance_fields; + std::vector instance_fields_access_flags; + std::vector direct_methods; + std::vector direct_methods_access_flags; + std::vector direct_methods_code; + std::vector virtual_methods; + std::vector virtual_methods_access_flags; + std::vector virtual_methods_code; + std::vector annotations; + }; + + struct MethodBody { + bool loaded; + std::vector referred_strings; + std::vector accessed_fields; + std::vector assigned_fields; + std::vector invoked_methods; + std::vector opcodes; + }; + + std::vector class_data; + absl::flat_hash_map> field_annotations; + absl::flat_hash_map> method_annotations; + absl::flat_hash_map> parameter_annotations; + + absl::flat_hash_map method_bodies; + }; } + 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); if (dex_size == -1) { env->ThrowNew(env->FindClass("java/io/IOException"), "Invalid dex data"); @@ -104,7 +127,9 @@ namespace lspd { } auto *dex_data = env->GetDirectBufferAddress(data); - dex::Reader dex(reinterpret_cast(dex_data), dex_size, nullptr, 0); + auto *dex_reader = new DexParser(reinterpret_cast(dex_data), dex_size); + env->SetLongArrayRegion(args, 0, 1, reinterpret_cast(&dex_reader)); + auto &dex = *dex_reader; if (dex.IsCompact()) { env->ThrowNew(env->FindClass("java/io/IOException"), "Compact dex is not supported"); return nullptr; @@ -118,7 +143,7 @@ namespace lspd { auto strings = dex.StringIds(); for (size_t i = 0; i < strings.size(); ++i) { const auto *ptr = dex.dataPtr(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(ptr)); env->SetObjectArrayElement(out0, static_cast(i), str); env->DeleteLocalRef(str); @@ -185,31 +210,33 @@ namespace lspd { env->DeleteLocalRef(out4); auto classes = dex.ClassDefs(); - auto out5 = env->NewObjectArray(static_cast(classes.size()), - int_array_class, nullptr); - auto out6 = env->NewObjectArray(static_cast(4 * classes.size()), - object_class, nullptr); + dex.class_data.resize(classes.size()); + + AnnotationList annotation_list; + for (size_t i = 0; i < classes.size(); ++i) { auto &class_def = classes[i]; - auto &interfaces = class_def.interfaces_off ? *dex.dataPtr( - class_def.interfaces_off) : empty_type_list; dex::u4 static_fields_count = 0; dex::u4 instance_fields_count = 0; dex::u4 direct_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 method_annotations_count = 0; dex::u4 parameter_annotations_count = 0; - const dex::u1 *class_data = nullptr; - const dex::AnnotationsDirectoryItem *annotations = nullptr; - const dex::AnnotationSetItem *class_annotation = nullptr; - if (class_def.class_data_off != 0) { - class_data = dex.dataPtr(class_def.class_data_off); - static_fields_count = dex::ReadULeb128(&class_data); - instance_fields_count = dex::ReadULeb128(&class_data); - direct_methods_count = dex::ReadULeb128(&class_data); - virtual_methods_count = dex::ReadULeb128(&class_data); + + auto &class_data = dex.class_data[i]; + + if (class_def.interfaces_off) { + auto defined_interfaces = dex.dataPtr(class_def.interfaces_off); + class_data.interfaces.resize(defined_interfaces->size); + for (size_t k = 0; k < class_data.interfaces.size(); ++k) { + class_data.interfaces[k] = defined_interfaces->list[k].type_idx; + } } if (class_def.annotations_off != 0) { @@ -223,131 +250,156 @@ namespace lspd { parameter_annotations_count = annotations->parameters_size; } - auto array_size = 4 + 1 + interfaces.size + 1 + 2 * static_fields_count + - 1 + 2 * instance_fields_count + 1 + 3 * direct_methods_count + - 1 + 3 * virtual_methods_count + 1 + - 1 + field_annotations_count + 1 + method_annotations_count + - 1 + parameter_annotations_count; - auto out5i = env->NewIntArray(static_cast(array_size)); - auto *out5i_ptr = env->GetIntArrayElements(out5i, nullptr); - size_t j = 0; - out5i_ptr[j++] = static_cast(class_def.class_idx); - out5i_ptr[j++] = static_cast(class_def.access_flags); - out5i_ptr[j++] = static_cast(class_def.superclass_idx); - out5i_ptr[j++] = static_cast(class_def.source_file_idx); - out5i_ptr[j++] = static_cast(interfaces.size); - for (size_t k = 0; k < interfaces.size; ++k) { - out5i_ptr[j++] = static_cast(interfaces.list[k].type_idx); - } - out5i_ptr[j++] = static_cast(static_fields_count); - for (size_t k = 0, field_idx = 0; k < static_fields_count; ++k) { - out5i_ptr[j++] = static_cast(field_idx += dex::ReadULeb128(&class_data)); - out5i_ptr[j++] = static_cast(dex::ReadULeb128(&class_data)); - } - out5i_ptr[j++] = static_cast(instance_fields_count); - for (size_t k = 0, field_idx = 0; k < instance_fields_count; ++k) { - out5i_ptr[j++] = static_cast(field_idx += dex::ReadULeb128(&class_data)); - out5i_ptr[j++] = static_cast(dex::ReadULeb128(&class_data)); - } - out5i_ptr[j++] = static_cast(direct_methods_count); - for (size_t k = 0, method_idx = 0; k < direct_methods_count; ++k) { - out5i_ptr[j++] = static_cast(method_idx += dex::ReadULeb128(&class_data)); - out5i_ptr[j++] = static_cast(dex::ReadULeb128(&class_data)); - out5i_ptr[j++] = static_cast(dex::ReadULeb128(&class_data)); - } - out5i_ptr[j++] = static_cast(virtual_methods_count); - for (size_t k = 0, method_idx = 0; k < virtual_methods_count; ++k) { - out5i_ptr[j++] = static_cast(method_idx += dex::ReadULeb128(&class_data)); - out5i_ptr[j++] = static_cast(dex::ReadULeb128(&class_data)); - out5i_ptr[j++] = static_cast(dex::ReadULeb128(&class_data)); + if (class_def.class_data_off != 0) { + class_data_ptr = dex.dataPtr(class_def.class_data_off); + static_fields_count = dex::ReadULeb128(&class_data_ptr); + instance_fields_count = dex::ReadULeb128(&class_data_ptr); + direct_methods_count = dex::ReadULeb128(&class_data_ptr); + virtual_methods_count = dex::ReadULeb128(&class_data_ptr); + class_data.static_fields.resize(static_fields_count); + class_data.static_fields_access_flags.resize(static_fields_count); + class_data.instance_fields.resize(instance_fields_count); + class_data.instance_fields_access_flags.resize(instance_fields_count); + class_data.direct_methods.resize(direct_methods_count); + class_data.direct_methods_access_flags.resize(direct_methods_count); + class_data.direct_methods_code.resize(direct_methods_count); + class_data.virtual_methods.resize(virtual_methods_count); + class_data.virtual_methods_access_flags.resize(virtual_methods_count); + class_data.virtual_methods_code.resize(virtual_methods_count); } - auto out6i0 = ParseAnnotation(env, dex, object_class, class_annotation); - env->SetObjectArrayElement(out6, static_cast(4 * i), out6i0); - env->DeleteLocalRef(out6i0); + if (class_data_ptr) { + for (size_t k = 0, field_idx = 0; k < static_fields_count; ++k) { + class_data.static_fields[k] = static_cast(field_idx += dex::ReadULeb128( + &class_data_ptr)); + class_data.static_fields_access_flags[k] = static_cast(dex::ReadULeb128( + &class_data_ptr)); + } - out5i_ptr[j++] = static_cast(class_annotation ? class_annotation->size : 0); + for (size_t k = 0, field_idx = 0; k < instance_fields_count; ++k) { + class_data.instance_fields[k] = static_cast(field_idx += dex::ReadULeb128( + &class_data_ptr)); + class_data.instance_fields_access_flags[k] = static_cast(dex::ReadULeb128( + &class_data_ptr)); + } + + for (size_t k = 0, method_idx = 0; k < direct_methods_count; ++k) { + class_data.direct_methods[k] = static_cast(method_idx += dex::ReadULeb128( + &class_data_ptr)); + class_data.direct_methods_access_flags[k] = static_cast(dex::ReadULeb128( + &class_data_ptr)); + auto code_off = dex::ReadULeb128(&class_data_ptr); + class_data.direct_methods_code[k] = code_off ? dex.dataPtr(code_off) + : nullptr; + } + + for (size_t k = 0, method_idx = 0; k < virtual_methods_count; ++k) { + class_data.virtual_methods[k] = static_cast(method_idx += dex::ReadULeb128( + &class_data_ptr)); + class_data.virtual_methods_access_flags[k] = static_cast(dex::ReadULeb128( + &class_data_ptr)); + auto code_off = dex::ReadULeb128(&class_data_ptr); + class_data.virtual_methods_code[k] = code_off ? dex.dataPtr(code_off) + : nullptr; + } + } + + ParseAnnotation(env, dex, annotation_list, class_data.annotations, class_annotation); - out5i_ptr[j++] = static_cast(field_annotations_count); auto *field_annotations = annotations ? reinterpret_cast( annotations + 1) : nullptr; - auto out6i1 = env->NewObjectArray(static_cast(field_annotations_count), - object_class, nullptr); for (size_t k = 0; k < field_annotations_count; ++k) { - out5i_ptr[j++] = static_cast(field_annotations[k].field_idx); auto *field_annotation = dex.dataPtr( field_annotations[k].annotations_off); - auto out6i1i = ParseAnnotation(env, dex, object_class, field_annotation); - env->SetObjectArrayElement(out6i1, static_cast(k), out6i1i); - env->DeleteLocalRef(out6i1i); + ParseAnnotation(env, dex, annotation_list, + dex.field_annotations[static_cast(field_annotations[k].field_idx)], + field_annotation); } - env->SetObjectArrayElement(out6, static_cast(4 * i + 1), out6i1); - env->DeleteLocalRef(out6i1); - out5i_ptr[j++] = static_cast(method_annotations_count); auto *method_annotations = field_annotations ? reinterpret_cast( field_annotations + field_annotations_count) : nullptr; - auto out6i2 = env->NewObjectArray(static_cast(method_annotations_count), - object_class, nullptr); for (size_t k = 0; k < method_annotations_count; ++k) { - out5i_ptr[j++] = static_cast(method_annotations[k].method_idx); auto *method_annotation = dex.dataPtr( method_annotations[k].annotations_off); - auto out6i2i = ParseAnnotation(env, dex, object_class, method_annotation); - env->SetObjectArrayElement(out6i2, static_cast(k), out6i2i); - env->DeleteLocalRef(out6i2i); + ParseAnnotation(env, dex, annotation_list, + dex.method_annotations[static_cast(method_annotations[k].method_idx)], + method_annotation); } - env->SetObjectArrayElement(out6, static_cast(4 * i + 2), out6i2); - out5i_ptr[j++] = static_cast(parameter_annotations_count); auto *parameter_annotations = method_annotations ? reinterpret_cast( method_annotations + method_annotations_count) : nullptr; - auto out6i3 = env->NewObjectArray(static_cast(parameter_annotations_count), - object_class, nullptr); for (size_t k = 0; k < parameter_annotations_count; ++k) { - out5i_ptr[j++] = static_cast(parameter_annotations[k].method_idx); auto *parameter_annotation = dex.dataPtr( parameter_annotations[k].annotations_off); - auto out6i3i = env->NewObjectArray( - static_cast(parameter_annotation->size), object_class, nullptr); + auto &indices = dex.parameter_annotations[static_cast(parameter_annotations[k].method_idx)]; for (size_t l = 0; l < parameter_annotation->size; ++l) { if (parameter_annotation->list[l].annotations_off != 0) { auto *parameter_annotation_item = dex.dataPtr( parameter_annotation->list[l].annotations_off); - auto out6i3ii = ParseAnnotation(env, dex, object_class, - parameter_annotation_item); - env->SetObjectArrayElement(out6i3i, static_cast(l), out6i3ii); - env->DeleteLocalRef(out6i3ii); + ParseAnnotation(env, dex, annotation_list, indices, + parameter_annotation_item); } + indices.emplace_back(dex::kNoIndex); } - env->SetObjectArrayElement(out6i3, static_cast(k), out6i3i); - env->DeleteLocalRef(out6i3i); } - env->SetObjectArrayElement(out6, static_cast(4 * i + 3), out6i3); - env->DeleteLocalRef(out6i3); - - env->ReleaseIntArrayElements(out5i, out5i_ptr, 0); - env->SetObjectArrayElement(out5, static_cast(i), out5i); - env->DeleteLocalRef(out5i); } + return out; + auto out5 = env->NewIntArray(static_cast(2 * annotation_list.size())); + auto out6 = env->NewObjectArray(static_cast(2 * annotation_list.size()), object_class, + nullptr); + auto out5_ptr = env->GetIntArrayElements(out5, nullptr); + size_t i = 0; + for (auto &[visibility, type, items]: annotation_list) { + auto out6i0 = env->NewIntArray(static_cast(2 * items.size())); + auto out6i0_ptr = env->GetIntArrayElements(out6i0, nullptr); + auto out6i1 = env->NewObjectArray(static_cast(items.size()), object_class, + nullptr); + size_t j = 0; + for (auto&[name, value_type, value]: items) { + auto java_value = env->NewDirectByteBuffer(const_cast(value.data()), + value.size()); + env->SetObjectArrayElement(out6i1, static_cast(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(2 * i), out6i0); + env->SetObjectArrayElement(out6, static_cast(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; } - LSP_DEF_NATIVE_METHOD(jobject, DexParserBridge, parseMethod, jobject data, jint code_offset) { - auto dex_size = env->GetDirectBufferCapacity(data); - auto *dex_data = env->GetDirectBufferAddress(data); - if (dex_size < 0 || dex_data == nullptr || code_offset >= dex_size) { - return nullptr; - } - auto *code = reinterpret_cast(reinterpret_cast(dex_data) + - code_offset); + LSP_DEF_NATIVE_METHOD(void, DexParserBridge, closeDex, jlong cookie) { + if (cookie != 0) + delete reinterpret_cast(cookie); + } + 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 kOpcodeNoOp = 0x00; static constexpr dex::u1 kOpcodeConstString = 0x1a; @@ -367,90 +419,251 @@ namespace lspd { static constexpr dex::u2 kInstPackedSwitchPlayLoad = 0x0100; static constexpr dex::u2 kInstSparseSwitchPlayLoad = 0x0200; static constexpr dex::u2 kInstFillArrayDataPlayLoad = 0x0300; - const dex::u2 *inst = code->insns; - const dex::u2 *end = inst + code->insns_size; - std::vector invoked_methods; - std::vector referred_strings; - std::vector accessed_fields; - std::vector assigned_fields; - std::vector opcodes; - while (inst < end) { - dex::u1 opcode = *inst & kOpcodeMask; - opcodes.push_back(static_cast(opcode)); - if (opcode == kOpcodeConstString) { - auto str_idx = inst[1]; - referred_strings.push_back(str_idx); - } - if (opcode == kOpcodeConstStringJumbo) { - auto str_idx = *reinterpret_cast(&inst[1]); - referred_strings.push_back(static_cast(str_idx)); - } - if ((opcode >= kOpcodeIGetStart && opcode <= kOpcodeIGetEnd) || - (opcode >= kOpcodeSGetStart && opcode <= kOpcodeSGetEnd)) { - auto field_idx = inst[1]; - accessed_fields.push_back(field_idx); - } - if ((opcode >= kOpcodeIPutStart && opcode <= kOpcodeIPutEnd) || - (opcode >= kOpcodeSPutStart && opcode <= kOpcodeSPutEnd)) { - auto field_idx = inst[1]; - assigned_fields.push_back(field_idx); - } - if ((opcode >= kOpcodeInvokeStart && opcode <= kOpcodeInvokeEnd) || - (opcode >= kOpcodeInvokeRangeStart && opcode <= kOpcodeInvokeRangeEnd)) { - auto callee = inst[1]; - invoked_methods.push_back(callee); - } - if (opcode == kOpcodeNoOp) { - if (*inst == kInstPackedSwitchPlayLoad) { - inst += inst[1] * 2 + 3; - } else if (*inst == kInstSparseSwitchPlayLoad) { - inst += inst[1] * 4 + 1; - } else if (*inst == kInstFillArrayDataPlayLoad) { - inst += (*reinterpret_cast(&inst[2]) * inst[1] + 1) / 2 + 3; + + if (cookie == 0) { + return; + } + auto &dex = *reinterpret_cast(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(class_data.interfaces.size())); + env->SetIntArrayRegion(interfaces, 0, + static_cast(class_data.interfaces.size()), + class_data.interfaces.data()); + auto static_fields = env->NewIntArray( + static_cast(class_data.static_fields.size())); + env->SetIntArrayRegion(static_fields, 0, + static_cast(class_data.static_fields.size()), + class_data.static_fields.data()); + auto static_fields_access_flags = env->NewIntArray( + static_cast(class_data.static_fields_access_flags.size())); + env->SetIntArrayRegion(static_fields_access_flags, 0, + static_cast( + class_data.static_fields_access_flags.size()), + class_data.static_fields_access_flags.data()); + auto instance_fields = env->NewIntArray( + static_cast(class_data.instance_fields.size())); + env->SetIntArrayRegion(instance_fields, 0, + static_cast(class_data.instance_fields.size()), + class_data.instance_fields.data()); + auto instance_fields_access_flags = env->NewIntArray( + static_cast(class_data.instance_fields_access_flags.size())); + env->SetIntArrayRegion(instance_fields_access_flags, 0, + static_cast( + class_data.instance_fields_access_flags.size()), + class_data.instance_fields_access_flags.data()); + auto direct_methods = env->NewIntArray( + static_cast(class_data.direct_methods.size())); + env->SetIntArrayRegion(direct_methods, 0, + static_cast(class_data.direct_methods.size()), + class_data.direct_methods.data()); + auto direct_methods_access_flags = env->NewIntArray( + static_cast(class_data.direct_methods_access_flags.size())); + env->SetIntArrayRegion(direct_methods_access_flags, 0, + static_cast( + class_data.direct_methods_access_flags.size()), + class_data.direct_methods_access_flags.data()); + auto virtual_methods = env->NewIntArray( + static_cast(class_data.virtual_methods.size())); + env->SetIntArrayRegion(virtual_methods, 0, + static_cast(class_data.virtual_methods.size()), + class_data.virtual_methods.data()); + auto virtual_methods_access_flags = env->NewIntArray( + static_cast(class_data.virtual_methods_access_flags.size())); + env->SetIntArrayRegion(virtual_methods_access_flags, 0, + static_cast( + class_data.virtual_methods_access_flags.size()), + class_data.virtual_methods_access_flags.data()); + auto class_annotations = env->NewIntArray( + static_cast(class_data.annotations.size())); + env->SetIntArrayRegion(class_annotations, 0, + static_cast(class_data.annotations.size()), + class_data.annotations.data()); + jobject member_visitor = env->CallObjectMethod(visitor, visit_class, + static_cast(class_def.class_idx), + static_cast(class_def.access_flags), + static_cast(class_def.superclass_idx), + interfaces, + static_cast(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(field_annotations.size())); + env->SetIntArrayRegion(annotations, 0, + static_cast(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; } } - inst += dex::opcode_len[opcode]; + 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(method_annotation.size())); + env->SetIntArrayRegion(method_annotations, 0, + static_cast(method_annotation.size()), + method_annotation.data()); + auto parameter_annotation = dex.parameter_annotations[method_idx]; + auto parameter_annotations = env->NewIntArray( + static_cast(parameter_annotation.size())); + env->SetIntArrayRegion(parameter_annotations, 0, + static_cast(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 *end = inst + code->insns_size; + while (inst < end) { + dex::u1 opcode = *inst & kOpcodeMask; + body.opcodes.push_back(static_cast(opcode)); + if (opcode == kOpcodeConstString) { + auto str_idx = inst[1]; + body.referred_strings.push_back(str_idx); + } + if (opcode == kOpcodeConstStringJumbo) { + auto str_idx = *reinterpret_cast(&inst[1]); + body.referred_strings.push_back(static_cast(str_idx)); + } + if ((opcode >= kOpcodeIGetStart && opcode <= kOpcodeIGetEnd) || + (opcode >= kOpcodeSGetStart && opcode <= kOpcodeSGetEnd)) { + auto field_idx = inst[1]; + body.accessed_fields.push_back(field_idx); + } + if ((opcode >= kOpcodeIPutStart && opcode <= kOpcodeIPutEnd) || + (opcode >= kOpcodeSPutStart && opcode <= kOpcodeSPutEnd)) { + auto field_idx = inst[1]; + body.assigned_fields.push_back(field_idx); + } + if ((opcode >= kOpcodeInvokeStart && + opcode <= kOpcodeInvokeEnd) || + (opcode >= kOpcodeInvokeRangeStart && + opcode <= kOpcodeInvokeRangeEnd)) { + auto callee = inst[1]; + body.invoked_methods.push_back(callee); + } + if (opcode == kOpcodeNoOp) { + if (*inst == kInstPackedSwitchPlayLoad) { + inst += inst[1] * 2 + 3; + } else if (*inst == kInstSparseSwitchPlayLoad) { + inst += inst[1] * 4 + 1; + } else if (*inst == kInstFillArrayDataPlayLoad) { + inst += (*reinterpret_cast(&inst[2]) * + inst[1] + 1) / + 2 + 3; + } + } + inst += dex::opcode_len[opcode]; + } + body.loaded = true; + } + auto referred_strings = env->NewIntArray( + static_cast(body.referred_strings.size())); + env->SetIntArrayRegion(referred_strings, 0, + static_cast(body.referred_strings.size()), + body.referred_strings.data()); + auto accessed_fields = env->NewIntArray( + static_cast(body.accessed_fields.size())); + env->SetIntArrayRegion(accessed_fields, 0, + static_cast(body.accessed_fields.size()), + body.accessed_fields.data()); + auto assigned_fields = env->NewIntArray( + static_cast(body.assigned_fields.size())); + env->SetIntArrayRegion(assigned_fields, 0, + static_cast(body.assigned_fields.size()), + body.assigned_fields.data()); + auto invoked_methods = env->NewIntArray( + static_cast(body.invoked_methods.size())); + env->SetIntArrayRegion(invoked_methods, 0, + static_cast(body.invoked_methods.size()), + body.invoked_methods.data()); + auto opcodes = env->NewByteArray( + static_cast(body.opcodes.size())); + env->SetByteArrayRegion(opcodes, 0, + static_cast(body.opcodes.size()), + body.opcodes.data()); + env->CallVoidMethod(body_visitor, visit_method_body, + referred_strings, + invoked_methods, + accessed_fields, assigned_fields, opcodes); + } + stopped = env->CallBooleanMethod(member_visitor, stop); + if (stopped == JNI_TRUE) break; + } + if (stopped == JNI_TRUE) break; + } + } + if (env->CallBooleanMethod(visitor, stop) == JNI_TRUE) break; } - auto res = env->NewObjectArray(5, env->FindClass("java/lang/Object"), nullptr); - auto res0 = env->NewIntArray(static_cast(invoked_methods.size())); - auto res0_ptr = env->GetIntArrayElements(res0, nullptr); - memcpy(res0_ptr, invoked_methods.data(), invoked_methods.size() * sizeof(jint)); - env->ReleaseIntArrayElements(res0, res0_ptr, 0); - env->SetObjectArrayElement(res, 0, res0); - env->DeleteLocalRef(res0); - auto res1 = env->NewIntArray(static_cast(accessed_fields.size())); - auto res1_ptr = env->GetIntArrayElements(res1, nullptr); - memcpy(res1_ptr, accessed_fields.data(), accessed_fields.size() * sizeof(jint)); - env->ReleaseIntArrayElements(res1, res1_ptr, 0); - env->SetObjectArrayElement(res, 1, res1); - env->DeleteLocalRef(res1); - auto res2 = env->NewIntArray(static_cast(assigned_fields.size())); - auto res2_ptr = env->GetIntArrayElements(res2, nullptr); - memcpy(res2_ptr, assigned_fields.data(), assigned_fields.size() * sizeof(jint)); - env->ReleaseIntArrayElements(res2, res2_ptr, 0); - env->SetObjectArrayElement(res, 2, res2); - env->DeleteLocalRef(res2); - auto res3 = env->NewIntArray(static_cast(referred_strings.size())); - auto res3_ptr = env->GetIntArrayElements(res3, nullptr); - memcpy(res3_ptr, referred_strings.data(), referred_strings.size() * sizeof(jint)); - env->ReleaseIntArrayElements(res3, res3_ptr, 0); - env->SetObjectArrayElement(res, 3, res3); - env->DeleteLocalRef(res3); - auto res4 = env->NewByteArray(static_cast(opcodes.size())); - auto res4_ptr = env->GetByteArrayElements(res4, nullptr); - memcpy(res4_ptr, opcodes.data(), opcodes.size() * sizeof(jbyte)); - env->ReleaseByteArrayElements(res4, res4_ptr, 0); - env->SetObjectArrayElement(res, 4, res4); - env->DeleteLocalRef(res4); - - return res; } static JNINativeMethod gMethods[] = { - LSP_NATIVE_METHOD(DexParserBridge, parseDex, - "(Ljava/nio/buffer/ByteBuffer;)Ljava/lang/Object;"), - LSP_NATIVE_METHOD(DexParserBridge, parseDex, - "(Ljava/nio/buffer/ByteBuffer;I)Ljava/lang/Object;"), + LSP_NATIVE_METHOD(DexParserBridge, openDex, + "(Ljava/nio/buffer/ByteBuffer;[J)Ljava/lang/Object;"), + LSP_NATIVE_METHOD(DexParserBridge, closeDex, "(J)V;"), }; diff --git a/libxposed/api/src/main/java/io/github/libxposed/utils/DexParser.java b/libxposed/api/src/main/java/io/github/libxposed/utils/DexParser.java index b5f43f99..3dcfc319 100644 --- a/libxposed/api/src/main/java/io/github/libxposed/utils/DexParser.java +++ b/libxposed/api/src/main/java/io/github/libxposed/utils/DexParser.java @@ -3,73 +3,11 @@ package io.github.libxposed.utils; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -public interface DexParser { +import java.io.Closeable; + +public interface DexParser extends Closeable { 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 { int getVisibility(); @@ -95,35 +33,6 @@ public interface DexParser { 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 { int getId(); } @@ -166,9 +75,6 @@ public interface DexParser { TypeId[] getParameters(); } - @NonNull - ClassDef[] getClassDef(); - @NonNull StringId[] getStringId(); @@ -183,4 +89,34 @@ public interface DexParser { @NonNull 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; }