[core] Introduce c++ dexbuilder to replace dexmaker (#207)

This commit is contained in:
LoveSy 2021-02-27 02:06:41 +08:00 committed by GitHub
parent bb3ffeee3c
commit 66dc5161b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 154 additions and 20 deletions

3
.gitmodules vendored
View File

@ -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

View File

@ -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

View File

@ -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})

View File

@ -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;

View File

@ -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) {

View File

@ -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);
}

View File

@ -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