[core] Remove isSamePackage hook (#690)
This commit is contained in:
parent
a6f0e666b8
commit
efa42a4eb0
|
|
@ -1 +1 @@
|
|||
Subproject commit 5c1ccd55836e4bf43cdb6adbad40443d18cd43db
|
||||
Subproject commit 41b9171852840cb421d8be22ef73d9467db02799
|
||||
|
|
@ -4,6 +4,11 @@
|
|||
#include <jni.h>
|
||||
|
||||
namespace yahfa {
|
||||
constexpr uint32_t kAccPublic = 0x0001; // class, field, method, ic
|
||||
constexpr uint32_t kAccPrivate = 0x0002; // field, method, ic
|
||||
constexpr uint32_t kAccProtected = 0x0004; // field, method, ic
|
||||
constexpr uint32_t kAccStatic = 0x0008; // field, method, ic
|
||||
|
||||
void init(JNIEnv *env, jclass clazz, jint sdkVersion);
|
||||
|
||||
jobject findMethodNative(JNIEnv *env, jclass clazz,
|
||||
|
|
@ -15,6 +20,10 @@ namespace yahfa {
|
|||
jobject backup);
|
||||
|
||||
void *getArtMethod(JNIEnv *env, jobject jmethod);
|
||||
|
||||
uint32_t getAccessFlags(void* art_method);
|
||||
|
||||
void setAccessFlags(void* art_method, uint32_t access_flags);
|
||||
}
|
||||
|
||||
#endif // HOOK_MAIN_H
|
||||
|
|
|
|||
|
|
@ -10,16 +10,10 @@ extern "C" {
|
|||
extern int SDKVersion;
|
||||
extern size_t OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod;
|
||||
|
||||
extern unsigned int hookCap; // capacity for trampolines
|
||||
extern unsigned int hookCount; // current count of used trampolines
|
||||
|
||||
extern unsigned char trampoline[];
|
||||
|
||||
int doInitHookCap(unsigned int cap);
|
||||
void setupTrampoline();
|
||||
void *genTrampoline(void *hookMethod);
|
||||
|
||||
#define DEFAULT_CAP 1 //size of each trampoline area would be no more than 4k Bytes(one page)
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -9,115 +9,103 @@
|
|||
|
||||
int SDKVersion;
|
||||
size_t OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod;
|
||||
namespace {
|
||||
constexpr size_t OFFSET_access_flags_in_ArtMethod = 4;
|
||||
constexpr uint32_t kAccCompileDontBother = 0x02000000;
|
||||
constexpr uint32_t kAccPublic = 0x0001; // class, field, method, ic
|
||||
constexpr uint32_t kAccPrivate = 0x0002; // field, method, ic
|
||||
constexpr uint32_t kAccProtected = 0x0004; // field, method, ic
|
||||
constexpr uint32_t kAccStatic = 0x0008; // field, method, ic
|
||||
constexpr uint32_t kAccFastInterpreterToInterpreterInvoke = 0x40000000;
|
||||
|
||||
size_t ArtMethodSize;
|
||||
uint32_t kAccPreCompiled = 0x00200000;
|
||||
|
||||
jfieldID fieldArtMethod = nullptr;
|
||||
|
||||
constexpr inline uint32_t read32(void *addr) {
|
||||
return *((uint32_t *) addr);
|
||||
}
|
||||
|
||||
constexpr inline void write32(void *addr, uint32_t value) {
|
||||
*((uint32_t *) addr) = value;
|
||||
}
|
||||
|
||||
constexpr inline void *readAddr(void *addr) {
|
||||
return *((void **) addr);
|
||||
}
|
||||
|
||||
constexpr inline void writeAddr(void *addr, void *value) {
|
||||
*((void **) addr) = value;
|
||||
}
|
||||
|
||||
void setNonCompilable(void *method) {
|
||||
uint32_t access_flags = read32((char *) method + OFFSET_access_flags_in_ArtMethod);
|
||||
LOGI("setNonCompilable: access flags is 0x%x", access_flags);
|
||||
access_flags |= kAccCompileDontBother;
|
||||
if (SDKVersion >= __ANDROID_API_R__)
|
||||
access_flags &= ~kAccPreCompiled;
|
||||
write32((char *) method + OFFSET_access_flags_in_ArtMethod, access_flags);
|
||||
}
|
||||
|
||||
void setPrivate(void *method) {
|
||||
uint32_t access_flags = read32((char *) method + OFFSET_access_flags_in_ArtMethod);
|
||||
if (!(access_flags & kAccStatic)) {
|
||||
LOGI("setPrivate: access flags is 0x%x", access_flags);
|
||||
access_flags |= kAccPrivate;
|
||||
access_flags &= ~kAccProtected;
|
||||
access_flags &= ~kAccPublic;
|
||||
write32((char *) method + OFFSET_access_flags_in_ArtMethod, access_flags);
|
||||
}
|
||||
}
|
||||
|
||||
int doBackupAndHook(void *targetMethod, void *hookMethod, void *backupMethod) {
|
||||
if (hookCount >= hookCap) {
|
||||
LOGI("not enough capacity. Allocating...");
|
||||
if (doInitHookCap(DEFAULT_CAP)) {
|
||||
LOGE("cannot hook method");
|
||||
return 1;
|
||||
}
|
||||
LOGI("Allocating done");
|
||||
}
|
||||
|
||||
LOGI("target method is at %p, hook method is at %p, backup method is at %p",
|
||||
targetMethod, hookMethod, backupMethod);
|
||||
|
||||
|
||||
// set kAccCompileDontBother for a method we do not want the compiler to compile
|
||||
// so that we don't need to worry about hotness_count_
|
||||
setNonCompilable(targetMethod);
|
||||
setNonCompilable(hookMethod);
|
||||
|
||||
if (backupMethod) {// do method backup
|
||||
// have to copy the whole target ArtMethod here
|
||||
// if the target method calls other methods which are to be resolved
|
||||
// then ToDexPC would be invoked for the caller(origin method)
|
||||
// in which case ToDexPC would use the entrypoint as a base for mapping pc to dex offset
|
||||
// so any changes to the target method's entrypoint would result in a wrong dex offset
|
||||
// and artQuickResolutionTrampoline would fail for methods called by the origin method
|
||||
memcpy(backupMethod, targetMethod, ArtMethodSize);
|
||||
setPrivate(backupMethod);
|
||||
}
|
||||
|
||||
// replace entry point
|
||||
void *newEntrypoint = genTrampoline(hookMethod);
|
||||
LOGI("origin ep is %p, new ep is %p",
|
||||
readAddr((char *) targetMethod + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod),
|
||||
newEntrypoint
|
||||
);
|
||||
if (newEntrypoint) {
|
||||
writeAddr((char *) targetMethod + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod,
|
||||
newEntrypoint);
|
||||
} else {
|
||||
LOGE("failed to allocate space for trampoline of target method");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (SDKVersion >= __ANDROID_API_Q__) {
|
||||
uint32_t access_flags = read32((char *) targetMethod + OFFSET_access_flags_in_ArtMethod);
|
||||
// On API 29 whether to use the fast path or not is cached in the ART method structure
|
||||
access_flags &= ~kAccFastInterpreterToInterpreterInvoke;
|
||||
write32((char *) targetMethod + OFFSET_access_flags_in_ArtMethod, access_flags);
|
||||
}
|
||||
|
||||
LOGI("hook and backup done");
|
||||
hookCount += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace yahfa {
|
||||
namespace {
|
||||
constexpr size_t OFFSET_access_flags_in_ArtMethod = 4;
|
||||
constexpr uint32_t kAccCompileDontBother = 0x02000000;
|
||||
constexpr uint32_t kAccFastInterpreterToInterpreterInvoke = 0x40000000;
|
||||
|
||||
size_t ArtMethodSize;
|
||||
uint32_t kAccPreCompiled = 0x00200000;
|
||||
|
||||
jfieldID fieldArtMethod = nullptr;
|
||||
|
||||
constexpr inline uint32_t read32(void *addr) {
|
||||
return *((uint32_t *) addr);
|
||||
}
|
||||
|
||||
constexpr inline void write32(void *addr, uint32_t value) {
|
||||
*((uint32_t *) addr) = value;
|
||||
}
|
||||
|
||||
constexpr inline void *readAddr(void *addr) {
|
||||
return *((void **) addr);
|
||||
}
|
||||
|
||||
constexpr inline void writeAddr(void *addr, void *value) {
|
||||
*((void **) addr) = value;
|
||||
}
|
||||
|
||||
void setNonCompilable(void *method) {
|
||||
uint32_t access_flags = getAccessFlags(method);
|
||||
LOGI("setNonCompilable: access flags is 0x%x", access_flags);
|
||||
access_flags |= kAccCompileDontBother;
|
||||
if (SDKVersion >= __ANDROID_API_R__)
|
||||
access_flags &= ~kAccPreCompiled;
|
||||
setAccessFlags(method, access_flags);
|
||||
}
|
||||
|
||||
void setPrivate(void *method) {
|
||||
uint32_t access_flags = getAccessFlags(method);
|
||||
if (!(access_flags & kAccStatic)) {
|
||||
LOGI("setPrivate: access flags is 0x%x", access_flags);
|
||||
access_flags |= kAccPrivate;
|
||||
access_flags &= ~kAccProtected;
|
||||
access_flags &= ~kAccPublic;
|
||||
setAccessFlags(method, access_flags);
|
||||
}
|
||||
}
|
||||
|
||||
int doBackupAndHook(void *targetMethod, void *hookMethod, void *backupMethod) {
|
||||
LOGI("target method is at %p, hook method is at %p, backup method is at %p",
|
||||
targetMethod, hookMethod, backupMethod);
|
||||
|
||||
|
||||
// set kAccCompileDontBother for a method we do not want the compiler to compile
|
||||
// so that we don't need to worry about hotness_count_
|
||||
setNonCompilable(targetMethod);
|
||||
setNonCompilable(hookMethod);
|
||||
|
||||
if (backupMethod) {// do method backup
|
||||
// have to copy the whole target ArtMethod here
|
||||
// if the target method calls other methods which are to be resolved
|
||||
// then ToDexPC would be invoked for the caller(origin method)
|
||||
// in which case ToDexPC would use the entrypoint as a base for mapping pc to dex offset
|
||||
// so any changes to the target method's entrypoint would result in a wrong dex offset
|
||||
// and artQuickResolutionTrampoline would fail for methods called by the origin method
|
||||
memcpy(backupMethod, targetMethod, ArtMethodSize);
|
||||
setPrivate(backupMethod);
|
||||
}
|
||||
|
||||
// replace entry point
|
||||
void *newEntrypoint = genTrampoline(hookMethod);
|
||||
LOGI("origin ep is %p, new ep is %p",
|
||||
readAddr((char *) targetMethod +
|
||||
OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod),
|
||||
newEntrypoint
|
||||
);
|
||||
if (newEntrypoint) {
|
||||
writeAddr((char *) targetMethod +
|
||||
OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod,
|
||||
newEntrypoint);
|
||||
} else {
|
||||
LOGE("failed to allocate space for trampoline of target method");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (SDKVersion >= __ANDROID_API_Q__) {
|
||||
uint32_t access_flags = getAccessFlags(targetMethod);
|
||||
// On API 29 whether to use the fast path or not is cached in the ART method structure
|
||||
access_flags &= ~kAccFastInterpreterToInterpreterInvoke;
|
||||
setAccessFlags(targetMethod, access_flags);
|
||||
}
|
||||
|
||||
LOGI("hook and backup done");
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void init(JNIEnv *env, [[maybe_unused]] jclass clazz, jint sdkVersion) {
|
||||
SDKVersion = sdkVersion;
|
||||
|
|
@ -160,6 +148,16 @@ namespace yahfa {
|
|||
}
|
||||
}
|
||||
|
||||
uint32_t getAccessFlags(void *art_method) {
|
||||
|
||||
return read32((char *) art_method + OFFSET_access_flags_in_ArtMethod);
|
||||
// On API 29 whether to use the fast path or not is cached in the ART method structure
|
||||
}
|
||||
|
||||
void setAccessFlags(void *art_method, uint32_t access_flags) {
|
||||
write32((char *) art_method + OFFSET_access_flags_in_ArtMethod, access_flags);
|
||||
}
|
||||
|
||||
jobject findMethodNative(JNIEnv *env, [[maybe_unused]] jclass clazz,
|
||||
jclass targetClass,
|
||||
jstring methodName,
|
||||
|
|
|
|||
|
|
@ -14,12 +14,6 @@
|
|||
#include "common.h"
|
||||
#include "trampoline.h"
|
||||
|
||||
static unsigned char *trampolineCode; // place where trampolines are saved
|
||||
static unsigned int trampolineSize; // trampoline size required for each hook
|
||||
|
||||
unsigned int hookCap = 0;
|
||||
unsigned int hookCount = 0;
|
||||
|
||||
// trampoline:
|
||||
// 1. set eax/r0/x0 to the hook ArtMethod addr
|
||||
// 2. jump into its entry point
|
||||
|
|
@ -74,9 +68,12 @@ static inline void FlushCache(void *addr, size_t size) {
|
|||
}
|
||||
|
||||
void *genTrampoline(void *hookMethod) {
|
||||
void *targetAddr;
|
||||
|
||||
targetAddr = trampolineCode + trampolineSize * hookCount;
|
||||
unsigned char *targetAddr = mmap(NULL, trampolineSize, PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
if (targetAddr == MAP_FAILED) {
|
||||
LOGE("mmap failed, errno = %s", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
memcpy(targetAddr, trampoline,
|
||||
sizeof(trampoline)); // do not use trampolineSize since it's a rounded size
|
||||
|
||||
|
|
@ -117,24 +114,3 @@ void setupTrampoline() {
|
|||
#error Unsupported architecture
|
||||
#endif
|
||||
}
|
||||
|
||||
int doInitHookCap(unsigned int cap) {
|
||||
if (cap == 0) {
|
||||
LOGE("invalid capacity: %d", cap);
|
||||
return 1;
|
||||
}
|
||||
if (hookCap) {
|
||||
LOGI("allocating new space for trampoline code");
|
||||
}
|
||||
unsigned int allSize = trampolineSize * cap;
|
||||
unsigned char *buf = mmap(NULL, allSize, PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
if (buf == MAP_FAILED) {
|
||||
LOGE("mmap failed, errno = %s", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
hookCap = cap;
|
||||
hookCount = 0;
|
||||
trampolineCode = buf;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,29 +41,6 @@ namespace art {
|
|||
return "";
|
||||
}
|
||||
|
||||
CREATE_MEM_HOOK_STUB_ENTRIES("_ZN3art6mirror5Class15IsInSamePackageENS_6ObjPtrIS1_EE",
|
||||
bool, IsInSamePackage, (void *thiz, void* that), {
|
||||
std::string storage1;
|
||||
std::string storage2;
|
||||
const char *thisDesc = GetDescriptor(thiz, &storage1);
|
||||
const char *thatDesc = GetDescriptor(that, &storage2);
|
||||
// Note: these identifiers should be consistent with those in Java layer
|
||||
if (strstr(thisDesc, "LspHooker_") != nullptr
|
||||
|| strstr(thatDesc, "LspHooker_") != nullptr
|
||||
|| strstr(thisDesc, "org/lsposed/") != nullptr
|
||||
|| strstr(thatDesc, "org/lsposed/") != nullptr) {
|
||||
return true;
|
||||
}
|
||||
// for MIUI resources hooking
|
||||
if (strstr(thisDesc, "android/content/res/MiuiTypedArray") != nullptr
|
||||
|| strstr(thatDesc, "android/content/res/MiuiTypedArray") != nullptr
|
||||
|| strstr(thisDesc, "android/content/res/XResources$XTypedArray") != nullptr
|
||||
|| strstr(thatDesc, "android/content/res/XResources$XTypedArray") != nullptr) {
|
||||
return true;
|
||||
}
|
||||
return backup(thiz, that);
|
||||
});
|
||||
|
||||
CREATE_MEM_FUNC_SYMBOL_ENTRY(void*, GetClassDef, void* thiz) {
|
||||
if (LIKELY(GetClassDefSym))
|
||||
return GetClassDefSym(thiz);
|
||||
|
|
@ -77,10 +54,7 @@ namespace art {
|
|||
static void Setup(void *handle) {
|
||||
RETRIEVE_MEM_FUNC_SYMBOL(GetDescriptor, "_ZN3art6mirror5Class13GetDescriptorEPNSt3__112"
|
||||
"basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE");
|
||||
|
||||
RETRIEVE_MEM_FUNC_SYMBOL(GetClassDef, "_ZN3art6mirror5Class11GetClassDefEv");
|
||||
|
||||
lspd::HookSyms(handle, IsInSamePackage);
|
||||
}
|
||||
|
||||
const char *GetDescriptor(std::string *storage) {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include <dl_util.h>
|
||||
#include <framework/androidfw/resource_types.h>
|
||||
#include <byte_order.h>
|
||||
#include <HookMain.h>
|
||||
#include "native_util.h"
|
||||
#include "resources_hook.h"
|
||||
|
||||
|
|
@ -95,13 +96,24 @@ namespace lspd {
|
|||
}
|
||||
|
||||
// @ApiSensitive(Level.MIDDLE)
|
||||
LSP_DEF_NATIVE_METHOD(jboolean, ResourcesHook, removeFinalFlagNative, jclass target_class) {
|
||||
LSP_DEF_NATIVE_METHOD(jboolean, ResourcesHook, makeInheritable, jclass target_class,
|
||||
jobjectArray constructors) {
|
||||
if (target_class) {
|
||||
auto class_clazz = JNI_FindClass(env, "java/lang/Class");
|
||||
jfieldID java_lang_Class_accessFlags = JNI_GetFieldID(
|
||||
static auto class_clazz = JNI_NewGlobalRef(env, JNI_FindClass(env, "java/lang/Class"));
|
||||
static jfieldID java_lang_Class_accessFlags = JNI_GetFieldID(
|
||||
env, class_clazz, "accessFlags", "I");
|
||||
jint access_flags = env->GetIntField(target_class, java_lang_Class_accessFlags);
|
||||
env->SetIntField(target_class, java_lang_Class_accessFlags, access_flags & ~kAccFinal);
|
||||
for (auto i = 0u; i < env->GetArrayLength(constructors); ++i) {
|
||||
auto constructor = env->GetObjectArrayElement(constructors, i);
|
||||
void *method = yahfa::getArtMethod(env, constructor);
|
||||
uint32_t flags = yahfa::getAccessFlags(method);
|
||||
if ((flags & yahfa::kAccPublic) == 0 && (flags & yahfa::kAccProtected) == 0) {
|
||||
flags |= yahfa::kAccProtected;
|
||||
flags &= ~yahfa::kAccPrivate;
|
||||
}
|
||||
yahfa::setAccessFlags(method, flags);
|
||||
}
|
||||
return JNI_TRUE;
|
||||
}
|
||||
return JNI_FALSE;
|
||||
|
|
@ -208,7 +220,8 @@ namespace lspd {
|
|||
|
||||
static JNINativeMethod gMethods[] = {
|
||||
LSP_NATIVE_METHOD(ResourcesHook, initXResourcesNative, "()Z"),
|
||||
LSP_NATIVE_METHOD(ResourcesHook, removeFinalFlagNative, "(Ljava/lang/Class;)Z"),
|
||||
LSP_NATIVE_METHOD(ResourcesHook, makeInheritable,
|
||||
"(Ljava/lang/Class;[Ljava/lang/reflect/Constructor;)Z"),
|
||||
LSP_NATIVE_METHOD(ResourcesHook, buildDummyClassLoader,
|
||||
"(Ljava/lang/ClassLoader;Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/ClassLoader;"),
|
||||
LSP_NATIVE_METHOD(ResourcesHook, rewriteXmlReferencesNative,
|
||||
|
|
|
|||
|
|
@ -58,8 +58,8 @@ namespace lspd {
|
|||
return lspd::isHooked(yahfa::getArtMethod(env, member));
|
||||
}
|
||||
|
||||
LSP_DEF_NATIVE_METHOD(jclass, Yahfa, buildHooker, jobject app_class_loader, jclass return_class,
|
||||
jobjectArray classes, jstring method_name) {
|
||||
LSP_DEF_NATIVE_METHOD(jclass, Yahfa, buildHooker, jobject app_class_loader, jchar return_class,
|
||||
jcharArray classes, jstring method_name) {
|
||||
static auto in_memory_classloader = JNI_NewGlobalRef(env, JNI_FindClass(env,
|
||||
"dalvik/system/InMemoryDexClassLoader"));
|
||||
static jmethodID initMid = JNI_GetMethodID(env, in_memory_classloader, "<init>",
|
||||
|
|
@ -70,15 +70,14 @@ namespace lspd {
|
|||
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));
|
||||
auto return_type =
|
||||
return_class == 'L' ? TypeDescriptor::Object : TypeDescriptor::FromDescriptor(
|
||||
(char) return_class);
|
||||
auto params = env->GetCharArrayElements(classes, nullptr);
|
||||
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));
|
||||
parameter_types.push_back(
|
||||
params[i] == 'L' ? TypeDescriptor::Object : TypeDescriptor::FromDescriptor(
|
||||
(char) params[i]));
|
||||
}
|
||||
|
||||
ClassBuilder cbuilder{dex_file.MakeClass("LspHooker_")};
|
||||
|
|
@ -136,7 +135,7 @@ namespace lspd {
|
|||
Instruction::Cast(tmp, Value::Type(type_def->orig_index)));
|
||||
hookBuilder.BuildReturn(tmp, true);
|
||||
}
|
||||
auto *hook_method = hookBuilder.Encode();
|
||||
[[maybe_unused]] auto *hook_method = hookBuilder.Encode();
|
||||
|
||||
auto backup_builder{
|
||||
cbuilder.CreateMethod("backup", Prototype{return_type, parameter_types})};
|
||||
|
|
@ -153,7 +152,7 @@ namespace lspd {
|
|||
backup_builder.BuildConst(zero, 0);
|
||||
backup_builder.BuildReturn(zero, /*is_object=*/!return_type.is_primitive(), false);
|
||||
}
|
||||
auto *back_method = backup_builder.Encode();
|
||||
[[maybe_unused]] auto *back_method = backup_builder.Encode();
|
||||
|
||||
slicer::MemView image{dex_file.CreateImage()};
|
||||
|
||||
|
|
@ -185,7 +184,7 @@ namespace lspd {
|
|||
LSP_NATIVE_METHOD(Yahfa, recordHooked, "(Ljava/lang/reflect/Executable;)V"),
|
||||
LSP_NATIVE_METHOD(Yahfa, isHooked, "(Ljava/lang/reflect/Executable;)Z"),
|
||||
LSP_NATIVE_METHOD(Yahfa, buildHooker,
|
||||
"(Ljava/lang/ClassLoader;Ljava/lang/Class;[Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Class;"),
|
||||
"(Ljava/lang/ClassLoader;C[CLjava/lang/String;)Ljava/lang/Class;"),
|
||||
};
|
||||
|
||||
void RegisterYahfa(JNIEnv *env) {
|
||||
|
|
|
|||
|
|
@ -103,8 +103,8 @@ public final class XposedBridge {
|
|||
} catch (Resources.NotFoundException nfe) {
|
||||
XposedBridge.log(nfe);
|
||||
}
|
||||
ResourcesHook.removeFinalFlagNative(resClass);
|
||||
ResourcesHook.removeFinalFlagNative(taClass);
|
||||
ResourcesHook.makeInheritable(resClass, resClass.getDeclaredConstructors());
|
||||
ResourcesHook.makeInheritable(taClass, taClass.getDeclaredConstructors());
|
||||
ClassLoader myCL = XposedBridge.class.getClassLoader();
|
||||
dummyClassLoader = ResourcesHook.buildDummyClassLoader(myCL.getParent(), resClass, taClass);
|
||||
dummyClassLoader.loadClass("xposed.dummy.XResourcesSuperClass");
|
||||
|
|
|
|||
|
|
@ -23,11 +23,13 @@ package org.lsposed.lspd.nativebridge;
|
|||
import android.content.res.Resources;
|
||||
import android.content.res.XResources;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
public class ResourcesHook {
|
||||
|
||||
public static native boolean initXResourcesNative();
|
||||
|
||||
public static native boolean removeFinalFlagNative(Class<?> clazz);
|
||||
public static native boolean makeInheritable(Class<?> clazz, Constructor<?>[] constructors);
|
||||
|
||||
public static native ClassLoader buildDummyClassLoader(ClassLoader parent, Class<?> resourceSuperClass, Class<?> typedArraySuperClass);
|
||||
|
||||
|
|
|
|||
|
|
@ -36,5 +36,5 @@ public class Yahfa {
|
|||
|
||||
public static native boolean isHooked(Executable member);
|
||||
|
||||
public static native Class<?> buildHooker(ClassLoader appClassLoader, Class<?> returnType, Class<?>[] params, String methodName);
|
||||
public static native Class<?> buildHooker(ClassLoader appClassLoader, char returnType, char[] params, String methodName);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import java.lang.reflect.Constructor;
|
|||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
|
||||
import de.robv.android.xposed.LspHooker;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
|
|
@ -38,6 +39,18 @@ public class HookerDexMaker {
|
|||
|
||||
public static final String METHOD_NAME_BACKUP = "backup";
|
||||
public static final String METHOD_NAME_SETUP = "setup";
|
||||
private static final HashMap<Class<?>, Character> descriptors = new HashMap<>() {{
|
||||
put(int.class, 'I');
|
||||
put(boolean.class, 'Z');
|
||||
put(char.class, 'C');
|
||||
put(long.class, 'J');
|
||||
put(short.class, 'S');
|
||||
put(float.class, 'F');
|
||||
put(double.class, 'D');
|
||||
put(byte.class, 'B');
|
||||
put(void.class, 'V');
|
||||
put(Object.class, 'L');
|
||||
}};
|
||||
|
||||
private Class<?> mReturnType;
|
||||
private Class<?>[] mActualParameterTypes;
|
||||
|
|
@ -49,18 +62,31 @@ public class HookerDexMaker {
|
|||
|
||||
private static Class<?>[] getParameterTypes(Executable method, boolean isStatic) {
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
for (int i = 0; i < parameterTypes.length; ++i) {
|
||||
parameterTypes[i] = parameterTypes[i].isPrimitive() ? parameterTypes[i] : Object.class;
|
||||
}
|
||||
if (isStatic) {
|
||||
return parameterTypes;
|
||||
}
|
||||
int parameterSize = parameterTypes.length;
|
||||
int targetParameterSize = parameterSize + 1;
|
||||
Class<?>[] newParameterTypes = new Class<?>[targetParameterSize];
|
||||
int offset = 1;
|
||||
newParameterTypes[0] = method.getDeclaringClass();
|
||||
System.arraycopy(parameterTypes, 0, newParameterTypes, offset, parameterTypes.length);
|
||||
Class<?>[] newParameterTypes = new Class<?>[parameterSize + 1];
|
||||
newParameterTypes[0] = Object.class;
|
||||
System.arraycopy(parameterTypes, 0, newParameterTypes, 1, parameterSize);
|
||||
return newParameterTypes;
|
||||
}
|
||||
|
||||
private static char getDescriptor(Class<?> clazz) {
|
||||
return descriptors.getOrDefault(clazz, 'L');
|
||||
}
|
||||
|
||||
private static char[] getDescriptors(Class<?>[] classes) {
|
||||
var descriptors = new char[classes.length];
|
||||
for (int i = 0; i < classes.length; ++i) {
|
||||
descriptors[i] = getDescriptor(classes[i]);
|
||||
}
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
public void start(Executable member, XposedBridge.AdditionalHookInfo hookInfo,
|
||||
ClassLoader appClassLoader) throws Exception {
|
||||
if (member instanceof Method) {
|
||||
|
|
@ -89,7 +115,7 @@ public class HookerDexMaker {
|
|||
}
|
||||
|
||||
private void doMake(String methodName) throws Exception {
|
||||
Class<?> hookClass = Yahfa.buildHooker(mAppClassLoader, mReturnType, mActualParameterTypes, methodName);
|
||||
Class<?> hookClass = Yahfa.buildHooker(mAppClassLoader, getDescriptor(mReturnType), getDescriptors(mActualParameterTypes), methodName);
|
||||
// Execute our newly-generated code in-process.
|
||||
Method backupMethod = hookClass.getMethod(METHOD_NAME_BACKUP, mActualParameterTypes);
|
||||
mHooker = new LspHooker(mHookInfo, mMember, backupMethod);
|
||||
|
|
|
|||
Loading…
Reference in New Issue