Skip to content

Commit

Permalink
1. don't use jni EP; 2. reset hotness_count for N
Browse files Browse the repository at this point in the history
  • Loading branch information
rk700 committed Jun 29, 2017
1 parent f01fa03 commit 7e7ce0e
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 79 deletions.
3 changes: 3 additions & 0 deletions library/src/main/java/lab/galaxy/yahfa/HookMain.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public void doHookDefault(ClassLoader patchClassLoader, ClassLoader originClassL
try {
Class<?> hookInfoClass = Class.forName("lab.galaxy.yahfa.HookInfo", true, patchClassLoader);
String[] hookItemNames = (String[])hookInfoClass.getField("hookItemNames").get(null);
initHookCap(hookItemNames.length);
for(String hookItemName : hookItemNames) {
doHookItemDefault(patchClassLoader, hookItemName, originClassLoader);
}
Expand Down Expand Up @@ -78,4 +79,6 @@ public native void findAndBackupAndHook(Class targetClass, String methodName, St
Method hook, Method backup);

private native void init(int SDK_version);

private native void initHookCap(int cap);
}
6 changes: 1 addition & 5 deletions library/src/main/jni/Android.mk
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

ifeq "$(TARGET_ARCH_ABI)" "x86"
LOCAL_SRC_FILES:= HookMain.c entrypoints_x86.S
else
LOCAL_SRC_FILES:= HookMain.c entrypoints_arm.S
endif
LOCAL_SRC_FILES:= HookMain.c

LOCAL_LDLIBS := -llog

Expand Down
217 changes: 191 additions & 26 deletions library/src/main/jni/HookMain.c
Original file line number Diff line number Diff line change
@@ -1,27 +1,86 @@
#include "jni.h"
#include <android/log.h>
#include <string.h>
#include <sys/mman.h>

#define LOG_TAG "YAHFA-Native"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

void hook_new_entry_24();
void hook_new_entry_23();
void hook_new_entry_22();
void hook_new_entry_21();
#define DEFAULT_CAP 128

#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define roundUpTo4(v) ((v+4-1) - ((v+4-1)&3))

#define ANDROID_L 21
#define ANDROID_L2 22
#define ANDROID_M 23
#define ANDROID_N 24
#define ANDROID_N2 25


static int SDKVersion;

static int OFFSET_dex_cache_resolved_methods_in_ArtMethod;
static int OFFSET_entry_point_from_interpreter_in_ArtMethod;
static int OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod;
static int OFFSET_entry_point_from_jni_in_ArtMethod;
static int OFFSET_dex_method_index_in_ArtMethod;
static int OFFSET_hotness_count_in_ArtMethod;
static int OFFSET_array_in_PointerArray;
static int OFFSET_ArtMehod_in_Object;
static size_t pointer_size;
static size_t ArtMethodSize;
static void *hook_new_entry;

static unsigned int hookCap; // capacity for trampolines
static unsigned int hookCount; // current count of used trampolines
static unsigned char *trampolineCode; // place where trampolines are saved
static unsigned int trampolineSize; // trampoline size required for each hook
static unsigned int trampolineCodeSize; // total size of trampoline code area


// trampoline1: set eax to a new ArtMethod addr and then jump into its entry point
#if defined(__i386__)
// b8 78 56 34 12 ; mov eax, 0x12345678
// ff 70 20 ; push dword [eax + 0x20]
// c3 ; ret
static unsigned char trampoline1[] = {0xb8, 0x78, 0x56, 0x34, 0x12, 0xff, 0x70, 0x20, 0xc3};
static unsigned int t1Size = roundUpTo4(sizeof(trampoline1)); // for alignment
#elif defined(__arm__)
// 00 00 9F E5 ; ldr r0, [pc]
// 20 F0 90 E5 ; ldr pc, [r0, 0x20]
// 78 56 34 12 ; 0x12345678
static unsigned char trampoline1[] = {0x00, 0x00, 0x9f, 0xe5, 0x20, 0xf0, 0x90, 0xe5, 0x78, 0x56, 0x34, 0x12};
static unsigned int t1Size = sizeof(trampoline1);
#else
#error Unsupported architecture
#endif

// trampoline2: clear hotness_count of ArtMethod in eax and then jump into its entry point
#if defined(__i386__)
// 66 c7 40 12 00 00 ; mov word [eax + 0x12], 0
// 68 78 56 34 12 ; push 0x12345678
// c3 ; ret
static unsigned char trampoline2[] = {0x66, 0xc7, 0x40, 0x12, 0x00, 0x00, 0x68, 0x78, 0x56, 0x34, 0x12, 0xc3};
static unsigned int t2Size = roundUpTo4(sizeof(trampoline2)); // for alignment
#elif defined(__arm__)
// 04 40 2D E5 ; push {r4}
// 00 40 A0 E3 ; mov r4, #0
// B2 41 C0 E1 ; strh r4, [r0, #18]
// 04 40 9D E4 ; pop {r4}
// 78 56 34 12 ; 0x12345678
static unsigned char trampoline2[] = {
0x04, 0x40, 0x2d, 0xe5,
0x00, 0x40, 0xa0, 0xe3,
0xb2, 0x41, 0xc0, 0xe1,
0x04, 0x40, 0x9d, 0xe4,
0x78, 0x56, 0x34, 0x12
};
static unsigned int t2Size = sizeof(trampoline2);
#else
#error Unsupported architecture
#endif

static unsigned int read32(void *addr) {
return *((unsigned int*)addr);
Expand All @@ -44,22 +103,95 @@ static unsigned long readAddr(void *addr) {
}
}

static int doInitHookCap(unsigned int cap) {
if(cap == 0) {
LOGE("invalid capacity: %d", cap);
return 1;
}
if(hookCap) {
LOGW("allocating new space for trampoline code");
}
int allSize = trampolineSize*cap;
char *buf = mmap(NULL, allSize, PROT_READ, MAP_ANON | MAP_PRIVATE, -1, 0);
if(buf == MAP_FAILED) {
LOGE("mmap failed");
return 1;
}
hookCap = cap;
hookCount = 0;
trampolineCode = buf;
trampolineCodeSize = allSize;
return 0;
}

void Java_lab_galaxy_yahfa_HookMain_initHookCap(JNIEnv *env, jclass clazz, jint cap) {
doInitHookCap(MAX(cap, DEFAULT_CAP));
}

static void *genTrampoline1(void *hookMethod) {
void *targetAddr;
if(mprotect(trampolineCode, trampolineCodeSize, PROT_READ | PROT_WRITE) == -1) {
LOGE("mprotect RW failed");
return NULL;
}
targetAddr = trampolineCode + trampolineSize*hookCount;
memcpy(targetAddr, trampoline1, sizeof(trampoline1)); // do not use t1size since it's a rounded size

// replace with hook ArtMethod addr
#if defined(__i386__)
memcpy(targetAddr+1, &hookMethod, pointer_size);
#elif defined(__arm__)
memcpy(targetAddr+8, &hookMethod, pointer_size);
#endif

if(mprotect(trampolineCode, trampolineCodeSize, PROT_READ | PROT_EXEC) == -1) {
LOGE("mprotect RX failed");
return NULL;
}
return targetAddr;
}

static void *genTrampoline2(void *entryPoint) {
void *targetAddr;
if(mprotect(trampolineCode, trampolineCodeSize, PROT_READ | PROT_WRITE) == -1) {
LOGE("mprotect RW failed");
return NULL;
}
targetAddr = trampolineCode + trampolineSize*hookCount + t1Size;
memcpy(targetAddr, trampoline2, sizeof(trampoline2)); // do not use t2size since it's a rounded size

// replace with real entrypoint
#if defined(__i386__)
memcpy(targetAddr+7, &entryPoint, pointer_size);
#elif defined(__arm__)
memcpy(targetAddr+16, &entryPoint, pointer_size);
#endif

if(mprotect(trampolineCode, trampolineCodeSize, PROT_READ | PROT_EXEC) == -1) {
LOGE("mprotect RX failed");
return NULL;
}
return targetAddr;
}

void Java_lab_galaxy_yahfa_HookMain_init(JNIEnv *env, jclass clazz, jint sdkVersion) {
SDKVersion = sdkVersion;
switch(sdkVersion) {
case 25:
case 24:
case ANDROID_N2:
case ANDROID_N:
LOGI("init to SDK %d", sdkVersion);
OFFSET_dex_cache_resolved_methods_in_ArtMethod = 20;
OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod = 32;
OFFSET_entry_point_from_jni_in_ArtMethod = 28;
OFFSET_dex_method_index_in_ArtMethod = 12;
OFFSET_hotness_count_in_ArtMethod = 18;
OFFSET_array_in_PointerArray = 0;
OFFSET_ArtMehod_in_Object = 0;
pointer_size = sizeof(void *);
ArtMethodSize = 36;
hook_new_entry = (void *)hook_new_entry_24;
trampolineSize = t1Size + t2Size;
break;
case 23:
case ANDROID_M:
LOGI("init to SDK %d", sdkVersion);
OFFSET_dex_cache_resolved_methods_in_ArtMethod = 4;
OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod = 36;
Expand All @@ -70,9 +202,9 @@ void Java_lab_galaxy_yahfa_HookMain_init(JNIEnv *env, jclass clazz, jint sdkVers
OFFSET_ArtMehod_in_Object = 0;
pointer_size = sizeof(void *);
ArtMethodSize = 40;
hook_new_entry = (void *)hook_new_entry_23;
trampolineSize = t1Size;
break;
case 22:
case ANDROID_L2:
LOGI("init to SDK %d", sdkVersion);
OFFSET_dex_cache_resolved_methods_in_ArtMethod = 12;
OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod = 44;
Expand All @@ -83,9 +215,9 @@ void Java_lab_galaxy_yahfa_HookMain_init(JNIEnv *env, jclass clazz, jint sdkVers
OFFSET_ArtMehod_in_Object = 8;
pointer_size = sizeof(void *);
ArtMethodSize = 48;
hook_new_entry = (void *)hook_new_entry_22;
trampolineSize = t1Size;
break;
case 21:
case ANDROID_L:
LOGI("init to SDK %d", sdkVersion);
OFFSET_dex_cache_resolved_methods_in_ArtMethod = 12;
OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod = 40;
Expand All @@ -96,15 +228,28 @@ void Java_lab_galaxy_yahfa_HookMain_init(JNIEnv *env, jclass clazz, jint sdkVers
OFFSET_ArtMehod_in_Object = 8;
pointer_size = sizeof(void *);
ArtMethodSize = 72;
hook_new_entry = (void *)hook_new_entry_21;
trampolineSize = t1Size;
break;
default:
LOGE("not compatible with SDK %d", sdkVersion);
break;
}
#if defined(__i386__)
trampoline1[7] = OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod;
#elif defined(__arm__)
trampoline1[4] = OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod;
#endif
}

static void doBackupAndHook(void *originMethod, void *hookMethod, void *backupMethod) {
void doBackupAndHook(void *originMethod, void *hookMethod, void *backupMethod) {
if(hookCount >= hookCap) {
LOGW("not enough capacity");
if(doInitHookCap(DEFAULT_CAP)) {
LOGE("cannot hook method");
return;
}
}

if(!backupMethod) {
LOGW("backup method is null");
}
Expand All @@ -120,25 +265,45 @@ static void doBackupAndHook(void *originMethod, void *hookMethod, void *backupMe
memcpy((void *)((char *)backupMethod+OFFSET_ArtMehod_in_Object),
(void *)((char *)originMethod+OFFSET_ArtMehod_in_Object),
ArtMethodSize-OFFSET_ArtMehod_in_Object);

// if Android N, then use trampoline2 to reset hotness_count
if(SDKVersion >= ANDROID_N) {
void *realEntryPoint = readAddr((char *) originMethod +
OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod);
void *newEntryPoint = genTrampoline2(realEntryPoint);
if(newEntryPoint) {
memcpy((char *) backupMethod + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod,
&newEntryPoint, pointer_size);
}
else {
LOGW("failed to allocate space for backup method trampoline");
}
}
// LOGI("backup method is at %p", backupMethod);
}

// replace entry point
void *newEntrypoint = genTrampoline1(hookMethod);
if(newEntrypoint) {
memcpy((char *) originMethod + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod,
&newEntrypoint,
pointer_size);
}
//do method replacement
//save the hook method at entry_point_from_jni_
memcpy((char *)originMethod+OFFSET_entry_point_from_jni_in_ArtMethod,
&hookMethod,
pointer_size);
//update the entrypoints
memcpy((char *)originMethod+OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod,
&hook_new_entry,
pointer_size);
else {
LOGW("failed to allocate space for trampoline");
}

if(OFFSET_entry_point_from_interpreter_in_ArtMethod != 0) {
memcpy((char *) originMethod + OFFSET_entry_point_from_interpreter_in_ArtMethod,
(char *) hookMethod + OFFSET_entry_point_from_interpreter_in_ArtMethod,
pointer_size);
}

LOGI("hook and backup done");
hookCount += 1;
}

void Java_lab_galaxy_yahfa_HookMain_findAndBackupAndHook(JNIEnv *env, jclass clazz,
void Java_lab_galaxy_yahfa_HookMain_findAndBackupAndHook(JNIEnv *env, jclass clazz,
jclass targetClass, jstring methodName, jstring methodSig, jobject hook, jobject backup) {
if(!methodName || !methodSig) {
LOGE("empty method name or signature");
Expand Down Expand Up @@ -166,7 +331,7 @@ void Java_lab_galaxy_yahfa_HookMain_findAndBackupAndHook(JNIEnv *env, jclass cla
goto end;
}
}
doBackupAndHook(targetMethod, (void *)(*env)->FromReflectedMethod(env, hook),
doBackupAndHook(targetMethod, (void *)(*env)->FromReflectedMethod(env, hook),
backup==NULL ? NULL : (void *)(*env)->FromReflectedMethod(env, backup));

end:
Expand Down
22 changes: 0 additions & 22 deletions library/src/main/jni/entrypoints_arm.S

This file was deleted.

26 changes: 0 additions & 26 deletions library/src/main/jni/entrypoints_x86.S

This file was deleted.

0 comments on commit 7e7ce0e

Please sign in to comment.