Skip to content

Commit

Permalink
call MakeInitializedClassesVisiblyInitialized explicitly
Browse files Browse the repository at this point in the history
  • Loading branch information
rk700 committed Dec 29, 2020
1 parent cd76a9d commit 5b60df8
Show file tree
Hide file tree
Showing 10 changed files with 204 additions and 25 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.2'
classpath 'com.android.tools.build:gradle:4.1.1'

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
43 changes: 43 additions & 0 deletions library/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Sets the minimum version of CMake required to build your native library.
# This ensures that a certain set of CMake features is available to
# your build.

cmake_minimum_required(VERSION 3.4.1)

# Specifies a library name, specifies whether the library is STATIC or
# SHARED, and provides relative paths to the source code. You can
# define multiple libraries by adding multiple add_library() commands,
# and CMake builds them for you. When you build your app, Gradle
# automatically packages shared libraries with your APK.

find_library( # Defines the name of the path variable that stores the
# location of the NDK library.
log-lib

# Specifies the name of the NDK library that
# CMake needs to locate.
log )


add_library( # Specifies the name of the library.
yahfa

# Sets the library as a shared library.
SHARED

# Provides a relative path to your source file(s).
src/main/jni/HookMain.c
src/main/jni/trampoline.c
)

find_package(dlfunc REQUIRED CONFIG)

target_link_libraries( # Specifies the target library.
yahfa

# Links the log library to the target library.
${log-lib}
dlfunc::dlfunc
)


26 changes: 24 additions & 2 deletions library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,37 @@ android {
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

externalNativeBuild {
cmake {
arguments "-DANDROID_STL=none"
}
}
}
externalNativeBuild {
ndkBuild {
path 'src/main/jni/Android.mk'
cmake {
path 'CMakeLists.txt'
}
}
buildFeatures {
prefab true
}

packagingOptions {
exclude "**/libdlfunc.so"
}

// testOptions {
// unitTests {
// packagingOptions {
// pickFirst "**/libdlfunc.so"
// }
// }
// }
}

dependencies {
implementation 'io.github.rk700:dlfunc:0.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-alpha4'
}
Expand Down
33 changes: 33 additions & 0 deletions library/src/main/java/lab/galaxy/yahfa/HookMain.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ private static void doHookItemDefault(ClassLoader patchClassLoader, String hookI
Log.e(TAG, "Cannot find hook for " + methodName);
return;
}

// has to visibly init the classes
// see the comment for function Utils.initClass()
if(Utils.initClass() != 0) {
Log.e(TAG, "Utils.initClass failed");
}

findAndBackupAndHook(clazz, methodName, methodSig, hook, backup);
} catch (Exception e) {
e.printStackTrace();
Expand Down Expand Up @@ -116,6 +123,12 @@ public static void backupAndHook(Object target, Method hook, Method backup) {
checkCompatibleMethods(target, backup, "Original", "Backup");
}

// has to visibly init the classes
// see the comment for function Utils.initClass()
if(Utils.initClass() != 0) {
Log.e(TAG, "Utils.initClass failed");
}

if (!backupAndHookNative(target, hook, backup)) {
throw new RuntimeException("Failed to hook " + target + " with " + hook);
}
Expand Down Expand Up @@ -184,4 +197,24 @@ private static void checkCompatibleMethods(Object original, Method replacement,
public static native Object findMethodNative(Class targetClass, String methodName, String methodSig);

private static native void init(int sdkVersion);

public static class Utils {
// https://github.com/PAGalaxyLab/YAHFA/pull/133#issuecomment-743728607
// class may be visible initialized after it's initialized after Android R
// so we have to call MakeInitializedClassesVisiblyInitialized explicitly before hooking
public static int initClass() {
// do nothing before Android R or on x86 devices
if(shouldVisiblyInit()) {
long thread = getThread();
return visiblyInit(thread);
}
else {
return 0;
}
}

private static native boolean shouldVisiblyInit();
private static native int visiblyInit(long thread);
private static native long getThread();
}
}
10 changes: 0 additions & 10 deletions library/src/main/jni/Android.mk

This file was deleted.

Empty file.
104 changes: 101 additions & 3 deletions library/src/main/jni/HookMain.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@

#include "common.h"
#include "trampoline.h"
#include "dlfunc.h"

int SDKVersion;
static int SDKVersion;
static uint32_t OFFSET_entry_point_from_interpreter_in_ArtMethod;
static uint32_t OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod;
static uint32_t OFFSET_ArtMehod_in_Object;
Expand All @@ -17,6 +18,13 @@ static uint32_t kAccFastInterpreterToInterpreterInvoke = 0x40000000;

static jfieldID fieldArtMethod = NULL;

static uint32_t OFFSET_classlinker_in_Runtime;
static char *classLinker = NULL;
typedef void (*InitClassFunc)(void *, int);
static InitClassFunc MakeInitializedClassesVisiblyInitialized = NULL;
static int shouldVisiblyInit();
static int findInitClassSymbols(JNIEnv *env);

static inline uint32_t read32(void *addr) {
return *((uint32_t *) addr);
}
Expand All @@ -33,9 +41,94 @@ static inline void writeAddr(void *addr, void *value) {
*((void **)addr) = value;
}

#ifndef __ANDROID_API_R__
#define __ANDROID_API_R__ 30
static int findInitClassSymbols(JNIEnv *env) {
if(dlfunc_init(env) != JNI_OK) {
LOGE("dlfunc init failed");
return 1;
}
void *handle = dlfunc_dlopen(env, "libart.so", RTLD_LAZY);
if(handle == NULL) {
LOGE("failed to find libart.so handle");
return 1;
}
else {
void **runtime_bss = dlfunc_dlsym(env, handle, "_ZN3art7Runtime9instance_E");
if(!runtime_bss) {
LOGE("failed to find Runtime::instance symbol");
return 1;
}
char *runtime = *runtime_bss;
if(!runtime) {
LOGE("Runtime::instance value is NULL");
return 1;
}
classLinker = runtime + OFFSET_classlinker_in_Runtime;

MakeInitializedClassesVisiblyInitialized =
dlfunc_dlsym(env, handle, "_ZN3art11ClassLinker40MakeInitializedClassesVisiblyInitializedEPNS_6ThreadEb");
if(!MakeInitializedClassesVisiblyInitialized) {
LOGE("failed to find MakeInitializedClassesVisiblyInitialized symbol");
return 1;
}
}
return 0;
}

jlong __attribute__((naked)) Java_lab_galaxy_yahfa_HookMain_00024Utils_getThread(JNIEnv *env, jclass clazz) {
#if defined(__aarch64__)
__asm__(
"mov x0, x19\n"
"ret\n"
);
#elif defined(__arm__)
__asm__(
"mov r0, r9\n"
"bx lr\n"
);
#elif defined(__x86_64__)
__asm__(
"mov %gs:0xe8, %rax\n" // offset on Android R
"ret\n"
);
#elif defined(__i386__)
__asm__(
"mov %fs:0xc4, %eax\n" // offset on Android R
"ret\n"
);
#endif
}

static int shouldVisiblyInit() {
#if defined(__i386__) || defined(__x86_64__)
return 0;
#else
if(SDKVersion < __ANDROID_API_R__) {
return 0;
}
else return 1;
#endif
}

jboolean Java_lab_galaxy_yahfa_HookMain_00024Utils_shouldVisiblyInit(JNIEnv *env, jclass clazz) {
return shouldVisiblyInit() != 0;
}

jint Java_lab_galaxy_yahfa_HookMain_00024Utils_visiblyInit(JNIEnv *env, jclass clazz, jlong thread) {
if(!shouldVisiblyInit()) {
return 0;
}

if(!classLinker || !MakeInitializedClassesVisiblyInitialized) {
if(findInitClassSymbols(env) != 0) {
LOGE("failed to find symbols: classLinker %p, MakeInitializedClassesVisiblyInitialized %p",
classLinker, MakeInitializedClassesVisiblyInitialized);
return 1;
}
}

MakeInitializedClassesVisiblyInitialized((void *)thread, 1);
return 0;
}

void Java_lab_galaxy_yahfa_HookMain_init(JNIEnv *env, jclass clazz, jint sdkVersion) {
SDKVersion = sdkVersion;
Expand All @@ -45,6 +138,11 @@ void Java_lab_galaxy_yahfa_HookMain_init(JNIEnv *env, jclass clazz, jint sdkVers
case __ANDROID_API_R__:
classExecutable = (*env)->FindClass(env, "java/lang/reflect/Executable");
fieldArtMethod = (*env)->GetFieldID(env, classExecutable, "artMethod", "J");
#if defined(__x86_64__) || defined(__aarch64__)
OFFSET_classlinker_in_Runtime = 472;
#else
OFFSET_classlinker_in_Runtime = 276;
#endif
case __ANDROID_API_Q__:
case __ANDROID_API_P__:
kAccCompileDontBother = 0x02000000;
Expand Down
5 changes: 0 additions & 5 deletions library/src/main/jni/trampoline.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@
#include "common.h"
#include "trampoline.h"

#define MAX(a, b) ((a) > (b) ? (a) : (b))

static unsigned char *trampolineCode; // place where trampolines are saved
static unsigned int trampolineSize; // trampoline size required for each hook

static unsigned char *currentTrampolineOff = 0;
static unsigned char *trampolineSpaceEnd = 0;

Expand Down
4 changes: 1 addition & 3 deletions library/src/main/jni/trampoline.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
#ifndef YAHFA_TAMPOLINE_H
#define YAHFA_TAMPOLINE_H

extern int SDKVersion;

void setupTrampoline(uint8_t offset);
void setupTrampoline(unsigned char offset);

void *genTrampoline(void *toMethod, void *entrypoint);

Expand Down

0 comments on commit 5b60df8

Please sign in to comment.