Skip to content

Commit

Permalink
Fix crash when hooked static method invokes class initialization
Browse files Browse the repository at this point in the history
  • Loading branch information
solohsu committed Jun 20, 2019
1 parent 1b45e8f commit ee2a3f5
Show file tree
Hide file tree
Showing 17 changed files with 185 additions and 27 deletions.
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: '0.4.4.7_alpha({build})'
version: '0.4.5.0_alpha({build})'

environment:
ANDROID_HOME: C:\android-sdk-windows
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
package com.elderdrivers.riru.edxp.art;

import com.elderdrivers.riru.common.KeepAll;
import com.elderdrivers.riru.edxp.core.Yahfa;

import java.lang.reflect.Member;
import java.util.function.Consumer;

import de.robv.android.xposed.PendingHooks;

public class ClassLinker implements KeepAll {

public static native void setEntryPointsToInterpreter(Member method);

public static void onPreFixupStaticTrampolines(Class clazz) {
// remove modified native flags to let FixupStaticTrampolines fill in right entrypoints
PendingHooks.removeNativeFlags(clazz);
}

public static void onPostFixupStaticTrampolines(Class clazz) {
// native flags will be re-set in hooking logic
PendingHooks.hookPendingMethod(clazz);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.elderdrivers.riru.edxp.config;

import com.elderdrivers.riru.edxp.core.Yahfa;
import com.elderdrivers.riru.edxp.deopt.PrebuiltMethodsDeopter;
import com.elderdrivers.riru.edxp.hook.HookProvider;

Expand Down Expand Up @@ -37,4 +38,9 @@ public void deoptMethodNative(Object method) {
public boolean initXResourcesNative() {
return false;
}

@Override
public void setNativeFlag(Member hookMethod, boolean isNative) {
Yahfa.setNativeFlag(hookMethod, isNative);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ public class Yahfa {
public static native void init(int SDK_version);

public static native void setMethodNonCompilable(Member member);

public static native boolean setNativeFlag(Member member, boolean isNative);
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,8 @@ public void forkAndSpecializePre(int uid, int gid, int[] gids, int debugFlags,
}

public void forkAndSpecializePost(int pid, String appDataDir, String niceName) {
// TODO consider processes without forkAndSpecializePost called
ConfigManager.appDataDir = appDataDir;
ConfigManager.niceName = niceName;
mRouter.prepare(false);
mRouter.onEnterChildProcess();
// load modules for each app process on its forked if dynamic modules mode is on
mRouter.loadModulesSafely(false, true);
mRouter.onForkFinish();
// TODO consider processes without forkAndSpecializePost being called
forkPostCommon(pid, false, appDataDir, niceName);
}

public void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits,
Expand All @@ -58,12 +52,20 @@ public void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, in

public void forkSystemServerPost(int pid) {
// in system_server process
ConfigManager.appDataDir = getDataPathPrefix() + "android";
ConfigManager.niceName = "system_server";
mRouter.prepare(true);
forkPostCommon(pid, true,
getDataPathPrefix() + "android", "system_server");
}


private void forkPostCommon(int pid, boolean isSystem, String appDataDir, String niceName) {
ConfigManager.appDataDir = appDataDir;
ConfigManager.niceName = niceName;
mRouter.prepare(isSystem);
mRouter.onEnterChildProcess();
// reload module list if dynamic mode is on
if (ConfigManager.isDynamicModulesEnabled()) {
// FIXME this could be error-prone because hooks installed inside old versions
// of initZygote instances of same module are not unhooked
mRouter.loadModulesSafely(false, true);
}
mRouter.onForkFinish();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import android.os.Build;

import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;

Expand Down Expand Up @@ -37,7 +38,7 @@ public static boolean isInitialized(Class clazz) {
}

public static boolean shouldDelayHook(Member hookMethod) {
if (hookMethod == null) {
if (hookMethod == null || hookMethod instanceof Constructor) {
return false;
}
Class declaringClass = hookMethod.getDeclaringClass();
Expand Down
4 changes: 2 additions & 2 deletions edxp-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import org.gradle.internal.os.OperatingSystem
apply plugin: 'com.android.library'

// Values set here will be overriden by AppVeyor, feel free to modify during development.
def buildVersionName = 'v0.4.4.7_alpha'
def buildVersionName = 'v0.4.5.0_alpha'
def buildVersionCode = 10000

if (System.env.APPVEYOR_BUILD_VERSION != null) {
Expand Down Expand Up @@ -165,7 +165,7 @@ afterEvaluate {
into "${zipPathMagiskRelease}/common"
filter { line -> line
.replaceAll('%VERSION%', "$version")
.replaceAll('%VERSION_CODE%', "$versionCode")
.replaceAll('%VERSION_CODE%', "$versionCode")
.replaceAll('%BACKEND%', "$backendCapped") }
}
copy {
Expand Down
2 changes: 2 additions & 0 deletions edxp-core/src/main/cpp/external/yahfa/include/HookMain.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ void Java_lab_galaxy_yahfa_HookMain_ensureMethodCached(JNIEnv *env, jclass clazz

void setNonCompilable(void *method);

bool setNativeFlag(void *method, bool isNative);

static void *getResolvedMethodsAddr(JNIEnv *, jobject);

#ifdef __cplusplus
Expand Down
29 changes: 22 additions & 7 deletions edxp-core/src/main/cpp/external/yahfa/src/HookMain.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <string.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <stdbool.h>

#include "common.h"
#include "env.h"
Expand Down Expand Up @@ -128,6 +129,26 @@ void setNonCompilable(void *method) {
);
}

bool setNativeFlag(void *method, bool isNative) {
int access_flags = read32((char *) method + OFFSET_access_flags_in_ArtMethod);
LOGI("setNativeFlag: access flags is 0x%x", access_flags);
int old_access_flags = access_flags;
if (isNative) {
access_flags |= kAccNative;
} else {
access_flags &= ~kAccNative;
}
if (access_flags != old_access_flags) {
memcpy(
(char *) method + OFFSET_access_flags_in_ArtMethod,
&access_flags,
4
);
return true;
}
return false;
}

static int doBackupAndHook(JNIEnv *env, void *targetMethod, void *hookMethod, void *backupMethod) {
if (hookCount >= hookCap) {
LOGI("not enough capacity. Allocating...");
Expand Down Expand Up @@ -182,13 +203,7 @@ static int doBackupAndHook(JNIEnv *env, void *targetMethod, void *hookMethod, vo

// set the target method to native so that Android O wouldn't invoke it with interpreter
if (SDKVersion >= ANDROID_O) {
int access_flags = read32((char *) targetMethod + OFFSET_access_flags_in_ArtMethod);
access_flags |= kAccNative;
memcpy(
(char *) targetMethod + OFFSET_access_flags_in_ArtMethod,
&access_flags,
4
);
setNativeFlag(targetMethod, true);
LOGI("access flags is 0x%x", access_flags);
}

Expand Down
14 changes: 13 additions & 1 deletion edxp-core/src/main/cpp/main/include/art/runtime/class_linker.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@

#include <JNIHelper.h>
#include <base/object.h>
#include <art/runtime/mirror/class.h>
#include <android-base/strings.h>
#include "runtime.h"
#include "jni_env_ext.h"
#include "edxp_context.h"
#include "jni/edxp_pending_hooks.h"

namespace art {

Expand All @@ -28,8 +31,17 @@ namespace art {
}

CREATE_HOOK_STUB_ENTRIES(void, FixupStaticTrampolines, void *thiz, void *clazz_ptr) {
art::mirror::Class clazz(clazz_ptr);
std::string storage;
const char *desc = clazz.GetDescriptor(&storage);
bool should_intercept = edxp::IsClassPending(desc);
if (UNLIKELY(should_intercept)) {
edxp::Context::GetInstance()->CallOnPreFixupStaticTrampolines(clazz_ptr);
}
FixupStaticTrampolinesBackup(thiz, clazz_ptr);
edxp::Context::GetInstance()->CallOnPostFixupStaticTrampolines(clazz_ptr);
if (UNLIKELY(should_intercept)) {
edxp::Context::GetInstance()->CallOnPostFixupStaticTrampolines(clazz_ptr);
}
}

public:
Expand Down
19 changes: 16 additions & 3 deletions edxp-core/src/main/cpp/main/src/edxp_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <art/runtime/mirror/class.h>
#include <android-base/strings.h>
#include <nativehelper/scoped_local_ref.h>
#include <jni/edxp_pending_hooks.h>
#include "edxp_context.h"
#include "config_manager.h"

Expand Down Expand Up @@ -43,8 +44,8 @@ namespace edxp {
return inject_class_loader_;
}

void Context::CallOnPostFixupStaticTrampolines(void *class_ptr) {
if (UNLIKELY(!post_fixup_static_mid_ || !class_linker_class_)) {
void Context::CallPostFixupStaticTrampolinesCallback(void *class_ptr, jmethodID callback_mid) {
if (UNLIKELY(!callback_mid || !class_linker_class_)) {
return;
}
if (!class_ptr) {
Expand All @@ -55,10 +56,18 @@ namespace edxp {
art::JNIEnvExt env_ext(env);
ScopedLocalRef clazz(env, env_ext.NewLocalRefer(class_ptr));
if (clazz != nullptr) {
JNI_CallStaticVoidMethod(env, class_linker_class_, post_fixup_static_mid_, clazz.get());
JNI_CallStaticVoidMethod(env, class_linker_class_, callback_mid, clazz.get());
}
}

void Context::CallOnPreFixupStaticTrampolines(void *class_ptr) {
CallPostFixupStaticTrampolinesCallback(class_ptr, pre_fixup_static_mid_);
}

void Context::CallOnPostFixupStaticTrampolines(void *class_ptr) {
CallPostFixupStaticTrampolinesCallback(class_ptr, post_fixup_static_mid_);
}

void Context::LoadDexAndInit(JNIEnv *env, const char *dex_path) {
if (LIKELY(initialized_)) {
return;
Expand Down Expand Up @@ -97,6 +106,9 @@ namespace edxp {
env->GetJavaVM(&vm_);
class_linker_class_ = (jclass) env->NewGlobalRef(
FindClassFromLoader(env, kClassLinkerClassName));
pre_fixup_static_mid_ = JNI_GetStaticMethodID(env, class_linker_class_,
"onPreFixupStaticTrampolines",
"(Ljava/lang/Class;)V");
post_fixup_static_mid_ = JNI_GetStaticMethodID(env, class_linker_class_,
"onPostFixupStaticTrampolines",
"(Ljava/lang/Class;)V");
Expand All @@ -110,6 +122,7 @@ namespace edxp {
RegisterArtClassLinker(env);
RegisterArtHeap(env);
RegisterEdxpYahfa(env);
RegisterPendingHooks(env);

// must call entry class's methods after all native methods registered
if (LIKELY(entry_class_)) {
Expand Down
5 changes: 5 additions & 0 deletions edxp-core/src/main/cpp/main/src/edxp_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ namespace edxp {

jobject GetCurrentClassLoader() const;

void CallOnPreFixupStaticTrampolines(void *class_ptr);

void CallOnPostFixupStaticTrampolines(void *class_ptr);

void PrepareJavaEnv(JNIEnv *env);
Expand Down Expand Up @@ -70,6 +72,7 @@ namespace edxp {
jstring nice_name_ = nullptr;
JavaVM *vm_ = nullptr;
jclass class_linker_class_ = nullptr;
jmethodID pre_fixup_static_mid_ = nullptr;
jmethodID post_fixup_static_mid_ = nullptr;

Context() {}
Expand All @@ -79,6 +82,8 @@ namespace edxp {
void LoadDexAndInit(JNIEnv *env, const char *dex_path);

jclass FindClassFromLoader(JNIEnv *env, jobject class_loader, const char *class_name) const;

void CallPostFixupStaticTrampolinesCallback(void *class_ptr, jmethodID mid);
};

}
30 changes: 30 additions & 0 deletions edxp-core/src/main/cpp/main/src/jni/edxp_pending_hooks.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

#include <nativehelper/jni_macros.h>
#include <set>
#include <string>
#include "jni.h"
#include "native_util.h"
#include "edxp_pending_hooks.h"

namespace edxp {

static std::set<std::string> class_descs_;

bool IsClassPending(const char *class_desc) {
return class_descs_.find(class_desc) != class_descs_.end();
}

static void PendingHooks_recordPendingMethodNative(JNI_START, jstring class_desc) {
const char *class_desc_chars = env->GetStringUTFChars(class_desc, JNI_FALSE);
class_descs_.insert(class_desc_chars);
}

static JNINativeMethod gMethods[] = {
NATIVE_METHOD(PendingHooks, recordPendingMethodNative, "(Ljava/lang/String;)V"),
};

void RegisterPendingHooks(JNIEnv *env) {
REGISTER_EDXP_NATIVE_METHODS("de.robv.android.xposed.PendingHooks");
}

}
12 changes: 12 additions & 0 deletions edxp-core/src/main/cpp/main/src/jni/edxp_pending_hooks.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

#pragma once

#include "jni.h"

namespace edxp {

bool IsClassPending(const char *);

void RegisterPendingHooks(JNIEnv *);

} // namespace edxp
14 changes: 14 additions & 0 deletions edxp-core/src/main/cpp/main/src/jni/edxp_yahfa.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,19 @@ namespace edxp {
setNonCompilable(art_method);
}

static jboolean Yahfa_setNativeFlag(JNI_START, jobject member, jboolean is_native) {
if (!member) {
LOGE("setNativeFlagNative: member is null");
return JNI_FALSE;
}
void *art_method = env->FromReflectedMethod(member);
if (!art_method) {
LOGE("setNativeFlagNative: art_method is null");
return JNI_FALSE;
}
return (jboolean) setNativeFlag(art_method, is_native);
}

static JNINativeMethod gMethods[] = {
NATIVE_METHOD(Yahfa, init, "(I)V"),
NATIVE_METHOD(Yahfa, findMethodNative,
Expand All @@ -48,6 +61,7 @@ namespace edxp {
NATIVE_METHOD(Yahfa, ensureMethodCached,
"(Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)V"),
NATIVE_METHOD(Yahfa, setMethodNonCompilable, "(Ljava/lang/reflect/Member;)V"),
NATIVE_METHOD(Yahfa, setNativeFlag, "(Ljava/lang/reflect/Member;Z)Z"),
};

void RegisterEdxpYahfa(JNIEnv *env) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,7 @@ public interface HookProvider {
boolean initXResourcesNative();

boolean removeFinalFlagNative(Class clazz);

void setNativeFlag(Member hookMethod, boolean isNative);

}
Loading

0 comments on commit ee2a3f5

Please sign in to comment.