From 7ac42ebe4dd52b9e5a1d314c38d90bc60f9b7f1b Mon Sep 17 00:00:00 2001 From: LoveSy Date: Sun, 8 Jan 2023 06:32:25 +0800 Subject: [PATCH] Finish DexParser --- .../org/lsposed/lspd/impl/LSPosedContext.java | 5 +- .../lspd/impl/utils/LSPosedDexParser.java | 90 ++++-- core/src/main/jni/src/jni/dex_parser.cpp | 269 +++++++++++++----- .../libxposed/XposedContextWrapper.java | 4 +- .../io/github/libxposed/XposedInterface.java | 2 +- .../io/github/libxposed/utils/DexParser.java | 25 +- 6 files changed, 294 insertions(+), 101 deletions(-) diff --git a/core/src/main/java/org/lsposed/lspd/impl/LSPosedContext.java b/core/src/main/java/org/lsposed/lspd/impl/LSPosedContext.java index 01d9cdcc..41b309ae 100644 --- a/core/src/main/java/org/lsposed/lspd/impl/LSPosedContext.java +++ b/core/src/main/java/org/lsposed/lspd/impl/LSPosedContext.java @@ -913,9 +913,8 @@ public class LSPosedContext extends XposedContext { Log.e(TAG, mPackageName + ": " + message, throwable); } - @Nullable @Override - public DexParser parseDex(ByteBuffer dexData) throws IOException { - return new LSPosedDexParser(dexData); + public DexParser parseDex(@NonNull ByteBuffer dexData, boolean includeAnnotations) throws IOException { + return new LSPosedDexParser(dexData, includeAnnotations); } } 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 f570e527..ff1f381f 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 @@ -13,15 +13,24 @@ import io.github.libxposed.utils.DexParser; public class LSPosedDexParser implements DexParser { long cookie; + @NonNull final ByteBuffer data; + @NonNull final StringId[] strings; + @NonNull final TypeId[] typeIds; + @NonNull final ProtoId[] protoIds; + @NonNull final FieldId[] fieldIds; + @NonNull final MethodId[] methodIds; + @NonNull final Annotation[] annotations; + @NonNull + final Array[] arrays; - public LSPosedDexParser(ByteBuffer buffer) throws IOException { + public LSPosedDexParser(@NonNull ByteBuffer buffer, boolean includeAnnotations) throws IOException { if (!buffer.isDirect() || !buffer.asReadOnlyBuffer().hasArray()) { data = ByteBuffer.allocateDirect(buffer.capacity()); data.put(buffer); @@ -29,7 +38,8 @@ public class LSPosedDexParser implements DexParser { data = buffer; } try { - long[] args = new long[1]; + long[] args = new long[2]; + args[1] = includeAnnotations ? 1 : 0; var out = (Object[]) DexParserBridge.openDex(buffer, args); cookie = args[0]; // out[0]: String[] @@ -37,7 +47,9 @@ public class LSPosedDexParser implements DexParser { // out[2]: int[][] // out[3]: int[] // out[4]: int[] + // out[5]: int[] // out[6]: Object[] + // out[7]: Object[] var strings = (Object[]) out[0]; this.strings = new StringId[strings.length]; for (int i = 0; i < strings.length; ++i) { @@ -78,6 +90,15 @@ public class LSPosedDexParser implements DexParser { } else { this.annotations = new Annotation[0]; } + if (out[7] != null) { + var b = (Object[]) out[7]; + this.arrays = new Array[b.length / 2]; + for (int i = 0; i < this.arrays.length; ++i) { + this.arrays[i] = new LSPosedArray((int[]) b[2 * i], (Object[]) b[2 * i + 1]); + } + } else { + this.arrays = new Array[0]; + } } catch (Throwable e) { throw new IOException("Invalid dex file", e); } @@ -245,19 +266,37 @@ public class LSPosedDexParser implements DexParser { } } + static class LSPosedArray implements Array { + @NonNull + final Value[] values; + + LSPosedArray(int[] elements, @NonNull Object[] values) { + this.values = new Value[values.length]; + for (int i = 0; i < values.length; ++i) { + this.values[i] = new LSPosedValue(elements[i], (ByteBuffer) values[i]); + } + } + + @NonNull + @Override + public Value[] getValues() { + return values; + } + } + class LSPosedAnnotation implements Annotation { int visibility; @NonNull final TypeId type; - @Nullable - final AnnotationElement[] elements; + @NonNull + final Element[] elements; LSPosedAnnotation(int visibility, int type, @NonNull int[] elements, @NonNull Object[] elementValues) { this.visibility = visibility; this.type = typeIds[type]; - this.elements = new AnnotationElement[elementValues.length]; + this.elements = new Element[elementValues.length]; for (int i = 0; i < elementValues.length; ++i) { - this.elements[i] = new LSPosedAnnotationElement(elements[i * 2], elements[i * 2 + 1], (ByteBuffer) elementValues[i]); + this.elements[i] = new LSPosedElement(elements[i * 2], elements[i * 2 + 1], (ByteBuffer) elementValues[i]); } } @@ -272,22 +311,19 @@ public class LSPosedDexParser implements DexParser { return type; } - @Nullable + @NonNull @Override - public AnnotationElement[] getElements() { + public Element[] getElements() { return elements; } } - class LSPosedAnnotationElement implements AnnotationElement { - @NonNull - final StringId name; + static class LSPosedValue implements Value { final int valueType; @Nullable final byte[] value; - LSPosedAnnotationElement(int name, int valueType, @Nullable ByteBuffer value) { - this.name = strings[name]; + LSPosedValue(int valueType, @Nullable ByteBuffer value) { this.valueType = valueType; if (value != null) { this.value = new byte[value.remaining()]; @@ -297,21 +333,31 @@ public class LSPosedDexParser implements DexParser { } } - @NonNull + @Nullable @Override - public StringId getName() { - return name; + public byte[] getValue() { + return value; } @Override public int getValueType() { return valueType; } + } - @Nullable + class LSPosedElement extends LSPosedValue implements Element { + @NonNull + final StringId name; + + LSPosedElement(int name, int valueType, @Nullable ByteBuffer value) { + super(valueType, value); + this.name = strings[name]; + } + + @NonNull @Override - public byte[] getValue() { - return value; + public StringId getName() { + return name; } } @@ -351,6 +397,12 @@ public class LSPosedDexParser implements DexParser { return annotations; } + @NonNull + @Override + public Array[] getArrays() { + return arrays; + } + @Override synchronized public void visitDefinedClasses(@NonNull ClassVisitor visitor) { if (cookie == 0) { diff --git a/core/src/main/jni/src/jni/dex_parser.cpp b/core/src/main/jni/src/jni/dex_parser.cpp index 264feb2e..ca320cbe 100644 --- a/core/src/main/jni/src/jni/dex_parser.cpp +++ b/core/src/main/jni/src/jni/dex_parser.cpp @@ -25,61 +25,13 @@ #include namespace { - 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; - } - for (size_t i = 0; i < annotation->size; ++i) { - auto *item = dex.dataPtr(annotation->entries[i]); - auto *annotation_data = item->annotation; - 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); - items.resize(size); - for (size_t j = 0; j < size; ++j) { - auto &[name, value_type, value] = items[j]; - name = static_cast(dex::ReadULeb128(&annotation_data)); - auto arg_and_type = *annotation_data++; - value_type = arg_and_type & 0x1f; - auto value_arg = arg_and_type >> 5; - switch (value_type) { - case 0x00: // byte - case 0x1f: // boolean - value = {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 = { - reinterpret_cast(const_cast(annotation_data)), - static_cast(value_arg)}; - break; - case 0x1c: // array - case 0x1d: // annotation - case 0x1e: // null - default: - // not supported - break; - } - } - } - } + using Value = std::tuple /*data*/>; + using Array = std::vector; + using ArrayList = std::list; + using Element = std::tuple; + using ElementList = std::vector; + using Annotation = std::tuple; + using AnnotationList = std::vector; class DexParser : public dex::Reader { public: @@ -116,6 +68,145 @@ namespace { absl::flat_hash_map method_bodies; }; + + template + static std::vector ParseIntValue(const dex::u1 **pptr, size_t size) { + static_assert(std::is_integral::value, "must be an integral type"); + std::vector ret(sizeof(T)); + T &value = *reinterpret_cast(ret.data()); + for (size_t i = 0; i < size; ++i) { + value |= T(*(*pptr)++) << (i * 8); + } + + // sign-extend? + if constexpr (std::is_signed_v) { + size_t shift = (sizeof(T) - size) * 8; + value = T(value << shift) >> shift; + } + return ret; + } + + template + static std::vector ParseFloatValue(const dex::u1 **pptr, size_t size) { + std::vector ret(sizeof(T)); + T &value = *reinterpret_cast(ret.data()); + int start_byte = sizeof(T) - size; + for (dex::u1 *p = reinterpret_cast(&value) + start_byte; size > 0; + --size) { + *p++ = *(*pptr)++; + } + return ret; + } + + Annotation + ParseAnnotation(const dex::u1 **annotation, AnnotationList &annotation_list, + ArrayList &array_list); + + Array + ParseArray(const dex::u1 **array, AnnotationList &annotation_list, ArrayList &array_list); + + Value + ParseValue(const dex::u1 **value, AnnotationList &annotation_list, ArrayList &array_list) { + Value res; + auto &[type, value_content] = res; + auto header = *(*value)++; + type = header & dex::kEncodedValueTypeMask; + dex::u1 arg = header >> dex::kEncodedValueArgShift; + switch (type) { + case dex::kEncodedByte: + value_content = ParseIntValue(value, arg + 1); + break; + case dex::kEncodedShort: + value_content = ParseIntValue(value, arg + 1); + break; + case dex::kEncodedChar: + value_content = ParseIntValue(value, arg + 1); + break; + case dex::kEncodedInt: + value_content = ParseIntValue(value, arg + 1); + break; + case dex::kEncodedLong: + value_content = ParseIntValue(value, arg + 1); + break; + case dex::kEncodedFloat: + value_content = ParseFloatValue(value, arg + 1); + break; + case dex::kEncodedDouble: + value_content = ParseFloatValue(value, arg + 1); + break; + case dex::kEncodedMethodType: + case dex::kEncodedMethodHandle: + case dex::kEncodedString: + case dex::kEncodedType: + case dex::kEncodedField: + case dex::kEncodedMethod: + case dex::kEncodedEnum: + value_content = ParseIntValue(value, arg + 1); + break; + case dex::kEncodedArray: + value_content.resize(sizeof(jint)); + *reinterpret_cast(value_content.data()) = static_cast(array_list.size()); + array_list.emplace_back(ParseArray(value, annotation_list, array_list)); + break; + case dex::kEncodedAnnotation: + value_content.resize(sizeof(jint)); + *reinterpret_cast(value_content.data()) = static_cast(annotation_list.size()); + annotation_list.emplace_back(ParseAnnotation(value, annotation_list, array_list)); + break; + case dex::kEncodedNull: + break; + case dex::kEncodedBoolean: + value_content = {static_cast(arg == 1)}; + break; + default: + __builtin_unreachable(); + } + return res; + } + + Annotation + ParseAnnotation(const dex::u1 **annotation, AnnotationList &annotation_list, + ArrayList &array_list) { + Annotation ret = {dex::kVisibilityEncoded, + dex::ReadULeb128(annotation), + ElementList{}}; + auto &[vis, type, element_list] = ret; + auto size = dex::ReadULeb128(annotation); + element_list.resize(size); + for (size_t j = 0; j < size; ++j) { + auto &[name, value] = element_list[j]; + name = static_cast(dex::ReadULeb128(annotation)); + value = ParseValue(annotation, annotation_list, array_list); + } + return ret; + } + + Array + ParseArray(const dex::u1 **array, AnnotationList &annotation_list, ArrayList &array_list) { + auto size = dex::ReadULeb128(array); + Array ret; + ret.reserve(size); + for (size_t i = 0; i < size; ++i) { + ret.emplace_back(ParseValue(array, annotation_list, array_list)); + } + return ret; + } + + void ParseAnnotationSet(dex::Reader &dex, AnnotationList &annotation_list, + ArrayList &array_list, std::vector &indices, + const dex::AnnotationSetItem *annotation_set) { + if (annotation_set == nullptr) { + return; + } + for (size_t i = 0; i < annotation_set->size; ++i) { + auto *item = dex.dataPtr(annotation_set->entries[i]); + auto *annotation_data = item->annotation; + indices.emplace_back(annotation_list.size()); + auto &[visibility, type, element_list] = annotation_list.emplace_back( + ParseAnnotation(&annotation_data, annotation_list, array_list)); + visibility = item->visibility; + } + } } namespace lspd { @@ -128,6 +219,9 @@ namespace lspd { auto *dex_data = env->GetDirectBufferAddress(data); auto *dex_reader = new DexParser(reinterpret_cast(dex_data), dex_size); + auto *args_ptr = env->GetLongArrayElements(args, nullptr); + auto include_annotations = args_ptr[1]; + env->ReleaseLongArrayElements(args, args_ptr, JNI_ABORT); env->SetLongArrayRegion(args, 0, 1, reinterpret_cast(&dex_reader)); auto &dex = *dex_reader; if (dex.IsCompact()) { @@ -137,7 +231,7 @@ namespace lspd { 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 out = env->NewObjectArray(8, object_class, nullptr); auto out0 = env->NewObjectArray(static_cast(dex.StringIds().size()), string_class, nullptr); auto strings = dex.StringIds(); @@ -213,6 +307,7 @@ namespace lspd { dex.class_data.resize(classes.size()); AnnotationList annotation_list; + ArrayList array_list; for (size_t i = 0; i < classes.size(); ++i) { auto &class_def = classes[i]; @@ -304,7 +399,9 @@ namespace lspd { } } - ParseAnnotation(env, dex, annotation_list, class_data.annotations, class_annotation); + if (!include_annotations) continue; + ParseAnnotationSet(dex, annotation_list, array_list, class_data.annotations, + class_annotation); auto *field_annotations = annotations ? reinterpret_cast( @@ -312,9 +409,9 @@ namespace lspd { for (size_t k = 0; k < field_annotations_count; ++k) { auto *field_annotation = dex.dataPtr( field_annotations[k].annotations_off); - ParseAnnotation(env, dex, annotation_list, - dex.field_annotations[static_cast(field_annotations[k].field_idx)], - field_annotation); + ParseAnnotationSet(dex, annotation_list, array_list, + dex.field_annotations[static_cast(field_annotations[k].field_idx)], + field_annotation); } auto *method_annotations = field_annotations @@ -324,9 +421,9 @@ namespace lspd { for (size_t k = 0; k < method_annotations_count; ++k) { auto *method_annotation = dex.dataPtr( method_annotations[k].annotations_off); - ParseAnnotation(env, dex, annotation_list, - dex.method_annotations[static_cast(method_annotations[k].method_idx)], - method_annotation); + ParseAnnotationSet(dex, annotation_list, array_list, + dex.method_annotations[static_cast(method_annotations[k].method_idx)], + method_annotation); } auto *parameter_annotations = method_annotations @@ -341,14 +438,16 @@ namespace lspd { if (parameter_annotation->list[l].annotations_off != 0) { auto *parameter_annotation_item = dex.dataPtr( parameter_annotation->list[l].annotations_off); - ParseAnnotation(env, dex, annotation_list, indices, - parameter_annotation_item); + ParseAnnotationSet(dex, annotation_list, array_list, indices, + parameter_annotation_item); } indices.emplace_back(dex::kNoIndex); } } } - return out; + + if (!include_annotations) 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); @@ -360,9 +459,10 @@ namespace lspd { 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()); + for (auto&[name, value]: items) { + auto &[value_type, value_data] = value; + auto java_value = value_data.empty() ? nullptr : env->NewDirectByteBuffer( + value_data.data(), value_data.size()); env->SetObjectArrayElement(out6i1, static_cast(j), java_value); out6i0_ptr[2 * j] = name; out6i0_ptr[2 * j + 1] = value_type; @@ -384,6 +484,35 @@ namespace lspd { env->DeleteLocalRef(out5); env->DeleteLocalRef(out6); + auto out7 = env->NewObjectArray(static_cast(2 * array_list.size()), object_class, + nullptr); + i = 0; + for (auto &array: array_list) { + + auto out7i0 = env->NewIntArray(static_cast(array.size())); + auto out7i0_ptr = env->GetIntArrayElements(out7i0, nullptr); + auto out7i1 = env->NewObjectArray(static_cast(array.size()), object_class, + nullptr); + size_t j = 0; + for (auto &value: array) { + auto &[value_type, value_data] = value; + auto java_value = value_data.empty() ? nullptr : env->NewDirectByteBuffer( + value_data.data(), value_data.size()); + out7i0_ptr[j] = value_type; + env->SetObjectArrayElement(out7i1, static_cast(j), java_value); + env->DeleteLocalRef(java_value); + ++j; + } + env->ReleaseIntArrayElements(out7i0, out7i0_ptr, 0); + env->SetObjectArrayElement(out7, static_cast(2 * i), out7i0); + env->SetObjectArrayElement(out7, static_cast(2 * i + 1), out7i1); + env->DeleteLocalRef(out7i0); + env->DeleteLocalRef(out7i1); + ++i; + } + env->SetObjectArrayElement(out, 7, out7); + env->DeleteLocalRef(out7); + return out; } @@ -664,6 +793,8 @@ namespace lspd { LSP_NATIVE_METHOD(DexParserBridge, openDex, "(Ljava/nio/buffer/ByteBuffer;[J)Ljava/lang/Object;"), LSP_NATIVE_METHOD(DexParserBridge, closeDex, "(J)V;"), + LSP_NATIVE_METHOD(DexParserBridge, visitClass, + "(JLio/github/libxposed/utils/DexParser$ClassVisitor;Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)V"), }; diff --git a/libxposed/api/src/main/java/io/github/libxposed/XposedContextWrapper.java b/libxposed/api/src/main/java/io/github/libxposed/XposedContextWrapper.java index 50ae1e67..46b13c4a 100644 --- a/libxposed/api/src/main/java/io/github/libxposed/XposedContextWrapper.java +++ b/libxposed/api/src/main/java/io/github/libxposed/XposedContextWrapper.java @@ -141,8 +141,8 @@ public class XposedContextWrapper extends ContextWrapper implements XposedInterf @Nullable @Override - final public DexParser parseDex(ByteBuffer dexData) throws IOException { - return getBaseContext().parseDex(dexData); + final public DexParser parseDex(@NonNull ByteBuffer dexData, boolean includeAnnotations) throws IOException { + return getBaseContext().parseDex(dexData, includeAnnotations); } @Override diff --git a/libxposed/api/src/main/java/io/github/libxposed/XposedInterface.java b/libxposed/api/src/main/java/io/github/libxposed/XposedInterface.java index cafa8f90..9a0269bc 100644 --- a/libxposed/api/src/main/java/io/github/libxposed/XposedInterface.java +++ b/libxposed/api/src/main/java/io/github/libxposed/XposedInterface.java @@ -158,5 +158,5 @@ public interface XposedInterface { void log(@NonNull String message, @NonNull Throwable throwable); @Nullable - DexParser parseDex(ByteBuffer dexData) throws IOException; + DexParser parseDex(@NonNull ByteBuffer dexData, boolean includeAnnotations) throws IOException; } 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 3dcfc319..1a5d820d 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 @@ -8,24 +8,32 @@ import java.io.Closeable; public interface DexParser extends Closeable { int NO_INDEX = 0xffffffff; + interface Array { + @NonNull + Value[] getValues(); + } + interface Annotation { int getVisibility(); @NonNull TypeId getType(); - @Nullable - AnnotationElement[] getElements(); + @NonNull + Element[] getElements(); } - interface AnnotationElement { - @NonNull - StringId getName(); - - int getValueType(); + interface Value { @Nullable byte[] getValue(); + + int getValueType(); + } + + interface Element extends Value { + @NonNull + StringId getName(); } interface TypeId { @@ -93,6 +101,9 @@ public interface DexParser extends Closeable { @NonNull Annotation[] getAnnotations(); + @NonNull + Array[] getArrays(); + interface EarlyStopVisitor { void stop(); }