[core] Introduce c++ dexbuilder to replace dexmaker (#207)
This commit is contained in:
parent
bb3ffeee3c
commit
66dc5161b4
|
|
@ -4,3 +4,6 @@
|
|||
[submodule "service"]
|
||||
path = service
|
||||
url = https://github.com/Xposed-Modules-Repo/XposedService.git
|
||||
[submodule "core/src/main/cpp/external/DexBuilder"]
|
||||
path = core/src/main/cpp/external/DexBuilder
|
||||
url = https://github.com/LSPosed/DexBuilder.git
|
||||
|
|
|
|||
|
|
@ -17,4 +17,7 @@ target_include_directories(dobby PUBLIC Dobby/include)
|
|||
target_include_directories(dobby PUBLIC Dobby/builtin-plugin/BionicLinkerRestriction)
|
||||
|
||||
add_subdirectory(SandHook)
|
||||
target_include_directories(sandhook.lspd PUBLIC SandHook)
|
||||
target_include_directories(sandhook.lspd PUBLIC SandHook)
|
||||
|
||||
add_subdirectory(DexBuilder)
|
||||
target_include_directories(dex_builder PUBLIC DexBuilder)
|
||||
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 3d8b28079de54a7312786e36f40af26f1ee72d0f
|
||||
|
|
@ -29,4 +29,4 @@ add_library(riru_lspd SHARED ${SRC_LIST} ${SRC_JNI_LIST})
|
|||
|
||||
find_package(riru REQUIRED CONFIG)
|
||||
find_library(log-lib log)
|
||||
target_link_libraries(riru_lspd yahfa riru::riru android dobby sandhook.lspd ${log-lib})
|
||||
target_link_libraries(riru_lspd yahfa riru::riru android dobby sandhook.lspd dex_builder ${log-lib})
|
||||
|
|
@ -133,7 +133,7 @@ namespace lspd {
|
|||
jclass
|
||||
Context::FindClassFromLoader(JNIEnv *env, jobject class_loader, std::string_view class_name) {
|
||||
if (class_loader == nullptr) return nullptr;
|
||||
static jclass clz = JNI_FindClass(env, "dalvik/system/DexClassLoader");
|
||||
static auto clz = (jclass)env->NewGlobalRef(env->FindClass( "dalvik/system/DexClassLoader"));
|
||||
static jmethodID mid = JNI_GetMethodID(env, clz, "loadClass",
|
||||
"(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
jclass ret = nullptr;
|
||||
|
|
|
|||
|
|
@ -27,23 +27,26 @@
|
|||
#include "art/runtime/thread_list.h"
|
||||
#include "art/runtime/thread.h"
|
||||
#include "art/runtime/gc/scoped_gc_critical_section.h"
|
||||
#include <dex_builder.h>
|
||||
|
||||
|
||||
namespace lspd {
|
||||
|
||||
using namespace startop::dex;
|
||||
LSP_DEF_NATIVE_METHOD(void, Yahfa, init, jint sdkVersion) {
|
||||
Java_lab_galaxy_yahfa_HookMain_init(env, clazz, sdkVersion);
|
||||
}
|
||||
|
||||
LSP_DEF_NATIVE_METHOD(jobject, Yahfa, findMethodNative, jclass targetClass,
|
||||
jstring methodName, jstring methodSig) {
|
||||
jstring methodName, jstring methodSig) {
|
||||
return Java_lab_galaxy_yahfa_HookMain_findMethodNative(env, clazz, targetClass, methodName,
|
||||
methodSig);
|
||||
}
|
||||
|
||||
LSP_DEF_NATIVE_METHOD(jboolean, Yahfa, backupAndHookNative, jobject target,
|
||||
jobject hook, jobject backup) {
|
||||
art::gc::ScopedGCCriticalSection section(art::Thread::Current().Get(), art::gc::kGcCauseDebugger, art::gc::kCollectorTypeDebugger);
|
||||
jobject hook, jobject backup) {
|
||||
art::gc::ScopedGCCriticalSection section(art::Thread::Current().Get(),
|
||||
art::gc::kGcCauseDebugger,
|
||||
art::gc::kCollectorTypeDebugger);
|
||||
art::thread_list::ScopedSuspendAll suspend("Yahfa Hook", false);
|
||||
return Java_lab_galaxy_yahfa_HookMain_backupAndHookNative(env, clazz, target, hook, backup);
|
||||
}
|
||||
|
|
@ -56,14 +59,129 @@ namespace lspd {
|
|||
return lspd::isHooked(getArtMethodYahfa(env, member));
|
||||
}
|
||||
|
||||
LSP_DEF_NATIVE_METHOD(jclass, Yahfa, buildHooker, jobject app_class_loader, jclass return_class, jobjectArray classes) {
|
||||
static auto in_memory_classloader = (jclass)env->NewGlobalRef(env->FindClass( "dalvik/system/InMemoryDexClassLoader"));
|
||||
static jmethodID initMid = JNI_GetMethodID(env, in_memory_classloader, "<init>",
|
||||
"(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
|
||||
DexBuilder dex_file;
|
||||
|
||||
auto parameter_length = env->GetArrayLength(classes);
|
||||
auto parameter_types = std::vector<TypeDescriptor>();
|
||||
parameter_types.reserve(parameter_length);
|
||||
std::string storage;
|
||||
auto current_thread = art::Thread::Current();
|
||||
auto return_type = TypeDescriptor::FromDescriptor(art::mirror::Class(current_thread.DecodeJObject(return_class)).GetDescriptor(&storage));
|
||||
for (int i = 0; i < parameter_length; ++i) {
|
||||
auto param = (jclass) env->GetObjectArrayElement(classes, i);
|
||||
auto *param_ref = current_thread.DecodeJObject(param);
|
||||
auto descriptor = art::mirror::Class(param_ref).GetDescriptor(&storage);
|
||||
parameter_types.push_back(TypeDescriptor::FromDescriptor(descriptor));
|
||||
}
|
||||
|
||||
ClassBuilder cbuilder{dex_file.MakeClass("LSPHooker")};
|
||||
cbuilder.set_source_file("LSP");
|
||||
|
||||
auto hooker_type =
|
||||
TypeDescriptor::FromClassname("de.robv.android.xposed.LspHooker");
|
||||
|
||||
auto *hooker_field = cbuilder.CreateField("hooker", hooker_type)
|
||||
.access_flags(dex::kAccStatic)
|
||||
.Encode();
|
||||
|
||||
auto setupBuilder{cbuilder.CreateMethod(
|
||||
"setup", Prototype{TypeDescriptor::Void, hooker_type})};
|
||||
setupBuilder
|
||||
.AddInstruction(Instruction::SetStaticObjectField(
|
||||
hooker_field->decl->orig_index, Value::Parameter(0)))
|
||||
.BuildReturn()
|
||||
.Encode();
|
||||
|
||||
auto hookBuilder{cbuilder.CreateMethod(
|
||||
"hook", Prototype{return_type, parameter_types})};
|
||||
// allocate tmp frist because of wide
|
||||
auto tmp{hookBuilder.AllocRegister()};
|
||||
hookBuilder.BuildConst(tmp, parameter_types.size());
|
||||
auto hook_params_array{hookBuilder.AllocRegister()};
|
||||
hookBuilder.BuildNewArray(hook_params_array, TypeDescriptor::Object, tmp);
|
||||
for (size_t i = 0u, j = 0u; i < parameter_types.size(); ++i, ++j) {
|
||||
hookBuilder.BuildBoxIfPrimitive(Value::Parameter(j), parameter_types[i],
|
||||
Value::Parameter(j));
|
||||
hookBuilder.BuildConst(tmp, i);
|
||||
hookBuilder.BuildAput(Instruction::Op::kAputObject, hook_params_array,
|
||||
Value::Parameter(j), tmp);
|
||||
if (parameter_types[i].is_wide()) ++j;
|
||||
}
|
||||
auto handle_hook_method{dex_file.GetOrDeclareMethod(
|
||||
hooker_type, "handleHookedMethod",
|
||||
Prototype{TypeDescriptor::Object, TypeDescriptor::Object.ToArray()})};
|
||||
hookBuilder.AddInstruction(
|
||||
Instruction::GetStaticObjectField(hooker_field->decl->orig_index, tmp));
|
||||
hookBuilder.AddInstruction(Instruction::InvokeVirtualObject(
|
||||
handle_hook_method.id, tmp, tmp, hook_params_array));
|
||||
if (return_type == TypeDescriptor::Void) {
|
||||
hookBuilder.BuildReturn();
|
||||
} else if (return_type.is_primitive()) {
|
||||
auto box_type{return_type.ToBoxType()};
|
||||
const ir::Type *type_def = dex_file.GetOrAddType(box_type);
|
||||
hookBuilder.AddInstruction(
|
||||
Instruction::Cast(tmp, Value::Type(type_def->orig_index)));
|
||||
hookBuilder.BuildUnBoxIfPrimitive(tmp, box_type, tmp);
|
||||
hookBuilder.BuildReturn(tmp, false, return_type.is_wide());
|
||||
} else {
|
||||
const ir::Type *type_def = dex_file.GetOrAddType(return_type);
|
||||
hookBuilder.AddInstruction(
|
||||
Instruction::Cast(tmp, Value::Type(type_def->orig_index)));
|
||||
hookBuilder.BuildReturn(tmp, true);
|
||||
}
|
||||
auto *hook_method = hookBuilder.Encode();
|
||||
|
||||
auto backup_builder{
|
||||
cbuilder.CreateMethod("backup", Prototype{return_type, parameter_types})};
|
||||
if (return_type == TypeDescriptor::Void) {
|
||||
backup_builder.BuildReturn();
|
||||
} else if (return_type.is_wide()) {
|
||||
LiveRegister zero = backup_builder.AllocRegister();
|
||||
LiveRegister zero_wide = backup_builder.AllocRegister();
|
||||
backup_builder.BuildConstWide(zero, 0);
|
||||
backup_builder.BuildReturn(zero, /*is_object=*/false, true);
|
||||
} else {
|
||||
LiveRegister zero = backup_builder.AllocRegister();
|
||||
LiveRegister zero_wide = backup_builder.AllocRegister();
|
||||
backup_builder.BuildConst(zero, 0);
|
||||
backup_builder.BuildReturn(zero, /*is_object=*/!return_type.is_primitive(), false);
|
||||
}
|
||||
auto *back_method = backup_builder.Encode();
|
||||
|
||||
slicer::MemView image{dex_file.CreateImage()};
|
||||
std::unique_ptr<char[]> buffer = std::make_unique<char[]>(image.size());
|
||||
memcpy(buffer.get(), image.ptr(), image.size());
|
||||
|
||||
auto dex_buffer = env->NewDirectByteBuffer(buffer.get(), image.size());
|
||||
jobject my_cl = JNI_NewObject(env, in_memory_classloader, initMid,
|
||||
dex_buffer, app_class_loader);
|
||||
|
||||
static jmethodID mid = JNI_GetMethodID(env, in_memory_classloader, "loadClass",
|
||||
"(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
if (!mid) {
|
||||
mid = JNI_GetMethodID(env, in_memory_classloader, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
}
|
||||
jobject target = env->CallObjectMethod(my_cl, mid, env->NewStringUTF("LSPHooker"));
|
||||
// LOGD("Created %zd", image.size());
|
||||
if (target) {
|
||||
return (jclass) target;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static JNINativeMethod gMethods[] = {
|
||||
LSP_NATIVE_METHOD(Yahfa, init, "(I)V"),
|
||||
LSP_NATIVE_METHOD(Yahfa, findMethodNative,
|
||||
"(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/reflect/Member;"),
|
||||
"(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/reflect/Member;"),
|
||||
LSP_NATIVE_METHOD(Yahfa, backupAndHookNative,
|
||||
"(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)Z"),
|
||||
"(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)Z"),
|
||||
LSP_NATIVE_METHOD(Yahfa, recordHooked, "(Ljava/lang/reflect/Member;)V"),
|
||||
LSP_NATIVE_METHOD(Yahfa, isHooked, "(Ljava/lang/reflect/Member;)Z")
|
||||
LSP_NATIVE_METHOD(Yahfa, isHooked, "(Ljava/lang/reflect/Member;)Z"),
|
||||
LSP_NATIVE_METHOD(Yahfa, buildHooker, "(Ljava/lang/ClassLoader;Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/Class;"),
|
||||
};
|
||||
|
||||
void RegisterYahfa(JNIEnv *env) {
|
||||
|
|
|
|||
|
|
@ -35,4 +35,6 @@ public class Yahfa {
|
|||
public static native void recordHooked(Member member);
|
||||
|
||||
public static native boolean isHooked(Member member);
|
||||
|
||||
public static native Class<?> buildHooker(ClassLoader appClassLoader, Class<?> returnType, Class<?>[] params);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import android.os.Build;
|
|||
|
||||
import io.github.lsposed.lspd.BuildConfig;
|
||||
import io.github.lsposed.lspd.core.yahfa.HookMain;
|
||||
import io.github.lsposed.lspd.nativebridge.Yahfa;
|
||||
import io.github.lsposed.lspd.util.ProxyClassLoader;
|
||||
|
||||
import java.io.File;
|
||||
|
|
@ -70,10 +71,11 @@ public class HookerDexMaker {
|
|||
hookerTypeId.getMethod(TypeId.OBJECT, "handleHookedMethod", objArrayTypeId);
|
||||
|
||||
private FieldId<?, LspHooker> mHookerFieldId;
|
||||
private Class<?> mReturnType;
|
||||
private Class<?>[] mActualParameterTypes;
|
||||
|
||||
private TypeId<?> mHookerTypeId;
|
||||
private TypeId<?>[] mParameterTypeIds;
|
||||
private Class<?>[] mActualParameterTypes;
|
||||
private TypeId<?> mReturnTypeId;
|
||||
|
||||
private DexMaker mDexMaker;
|
||||
|
|
@ -112,15 +114,14 @@ public class HookerDexMaker {
|
|||
|
||||
public void start(Member member, XposedBridge.AdditionalHookInfo hookInfo,
|
||||
ClassLoader appClassLoader) throws Exception {
|
||||
Class<?> returnType;
|
||||
boolean isStatic;
|
||||
if (member instanceof Method) {
|
||||
Method method = (Method) member;
|
||||
isStatic = Modifier.isStatic(method.getModifiers());
|
||||
returnType = method.getReturnType();
|
||||
if (returnType.equals(Void.class) || returnType.equals(void.class)
|
||||
|| returnType.isPrimitive()) {
|
||||
mReturnTypeId = TypeId.get(returnType);
|
||||
mReturnType = method.getReturnType();
|
||||
if (mReturnType.equals(Void.class) || mReturnType.equals(void.class)
|
||||
|| mReturnType.isPrimitive()) {
|
||||
mReturnTypeId = TypeId.get(mReturnType);
|
||||
} else {
|
||||
// all others fallback to plain Object for convenience
|
||||
mReturnTypeId = TypeId.OBJECT;
|
||||
|
|
@ -149,7 +150,11 @@ public class HookerDexMaker {
|
|||
mAppClassLoader = appClassLoader;
|
||||
mAppClassLoader = new ProxyClassLoader(mAppClassLoader, getClass().getClassLoader());
|
||||
}
|
||||
|
||||
long startTime = System.nanoTime();
|
||||
doMake(member.getDeclaringClass().getName());
|
||||
long endTime = System.nanoTime();
|
||||
DexLog.d("Hook time: " + (endTime - startTime) / 1e6 + "ms");
|
||||
}
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
|
|
@ -158,14 +163,16 @@ public class HookerDexMaker {
|
|||
Class<?> hookClass = null;
|
||||
// Generate a Hooker class.
|
||||
String className = CLASS_NAME_PREFIX;
|
||||
if (canCache) {
|
||||
mDexMaker = new DexMaker();
|
||||
// className is also used as dex file name
|
||||
// so it should be different from each other
|
||||
hookClass = Yahfa.buildHooker(mAppClassLoader, mReturnType, mActualParameterTypes);
|
||||
if (canCache && hookClass == null) {
|
||||
String suffix = DexMakerUtils.getSha1Hex(mMember.toString());
|
||||
className = className + suffix;
|
||||
String dexFileName = className + ".jar";
|
||||
File dexFile = new File(serviceClient.getCachePath(dexFileName));
|
||||
DexLog.d("dex builder failed, generating " + dexFileName);
|
||||
mDexMaker = new DexMaker();
|
||||
// className is also used as dex file name
|
||||
// so it should be different from each other
|
||||
if (dexFile.exists()) {
|
||||
try {
|
||||
// if file exists, reuse it and skip generating
|
||||
|
|
|
|||
Loading…
Reference in New Issue