Implement native part of dex parser
This commit is contained in:
parent
0625839b42
commit
fdcd4ae3df
|
|
@ -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,10 +271,15 @@ 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 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) {
|
||||
|
|
@ -306,10 +300,7 @@ public class LSPosedDexParser implements DexParser {
|
|||
for (int i = 0; i < referredStrings.length; ++i) {
|
||||
this.referredStrings[i] = strings[referredStrings[i]];
|
||||
}
|
||||
opcodes = (int[]) out[4];
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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<jint>(2 * annotation->size));
|
||||
auto *a_ptr = env->GetIntArrayElements(a, nullptr);
|
||||
auto b = env->NewObjectArray(static_cast<jint>(annotation->size), object_class, nullptr);
|
||||
for (size_t i = 0; i < annotation->size; ++i) {
|
||||
auto *item = dex.dataPtr<dex::AnnotationItem>(annotation->entries[i]);
|
||||
a_ptr[2 * i] = item->visibility;
|
||||
auto *annotation_data = item->annotation;
|
||||
a_ptr[2 * i + 1] = static_cast<jint>(dex::ReadULeb128(&annotation_data));
|
||||
auto size = dex::ReadULeb128(&annotation_data);
|
||||
auto bi = env->NewObjectArray(static_cast<jint>(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<void *>(const_cast<dex::u1 *>(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<void *>(const_cast<dex::u1 *>(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<jint>(j), value);
|
||||
env->DeleteLocalRef(value);
|
||||
}
|
||||
env->SetObjectArrayElement(b, static_cast<jint>(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::u1 *>(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<jint>(dex.StringIds().size()),
|
||||
string_class, nullptr);
|
||||
auto strings = dex.StringIds();
|
||||
for (size_t i = 0; i < strings.size(); ++i) {
|
||||
const auto *ptr = dex.dataPtr<dex::u1>(strings[i].string_data_off);
|
||||
size_t len = dex::ReadULeb128(&ptr);
|
||||
auto str = env->NewStringUTF(reinterpret_cast<const char *>(ptr));
|
||||
env->SetObjectArrayElement(out0, static_cast<jint>(i), str);
|
||||
env->DeleteLocalRef(str);
|
||||
}
|
||||
env->SetObjectArrayElement(out, 0, out0);
|
||||
env->DeleteLocalRef(out0);
|
||||
|
||||
auto types = dex.TypeIds();
|
||||
auto out1 = env->NewIntArray(static_cast<jint>(types.size()));
|
||||
auto *out1_ptr = env->GetIntArrayElements(out1, nullptr);
|
||||
for (size_t i = 0; i < types.size(); ++i) {
|
||||
out1_ptr[i] = static_cast<jint>(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<jint>(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<dex::TypeList>(
|
||||
proto.parameters_off) : empty_type_list;
|
||||
|
||||
auto out2i = env->NewIntArray(static_cast<jint>(2 + params.size));
|
||||
auto *out2i_ptr = env->GetIntArrayElements(out2i, nullptr);
|
||||
out2i_ptr[0] = static_cast<jint>(proto.shorty_idx);
|
||||
out2i_ptr[1] = static_cast<jint>(proto.return_type_idx);
|
||||
for (size_t j = 0; j < params.size; ++j) {
|
||||
out2i_ptr[2 + j] = static_cast<jint>(params.list[j].type_idx);
|
||||
}
|
||||
env->ReleaseIntArrayElements(out2i, out2i_ptr, 0);
|
||||
env->SetObjectArrayElement(out2, static_cast<jint>(i), out2i);
|
||||
env->DeleteLocalRef(out2i);
|
||||
}
|
||||
env->SetObjectArrayElement(out, 2, out2);
|
||||
env->DeleteLocalRef(out2);
|
||||
|
||||
auto fields = dex.FieldIds();
|
||||
auto out3 = env->NewIntArray(static_cast<jint>(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<jint>(field.class_idx);
|
||||
out3_ptr[3 * i + 1] = static_cast<jint>(field.type_idx);
|
||||
out3_ptr[3 * i + 2] = static_cast<jint>(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<jint>(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<jint>(methods[i].class_idx);
|
||||
out4_ptr[3 * i + 1] = static_cast<jint>(methods[i].proto_idx);
|
||||
out4_ptr[3 * i + 2] = static_cast<jint>(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<jint>(classes.size()),
|
||||
int_array_class, nullptr);
|
||||
auto out6 = env->NewObjectArray(static_cast<jint>(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<dex::TypeList>(
|
||||
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<dex::u1>(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<dex::AnnotationsDirectoryItem>(class_def.annotations_off);
|
||||
if (annotations->class_annotations_off != 0) {
|
||||
class_annotation = dex.dataPtr<dex::AnnotationSetItem>(
|
||||
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<int>(array_size));
|
||||
auto *out5i_ptr = env->GetIntArrayElements(out5i, nullptr);
|
||||
size_t j = 0;
|
||||
out5i_ptr[j++] = static_cast<jint>(class_def.class_idx);
|
||||
out5i_ptr[j++] = static_cast<jint>(class_def.access_flags);
|
||||
out5i_ptr[j++] = static_cast<jint>(class_def.superclass_idx);
|
||||
out5i_ptr[j++] = static_cast<jint>(class_def.source_file_idx);
|
||||
out5i_ptr[j++] = static_cast<jint>(interfaces.size);
|
||||
for (size_t k = 0; k < interfaces.size; ++k) {
|
||||
out5i_ptr[j++] = static_cast<jint>(interfaces.list[k].type_idx);
|
||||
}
|
||||
out5i_ptr[j++] = static_cast<jint>(static_fields_count);
|
||||
for (size_t k = 0, field_idx = 0; k < static_fields_count; ++k) {
|
||||
out5i_ptr[j++] = static_cast<jint>(field_idx += dex::ReadULeb128(&class_data));
|
||||
out5i_ptr[j++] = static_cast<jint>(dex::ReadULeb128(&class_data));
|
||||
}
|
||||
out5i_ptr[j++] = static_cast<jint>(instance_fields_count);
|
||||
for (size_t k = 0, field_idx = 0; k < instance_fields_count; ++k) {
|
||||
out5i_ptr[j++] = static_cast<jint>(field_idx += dex::ReadULeb128(&class_data));
|
||||
out5i_ptr[j++] = static_cast<jint>(dex::ReadULeb128(&class_data));
|
||||
}
|
||||
out5i_ptr[j++] = static_cast<jint>(direct_methods_count);
|
||||
for (size_t k = 0, method_idx = 0; k < direct_methods_count; ++k) {
|
||||
out5i_ptr[j++] = static_cast<jint>(method_idx += dex::ReadULeb128(&class_data));
|
||||
out5i_ptr[j++] = static_cast<jint>(dex::ReadULeb128(&class_data));
|
||||
out5i_ptr[j++] = static_cast<jint>(dex::ReadULeb128(&class_data));
|
||||
}
|
||||
out5i_ptr[j++] = static_cast<jint>(virtual_methods_count);
|
||||
for (size_t k = 0, method_idx = 0; k < virtual_methods_count; ++k) {
|
||||
out5i_ptr[j++] = static_cast<jint>(method_idx += dex::ReadULeb128(&class_data));
|
||||
out5i_ptr[j++] = static_cast<jint>(dex::ReadULeb128(&class_data));
|
||||
out5i_ptr[j++] = static_cast<jint>(dex::ReadULeb128(&class_data));
|
||||
}
|
||||
|
||||
auto out6i0 = ParseAnnotation(env, dex, object_class, class_annotation);
|
||||
env->SetObjectArrayElement(out6, static_cast<jint>(4 * i), out6i0);
|
||||
env->DeleteLocalRef(out6i0);
|
||||
|
||||
out5i_ptr[j++] = static_cast<jint>(class_annotation ? class_annotation->size : 0);
|
||||
|
||||
out5i_ptr[j++] = static_cast<jint>(field_annotations_count);
|
||||
auto *field_annotations = annotations
|
||||
? reinterpret_cast<const dex::FieldAnnotationsItem *>(
|
||||
annotations + 1) : nullptr;
|
||||
auto out6i1 = env->NewObjectArray(static_cast<jint>(field_annotations_count),
|
||||
object_class, nullptr);
|
||||
for (size_t k = 0; k < field_annotations_count; ++k) {
|
||||
out5i_ptr[j++] = static_cast<jint>(field_annotations[k].field_idx);
|
||||
auto *field_annotation = dex.dataPtr<dex::AnnotationSetItem>(
|
||||
field_annotations[k].annotations_off);
|
||||
auto out6i1i = ParseAnnotation(env, dex, object_class, field_annotation);
|
||||
env->SetObjectArrayElement(out6i1, static_cast<jint>(k), out6i1i);
|
||||
env->DeleteLocalRef(out6i1i);
|
||||
}
|
||||
env->SetObjectArrayElement(out6, static_cast<jint>(4 * i + 1), out6i1);
|
||||
env->DeleteLocalRef(out6i1);
|
||||
|
||||
out5i_ptr[j++] = static_cast<jint>(method_annotations_count);
|
||||
auto *method_annotations = field_annotations
|
||||
? reinterpret_cast<const dex::MethodAnnotationsItem *>(
|
||||
field_annotations + field_annotations_count)
|
||||
: nullptr;
|
||||
auto out6i2 = env->NewObjectArray(static_cast<jint>(method_annotations_count),
|
||||
object_class, nullptr);
|
||||
for (size_t k = 0; k < method_annotations_count; ++k) {
|
||||
out5i_ptr[j++] = static_cast<jint>(method_annotations[k].method_idx);
|
||||
auto *method_annotation = dex.dataPtr<dex::AnnotationSetItem>(
|
||||
method_annotations[k].annotations_off);
|
||||
auto out6i2i = ParseAnnotation(env, dex, object_class, method_annotation);
|
||||
env->SetObjectArrayElement(out6i2, static_cast<jint>(k), out6i2i);
|
||||
env->DeleteLocalRef(out6i2i);
|
||||
}
|
||||
env->SetObjectArrayElement(out6, static_cast<jint>(4 * i + 2), out6i2);
|
||||
|
||||
out5i_ptr[j++] = static_cast<jint>(parameter_annotations_count);
|
||||
auto *parameter_annotations = method_annotations
|
||||
? reinterpret_cast<const dex::ParameterAnnotationsItem *>(
|
||||
method_annotations + method_annotations_count)
|
||||
: nullptr;
|
||||
auto out6i3 = env->NewObjectArray(static_cast<jint>(parameter_annotations_count),
|
||||
object_class, nullptr);
|
||||
for (size_t k = 0; k < parameter_annotations_count; ++k) {
|
||||
out5i_ptr[j++] = static_cast<jint>(parameter_annotations[k].method_idx);
|
||||
auto *parameter_annotation = dex.dataPtr<dex::AnnotationSetRefList>(
|
||||
parameter_annotations[k].annotations_off);
|
||||
auto out6i3i = env->NewObjectArray(
|
||||
static_cast<jint>(parameter_annotation->size), object_class, nullptr);
|
||||
for (size_t l = 0; l < parameter_annotation->size; ++l) {
|
||||
auto *parameter_annotation_item = dex.dataPtr<dex::AnnotationSetItem>(
|
||||
parameter_annotation->list[l].annotations_off);
|
||||
auto out6i3ii = ParseAnnotation(env, dex, object_class,
|
||||
parameter_annotation_item);
|
||||
env->SetObjectArrayElement(out6i3i, static_cast<jint>(l), out6i3ii);
|
||||
env->DeleteLocalRef(out6i3ii);
|
||||
}
|
||||
env->SetObjectArrayElement(out6i3, static_cast<jint>(k), out6i3i);
|
||||
env->DeleteLocalRef(out6i3i);
|
||||
}
|
||||
env->SetObjectArrayElement(out6, static_cast<jint>(4 * i + 3), out6i3);
|
||||
env->DeleteLocalRef(out6i3);
|
||||
|
||||
env->ReleaseIntArrayElements(out5i, out5i_ptr, 0);
|
||||
env->SetObjectArrayElement(out5, static_cast<jint>(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<dex::Code *>(reinterpret_cast<dex::u1 *>(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<jint> invoked_methods;
|
||||
std::vector<jint> referred_strings;
|
||||
std::vector<jint> accessed_fields;
|
||||
std::vector<jint> assigned_fields;
|
||||
std::vector<jbyte> opcodes;
|
||||
while (inst < end) {
|
||||
dex::u1 opcode = *inst & kOpcodeMask;
|
||||
opcodes.push_back(static_cast<jbyte>(opcode));
|
||||
if (opcode == kOpcodeConstString) {
|
||||
auto str_idx = inst[1];
|
||||
referred_strings.push_back(str_idx);
|
||||
}
|
||||
if (opcode == kOpcodeConstStringJumbo) {
|
||||
auto str_idx = *reinterpret_cast<const dex::u4 *>(&inst[1]);
|
||||
referred_strings.push_back(static_cast<jint>(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<const dex::u4 *>(&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<jint>(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<jint>(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<jint>(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<jint>(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<jint>(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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright (C) 2023 LSPosed Contributors
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
namespace lspd {
|
||||
void RegisterDexParserBridge(JNIEnv *env);
|
||||
}
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright (C) 2022 LSPosed Contributors
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
namespace lspd {
|
||||
void RegisterHookBridge(JNIEnv* env);
|
||||
void RegisterHookBridge(JNIEnv *env);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <jni.h>
|
||||
|
||||
namespace lspd {
|
||||
void RegisterNativeAPI(JNIEnv *);
|
||||
}
|
||||
|
||||
|
||||
#endif //LSPOSED_JNI_NATIVE_API_H
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in New Issue