Skip to content

Commit

Permalink
rollback to using cached method
Browse files Browse the repository at this point in the history
  • Loading branch information
rk700 committed Dec 6, 2017
1 parent 5e79fd9 commit a929fce
Show file tree
Hide file tree
Showing 9 changed files with 127 additions and 183 deletions.
1 change: 1 addition & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 3 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ YAHFA is a hook framework for Android ART. It provides an efficient way for Java
- Android 5.0(API 21)
- Android 5.1(API 22)
- Android 6.0(API 23)
- __EXPERIMENTAL__ Android 7.0(API 24)
- __EXPERIMENTAL__ Android 7.1(API 25)
- Android 7.0(API 24)
- Android 7.1(API 25)
- Android 8.0(API 26)

with ABI:

Expand Down Expand Up @@ -74,40 +75,6 @@ Here the value of register `lr` is hardcoded instead of reading from entry point

A simple workaround is to build the APP with debuggable option on, in which case the inlining optimization will not apply. However the option `--debuggable` of `dex2oat` is not available until API 23. So please take a look at machine instructions of the target when the hook doesn't work.

## Hooking JNI methods

JNI methods can be hooked without calling origin method. For example, the target App contains the following JNI method:

```java
package lab.galaxy.yahfa.demoApp;

public class ClassWithJNIMethod {
static {
System.loadLibrary("hello");
}

public native static String fromJNI();
}
```
Then the method `fromJNI` can be hooked with the following plugin code:

```java
public class Hook_ClassWithJNIMethod_fromJNI {
public static String className = "lab.galaxy.yahfa.demoApp.ClassWithJNIMethod";
public static String methodName = "fromJNI";
public static String methodSig = "()Ljava/lang/String;";

public static String hook() {
Log.w("YAHFA", "calling fromJNI");
return "new string";
}
}
```

## Android N Support

Support for Android N(7.0 and 7.1) is experimental and not stable.

## License

YAHFA is distributed under GNU GPL V3.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@

public class ClassWithStaticMethod {
public static String tac(String a, String b, String c, String d) {
return d+c+b+a;
try {
return d + c + b + a;
}
catch (Exception e) {
e.printStackTrace();
return "";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@

public class ClassWithVirtualMethod {
public String tac(String a, String b, String c, String d) {
return d+c+b+a;
try {
return d + c + b + a;
}
catch (Exception e) {
e.printStackTrace();
return "";
}
}
}
8 changes: 4 additions & 4 deletions demoPlugin/src/main/java/lab/galaxy/yahfa/HookInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@

public class HookInfo {
public static String[] hookItemNames = {
// "lab.galaxy.yahfa.demoPlugin.Hook_Log_e",
// "lab.galaxy.yahfa.demoPlugin.Hook_String_startsWith",
"lab.galaxy.yahfa.demoPlugin.Hook_Log_e",
"lab.galaxy.yahfa.demoPlugin.Hook_String_startsWith",
"lab.galaxy.yahfa.demoPlugin.Hook_ClassWithVirtualMethod_tac",
// "lab.galaxy.yahfa.demoPlugin.Hook_ClassWithStaticMethod_tac",
// "lab.galaxy.yahfa.demoPlugin.Hook_ClassWithJNIMethod_fromJNI"
"lab.galaxy.yahfa.demoPlugin.Hook_ClassWithStaticMethod_tac",
"lab.galaxy.yahfa.demoPlugin.Hook_ClassWithJNIMethod_fromJNI"
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ public class Hook_ClassWithStaticMethod_tac {

public static String hook(String a, String b, String c, String d) {
Log.w("YAHFA", "in ClassWithStaticMethod.tac(): "+a+", "+b+", "+c+", "+d);
return backup(a, b, c, d);
return "test"+a;
}

/*
public static String backup(String a, String b, String c, String d) {
Log.w("YAHFA", "ClassWithStaticMethod.tac() should not be here");
return "";
}
*/
}
66 changes: 34 additions & 32 deletions library/src/main/jni/HookMain.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ static int OFFSET_dex_method_index_in_ArtMethod;
static int OFFSET_dex_cache_resolved_methods_in_ArtMethod;
static int OFFSET_array_in_PointerArray;
static int OFFSET_ArtMehod_in_Object;
static int OFFSET_access_flags_in_ArtMethod;
static int ArtMethodSize;
static int kAccNative = 0x0100;

static inline uint16_t read16(void *addr) {
return *((uint16_t *)addr);
Expand All @@ -36,7 +38,11 @@ void Java_lab_galaxy_yahfa_HookMain_init(JNIEnv *env, jclass clazz, jint sdkVers
switch(sdkVersion) {
case ANDROID_O:
OFFSET_ArtMehod_in_Object = 0;
OFFSET_access_flags_in_ArtMethod = 4;
OFFSET_hotness_count_in_ArtMethod = 4*4+2;
OFFSET_dex_method_index_in_ArtMethod = 4*3;
OFFSET_dex_cache_resolved_methods_in_ArtMethod = roundUpToPtrSize(4*4+2*2);
OFFSET_array_in_PointerArray = 0;
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;
Expand Down Expand Up @@ -70,45 +76,27 @@ void Java_lab_galaxy_yahfa_HookMain_init(JNIEnv *env, jclass clazz, jint sdkVers
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;
OFFSET_dex_method_index_in_ArtMethod = OFFSET_ArtMehod_in_Object + 4*5;
OFFSET_dex_cache_resolved_methods_in_ArtMethod = OFFSET_ArtMehod_in_Object + 4;
OFFSET_array_in_PointerArray = 12;
ArtMethodSize = OFFSET_entry_point_from_interpreter_in_ArtMethod+pointer_size*3;
break;
case ANDROID_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;
OFFSET_dex_method_index_in_ArtMethod = OFFSET_ArtMehod_in_Object+4*4+8*4+4*2;
OFFSET_dex_cache_resolved_methods_in_ArtMethod = OFFSET_ArtMehod_in_Object+4;
OFFSET_array_in_PointerArray = 12;
ArtMethodSize = OFFSET_ArtMehod_in_Object+4*4+8*4+4*4;
break;
default:
LOGE("not compatible with SDK %d", sdkVersion);
break;
}

/*
#if defined(__i386__)
trampoline1[18] = OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod;
if(SDKVersion < ANDROID_N) { // do not set hotness_count before N
memset(trampoline1, '\x90', 11);
}
#elif defined(__arm__)
trampoline1[4] = (unsigned char)OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod;
if(SDKVersion < ANDROID_N) { // do not set hotness_count before N
for(i=4; i<=16; i+=4) {
memcpy(trampoline2+i, "\x00\x00\xa0\xe1", 4); // mov r0, r0
}
}
#elif defined(__aarch64__)
if(SDKVersion < ANDROID_N) { // do not set hotness_count before N
memcpy(trampoline2+4, "\x1f\x20\x03\xd5", 4); // nop
if(SDKVersion == ANDROID_L2) {
memcpy(trampoline1+4, "\x10\x1c\x40\xf9", 4); //101c40f9 ; ldr x16, [x0, #56] set entry point offset
}
else if(SDKVersion == ANDROID_L) {
memcpy(trampoline1+4, "\x10\x14\x40\xf9", 4); //101440f9 ; ldr x16, [x0, #40] set entry point offset
}
}
#endif
*/
setupTrampoline();
}

static int doBackupAndHook(void *targetMethod, void *hookMethod, void *backupMethod) {
Expand All @@ -127,13 +115,15 @@ static int doBackupAndHook(void *targetMethod, void *hookMethod, void *backupMet
if(!backupMethod) {
LOGW("Origin method is null. Cannot call origin");
}
else { //do method backup
//first update the cached method manually
void *dexCacheResolvedMethods = (void *) readAddr((void *) ((char *) hookMethod +
OFFSET_dex_cache_resolved_methods_in_ArtMethod));
else { // do method backup
// update the cached method manually
// first we find the array of cached methods
void *dexCacheResolvedMethods = (void *) readAddr(
(void *) ((char *) hookMethod + OFFSET_dex_cache_resolved_methods_in_ArtMethod));
// then we get the dex method index of the static backup method
int methodIndex = read32((void *) ((char *) backupMethod + OFFSET_dex_method_index_in_ArtMethod));
memcpy((char *) dexCacheResolvedMethods + OFFSET_array_in_PointerArray +
pointer_size * methodIndex,
// finally the addr of backup method is put at the corresponding location in cached methods array
memcpy((char *) dexCacheResolvedMethods + OFFSET_array_in_PointerArray + pointer_size * methodIndex,
(&backupMethod),
pointer_size);

Expand All @@ -147,7 +137,7 @@ static int doBackupAndHook(void *targetMethod, void *hookMethod, void *backupMet
}

// replace entry point
void *newEntrypoint = genTrampoline1(hookMethod, backupMethod);
void *newEntrypoint = genTrampoline(hookMethod, backupMethod);
LOGI("origin ep is %p, new ep is %p",
readAddr((char *) targetMethod + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod),
newEntrypoint
Expand All @@ -168,6 +158,18 @@ static int doBackupAndHook(void *targetMethod, void *hookMethod, void *backupMet
pointer_size);
}

// 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);
LOGE("access flags is 0x%x", access_flags);
access_flags |= kAccNative;
memcpy(
(char *) targetMethod + OFFSET_access_flags_in_ArtMethod,
&access_flags,
4
);
}

LOGI("hook and backup done");
hookCount += 1;
return 0;
Expand Down
Loading

0 comments on commit a929fce

Please sign in to comment.