From fdcd4ae3df25edeb435ba629464c32288b0a853c Mon Sep 17 00:00:00 2001 From: LoveSy Date: Fri, 6 Jan 2023 22:02:27 +0800 Subject: [PATCH] Implement native part of dex parser --- .../lspd/impl/utils/LSPosedDexParser.java | 179 ++++--- .../lspd/nativebridge/DexParserBridge.java | 6 +- core/src/main/jni/src/jni/dex_parser.cpp | 452 ++++++++++++++++++ core/src/main/jni/src/jni/dex_parser.h | 25 + core/src/main/jni/src/jni/hook_bridge.h | 24 +- core/src/main/jni/src/jni/native_api.h | 10 +- .../io/github/libxposed/utils/DexParser.java | 7 +- 7 files changed, 589 insertions(+), 114 deletions(-) create mode 100644 core/src/main/jni/src/jni/dex_parser.cpp create mode 100644 core/src/main/jni/src/jni/dex_parser.h 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 b580bb2a..53dea714 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 @@ -7,14 +7,11 @@ import org.lsposed.lspd.nativebridge.DexParserBridge; import java.io.IOException; import java.nio.ByteBuffer; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; import io.github.libxposed.utils.DexParser; public class LSPosedDexParser implements DexParser { - ReadWriteLock lock = new ReentrantReadWriteLock(); - long cookie; + ByteBuffer data; StringId[] strings; TypeId[] typeIds; ProtoId[] protoIds; @@ -23,67 +20,59 @@ public class LSPosedDexParser implements DexParser { ClassDef[] classDefs; public LSPosedDexParser(ByteBuffer buffer) throws IOException { - lock.writeLock().lock(); if (!buffer.isDirect() || !buffer.asReadOnlyBuffer().hasArray()) { - var newBuffer = ByteBuffer.allocateDirect(buffer.capacity()); - newBuffer.put(buffer); - buffer = newBuffer; + data = ByteBuffer.allocateDirect(buffer.capacity()); + data.put(buffer); + } else { + data = buffer; } try { var out = (Object[]) DexParserBridge.parseDex(buffer); - // out[0]: Long - // out[1]: String[] + // out[0]: String[] + // out[1]: int[] // out[2]: int[][] - // out[3]: int[][3] + // out[3]: int[] // out[4]: int[][3] - // out[5]: int[][3] - // out[6]: int[][] - // out[7]: Object[][][][] - cookie = (Long) out[0]; - var strings = (Object[]) out[1]; + // out[5]: int[][] + // out[6]: Object[][][][] + var strings = (Object[]) out[0]; this.strings = new StringId[strings.length]; for (int i = 0; i < strings.length; ++i) { this.strings[i] = new LSPosedStringId(i, strings[i]); } - var typeIds = (int[]) out[2]; + var typeIds = (int[]) out[1]; this.typeIds = new TypeId[typeIds.length]; for (int i = 0; i < typeIds.length; ++i) { this.typeIds[i] = new LSPosedTypeId(i, typeIds[i]); } - var protoIds = (int[][]) out[3]; + var protoIds = (int[][]) out[2]; this.protoIds = new ProtoId[protoIds.length]; for (int i = 0; i < protoIds.length; ++i) { this.protoIds[i] = new LSPosedProtoId(i, protoIds[i]); } - var fieldIds = (int[][]) out[4]; + var fieldIds = (int[]) out[3]; this.fieldIds = new FieldId[fieldIds.length]; for (int i = 0; i < fieldIds.length; ++i) { - this.fieldIds[i] = new LSPosedFieldId(i, fieldIds[i]); + this.fieldIds[i] = new LSPosedFieldId(i, fieldIds[3 * i], fieldIds[3 * i + 1], fieldIds[3 * i + 2]); } - var methodIds = (int[][]) out[5]; + var methodIds = (int[]) out[4]; this.methodIds = new MethodId[methodIds.length]; for (int i = 0; i < methodIds.length; ++i) { - this.methodIds[i] = new LSPosedMethodId(i, methodIds[i]); + this.methodIds[i] = new LSPosedMethodId(i, methodIds[3 * i], methodIds[3 * i + 1], methodIds[3 * i + 2]); } - var classDefs = (int[][]) out[6]; + var classDefs = (int[][]) out[5]; this.classDefs = new ClassDef[classDefs.length]; - var annotations = (Object[][][][]) out[7]; + var annotations = (Object[][]) out[6]; for (int i = 0; i < classDefs.length; ++i) { - this.classDefs[i] = new LSPosedClassDef(classDefs[i], annotations[i]); + this.classDefs[i] = new LSPosedClassDef(classDefs[i], annotations[4 * i], annotations[4 * i + 1], annotations[4 * i + 2], annotations[4 * i + 3]); } - } catch (IOException e) { - close(); - throw e; } catch (Throwable e) { - close(); throw new IOException("Invalid dex file", e); - } finally { - lock.writeLock().unlock(); } } @@ -181,11 +170,11 @@ public class LSPosedDexParser implements DexParser { @NonNull final StringId name; - LSPosedFieldId(int id, @NonNull int[] fieldId) { + LSPosedFieldId(int id, int type, int declaringClass, int name) { super(id); - this.type = typeIds[fieldId[0]]; - this.declaringClass = typeIds[fieldId[1]]; - this.name = strings[fieldId[2]]; + this.type = typeIds[type]; + this.declaringClass = typeIds[declaringClass]; + this.name = strings[name]; } @NonNull @@ -215,11 +204,11 @@ public class LSPosedDexParser implements DexParser { @NonNull final StringId name; - LSPosedMethodId(int id, @NonNull int[] methodId) { + LSPosedMethodId(int id, int declaringClass, int prototype, int name) { super(id); - declaringClass = typeIds[methodId[0]]; - prototype = protoIds[methodId[1]]; - name = strings[methodId[2]]; + this.declaringClass = typeIds[declaringClass]; + this.prototype = protoIds[prototype]; + this.name = strings[name]; } @NonNull @@ -272,7 +261,7 @@ public class LSPosedDexParser implements DexParser { FieldId[] accessedFields = null; FieldId[] assignedFields = null; StringId[] referredStrings = null; - int[] opcodes = null; + byte[] opcodes = null; LSPosedEncodedMethod(int method, int accessFlags, int code) { this.method = methodIds[method]; @@ -282,34 +271,36 @@ public class LSPosedDexParser implements DexParser { synchronized void parseMethod() { if (invokedMethods != null) return; - lock.readLock().lock(); - try { - if (cookie == 0) throw new IllegalStateException("DexParser is closed"); - var out = (Object[]) DexParserBridge.parseMethod(cookie, code); - 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 = (int[]) out[4]; - } finally { - lock.readLock().unlock(); + 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 @@ -352,7 +343,7 @@ public class LSPosedDexParser implements DexParser { @NonNull @Override - public int[] getOpcodes() { + public byte[] getOpcodes() { if (opcodes == null) { parseMethod(); } @@ -550,6 +541,9 @@ public class LSPosedDexParser implements DexParser { @NonNull final EncodedMethod[] virtualMethods; + @NonNull + final Annotation[] classAnnotations; + @NonNull final FieldAnnotation[] fieldAnnotations; @NonNull @@ -557,9 +551,7 @@ public class LSPosedDexParser implements DexParser { @NonNull final ParameterAnnotation[] parameterAnnotations; - LSPosedClassDef(int[] classDef, Object[][][] annotation) { - // annotation[0]: int[] - // annotation[1]: int[] + LSPosedClassDef(int[] classDef, Object[] classAnnotations, Object[] fieldAnnotations, Object[] methodAnnotations, Object[] parameterAnnotations) { var iter = 0; type = typeIds[classDef[iter++]]; accessFlags = classDef[iter++]; @@ -603,25 +595,35 @@ public class LSPosedDexParser implements DexParser { 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], b[i]); + } + } + var num_field_annotations = classDef[iter++]; - fieldAnnotations = new FieldAnnotation[num_field_annotations]; + this.fieldAnnotations = new FieldAnnotation[num_field_annotations]; for (int i = 0; i < num_field_annotations; ++i) { var field = classDef[iter++]; - fieldAnnotations[i] = new LSPosedFieldAnnotation(field, annotation[0][i]); + this.fieldAnnotations[i] = new LSPosedFieldAnnotation(field, (Object[]) fieldAnnotations[i]); } var num_method_annotations = classDef[iter++]; - methodAnnotations = new MethodAnnotation[num_method_annotations]; + this.methodAnnotations = new MethodAnnotation[num_method_annotations]; for (int i = 0; i < num_method_annotations; ++i) { var method = classDef[iter++]; - methodAnnotations[i] = new LSPosedMethodAnnotation(method, annotation[1][i]); + this.methodAnnotations[i] = new LSPosedMethodAnnotation(method, (Object[]) methodAnnotations[i]); } var num_parameter_annotations = classDef[iter++]; - parameterAnnotations = new ParameterAnnotation[num_parameter_annotations]; + this.parameterAnnotations = new ParameterAnnotation[num_parameter_annotations]; for (int i = 0; i < num_parameter_annotations; ++i) { var method = classDef[iter++]; - parameterAnnotations[i] = new LSPosedParameterAnnotation(method, (Object[][]) annotation[2][i]); + this.parameterAnnotations[i] = new LSPosedParameterAnnotation(method, (Object[][]) parameterAnnotations[i]); } } @@ -678,6 +680,12 @@ public class LSPosedDexParser implements DexParser { return virtualMethods; } + @Nullable + @Override + public Annotation[] getClassAnnotations() { + return classAnnotations; + } + @Nullable @Override public FieldAnnotation[] getFieldAnnotations() { @@ -732,21 +740,4 @@ public class LSPosedDexParser implements DexParser { public ProtoId[] getProtoId() { return protoIds; } - - @Override - public void close() { - lock.writeLock().lock(); - try { - if (cookie == 0) return; - DexParserBridge.closeDex(cookie); - cookie = 0L; - } finally { - lock.writeLock().unlock(); - } - } - - @Override - protected void finalize() { - close(); - } } 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 d78d9a7f..0dc83a29 100644 --- a/core/src/main/java/org/lsposed/lspd/nativebridge/DexParserBridge.java +++ b/core/src/main/java/org/lsposed/lspd/nativebridge/DexParserBridge.java @@ -4,9 +4,7 @@ import java.io.IOException; import java.nio.ByteBuffer; public class DexParserBridge { - public static native Object parseDex(ByteBuffer byteBuffer) throws IOException; + public static native Object parseDex(ByteBuffer dex) throws IOException; - public static native Object parseMethod(long cookie, int code); - - public static native void closeDex(long cookie); + public static native Object parseMethod(ByteBuffer dex, int codeOffset); } diff --git a/core/src/main/jni/src/jni/dex_parser.cpp b/core/src/main/jni/src/jni/dex_parser.cpp new file mode 100644 index 00000000..31815aa0 --- /dev/null +++ b/core/src/main/jni/src/jni/dex_parser.cpp @@ -0,0 +1,452 @@ +/* + * This file is part of LSPosed. + * + * LSPosed is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LSPosed is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LSPosed. If not, see . + * + * Copyright (C) 2023 LSPosed Contributors + */ + +#include "dex_parser.h" +#include "native_util.h" +#include "slicer/reader.h" + +namespace { + jobject ParseAnnotation(JNIEnv *env, dex::Reader &dex, jclass object_class, + const dex::AnnotationSetItem *annotation) { + if (annotation == nullptr) { + return nullptr; + } + auto a = env->NewIntArray(static_cast(2 * annotation->size)); + auto *a_ptr = env->GetIntArrayElements(a, nullptr); + auto b = env->NewObjectArray(static_cast(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)); + auto size = dex::ReadULeb128(&annotation_data); + auto bi = env->NewObjectArray(static_cast(size), object_class, nullptr); + for (size_t j = 0; j < size; ++j) { + auto name = dex::ReadULeb128(&annotation_data); + auto arg_and_type = *annotation_data++; + auto 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); + break; + case 0x02: // short + case 0x03: // char + case 0x04: // int + case 0x06: // long + case 0x10: // float + case 0x11: // double + case 0x17: // string + case 0x18: // type + case 0x19: // field + case 0x1a: // method + case 0x1b: // enum + value = env->NewDirectByteBuffer( + reinterpret_cast(const_cast(annotation_data)), + value_arg); + break; + case 0x1c: // array + case 0x1d: // annotation + case 0x1e: // null + // not supported + value = nullptr; + break; + default: + break; + } + env->SetObjectArrayElement(bi, static_cast(j), value); + env->DeleteLocalRef(value); + } + env->SetObjectArrayElement(b, static_cast(i), bi); + env->DeleteLocalRef(bi); + } + 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; + } +} +namespace lspd { + LSP_DEF_NATIVE_METHOD(jobject, DexParserBridge, parseDex, jobject data) { + auto dex_size = env->GetDirectBufferCapacity(data); + if (dex_size == -1) { + env->ThrowNew(env->FindClass("java/io/IOException"), "Invalid dex data"); + return nullptr; + } + auto *dex_data = env->GetDirectBufferAddress(data); + + dex::Reader dex(reinterpret_cast(dex_data), dex_size, nullptr, 0); + if (dex.IsCompact()) { + env->ThrowNew(env->FindClass("java/io/IOException"), "Compact dex is not supported"); + return nullptr; + } + auto object_class = env->FindClass("java/lang/Object"); + auto string_class = env->FindClass("java/lang/String"); + auto int_array_class = env->FindClass("[I"); + auto out = env->NewObjectArray(7, object_class, nullptr); + auto out0 = env->NewObjectArray(static_cast(dex.StringIds().size()), + string_class, nullptr); + 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); + auto str = env->NewStringUTF(reinterpret_cast(ptr)); + env->SetObjectArrayElement(out0, static_cast(i), str); + env->DeleteLocalRef(str); + } + env->SetObjectArrayElement(out, 0, out0); + env->DeleteLocalRef(out0); + + auto types = dex.TypeIds(); + auto out1 = env->NewIntArray(static_cast(types.size())); + auto *out1_ptr = env->GetIntArrayElements(out1, nullptr); + for (size_t i = 0; i < types.size(); ++i) { + out1_ptr[i] = static_cast(types[i].descriptor_idx); + } + env->ReleaseIntArrayElements(out1, out1_ptr, 0); + env->SetObjectArrayElement(out, 1, out1); + env->DeleteLocalRef(out1); + + auto protos = dex.ProtoIds(); + auto out2 = env->NewObjectArray(static_cast(protos.size()), + int_array_class, nullptr); + auto empty_type_list = dex::TypeList{.size = 0, .list = {}}; + for (size_t i = 0; i < protos.size(); ++i) { + auto &proto = protos[i]; + const auto ¶ms = proto.parameters_off ? *dex.dataPtr( + proto.parameters_off) : empty_type_list; + + auto out2i = env->NewIntArray(static_cast(2 + params.size)); + auto *out2i_ptr = env->GetIntArrayElements(out2i, nullptr); + out2i_ptr[0] = static_cast(proto.shorty_idx); + out2i_ptr[1] = static_cast(proto.return_type_idx); + for (size_t j = 0; j < params.size; ++j) { + out2i_ptr[2 + j] = static_cast(params.list[j].type_idx); + } + env->ReleaseIntArrayElements(out2i, out2i_ptr, 0); + env->SetObjectArrayElement(out2, static_cast(i), out2i); + env->DeleteLocalRef(out2i); + } + env->SetObjectArrayElement(out, 2, out2); + env->DeleteLocalRef(out2); + + auto fields = dex.FieldIds(); + auto out3 = env->NewIntArray(static_cast(3 * fields.size())); + auto *out3_ptr = env->GetIntArrayElements(out3, nullptr); + for (size_t i = 0; i < fields.size(); ++i) { + auto &field = fields[i]; + out3_ptr[3 * i] = static_cast(field.class_idx); + out3_ptr[3 * i + 1] = static_cast(field.type_idx); + out3_ptr[3 * i + 2] = static_cast(field.name_idx); + } + env->ReleaseIntArrayElements(out3, out3_ptr, 0); + env->SetObjectArrayElement(out, 3, out3); + env->DeleteLocalRef(out3); + + auto methods = dex.MethodIds(); + auto out4 = env->NewIntArray(static_cast(3 * methods.size())); + auto *out4_ptr = env->GetIntArrayElements(out4, nullptr); + for (size_t i = 0; i < methods.size(); ++i) { + out4_ptr[3 * i] = static_cast(methods[i].class_idx); + out4_ptr[3 * i + 1] = static_cast(methods[i].proto_idx); + out4_ptr[3 * i + 2] = static_cast(methods[i].name_idx); + } + env->ReleaseIntArrayElements(out4, out4_ptr, 0); + env->SetObjectArrayElement(out, 4, out4); + 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); + 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; + 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); + } + + if (class_def.annotations_off != 0) { + annotations = dex.dataPtr(class_def.annotations_off); + if (annotations->class_annotations_off != 0) { + class_annotation = dex.dataPtr( + annotations->class_annotations_off); + } + field_annotations_count = annotations->fields_size; + method_annotations_count = annotations->methods_size; + 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)); + } + + auto out6i0 = ParseAnnotation(env, dex, object_class, class_annotation); + env->SetObjectArrayElement(out6, static_cast(4 * i), out6i0); + env->DeleteLocalRef(out6i0); + + out5i_ptr[j++] = static_cast(class_annotation ? class_annotation->size : 0); + + 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); + } + 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); + } + 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); + for (size_t l = 0; l < parameter_annotation->size; ++l) { + 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); + } + 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; + } + + 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); + + static constexpr dex::u1 kOpcodeMask = 0xff; + static constexpr dex::u1 kOpcodeNoOp = 0x00; + static constexpr dex::u1 kOpcodeConstString = 0x1a; + static constexpr dex::u1 kOpcodeConstStringJumbo = 0x1b; + static constexpr dex::u1 kOpcodeIGetStart = 0x52; + static constexpr dex::u1 kOpcodeIGetEnd = 0x58; + static constexpr dex::u1 kOpcodeSGetStart = 0x60; + static constexpr dex::u1 kOpcodeSGetEnd = 0x66; + static constexpr dex::u1 kOpcodeIPutStart = 0x59; + static constexpr dex::u1 kOpcodeIPutEnd = 0x5f; + static constexpr dex::u1 kOpcodeSPutStart = 0x67; + static constexpr dex::u1 kOpcodeSPutEnd = 0x6d; + static constexpr dex::u1 kOpcodeInvokeStart = 0x6e; + static constexpr dex::u1 kOpcodeInvokeEnd = 0x72; + static constexpr dex::u1 kOpcodeInvokeRangeStart = 0x74; + static constexpr dex::u1 kOpcodeInvokeRangeEnd = 0x78; + 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; + } + } + inst += dex::opcode_len[opcode]; + } + 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;"), + }; + + + void RegisterDexParserBridge(JNIEnv *env) { + REGISTER_LSP_NATIVE_METHODS(DexParserBridge); + } +} diff --git a/core/src/main/jni/src/jni/dex_parser.h b/core/src/main/jni/src/jni/dex_parser.h new file mode 100644 index 00000000..c6785d46 --- /dev/null +++ b/core/src/main/jni/src/jni/dex_parser.h @@ -0,0 +1,25 @@ +/* + * This file is part of LSPosed. + * + * LSPosed is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LSPosed is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LSPosed. If not, see . + * + * Copyright (C) 2023 LSPosed Contributors + */ +#pragma once + +#include + +namespace lspd { + void RegisterDexParserBridge(JNIEnv *env); +} diff --git a/core/src/main/jni/src/jni/hook_bridge.h b/core/src/main/jni/src/jni/hook_bridge.h index 035b4e87..d2c99465 100644 --- a/core/src/main/jni/src/jni/hook_bridge.h +++ b/core/src/main/jni/src/jni/hook_bridge.h @@ -1,11 +1,25 @@ -// -// Created by loves on 3/13/2022. -// - +/* + * This file is part of LSPosed. + * + * LSPosed is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LSPosed is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LSPosed. If not, see . + * + * Copyright (C) 2022 LSPosed Contributors + */ #pragma once #include namespace lspd { -void RegisterHookBridge(JNIEnv* env); + void RegisterHookBridge(JNIEnv *env); } diff --git a/core/src/main/jni/src/jni/native_api.h b/core/src/main/jni/src/jni/native_api.h index 6776c823..be541808 100644 --- a/core/src/main/jni/src/jni/native_api.h +++ b/core/src/main/jni/src/jni/native_api.h @@ -18,18 +18,10 @@ * Copyright (C) 2021 LSPosed Contributors */ -// -// Created by εŒθ‰ι…Έι…― on 2/7/21. -// - -#ifndef LSPOSED_JNI_NATIVE_API_H -#define LSPOSED_JNI_NATIVE_API_H +#pragma once #include namespace lspd { void RegisterNativeAPI(JNIEnv *); } - - -#endif //LSPOSED_JNI_NATIVE_API_H 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 253ad9f6..c230c140 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,7 +3,7 @@ package io.github.libxposed.utils; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -public interface DexParser extends AutoCloseable { +public interface DexParser { int NO_INDEX = 0xffffffff; interface ClassDef { @@ -33,6 +33,9 @@ public interface DexParser extends AutoCloseable { @NonNull EncodedMethod[] getVirtualMethods(); + @Nullable + Annotation[] getClassAnnotations(); + @Nullable FieldAnnotation[] getFieldAnnotations(); @@ -116,7 +119,7 @@ public interface DexParser extends AutoCloseable { FieldId[] getAssignedFields(); @NonNull - int[] getOpcodes(); + byte[] getOpcodes(); @NonNull StringId[] getReferredString();