[core] Remove isSamePackage hook (#690)

This commit is contained in:
LoveSy 2021-05-28 19:58:01 +08:00 committed by GitHub
parent a6f0e666b8
commit efa42a4eb0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 188 additions and 197 deletions

@ -1 +1 @@
Subproject commit 5c1ccd55836e4bf43cdb6adbad40443d18cd43db
Subproject commit 41b9171852840cb421d8be22ef73d9467db02799

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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