Skip to content

Commit

Permalink
update trampoline for redirecting backup method to original target me…
Browse files Browse the repository at this point in the history
…thod
  • Loading branch information
rk700 committed Mar 31, 2020
1 parent debcd06 commit 9824bdd
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@ public static String hook(Object thiz, String a, String b, String c, String d) {
}

public static String backup(Object thiz, String a, String b, String c, String d) {
Log.w(TAG, "ClassWithVirtualMethod.tac() should not be here");
try {
Log.w(TAG, "ClassWithVirtualMethod.tac() should not be here");
}
catch (Exception e) {

}
return "";
}
}

63 changes: 30 additions & 33 deletions library/src/main/jni/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 <dlfcn.h>

#include "common.h"
#include "trampoline.h"
Expand All @@ -11,11 +12,11 @@ static int OFFSET_entry_point_from_interpreter_in_ArtMethod;
int OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod;
static int OFFSET_ArtMehod_in_Object;
static int OFFSET_access_flags_in_ArtMethod;
static size_t ArtMethodSize;
static int kAccNative = 0x0100;
static int kAccCompileDontBother = 0x01000000;
static int kAccFastInterpreterToInterpreterInvoke = 0x40000000;


static inline uint32_t read32(void *addr) {
return *((uint32_t *) addr);
}
Expand All @@ -41,7 +42,6 @@ void Java_lab_galaxy_yahfa_HookMain_init(JNIEnv *env, jclass clazz, jint sdkVers
//OFFSET_dex_method_index_in_ArtMethod = 4*3;
OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod =
roundUpToPtrSize(4 * 4 + 2 * 2) + pointer_size;
ArtMethodSize = roundUpToPtrSize(4 * 4 + 2 * 2) + pointer_size * 2;
break;
case __ANDROID_API_O_MR1__:
kAccCompileDontBother = 0x02000000;
Expand All @@ -50,7 +50,6 @@ void Java_lab_galaxy_yahfa_HookMain_init(JNIEnv *env, jclass clazz, jint sdkVers
OFFSET_access_flags_in_ArtMethod = 4;
OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod =
roundUpToPtrSize(4 * 4 + 2 * 2) + pointer_size * 2;
ArtMethodSize = roundUpToPtrSize(4 * 4 + 2 * 2) + pointer_size * 3;
break;
case __ANDROID_API_N_MR1__:
case __ANDROID_API_N__:
Expand All @@ -59,30 +58,25 @@ void Java_lab_galaxy_yahfa_HookMain_init(JNIEnv *env, jclass clazz, jint sdkVers
// ptr_sized_fields_ is rounded up to pointer_size in ArtMethod
OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod =
roundUpToPtrSize(4 * 4 + 2 * 2) + pointer_size * 3;

ArtMethodSize = roundUpToPtrSize(4 * 4 + 2 * 2) + pointer_size * 4;
break;
case __ANDROID_API_M__:
OFFSET_ArtMehod_in_Object = 0;
OFFSET_entry_point_from_interpreter_in_ArtMethod = roundUpToPtrSize(4 * 7);
OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod =
OFFSET_entry_point_from_interpreter_in_ArtMethod + pointer_size * 2;
ArtMethodSize = roundUpToPtrSize(4 * 7) + pointer_size * 3;
break;
case __ANDROID_API_L_MR1__:
OFFSET_ArtMehod_in_Object = 4 * 2;
OFFSET_entry_point_from_interpreter_in_ArtMethod = roundUpToPtrSize(
OFFSET_ArtMehod_in_Object + 4 * 7);
OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod =
OFFSET_entry_point_from_interpreter_in_ArtMethod + pointer_size * 2;
ArtMethodSize = OFFSET_entry_point_from_interpreter_in_ArtMethod + pointer_size * 3;
break;
case __ANDROID_API_L__:
OFFSET_ArtMehod_in_Object = 4 * 2;
OFFSET_entry_point_from_interpreter_in_ArtMethod = OFFSET_ArtMehod_in_Object + 4 * 4;
OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod =
OFFSET_entry_point_from_interpreter_in_ArtMethod + 8 * 2;
ArtMethodSize = OFFSET_ArtMehod_in_Object + 4 * 4 + 8 * 4 + 4 * 4;
break;
default:
LOGE("not compatible with SDK %d", sdkVersion);
Expand All @@ -97,12 +91,14 @@ static void setNonCompilable(void *method) {
return;
}
int access_flags = read32((char *) method + OFFSET_access_flags_in_ArtMethod);
LOGI("setNonCompilable: access flags is 0x%x", access_flags);
int old_flags = access_flags;
access_flags |= kAccCompileDontBother;
write32((char *) method + OFFSET_access_flags_in_ArtMethod, access_flags);
LOGI("setNonCompilable: change access flags from 0x%x to 0x%x", old_flags, access_flags);

}

static int doHook(void *targetMethod, void *hookMethod) {
static int replaceMethod(void *fromMethod, void *toMethod, int isBackup) {
if (hookCount >= hookCap) {
LOGI("not enough capacity. Allocating...");
if (doInitHookCap(DEFAULT_CAP)) {
Expand All @@ -112,16 +108,24 @@ static int doHook(void *targetMethod, void *hookMethod) {
LOGI("Allocating done");
}

LOGI("target method is at %p, hook method is at %p", targetMethod, hookMethod);
LOGI("replace method from %p to %p", fromMethod, toMethod);

// 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),
void *newEntrypoint = NULL;
if(isBackup) {
void *originEntrypoint = readAddr((char *) toMethod + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod);
newEntrypoint = genTrampoline(toMethod, originEntrypoint);
}
else {
newEntrypoint = genTrampoline(toMethod, NULL);
}

LOGI("replace entry point from %p to %p",
readAddr((char *) fromMethod + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod),
newEntrypoint
);
if (newEntrypoint) {
memcpy((char *) targetMethod + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod,
memcpy((char *) fromMethod + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod,
&newEntrypoint,
pointer_size);
} else {
Expand All @@ -130,24 +134,24 @@ static int doHook(void *targetMethod, void *hookMethod) {
}

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

// set the target method to native so that Android O wouldn't invoke it with interpreter
if (SDKVersion >= __ANDROID_API_O__) {
int access_flags = read32((char *) targetMethod + OFFSET_access_flags_in_ArtMethod);
int access_flags = read32((char *) fromMethod + OFFSET_access_flags_in_ArtMethod);
int old_flags = access_flags;
access_flags |= kAccNative;
if (SDKVersion >= __ANDROID_API_Q__) {
// 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("access flags is 0x%x", access_flags);
write32((char *) fromMethod + OFFSET_access_flags_in_ArtMethod, access_flags);
LOGI("change access flags from 0x%x to 0x%x", old_flags, access_flags);
}

LOGI("hook done");
hookCount += 1;
return 0;

Expand All @@ -163,24 +167,17 @@ static int doBackupAndHook(void *targetMethod, void *hookMethod, void *backupMet
// so that we don't need to worry about hotness_count_
if (SDKVersion >= __ANDROID_API_N__) {
setNonCompilable(targetMethod);
setNonCompilable(hookMethod);
// setNonCompilable(hookMethod);
if(backupMethod) setNonCompilable(backupMethod);
}

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
void *backupMethod_raw = malloc(ArtMethodSize);
memcpy(backupMethod_raw, targetMethod, ArtMethodSize);

res += doHook(backupMethod, backupMethod_raw);
// we use the same way as hooking target method
// hook backup method and redirect back to the original target method
res += replaceMethod(backupMethod, targetMethod, 1);
}

res += doHook(targetMethod, hookMethod);
res += replaceMethod(targetMethod, hookMethod, 0);

LOGI("hook and backup done");
return res;
Expand Down
109 changes: 98 additions & 11 deletions library/src/main/jni/trampoline.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,22 @@
#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

unsigned int hookCap = 0;
unsigned int hookCount = 0;

// trampoline:
// 1. set eax/r0/x0 to the hook ArtMethod addr
// 1. set eax/rdi/r0/x0 to the hook ArtMethod addr
// 2. jump into its entry point

// trampoline for backup:
// 1. set eax/rdi/r0/x0 to the target ArtMethod addr
// 2. ret to the original entry point

#if defined(__i386__)
// b8 78 56 34 12 ; mov eax, 0x12345678 (addr of the hook method)
// ff 70 20 ; push DWORD PTR [eax + 0x20]
Expand All @@ -27,6 +34,15 @@ unsigned char trampoline[] = {
0xc3
};

// b8 78 56 34 12 ; mov eax, 0x12345678 (addr of the target method)
// 68 78 56 34 12 ; push 0x12345678 (original entry point of the target method)
// c3 ; ret
unsigned char trampolineForBackup[] = {
0xb8, 0x78, 0x56, 0x34, 0x12,
0x68, 0x78, 0x56, 0x34, 0x12,
0xc3
};

#elif defined(__x86_64__)
// 48 bf 78 56 34 12 78 56 34 12 ; movabs rdi, 0x1234567812345678
// ff 77 20 ; push QWORD PTR [rdi + 0x20]
Expand All @@ -37,6 +53,17 @@ unsigned char trampoline[] = {
0xc3
};

// 48 bf 78 56 34 12 78 56 34 12 ; movabs rdi, 0x1234567812345678
// 57 ; push rdi (original entry point of the target method)
// 48 bf 78 56 34 12 78 56 34 12 ; movabs rdi, 0x1234567812345678 (addr of the target method)
// c3 ; ret
unsigned char trampolineForBackup[] = {
0x48, 0xbf, 0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12,
0x57,
0x48, 0xbf, 0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12,
0xc3
};

#elif defined(__arm__)
// 00 00 9F E5 ; ldr r0, [pc, #0]
// 20 F0 90 E5 ; ldr pc, [r0, 0x20]
Expand All @@ -47,6 +74,21 @@ unsigned char trampoline[] = {
0x78, 0x56, 0x34, 0x12
};

// 0c 00 9F E5 ; ldr r0, [pc, #12]
// 01 00 2d e9 ; push {r0}
// 00 00 9F E5 ; ldr r0, [pc, #0]
// 00 80 bd e8 ; pop {pc}
// 78 56 34 12 ; 0x12345678 (addr of the hook method)
// 78 56 34 12 ; 0x12345678 (original entry point of the target method)
unsigned char trampolineForBackup[] = {
0x0c, 0x00, 0x9f, 0xe5,
0x01, 0x00, 0x2d, 0xe9,
0x00, 0x00, 0x9f, 0xe5,
0x00, 0x80, 0xbd, 0xe8,
0x78, 0x56, 0x34, 0x12,
0x78, 0x56, 0x34, 0x12
};

#elif defined(__aarch64__)
// 60 00 00 58 ; ldr x0, 12
// 10 00 40 F8 ; ldr x16, [x0, #0x00]
Expand All @@ -60,28 +102,73 @@ unsigned char trampoline[] = {
0x78, 0x56, 0x34, 0x12,
0x89, 0x67, 0x45, 0x23
};

// 60 00 00 58 ; ldr x0, 12
// 90 00 00 58 ; ldr x16, 16
// 00 02 1f d6 ; br x16
// 78 56 34 12
// 89 67 45 23 ; 0x2345678912345678 (addr of the hook method)
// 78 56 34 12
// 89 67 45 23 ; 0x2345678912345678 (original entry point of the target method)
unsigned char trampolineForBackup[] = {
0x60, 0x00, 0x00, 0x58,
0x90, 0x00, 0x00, 0x58,
0x00, 0x02, 0x1f, 0xd6,
0x78, 0x56, 0x34, 0x12,
0x89, 0x67, 0x45, 0x23,
0x78, 0x56, 0x34, 0x12,
0x89, 0x67, 0x45, 0x23
};
#endif
static unsigned int trampolineSize = roundUpToPtrSize(sizeof(trampoline));
static unsigned int trampolineSize = roundUpToPtrSize(MAX(sizeof(trampoline), sizeof(trampolineForBackup)));

void *genTrampoline(void *hookMethod) {
void *targetAddr;
void *genTrampoline(void *toMethod, void *entrypoint) {
unsigned char *targetAddr = trampolineCode + trampolineSize * hookCount;

targetAddr = trampolineCode + trampolineSize * hookCount;
memcpy(targetAddr, trampoline,
sizeof(trampoline)); // do not use trampolineSize since it's a rounded size
if(entrypoint != NULL) {
memcpy(targetAddr, trampolineForBackup, sizeof(trampolineForBackup));
}
else {
memcpy(targetAddr, trampoline,
sizeof(trampoline)); // do not use trampolineSize since it's a rounded size
}

// replace with the actual ArtMethod addr
#if defined(__i386__)
memcpy(targetAddr + 1, &hookMethod, pointer_size);
if(entrypoint) {
memcpy(targetAddr + 1, &toMethod, pointer_size);
memcpy(targetAddr + 6, &entrypoint, pointer_size);
}
else {
memcpy(targetAddr + 1, &toMethod, pointer_size);
}

#elif defined(__x86_64__)
memcpy((char*)targetAddr + 2, &hookMethod, pointer_size);
if(entrypoint) {
memcpy(targetAddr + 2, &entrypoint, pointer_size);
memcpy(targetAddr + 13, &toMethod, pointer_size);
}
else {
memcpy(targetAddr + 2, &toMethod, pointer_size);
}

#elif defined(__arm__)
memcpy(targetAddr+8, &hookMethod, pointer_size);
if(entrypoint) {
memcpy(targetAddr + 20, &entrypoint, pointer_size);
memcpy(targetAddr + 16, &toMethod, pointer_size);
}
else {
memcpy(targetAddr + 8, &toMethod, pointer_size);
}

#elif defined(__aarch64__)
memcpy(targetAddr + 12, &hookMethod, pointer_size);
if(entrypoint) {
memcpy(targetAddr + 20, &entrypoint, pointer_size);
memcpy(targetAddr + 12, &toMethod, pointer_size);
}
else {
memcpy(targetAddr + 12, &toMethod, pointer_size);
}

#else
#error Unsupported architecture
Expand Down
2 changes: 1 addition & 1 deletion library/src/main/jni/trampoline.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ int doInitHookCap(unsigned int cap);

void setupTrampoline();

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

#define DEFAULT_CAP 100 //size of each trampoline area would be no more than 4k Bytes(one page)

Expand Down

0 comments on commit 9824bdd

Please sign in to comment.