[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>
|
#include <jni.h>
|
||||||
|
|
||||||
namespace yahfa {
|
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);
|
void init(JNIEnv *env, jclass clazz, jint sdkVersion);
|
||||||
|
|
||||||
jobject findMethodNative(JNIEnv *env, jclass clazz,
|
jobject findMethodNative(JNIEnv *env, jclass clazz,
|
||||||
|
|
@ -15,6 +20,10 @@ namespace yahfa {
|
||||||
jobject backup);
|
jobject backup);
|
||||||
|
|
||||||
void *getArtMethod(JNIEnv *env, jobject jmethod);
|
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
|
#endif // HOOK_MAIN_H
|
||||||
|
|
|
||||||
|
|
@ -10,16 +10,10 @@ extern "C" {
|
||||||
extern int SDKVersion;
|
extern int SDKVersion;
|
||||||
extern size_t OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod;
|
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[];
|
extern unsigned char trampoline[];
|
||||||
|
|
||||||
int doInitHookCap(unsigned int cap);
|
|
||||||
void setupTrampoline();
|
void setupTrampoline();
|
||||||
void *genTrampoline(void *hookMethod);
|
void *genTrampoline(void *hookMethod);
|
||||||
|
|
||||||
#define DEFAULT_CAP 1 //size of each trampoline area would be no more than 4k Bytes(one page)
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,11 @@
|
||||||
|
|
||||||
int SDKVersion;
|
int SDKVersion;
|
||||||
size_t OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod;
|
size_t OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod;
|
||||||
|
|
||||||
|
namespace yahfa {
|
||||||
namespace {
|
namespace {
|
||||||
constexpr size_t OFFSET_access_flags_in_ArtMethod = 4;
|
constexpr size_t OFFSET_access_flags_in_ArtMethod = 4;
|
||||||
constexpr uint32_t kAccCompileDontBother = 0x02000000;
|
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;
|
constexpr uint32_t kAccFastInterpreterToInterpreterInvoke = 0x40000000;
|
||||||
|
|
||||||
size_t ArtMethodSize;
|
size_t ArtMethodSize;
|
||||||
|
|
@ -40,35 +38,26 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
void setNonCompilable(void *method) {
|
void setNonCompilable(void *method) {
|
||||||
uint32_t access_flags = read32((char *) method + OFFSET_access_flags_in_ArtMethod);
|
uint32_t access_flags = getAccessFlags(method);
|
||||||
LOGI("setNonCompilable: access flags is 0x%x", access_flags);
|
LOGI("setNonCompilable: access flags is 0x%x", access_flags);
|
||||||
access_flags |= kAccCompileDontBother;
|
access_flags |= kAccCompileDontBother;
|
||||||
if (SDKVersion >= __ANDROID_API_R__)
|
if (SDKVersion >= __ANDROID_API_R__)
|
||||||
access_flags &= ~kAccPreCompiled;
|
access_flags &= ~kAccPreCompiled;
|
||||||
write32((char *) method + OFFSET_access_flags_in_ArtMethod, access_flags);
|
setAccessFlags(method, access_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setPrivate(void *method) {
|
void setPrivate(void *method) {
|
||||||
uint32_t access_flags = read32((char *) method + OFFSET_access_flags_in_ArtMethod);
|
uint32_t access_flags = getAccessFlags(method);
|
||||||
if (!(access_flags & kAccStatic)) {
|
if (!(access_flags & kAccStatic)) {
|
||||||
LOGI("setPrivate: access flags is 0x%x", access_flags);
|
LOGI("setPrivate: access flags is 0x%x", access_flags);
|
||||||
access_flags |= kAccPrivate;
|
access_flags |= kAccPrivate;
|
||||||
access_flags &= ~kAccProtected;
|
access_flags &= ~kAccProtected;
|
||||||
access_flags &= ~kAccPublic;
|
access_flags &= ~kAccPublic;
|
||||||
write32((char *) method + OFFSET_access_flags_in_ArtMethod, access_flags);
|
setAccessFlags(method, access_flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int doBackupAndHook(void *targetMethod, void *hookMethod, void *backupMethod) {
|
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",
|
LOGI("target method is at %p, hook method is at %p, backup method is at %p",
|
||||||
targetMethod, hookMethod, backupMethod);
|
targetMethod, hookMethod, backupMethod);
|
||||||
|
|
||||||
|
|
@ -92,11 +81,13 @@ namespace {
|
||||||
// replace entry point
|
// replace entry point
|
||||||
void *newEntrypoint = genTrampoline(hookMethod);
|
void *newEntrypoint = genTrampoline(hookMethod);
|
||||||
LOGI("origin ep is %p, new ep is %p",
|
LOGI("origin ep is %p, new ep is %p",
|
||||||
readAddr((char *) targetMethod + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod),
|
readAddr((char *) targetMethod +
|
||||||
|
OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod),
|
||||||
newEntrypoint
|
newEntrypoint
|
||||||
);
|
);
|
||||||
if (newEntrypoint) {
|
if (newEntrypoint) {
|
||||||
writeAddr((char *) targetMethod + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod,
|
writeAddr((char *) targetMethod +
|
||||||
|
OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod,
|
||||||
newEntrypoint);
|
newEntrypoint);
|
||||||
} else {
|
} else {
|
||||||
LOGE("failed to allocate space for trampoline of target method");
|
LOGE("failed to allocate space for trampoline of target method");
|
||||||
|
|
@ -104,21 +95,18 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SDKVersion >= __ANDROID_API_Q__) {
|
if (SDKVersion >= __ANDROID_API_Q__) {
|
||||||
uint32_t access_flags = read32((char *) targetMethod + OFFSET_access_flags_in_ArtMethod);
|
uint32_t access_flags = getAccessFlags(targetMethod);
|
||||||
// On API 29 whether to use the fast path or not is cached in the ART method structure
|
// On API 29 whether to use the fast path or not is cached in the ART method structure
|
||||||
access_flags &= ~kAccFastInterpreterToInterpreterInvoke;
|
access_flags &= ~kAccFastInterpreterToInterpreterInvoke;
|
||||||
write32((char *) targetMethod + OFFSET_access_flags_in_ArtMethod, access_flags);
|
setAccessFlags(targetMethod, access_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGI("hook and backup done");
|
LOGI("hook and backup done");
|
||||||
hookCount += 1;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace yahfa {
|
|
||||||
|
|
||||||
void init(JNIEnv *env, [[maybe_unused]] jclass clazz, jint sdkVersion) {
|
void init(JNIEnv *env, [[maybe_unused]] jclass clazz, jint sdkVersion) {
|
||||||
SDKVersion = sdkVersion;
|
SDKVersion = sdkVersion;
|
||||||
jclass classExecutable = env->FindClass("java/lang/reflect/Executable");
|
jclass classExecutable = env->FindClass("java/lang/reflect/Executable");
|
||||||
|
|
@ -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,
|
jobject findMethodNative(JNIEnv *env, [[maybe_unused]] jclass clazz,
|
||||||
jclass targetClass,
|
jclass targetClass,
|
||||||
jstring methodName,
|
jstring methodName,
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,6 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "trampoline.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:
|
// trampoline:
|
||||||
// 1. set eax/r0/x0 to the hook ArtMethod addr
|
// 1. set eax/r0/x0 to the hook ArtMethod addr
|
||||||
// 2. jump into its entry point
|
// 2. jump into its entry point
|
||||||
|
|
@ -74,9 +68,12 @@ static inline void FlushCache(void *addr, size_t size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void *genTrampoline(void *hookMethod) {
|
void *genTrampoline(void *hookMethod) {
|
||||||
void *targetAddr;
|
unsigned char *targetAddr = mmap(NULL, trampolineSize, PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||||
|
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||||
targetAddr = trampolineCode + trampolineSize * hookCount;
|
if (targetAddr == MAP_FAILED) {
|
||||||
|
LOGE("mmap failed, errno = %s", strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
memcpy(targetAddr, trampoline,
|
memcpy(targetAddr, trampoline,
|
||||||
sizeof(trampoline)); // do not use trampolineSize since it's a rounded size
|
sizeof(trampoline)); // do not use trampolineSize since it's a rounded size
|
||||||
|
|
||||||
|
|
@ -117,24 +114,3 @@ void setupTrampoline() {
|
||||||
#error Unsupported architecture
|
#error Unsupported architecture
|
||||||
#endif
|
#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 "";
|
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) {
|
CREATE_MEM_FUNC_SYMBOL_ENTRY(void*, GetClassDef, void* thiz) {
|
||||||
if (LIKELY(GetClassDefSym))
|
if (LIKELY(GetClassDefSym))
|
||||||
return GetClassDefSym(thiz);
|
return GetClassDefSym(thiz);
|
||||||
|
|
@ -77,10 +54,7 @@ namespace art {
|
||||||
static void Setup(void *handle) {
|
static void Setup(void *handle) {
|
||||||
RETRIEVE_MEM_FUNC_SYMBOL(GetDescriptor, "_ZN3art6mirror5Class13GetDescriptorEPNSt3__112"
|
RETRIEVE_MEM_FUNC_SYMBOL(GetDescriptor, "_ZN3art6mirror5Class13GetDescriptorEPNSt3__112"
|
||||||
"basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE");
|
"basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE");
|
||||||
|
|
||||||
RETRIEVE_MEM_FUNC_SYMBOL(GetClassDef, "_ZN3art6mirror5Class11GetClassDefEv");
|
RETRIEVE_MEM_FUNC_SYMBOL(GetClassDef, "_ZN3art6mirror5Class11GetClassDefEv");
|
||||||
|
|
||||||
lspd::HookSyms(handle, IsInSamePackage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *GetDescriptor(std::string *storage) {
|
const char *GetDescriptor(std::string *storage) {
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
#include <dl_util.h>
|
#include <dl_util.h>
|
||||||
#include <framework/androidfw/resource_types.h>
|
#include <framework/androidfw/resource_types.h>
|
||||||
#include <byte_order.h>
|
#include <byte_order.h>
|
||||||
|
#include <HookMain.h>
|
||||||
#include "native_util.h"
|
#include "native_util.h"
|
||||||
#include "resources_hook.h"
|
#include "resources_hook.h"
|
||||||
|
|
||||||
|
|
@ -95,13 +96,24 @@ namespace lspd {
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ApiSensitive(Level.MIDDLE)
|
// @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) {
|
if (target_class) {
|
||||||
auto class_clazz = JNI_FindClass(env, "java/lang/Class");
|
static auto class_clazz = JNI_NewGlobalRef(env, JNI_FindClass(env, "java/lang/Class"));
|
||||||
jfieldID java_lang_Class_accessFlags = JNI_GetFieldID(
|
static jfieldID java_lang_Class_accessFlags = JNI_GetFieldID(
|
||||||
env, class_clazz, "accessFlags", "I");
|
env, class_clazz, "accessFlags", "I");
|
||||||
jint access_flags = env->GetIntField(target_class, java_lang_Class_accessFlags);
|
jint access_flags = env->GetIntField(target_class, java_lang_Class_accessFlags);
|
||||||
env->SetIntField(target_class, java_lang_Class_accessFlags, access_flags & ~kAccFinal);
|
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_TRUE;
|
||||||
}
|
}
|
||||||
return JNI_FALSE;
|
return JNI_FALSE;
|
||||||
|
|
@ -208,7 +220,8 @@ namespace lspd {
|
||||||
|
|
||||||
static JNINativeMethod gMethods[] = {
|
static JNINativeMethod gMethods[] = {
|
||||||
LSP_NATIVE_METHOD(ResourcesHook, initXResourcesNative, "()Z"),
|
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,
|
LSP_NATIVE_METHOD(ResourcesHook, buildDummyClassLoader,
|
||||||
"(Ljava/lang/ClassLoader;Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/ClassLoader;"),
|
"(Ljava/lang/ClassLoader;Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/ClassLoader;"),
|
||||||
LSP_NATIVE_METHOD(ResourcesHook, rewriteXmlReferencesNative,
|
LSP_NATIVE_METHOD(ResourcesHook, rewriteXmlReferencesNative,
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,8 @@ namespace lspd {
|
||||||
return lspd::isHooked(yahfa::getArtMethod(env, member));
|
return lspd::isHooked(yahfa::getArtMethod(env, member));
|
||||||
}
|
}
|
||||||
|
|
||||||
LSP_DEF_NATIVE_METHOD(jclass, Yahfa, buildHooker, jobject app_class_loader, jclass return_class,
|
LSP_DEF_NATIVE_METHOD(jclass, Yahfa, buildHooker, jobject app_class_loader, jchar return_class,
|
||||||
jobjectArray classes, jstring method_name) {
|
jcharArray classes, jstring method_name) {
|
||||||
static auto in_memory_classloader = JNI_NewGlobalRef(env, JNI_FindClass(env,
|
static auto in_memory_classloader = JNI_NewGlobalRef(env, JNI_FindClass(env,
|
||||||
"dalvik/system/InMemoryDexClassLoader"));
|
"dalvik/system/InMemoryDexClassLoader"));
|
||||||
static jmethodID initMid = JNI_GetMethodID(env, in_memory_classloader, "<init>",
|
static jmethodID initMid = JNI_GetMethodID(env, in_memory_classloader, "<init>",
|
||||||
|
|
@ -70,15 +70,14 @@ namespace lspd {
|
||||||
auto parameter_types = std::vector<TypeDescriptor>();
|
auto parameter_types = std::vector<TypeDescriptor>();
|
||||||
parameter_types.reserve(parameter_length);
|
parameter_types.reserve(parameter_length);
|
||||||
std::string storage;
|
std::string storage;
|
||||||
auto current_thread = art::Thread::Current();
|
auto return_type =
|
||||||
auto return_type = TypeDescriptor::FromDescriptor(
|
return_class == 'L' ? TypeDescriptor::Object : TypeDescriptor::FromDescriptor(
|
||||||
art::mirror::Class(current_thread.DecodeJObject(return_class)).GetDescriptor(
|
(char) return_class);
|
||||||
&storage));
|
auto params = env->GetCharArrayElements(classes, nullptr);
|
||||||
for (int i = 0; i < parameter_length; ++i) {
|
for (int i = 0; i < parameter_length; ++i) {
|
||||||
auto param = (jclass) env->GetObjectArrayElement(classes, i);
|
parameter_types.push_back(
|
||||||
auto *param_ref = current_thread.DecodeJObject(param);
|
params[i] == 'L' ? TypeDescriptor::Object : TypeDescriptor::FromDescriptor(
|
||||||
auto descriptor = art::mirror::Class(param_ref).GetDescriptor(&storage);
|
(char) params[i]));
|
||||||
parameter_types.push_back(TypeDescriptor::FromDescriptor(descriptor));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassBuilder cbuilder{dex_file.MakeClass("LspHooker_")};
|
ClassBuilder cbuilder{dex_file.MakeClass("LspHooker_")};
|
||||||
|
|
@ -136,7 +135,7 @@ namespace lspd {
|
||||||
Instruction::Cast(tmp, Value::Type(type_def->orig_index)));
|
Instruction::Cast(tmp, Value::Type(type_def->orig_index)));
|
||||||
hookBuilder.BuildReturn(tmp, true);
|
hookBuilder.BuildReturn(tmp, true);
|
||||||
}
|
}
|
||||||
auto *hook_method = hookBuilder.Encode();
|
[[maybe_unused]] auto *hook_method = hookBuilder.Encode();
|
||||||
|
|
||||||
auto backup_builder{
|
auto backup_builder{
|
||||||
cbuilder.CreateMethod("backup", Prototype{return_type, parameter_types})};
|
cbuilder.CreateMethod("backup", Prototype{return_type, parameter_types})};
|
||||||
|
|
@ -153,7 +152,7 @@ namespace lspd {
|
||||||
backup_builder.BuildConst(zero, 0);
|
backup_builder.BuildConst(zero, 0);
|
||||||
backup_builder.BuildReturn(zero, /*is_object=*/!return_type.is_primitive(), false);
|
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()};
|
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, recordHooked, "(Ljava/lang/reflect/Executable;)V"),
|
||||||
LSP_NATIVE_METHOD(Yahfa, isHooked, "(Ljava/lang/reflect/Executable;)Z"),
|
LSP_NATIVE_METHOD(Yahfa, isHooked, "(Ljava/lang/reflect/Executable;)Z"),
|
||||||
LSP_NATIVE_METHOD(Yahfa, buildHooker,
|
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) {
|
void RegisterYahfa(JNIEnv *env) {
|
||||||
|
|
|
||||||
|
|
@ -103,8 +103,8 @@ public final class XposedBridge {
|
||||||
} catch (Resources.NotFoundException nfe) {
|
} catch (Resources.NotFoundException nfe) {
|
||||||
XposedBridge.log(nfe);
|
XposedBridge.log(nfe);
|
||||||
}
|
}
|
||||||
ResourcesHook.removeFinalFlagNative(resClass);
|
ResourcesHook.makeInheritable(resClass, resClass.getDeclaredConstructors());
|
||||||
ResourcesHook.removeFinalFlagNative(taClass);
|
ResourcesHook.makeInheritable(taClass, taClass.getDeclaredConstructors());
|
||||||
ClassLoader myCL = XposedBridge.class.getClassLoader();
|
ClassLoader myCL = XposedBridge.class.getClassLoader();
|
||||||
dummyClassLoader = ResourcesHook.buildDummyClassLoader(myCL.getParent(), resClass, taClass);
|
dummyClassLoader = ResourcesHook.buildDummyClassLoader(myCL.getParent(), resClass, taClass);
|
||||||
dummyClassLoader.loadClass("xposed.dummy.XResourcesSuperClass");
|
dummyClassLoader.loadClass("xposed.dummy.XResourcesSuperClass");
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,13 @@ package org.lsposed.lspd.nativebridge;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.content.res.XResources;
|
import android.content.res.XResources;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
|
||||||
public class ResourcesHook {
|
public class ResourcesHook {
|
||||||
|
|
||||||
public static native boolean initXResourcesNative();
|
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);
|
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 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.Executable;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
import de.robv.android.xposed.LspHooker;
|
import de.robv.android.xposed.LspHooker;
|
||||||
import de.robv.android.xposed.XposedBridge;
|
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_BACKUP = "backup";
|
||||||
public static final String METHOD_NAME_SETUP = "setup";
|
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<?> mReturnType;
|
||||||
private Class<?>[] mActualParameterTypes;
|
private Class<?>[] mActualParameterTypes;
|
||||||
|
|
@ -49,18 +62,31 @@ public class HookerDexMaker {
|
||||||
|
|
||||||
private static Class<?>[] getParameterTypes(Executable method, boolean isStatic) {
|
private static Class<?>[] getParameterTypes(Executable method, boolean isStatic) {
|
||||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||||
|
for (int i = 0; i < parameterTypes.length; ++i) {
|
||||||
|
parameterTypes[i] = parameterTypes[i].isPrimitive() ? parameterTypes[i] : Object.class;
|
||||||
|
}
|
||||||
if (isStatic) {
|
if (isStatic) {
|
||||||
return parameterTypes;
|
return parameterTypes;
|
||||||
}
|
}
|
||||||
int parameterSize = parameterTypes.length;
|
int parameterSize = parameterTypes.length;
|
||||||
int targetParameterSize = parameterSize + 1;
|
Class<?>[] newParameterTypes = new Class<?>[parameterSize + 1];
|
||||||
Class<?>[] newParameterTypes = new Class<?>[targetParameterSize];
|
newParameterTypes[0] = Object.class;
|
||||||
int offset = 1;
|
System.arraycopy(parameterTypes, 0, newParameterTypes, 1, parameterSize);
|
||||||
newParameterTypes[0] = method.getDeclaringClass();
|
|
||||||
System.arraycopy(parameterTypes, 0, newParameterTypes, offset, parameterTypes.length);
|
|
||||||
return newParameterTypes;
|
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,
|
public void start(Executable member, XposedBridge.AdditionalHookInfo hookInfo,
|
||||||
ClassLoader appClassLoader) throws Exception {
|
ClassLoader appClassLoader) throws Exception {
|
||||||
if (member instanceof Method) {
|
if (member instanceof Method) {
|
||||||
|
|
@ -89,7 +115,7 @@ public class HookerDexMaker {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doMake(String methodName) throws Exception {
|
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.
|
// Execute our newly-generated code in-process.
|
||||||
Method backupMethod = hookClass.getMethod(METHOD_NAME_BACKUP, mActualParameterTypes);
|
Method backupMethod = hookClass.getMethod(METHOD_NAME_BACKUP, mActualParameterTypes);
|
||||||
mHooker = new LspHooker(mHookInfo, mMember, backupMethod);
|
mHooker = new LspHooker(mHookInfo, mMember, backupMethod);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue