├── Android.mk ├── README.md ├── arm_debug.S ├── hook-arm32.S ├── hook-arm64.S ├── hook-asm.h ├── hook.h ├── init.cpp ├── jnihelp.h ├── log.h ├── main.cpp ├── method-entries.asm ├── method-list.def ├── stub.cpp └── user-callback.cpp /Android.mk: -------------------------------------------------------------------------------- 1 | 2 | LOCAL_PATH := $(call my-dir) 3 | include $(CLEAR_VARS) 4 | 5 | enable_debug64:=false 6 | enable_debug32:=false 7 | 8 | 9 | LOCAL_C_INCLUDES := \ 10 | $(JNI_H_INCLUDE) \ 11 | 12 | ifneq ($(shell expr $(PLATFORM_SDK_VERSION) \>= 21),0) 13 | include art/build/Android.common_build.mk 14 | LOCAL_C_INCLUDES += external/libcxx/include 15 | ART_UTILS_SHARED_LIBS += libc++ 16 | ART_UTILS_SHARED_LIBS += libart 17 | LOCAL_CFLAGS += $(MTK_CFLAGS) 18 | else 19 | include art/build/Android.common.mk 20 | LOCAL_C_INCLUDES += external/stlport/stlport \ 21 | bionic \ 22 | bionic/libstdc++/include 23 | LOCAL_CFLAGS += -DANDROID_SMP=1 24 | endif 25 | 26 | LOCAL_C_INCLUDES += art/runtime \ 27 | $(ART_C_INCLUDES) 28 | 29 | SOURCE_FLAGS:= -DPLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION) 30 | 31 | ifeq (true,$(enable_debug64)) 32 | SOURCE_FLAGS += -DENABLE_DEBUG64 33 | endif 34 | 35 | ifeq (true,$(enable_debug32)) 36 | SOURCE_FLAGS += -DENABLE_DEBUG32 37 | endif 38 | 39 | ASM_SRC_FILES:= \ 40 | hook-arm64.S \ 41 | hook-arm32.S 42 | 43 | LOCAL_SRC_FILES := \ 44 | main.cpp \ 45 | $(ASM_SRC_FILES) \ 46 | stub.cpp \ 47 | init.cpp \ 48 | user-callback.cpp 49 | 50 | LOCAL_SHARED_LIBRARIES := liblog $(ART_UTILS_SHARED_LIBS) 51 | LOCAL_MODULE_TAGS := optional 52 | LOCAL_MODULE := libzygotehook 53 | LOCAL_CLANG := true 54 | LOCAL_CFLAGS := -Werror $(SOURCE_FLAGS) $(MTK_CFLAGS) \ 55 | -DIMT_SIZE=64 \ 56 | $(ART_TARGET_CFLAGS) 57 | 58 | LOCAL_ASFLAGS:= -Werror $(SOURCE_FLAGS) $(MTK_CFLAGS) 59 | 60 | #LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk 61 | 62 | include $(BUILD_SHARED_LIBRARY) 63 | 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zygote_hook 2 | zygote hook for art in android 6.0 and more 3 | -------------------------------------------------------------------------------- /arm_debug.S: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2014, Xiaomi Inc. All rights reserved. 3 | */ 4 | 5 | 6 | #ifdef __LP64__ 7 | .macro PUSH_REGS 8 | stp x29, x30, [sp, #-176]! 9 | stp x0, x1, [sp, #16] 10 | stp x2, x3, [sp, #32] 11 | stp x4, x5, [sp, #48] 12 | stp x6, x7, [sp, #64] 13 | stp x8, x9, [sp, #80] 14 | stp x10, x11, [sp, #96] 15 | stp x12, x13, [sp, #112] 16 | stp x14, x15, [sp, #128] 17 | stp x16, x17, [sp, #144] 18 | stp x18, x19, [sp, #160] 19 | .endm 20 | .macro POP_REGS 21 | ldp x0, x1, [sp, #16] 22 | ldp x2, x3, [sp, #32] 23 | ldp x4, x5, [sp, #48] 24 | ldp x6, x7, [sp, #64] 25 | ldp x8, x9, [sp, #80] 26 | ldp x10, x11, [sp, #96] 27 | ldp x12, x13, [sp, #112] 28 | ldp x14, x15, [sp, #128] 29 | ldp x16, x17, [sp, #144] 30 | ldp x18, x19, [sp, #160] 31 | ldp x29, x30, [sp], 176 32 | .endm 33 | 34 | #define BL bl 35 | 36 | #define DEBUG_SAVED_REG_SIZE 176 37 | 38 | #else 39 | .macro PUSH_REGS 40 | push {r0-r12, lr} 41 | .endm 42 | 43 | .macro POP_REGS 44 | pop {r0-r12, lr} 45 | .endm 46 | 47 | #define DEBUG_SAVED_REG_SIZE 32 48 | #define BL blx 49 | #endif 50 | 51 | #ifdef ENABLE_DEBUG 52 | /* w0 is index, x1 is reg value */ 53 | .type show_reg, %function 54 | .global print_reg 55 | show_reg: 56 | PUSH_REGS 57 | BL print_reg 58 | POP_REGS 59 | #ifdef __LP64__ 60 | ret 61 | #else 62 | bx lr 63 | #endif 64 | .size show_reg, .-show_reg 65 | 66 | .global show_r0_7 67 | .macro SHOW_R0_7 68 | PUSH_REGS 69 | BL show_r0_7 70 | POP_REGS 71 | .endm 72 | 73 | .global show_sp_values 74 | .global show_mem_values 75 | .global show_call_hook_args 76 | .global show_call_original_args 77 | .global entry_call 78 | .global exit_call 79 | 80 | #ifdef __LP64__ 81 | .macro SHOW_XREG idx 82 | stp x0, x1, [sp, #-32]! 83 | stp x29,x30, [sp, 16] 84 | mov x1, x\idx 85 | mov w0, #\idx 86 | BL show_reg 87 | ldp x29, x30, [sp, 16] 88 | ldp x0, x1, [sp], 32 89 | .endm 90 | .macro SHOW_CALL_HOOK_ARGS pMethod, pHandlerArgs, pOriginalArgs 91 | PUSH_REGS 92 | mov x0, \pMethod 93 | mov x1, \pHandlerArgs 94 | mov x2, \pOriginalArgs 95 | BL show_call_hook_args 96 | POP_REGS 97 | .endm 98 | .macro SHOW_CALL_ORIGINAL_ARGS pMethod, pOriginalArgs, pCallerArgs 99 | PUSH_REGS 100 | mov x0, \pMethod 101 | mov x1, \pOriginalArgs 102 | mov x2, \pCallerArgs 103 | BL show_call_original_args 104 | POP_REGS 105 | .endm 106 | .macro ENTRY_CALL type 107 | PUSH_REGS 108 | mov x0, PMethodItem 109 | mov w1, \type 110 | BL entry_call 111 | POP_REGS 112 | .endm 113 | .macro EXIT_CALL type 114 | PUSH_REGS 115 | mov x0, x22 116 | mov w1, \type 117 | BL exit_call 118 | POP_REGS 119 | .endm 120 | 121 | .macro SHOW_SP_VALUES count 122 | PUSH_REGS 123 | mov x0, sp 124 | add x0, x0, DEBUG_SAVED_REG_SIZE 125 | mov w1, \count 126 | BL show_sp_values 127 | POP_REGS 128 | .endm 129 | 130 | .macro SHOW_MEM_VALUES reg, count 131 | PUSH_REGS 132 | mov x0, x\reg 133 | mov w1, \count 134 | mov w2, \reg 135 | BL show_mem_values 136 | POP_REGS 137 | .endm 138 | 139 | #else 140 | .macro SHOW_XREG idx 141 | PUSH_REGS 142 | mov r1, r\idx 143 | mov r0, #\idx 144 | BL print_reg 145 | POP_REGS 146 | .endm 147 | .macro SHOW_CALL_HOOK_ARGS 148 | PUSH_REGS 149 | mov r0, PMethodItem 150 | add r1, sp, #32 + STACK_REFERENCE_SIZE 151 | add r2, SavedSP, #SAVED_REG_SIZE + STACK_REFERENCE_SIZE 152 | BL show_call_hook_args 153 | POP_REGS 154 | .endm 155 | .macro SHOW_CALL_ORIGINAL_ARGS 156 | PUSH_REGS 157 | mov r0, PMethodItem 158 | add r1, sp, #32 + STACK_REFERENCE_SIZE 159 | add r2, SavedSP, #SAVED_REG_SIZE + STACK_REFERENCE_SIZE 160 | BL show_call_original_args 161 | POP_REGS 162 | .endm 163 | .macro ENTRY_CALL type 164 | PUSH_REGS 165 | mov r0, PMethodItem 166 | mov r1, \type 167 | BL entry_call 168 | POP_REGS 169 | .endm 170 | .macro EXIT_CALL type 171 | PUSH_REGS 172 | mov r0, PMethodItem 173 | mov r1, \type 174 | BL exit_call 175 | POP_REGS 176 | .endm 177 | 178 | .macro SHOW_SP_VALUES count 179 | PUSH_REGS 180 | mov r0, sp 181 | add r0, DEBUG_SAVED_REG_SIZE 182 | mov r1, \count 183 | BL show_sp_values 184 | POP_REGS 185 | .endm 186 | 187 | .macro SHOW_MEM_VALUES reg, count 188 | PUSH_REGS 189 | mov r0, r\reg 190 | mov r1, \count 191 | mov r2, \reg 192 | BL show_mem_values 193 | POP_REGS 194 | .endm 195 | 196 | #endif 197 | 198 | .macro SHOW_SP 199 | PUSH_REGS 200 | mov x1, sp 201 | mov x0, 32 202 | add x1, x1, DEBUG_SAVED_REG_SIZE 203 | BL show_reg 204 | POP_REGS 205 | .endm 206 | #else 207 | .macro SHOW_XREG idx 208 | .endm 209 | .macro SHOW_CALL_HOOK_ARGS 210 | .endm 211 | .macro SHOW_CALL_ORIGINAL_ARGS 212 | .endm 213 | .macro SHOW_R0_7 214 | .endm 215 | .macro ENTRY_CALL type 216 | .endm 217 | .macro EXIT_CALL type 218 | .endm 219 | .macro SHOW_SP 220 | .endm 221 | 222 | #endif 223 | 224 | -------------------------------------------------------------------------------- /hook-arm32.S: -------------------------------------------------------------------------------- 1 | #if !defined(__LP64__) && defined(__arm__) 2 | 3 | .file "hook-arm32.S" 4 | .arch armv7-a 5 | .thumb 6 | 7 | .text 8 | 9 | #include "hook-asm.h" 10 | 11 | .global call_user_callback 12 | 13 | #if PLATFORM_SDK_VERSION >= 23 14 | #define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 112 15 | #else 16 | #define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 176 17 | #endif 18 | 19 | #define PThreadSelf r9 20 | #define PMethodItem r8 21 | 22 | #include "arm_debug.S" 23 | 24 | .macro access_r0_r3 access, arg_mem, temp1, temp2 25 | 26 | ldr r\temp1, [PMethodItem, #METHOD_ITEM_R0_R3_OFFSET] 27 | SHOW_XREG \temp1 28 | mov r\temp2, r\temp1 29 | and r\temp2, 0xff 30 | cmp r\temp2, 0xff 31 | beq 1f 32 | \access r0, [r\arg_mem, r\temp2] 33 | 1: 34 | mov r\temp2, r\temp1, lsr #8 // rtemp2 = rtemp >> 8 35 | and r\temp2, 0xff 36 | cmp r\temp2, 0xff 37 | beq 2f 38 | \access r1, [r\arg_mem, r\temp2] 39 | 2: 40 | mov r\temp2, r\temp1, lsr #16 41 | and r\temp2, 0xff 42 | cmp r\temp2, 0xff 43 | beq 3f 44 | \access r2, [r\arg_mem, r\temp2] 45 | 3: 46 | mov r\temp2, r\temp1, lsr #24 47 | and r\temp2, 0xff 48 | cmp r\temp2, 0xff 49 | beq 4f 50 | \access r3, [r\arg_mem, r\temp2] 51 | 4: 52 | .endm 53 | 54 | .macro access_sx freg, name, access, rarg_mem, roffset, rhandle, rtemp 55 | .balign 16 56 | add \rtemp, \rarg_mem, \roffset 57 | \access \freg, [\rtemp] 58 | add \rhandle, 16 59 | b .Laccess_s0_s15_run_\name 60 | .endm 61 | 62 | .macro access_s0_s15 name, access, roffset, rarg_mem, rhandle, rchr, rhandle_count, rtemp 63 | #if PLATFORM_SDK_VERSION >= 23 64 | add \roffset, PMethodItem, #METHOD_ITEM_S0_S15_OFFSET 65 | 66 | adr \rhandle, .Laccess_s0_s15_\name 67 | mov \rhandle_count, 0 68 | 69 | .Laccess_s0_s15_run_\name: 70 | cmp \rhandle_count, #16 * 16 71 | beq .Laccess_s0_s15_end_\name 72 | 73 | ldrb \rchr, [\roffset], 1 74 | and \rchr, 0xff 75 | 76 | cmp \rchr, 0xff 77 | beq .Laccess_s0_s15_end_\name 78 | 79 | cmp \rchr, 0xfe //skip me 80 | beq .Laccess_s0_s15_run_\name 81 | 82 | 83 | add \rhandle_count, 16 84 | bx \rhandle 85 | 86 | 87 | .balign 16 88 | .Laccess_s0_s15_\name: 89 | access_sx s0, \name, \access, \rarg_mem, \rchr, \rhandle, \rtemp 90 | access_sx s1, \name, \access, \rarg_mem, \rchr, \rhandle, \rtemp 91 | access_sx s2, \name, \access, \rarg_mem, \rchr, \rhandle, \rtemp 92 | access_sx s3, \name, \access, \rarg_mem, \rchr, \rhandle, \rtemp 93 | access_sx s4, \name, \access, \rarg_mem, \rchr, \rhandle, \rtemp 94 | access_sx s5, \name, \access, \rarg_mem, \rchr, \rhandle, \rtemp 95 | access_sx s6, \name, \access, \rarg_mem, \rchr, \rhandle, \rtemp 96 | access_sx s7, \name, \access, \rarg_mem, \rchr, \rhandle, \rtemp 97 | access_sx s8, \name, \access, \rarg_mem, \rchr, \rhandle, \rtemp 98 | access_sx s9, \name, \access, \rarg_mem, \rchr, \rhandle, \rtemp 99 | access_sx s10, \name, \access, \rarg_mem, \rchr, \rhandle, \rtemp 100 | access_sx s11, \name, \access, \rarg_mem, \rchr, \rhandle, \rtemp 101 | access_sx s12, \name, \access, \rarg_mem, \rchr, \rhandle, \rtemp 102 | access_sx s13, \name, \access, \rarg_mem, \rchr, \rhandle, \rtemp 103 | access_sx s14, \name, \access, \rarg_mem, \rchr, \rhandle, \rtemp 104 | access_sx s15, \name, \access, \rarg_mem, \rchr, \rhandle, \rtemp 105 | .Laccess_s0_s15_end_\name: 106 | #endif 107 | .endm 108 | 109 | 110 | .balign 16 111 | .type hook_entry, %function 112 | hook_entry: 113 | push {r4-r11} 114 | #if PLATFORM_SDK_VERSION >= 23 115 | vpush {s16-s31} 116 | #else 117 | vpush {s0-s31} 118 | #endif 119 | sub sp, #12 120 | 121 | 122 | SHOW_XREG 9 123 | 124 | .global get_method_item 125 | 126 | //load g_method_item 127 | push {r0-r3} 128 | mov r0, lr 129 | bl get_method_item 130 | mov PMethodItem, r0 131 | pop {r0-r3} 132 | 133 | ENTRY_CALL 0 134 | SHOW_XREG 8 135 | 136 | //save r0~r3 137 | mov r7, sp 138 | add r7, FRAME_SIZE_SAVE_ALL_CALLEE_SAVE + STACK_REFERENCE_SIZE 139 | access_r0_r3 str, 7, 5, 6 140 | access_s0_s15 hook_entry, vstr, r5, r7, r4, r6, r3, r2 141 | 142 | //alloc result 143 | sub sp, 16 144 | mov r0, 0 145 | str r0, [sp, 8] 146 | str r0, [sp, 12] 147 | 148 | //call user callback 149 | mov r0, PMethodItem 150 | add r1, sp, FRAME_SIZE_SAVE_ALL_CALLEE_SAVE + STACK_REFERENCE_SIZE + 16 151 | add r2, sp, 16 //runtime method pointer 152 | add r3, sp, 8 153 | str r3, [sp] //save the result pointer 154 | mov r3, PThreadSelf 155 | bl call_user_callback 156 | 157 | //load result type 158 | ldr r4, [PMethodItem, #METHOD_ITEM_SHORTY] 159 | ldrb r5, [r4] 160 | 161 | cmp r5, #'D' 162 | bne 1f 163 | vldr d0, [sp, 8] 164 | b 3f 165 | 1: 166 | cmp r5, #'F' 167 | bne 2f 168 | vldr s0, [sp, 8] 169 | b 3f 170 | 2: 171 | ldr r0, [sp, 8] 172 | ldr r1, [sp, 12] 173 | 3: 174 | add sp, 16+12 175 | 176 | EXIT_CALL 0 177 | 178 | ldr lr, [PThreadSelf, #THREAD_EXCEPTION_OFFSET] 179 | cmp lr, 0 180 | bne .Lhook_entry_throw_exception 181 | 182 | #if PLATFORM_SDK_VERSION >= 23 183 | vpop {s16-s31} 184 | #else 185 | vpop {s0-s31} 186 | #endif 187 | pop {r4-r11, pc} 188 | 189 | .Lhook_entry_throw_exception: 190 | mov r0, lr 191 | ldr lr, [PThreadSelf, #THREAD_DELIVER_EXCEPTION_ENTRY_OFFSET] 192 | bx lr 193 | 194 | .size hook_entry, .-hook_entry 195 | 196 | 197 | /* r0 -- pMethodItem 198 | * r1 -- args 199 | * r2 -- PThread 200 | * r3 -- result 201 | */ 202 | .global call_original_method 203 | .type call_original_method, %function 204 | call_original_method: 205 | push {r4, r5, r6, r7, r8, r9, r10, lr} 206 | 207 | mov PMethodItem, r0 208 | mov PThreadSelf, r2 209 | mov r7, r3 //result 210 | mov r10, sp 211 | 212 | ENTRY_CALL 1 213 | 214 | //alloc size 215 | ldr r2, [PMethodItem, #METHOD_ITEM_ARG_SIZE] 216 | add r4, r2, #16 217 | and r4, #0xfffffff0 218 | sub sp, r4 219 | //memcpy 220 | add r0, sp, #STACK_REFERENCE_SIZE //dest 221 | blx memcpy /*memcpy(r0, r1, r2) */ 222 | 223 | add r4, sp, #STACK_REFERENCE_SIZE 224 | access_s0_s15 call_original_method, vldr, r5, r4, r6, r2, r3, r1 225 | access_r0_r3 ldr, 4, 5, 6 226 | mov r0, 0 227 | str r0, [sp] //art method is null 228 | ldr r0, [PMethodItem, #METHOD_ITEM_ORIGINAL_METHOD] 229 | 230 | ldr ip, [PMethodItem, #METHOD_ITEM_ORIGINAL_ENTRY] 231 | blx ip 232 | 233 | //save the return value 234 | ldr r4, [PMethodItem, #METHOD_ITEM_SHORTY] 235 | ldrb r3, [r4] 236 | 237 | cmp r3, #'D' 238 | bne 1f 239 | vstr s0, [r7] 240 | vstr s1, [r7, #4] 241 | b 3f 242 | 1: 243 | cmp r3, #'F' 244 | bne 2f 245 | vstr s0, [r7] 246 | b 3f 247 | 2: 248 | str r0, [r7] 249 | str r1, [r7, #4] 250 | 3: 251 | mov sp, r10 252 | 253 | EXIT_CALL 1 254 | pop {r4, r5, r6, r7, r8, r9, r10, pc} 255 | 256 | .size call_original_method, .-call_original_method 257 | 258 | 259 | 260 | .macro AsmHookEntry func_name, index 261 | .global \func_name 262 | .type \func_name, %function 263 | \func_name: 264 | push {lr} 265 | mov lr, \index 266 | b hook_entry 267 | 268 | .size \func_name, .-\func_name 269 | .endm 270 | 271 | #include "method-entries.asm" 272 | 273 | #endif 274 | -------------------------------------------------------------------------------- /hook-arm64.S: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2014, Xiaomi Inc. All rights reserved. 3 | */ 4 | 5 | #ifdef __LP64__ 6 | .file "hook-arm64.S" 7 | .text 8 | .align 2 9 | .balign 16 10 | 11 | #include "hook-asm.h" 12 | 13 | #define PMethodItem x25 14 | #if PLATFORM_SDK_VERSION <= 23 15 | #define PThreadSelf x18 16 | #define wSUSPEND w19 17 | #else 18 | #define PThreadSelf x19 19 | #endif 20 | #define PThreadBack x24 21 | 22 | #define SUSPEND_CHECK_INTERVAL 96 23 | #if STACK_REFERENCE_SIZE == 4 24 | #define STACK_REFERENCE_REG(reg) w##reg 25 | #elif STACK_REFERENCE_SIZE == 8 26 | #define STACK_REFERENCE_REG(reg) x##reg 27 | #endif 28 | 29 | #include "arm_debug.S" 30 | 31 | .global call_user_callback 32 | 33 | .macro CallHandlerAccesArg access, reg, size, counter_reg, return_label 34 | .balign 16 35 | \access \reg, [x9], #\size 36 | add \counter_reg, \counter_reg, 16 37 | b \return_label 38 | .endm 39 | 40 | 41 | .macro AccessRegsWithStack name, access 42 | /* 43 | * x10 shorty 44 | * x9 target/source memory 45 | * save x1~x7, d0~d7 46 | */ 47 | .type \name, %function 48 | \name: 49 | /* x9 the memeory 50 | */ 51 | stp x16, x17, [sp, #-16]! 52 | 53 | adr x12, .Lwx_\name 54 | adr x13, .Lxx_\name 55 | adr x14, .Lsx_\name 56 | adr x15, .Ldx_\name 57 | mov x16, 0 58 | mov x17, 0 59 | 60 | ldr w8, [PMethodItem, #METHOD_ITEM_ISSTATIC] 61 | cbnz w8, .Laccess_regs_\name /* is static, goto continue*/ 62 | br x12 /* access x1, x1 is this */ 63 | 64 | .Laccess_regs_\name: 65 | ldrb w11, [x10], 1 66 | //SHOW_XREG 11 67 | cbz w11, .Lend_\name 68 | 69 | cmp w11, #'F' 70 | bne .Lis_double_\name 71 | // is float 72 | cmp x17, #8 * 16 // is overflow ? 73 | beq .Ltest_gpr_\name 74 | // save sN 75 | add x8, x14, x17 76 | br x8 77 | 78 | .Lis_double_\name: 79 | cmp w11, #'D' 80 | bne .Lis_long_\name 81 | 82 | cmp x17, #8 * 16 83 | beq .Ltest_gpr_\name 84 | add x8, x15, x17 85 | br x8 86 | 87 | .Lis_long_\name: 88 | cmp w11, #'J' 89 | bne .Ldefault_\name 90 | 91 | cmp x16, #7 * 16 92 | beq .Ltest_fpr_\name 93 | 94 | add x8, x13, x16 95 | br x8 96 | 97 | .Ldefault_\name: 98 | cmp x16, #7 * 16 99 | beq .Ltest_fpr_\name 100 | add x8, x12, x16 101 | br x8 102 | 103 | .Ltest_fpr_\name: 104 | cmp x17, #8 * 16 105 | beq .Lend_\name 106 | b .Laccess_regs_\name 107 | 108 | .Ltest_gpr_\name: 109 | cmp x16, #7 * 16 110 | beq .Lend_\name 111 | b .Laccess_regs_\name 112 | 113 | .Lend_\name: 114 | ldp x16, x17, [sp], #16 115 | ret 116 | 117 | .balign 16 118 | .Lwx_\name: 119 | CallHandlerAccesArg \access, w1, 4, x16, .Laccess_regs_\name 120 | CallHandlerAccesArg \access, w2, 4, x16, .Laccess_regs_\name 121 | CallHandlerAccesArg \access, w3, 4, x16, .Laccess_regs_\name 122 | CallHandlerAccesArg \access, w4, 4, x16, .Laccess_regs_\name 123 | CallHandlerAccesArg \access, w5, 4, x16, .Laccess_regs_\name 124 | CallHandlerAccesArg \access, w6, 4, x16, .Laccess_regs_\name 125 | CallHandlerAccesArg \access, w7, 4, x16, .Laccess_regs_\name 126 | .balign 16 127 | .Lxx_\name: 128 | CallHandlerAccesArg \access, x1, 8, x16, .Laccess_regs_\name 129 | CallHandlerAccesArg \access, x2, 8, x16, .Laccess_regs_\name 130 | CallHandlerAccesArg \access, x3, 8, x16, .Laccess_regs_\name 131 | CallHandlerAccesArg \access, x4, 8, x16, .Laccess_regs_\name 132 | CallHandlerAccesArg \access, x5, 8, x16, .Laccess_regs_\name 133 | CallHandlerAccesArg \access, x6, 8, x16, .Laccess_regs_\name 134 | CallHandlerAccesArg \access, x7, 8, x16, .Laccess_regs_\name 135 | .balign 16 136 | .Lsx_\name: 137 | CallHandlerAccesArg \access, s0, 4, x17, .Laccess_regs_\name 138 | CallHandlerAccesArg \access, s1, 4, x17, .Laccess_regs_\name 139 | CallHandlerAccesArg \access, s2, 4, x17, .Laccess_regs_\name 140 | CallHandlerAccesArg \access, s3, 4, x17, .Laccess_regs_\name 141 | CallHandlerAccesArg \access, s4, 4, x17, .Laccess_regs_\name 142 | CallHandlerAccesArg \access, s5, 4, x17, .Laccess_regs_\name 143 | CallHandlerAccesArg \access, s6, 4, x17, .Laccess_regs_\name 144 | CallHandlerAccesArg \access, s7, 4, x17, .Laccess_regs_\name 145 | .balign 16 146 | .Ldx_\name: 147 | CallHandlerAccesArg \access, d0, 8, x17, .Laccess_regs_\name 148 | CallHandlerAccesArg \access, d1, 8, x17, .Laccess_regs_\name 149 | CallHandlerAccesArg \access, d2, 8, x17, .Laccess_regs_\name 150 | CallHandlerAccesArg \access, d3, 8, x17, .Laccess_regs_\name 151 | CallHandlerAccesArg \access, d4, 8, x17, .Laccess_regs_\name 152 | CallHandlerAccesArg \access, d5, 8, x17, .Laccess_regs_\name 153 | CallHandlerAccesArg \access, d6, 8, x17, .Laccess_regs_\name 154 | CallHandlerAccesArg \access, d7, 8, x17, .Laccess_regs_\name 155 | 156 | .size \name, .-\name 157 | .endm 158 | 159 | AccessRegsWithStack save_regs_to_stack, str 160 | AccessRegsWithStack load_regs_from_stack, ldr 161 | 162 | 163 | #if PLATFORM_SDK_VERSION >= 24 164 | #define _FP_OFF(n) (n+8) 165 | #define _SAVE_THREAD_SELF str PThreadSelf, [sp, #80] 166 | #else 167 | #define _FP_OFF(n) n 168 | #define _SAVE_THREAD_SELF stp PThreadSelf, x19, [sp, #72] 169 | #endif 170 | 171 | .type hook_entry, %function 172 | hook_entry: 173 | // save all callee save frame 174 | sub sp, sp, #160 175 | 176 | stp d8, d9, [sp, #_FP_OFF(8)] 177 | stp d10, d11, [sp, #_FP_OFF(24)] 178 | stp d12, d13, [sp, #_FP_OFF(40)] 179 | stp d14, d15, [sp, #_FP_OFF(56)] 180 | 181 | _SAVE_THREAD_SELF 182 | stp x20, x21, [sp, #88] 183 | stp x22, x23, [sp, #104] 184 | stp x24, x25, [sp, #120] 185 | stp x26, x27, [sp, #136] 186 | str x28, [sp, #152] 187 | 188 | mov PThreadBack, PThreadSelf 189 | mov x28, sp 190 | 191 | ENTRY_CALL 0 192 | SHOW_R0_7 193 | SHOW_XREG 30 194 | 195 | //load the MethodItem 196 | adrp x16, :got:g_method_items 197 | ldr x16, [x16, #:got_lo12:g_method_items] //got g_method_item to x16 198 | SHOW_XREG 16 199 | 200 | mov x17, #METHOD_ITEM_SIZE 201 | mul x30, x30, x17 202 | SHOW_XREG 30 203 | add PMethodItem, x16, x30 204 | SHOW_XREG 25 205 | 206 | ldr x10, [PMethodItem, #METHOD_ITEM_SHORTY] 207 | add x10, x10, #1 // skip the return type 208 | add x9, x28, #176 + STACK_REFERENCE_SIZE 209 | 210 | // save x0~x7, d0 ~ d7 211 | bl save_regs_to_stack 212 | 213 | sub sp, sp, 16 //for result 214 | str xzr, [sp] // zero the result 215 | 216 | // call user callback 217 | mov x0, PMethodItem 218 | add x1, x28, #(176 + STACK_REFERENCE_SIZE) // args 219 | mov x2, x28 //runtime method pointer 220 | mov x3, PThreadSelf 221 | mov x4, sp 222 | bl call_user_callback 223 | 224 | // load the result type 225 | ldr x10, [PMethodItem, #METHOD_ITEM_SHORTY] 226 | ldrb w5, [x10] 227 | 228 | // is double ? 229 | cmp w5, #'D' 230 | bne 1f 231 | ldr d0, [sp] 232 | b 3f 233 | 1: 234 | // is float ? 235 | cmp w5, #'F' 236 | bne 2f 237 | ldr s0, [sp] 238 | b 3f 239 | 2: 240 | ldr x0, [sp] 241 | 3: 242 | EXIT_CALL 0 243 | // finish 244 | mov PThreadSelf, PThreadBack 245 | mov sp, x28 246 | 247 | ldr x8, [PThreadSelf, #THREAD_EXCEPTION_OFFSET] 248 | cbnz x8, 1f 249 | 250 | #if PLATFORM_SDK_VERSION >= 24 251 | ldr x19, [sp, #80] 252 | #else 253 | ldp PThreadSelf, x19, [sp, #72] 254 | #endif 255 | 256 | ldp d8, d9, [sp, #_FP_OFF(8)] 257 | ldp d10, d11, [sp, #_FP_OFF(24)] 258 | ldp d12, d13, [sp, #_FP_OFF(40)] 259 | ldp d14, d15, [sp, #_FP_OFF(56)] 260 | 261 | ldp x20, x21, [sp, #88] 262 | ldp x22, x23, [sp, #104] 263 | ldp x24, x25, [sp, #120] 264 | ldp x26, x27, [sp, #136] 265 | ldp x28, x29, [sp, #152] 266 | ldr x30, [sp, #168] 267 | add sp, sp, #176 268 | 269 | ret 270 | 1: 271 | // exception 272 | mov x0, x8 273 | ldr x9, [PThreadSelf, #THREAD_DELIVER_EXCEPTION_ENTRY_OFFSET] 274 | br x9 275 | .size hook_entry, .-hook_entry 276 | 277 | 278 | // call original function 279 | // x0 -- PMethodItem 280 | // x1 -- args 281 | // x2 -- PThread 282 | // x3 -- result 283 | .global call_original_method 284 | .type call_original_method, %function 285 | call_original_method: 286 | sub sp, sp, #80 287 | stp x29, x30, [sp, #64] 288 | stp x27, x28, [sp, #48] 289 | stp x25, x26, [sp, #32] 290 | stp x23, x24, [sp, #16] 291 | stp x18, x19, [sp, #0] 292 | 293 | mov PMethodItem, x0 294 | mov x23, x1 295 | mov PThreadSelf, x2 296 | mov x24, x3 297 | mov x27, sp 298 | 299 | ENTRY_CALL 1 300 | 301 | // alloc the size of args 302 | ldr w8, [PMethodItem, #METHOD_ITEM_ARG_SIZE] 303 | SHOW_XREG 8 304 | add x9, x8, STACK_REFERENCE_SIZE 305 | sub x9, sp, x9 306 | // 16 align 307 | and x9, x9, #~0xf 308 | mov sp, x9 309 | SHOW_XREG 9 310 | 311 | // get the target args pointer 312 | add x9, x9, STACK_REFERENCE_SIZE 313 | mov x10, x23 314 | SHOW_XREG 9 315 | SHOW_XREG 10 316 | SHOW_XREG 8 317 | // copy args 318 | 1: 319 | ldr x11, [x10], 8 320 | str x11, [x9], 8 321 | sub x8, x8, 8 322 | cmp x8, 0 323 | bgt 1b 324 | 325 | mov x9, x23 326 | ldr x10, [PMethodItem, #METHOD_ITEM_SHORTY] 327 | add x10, x10, 1 /* skip result */ 328 | bl load_regs_from_stack 329 | SHOW_R0_7 330 | 331 | // save the method item 332 | str STACK_REFERENCE_REG(zr), [sp] 333 | ldr x0, [PMethodItem, #METHOD_ITEM_ORIGINAL_METHOD] 334 | #if PLATFORM_SDK_VERSION <= 23 335 | mov wSUSPEND, #SUSPEND_CHECK_INTERVAL 336 | #endif 337 | ldr x8, [PMethodItem, #METHOD_ITEM_ORIGINAL_ENTRY] 338 | SHOW_R0_7 339 | blr x8 340 | 341 | // save the return value 342 | ldr x10, [PMethodItem, #METHOD_ITEM_SHORTY] 343 | ldrb w8, [x10] 344 | 345 | // is double 346 | cmp w8, #'D' 347 | bne 1f 348 | str d0, [x24] 349 | b 3f 350 | 1: 351 | // is float 352 | cmp w8, #'F' 353 | bne 2f 354 | str s0, [x24] 355 | b 3f 356 | 2: 357 | str x0, [x24] 358 | 359 | 3: 360 | mov sp, x27 361 | 362 | EXIT_CALL 0 363 | //finish 364 | ldp x29, x30, [sp, #64] 365 | ldp x27, x28, [sp, #48] 366 | ldp x25, x26, [sp, #32] 367 | ldp x23, x24, [sp, #16] 368 | ldp x18, x19, [sp, #0] 369 | 370 | add sp, sp, #80 371 | ret 372 | 373 | .size call_original_method, .-call_original_method 374 | 375 | 376 | 377 | .macro AsmHookEntry func_name, index 378 | .global \func_name 379 | .type \func_name, %function 380 | \func_name: 381 | stp x29, x30, [sp, #-16]! 382 | mov x30, \index 383 | b hook_entry 384 | 385 | .size \func_name, .-\func_name 386 | .endm 387 | 388 | 389 | #include "method-entries.asm" 390 | 391 | #endif 392 | 393 | -------------------------------------------------------------------------------- /hook-asm.h: -------------------------------------------------------------------------------- 1 | #ifndef HOOK_ASM_H 2 | #define HOOK_ASM_H 3 | 4 | #define HOOK_METHOD_ENTRY(name) hook_entry_##name 5 | #define STACK_REFERENCE_SIZE 4 6 | 7 | #ifdef __LP64__ 8 | #define METHOD_ITEM_CALLBACK 24 9 | #define METHOD_ITEM_ORIGINAL_ENTRY 40 10 | #define METHOD_ITEM_SHORTY 48 11 | #define METHOD_ITEM_ORIGINAL_METHOD 56 12 | #define METHOD_ITEM_ARG_SIZE 72 13 | #define METHOD_ITEM_ISSTATIC 76 14 | #define METHOD_ITEM_SIZE 96 15 | 16 | # define MANAGED_STACK_SIZE 24 17 | # define THREAD_EXCEPTION_OFFSET 136 18 | # if defined(__x86_64__) 19 | # define THREAD_DELIVER_EXCEPTION_ENTRY_OFFSET 1264 20 | # else //defined __x86_64__ 21 | # define THREAD_DELIVER_EXCEPTION_ENTRY_OFFSET 1088 22 | # endif //end defined(__x86_64__) 23 | # undef STACK_REFERENCE_SIZE 24 | # define STACK_REFERENCE_SIZE 8 25 | #else 26 | #define METHOD_ITEM_CALLBACK 12 27 | #define METHOD_ITEM_ORIGINAL_ENTRY 20 28 | #define METHOD_ITEM_SHORTY 24 29 | #define METHOD_ITEM_ORIGINAL_METHOD 28 30 | #define METHOD_ITEM_ARG_SIZE 36 31 | #define METHOD_ITEM_ISSTATIC 40 32 | #define METHOD_ITEM_SIZE 60 33 | 34 | # define MANAGED_STACK_SIZE 12 35 | # define THREAD_EXCEPTION_OFFSET 132 36 | 37 | # if defined(__i386__) 38 | # define THREAD_DELIVER_EXCEPTION_ENTRY_OFFSET 692 39 | # else 40 | # define THREAD_DELIVER_EXCEPTION_ENTRY_OFFSET 608 41 | # endif 42 | 43 | #ifdef __arm__ 44 | #define METHOD_ITEM_R0_R3_OFFSET 44 45 | #define METHOD_ITEM_S0_S15_OFFSET 48 46 | #undef METHOD_ITEM_SIZE 47 | #define METHOD_ITEM_SIZE 80 48 | #endif 49 | 50 | #endif 51 | 52 | #if defined(ENABLE_DEBUG64) && defined(__LP64__) 53 | #define ENABLE_DEBUG 1 54 | #endif 55 | 56 | #if defined (ENABLE_DEBUG32) && !defined(__LP64__) 57 | #define ENABLE_DEBUG 1 58 | #endif 59 | 60 | #endif 61 | 62 | -------------------------------------------------------------------------------- /hook.h: -------------------------------------------------------------------------------- 1 | #ifndef HOOK_H 2 | #define HOOK_H 3 | #include 4 | #include "hook-asm.h" 5 | #include "log.h" 6 | 7 | struct MethodItem; 8 | typedef void (*PMethodCallback)(JNIEnv* env, MethodItem*, void* context, const jvalue* args, int arg_count, void* pthread, void* result); 9 | struct MethodItem { 10 | const char* className; 11 | const char* methodName; 12 | const char* methodSignature; 13 | PMethodCallback callback; 14 | void *hookEntry; 15 | void *originalEntry; 16 | char *shorty; 17 | jmethodID methodId; 18 | jclass ownerClass; 19 | int argSize; 20 | int isStatic; 21 | 22 | #ifdef __arm__ 23 | uint8_t off_r0_r3[4]; 24 | uint8_t off_s0_s15[16]; 25 | #endif 26 | 27 | uint8_t object_arg_count; 28 | uint8_t object_arg_offsets[15]; 29 | }; 30 | 31 | 32 | extern "C" MethodItem g_method_items[]; 33 | 34 | void call_original_entry(MethodItem* pMethodItem, void* context, void* pthread, void* result); 35 | 36 | enum MethodItem_List_Index { 37 | MethodItem_List_base = -1, 38 | #define DEFINE(name, class_name, method_name, signature, callback, index) \ 39 | MethodItem_Index_##name, 40 | #include "method-list.def" 41 | #undef DEFINE 42 | MethodItem_Index_Max 43 | }; 44 | 45 | #ifdef ENABLE_DEBUG 46 | void show_method_item(MethodItem* methodItem); 47 | #else 48 | static inline void show_method_item(MethodItem*) { } 49 | #endif 50 | 51 | struct MethodArgs { 52 | JNIEnv *env; 53 | jobject method; 54 | jobjectArray args; 55 | 56 | MethodArgs(JNIEnv* env, MethodItem* pMethodItem, const jvalue* values, int count); 57 | ~MethodArgs(); 58 | }; 59 | 60 | 61 | #endif 62 | 63 | -------------------------------------------------------------------------------- /init.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "hook.h" 7 | 8 | 9 | void * get_method_entry(jmethodID methodId); 10 | void set_method_entry(jmethodID methodId, void*); 11 | 12 | static bool createShorty(MethodItem* methodItem) { 13 | const char* signature = methodItem->methodSignature; 14 | 15 | char szShorty[32]; 16 | int idx = 1; 17 | 18 | if (!signature || *signature != '(') { 19 | ALOGE("Invalidate signature:%s in %s.%s", (signature ? signature : "NULL"), 20 | methodItem->className, methodItem->methodName); 21 | return false; 22 | } 23 | 24 | signature ++; 25 | 26 | while (*signature && *signature !=')') { 27 | szShorty[idx++] = *signature; 28 | if (*signature == 'L' || *signature == '[') { 29 | while(*signature && *signature != ';' && *signature != ')') { 30 | signature ++; 31 | } 32 | } 33 | signature ++; 34 | } 35 | 36 | if (*signature != ')') { 37 | ALOGE("Invalidate signature:%s in %s.%s", methodItem->methodSignature, 38 | methodItem->className, methodItem->methodSignature); 39 | return false; 40 | } 41 | 42 | signature ++; 43 | szShorty[0] = *signature; 44 | szShorty[idx] = 0; 45 | 46 | methodItem->shorty = strdup(szShorty); 47 | 48 | 49 | return true; 50 | } 51 | 52 | #if PLATFORM_SDK_VERSION >= 23 && defined(__arm__) 53 | static char next_none_floating_arg(const char* &str_shorty, int& len) { 54 | while (*str_shorty) { 55 | if (*str_shorty == 'D') len += sizeof(jdouble); 56 | else if (*str_shorty == 'F') len += sizeof(jfloat); 57 | else return *str_shorty ++; 58 | str_shorty ++; 59 | } 60 | return '\0'; 61 | } 62 | static void init_r0_r3(MethodItem *methodItem) { 63 | /* Rx is the Param Of Orignal Method 64 | * R0 -- ArtMethod * 65 | * R1 -- Arg1 66 | * R2 -- Arg2 67 | * R3 -- Arg3 68 | * 69 | * for method foo (param1, param2, param3 ...); 70 | * if foo is none-static method: 71 | * Arg1, Arg2, Arg3 : this, param1, param2 72 | * if foo is static method: 73 | * Arg1, Arg2, Arg3 : param1, param2, param3 74 | * 75 | * 76 | * The general register (R0, R1, R2, R3 ..) only store the non-floating paramters, 77 | * the floating paramters will be stored into the float regiters, 78 | * e.g. static void foo(double param1, double param2, int param3, float param4, Object param5 ...) 79 | * Arg1 = param3; 80 | * Arg2 = param5; 81 | * .... 82 | * 83 | * if Arg1 is long: 84 | * R1 : ignore 85 | * R2 : Arg1.low32 86 | * R3 : Arg1.hi32 87 | * else if Arg1 is no-long && Arg2 is long: 88 | * R1 : Arg1 89 | * R2 : Arg2.low32 90 | * R3 : Arg2.hi32 91 | * else if Arg1, Arg2 is no-long && Arg3 is long: 92 | * R1 : Arg1 93 | * R2 : Arg3 94 | * R3 : ingore 95 | * else 96 | * R1 : Arg1 97 | * R2 : Arg2 98 | * R3 : Arg3 99 | */ 100 | 101 | uint8_t* pr0_r3 = methodItem->off_r0_r3; 102 | const char* shorty = methodItem->shorty; 103 | 104 | uint8_t& r1_offset = pr0_r3[1]; 105 | uint8_t& r2_offset = pr0_r3[2]; 106 | uint8_t& r3_offset = pr0_r3[3]; 107 | r1_offset = 0xff; 108 | r2_offset = 0xff; 109 | r3_offset = 0xff; 110 | int len = 0; 111 | 112 | bool isLong = false; 113 | const char *str_shorty = shorty + 1; //skip shorty[0], which is return type 114 | char arg = '\0'; 115 | 116 | //process Arg1 117 | if (methodItem->isStatic) { 118 | if((arg = next_none_floating_arg(str_shorty, len)) == '\0') return; 119 | isLong = (arg == 'J'); 120 | len += 4; //a thiz paramter(null) is set 121 | } 122 | //Arg1 is long 123 | if (isLong) { 124 | //r1 ignored; 125 | r2_offset = len; //Arg1.low32 126 | r3_offset = r2_offset + 4; //Arg1.hi32 127 | return ; 128 | } 129 | r1_offset = len; //r1 : Arg1 130 | len += 4; 131 | 132 | //process Arg2 133 | if((arg = next_none_floating_arg(str_shorty, len)) == '\0') return; 134 | isLong = (arg == 'J'); 135 | if (isLong) { //Arg2 is Long 136 | r2_offset = len; //Arg2.low32 137 | r3_offset = r2_offset + 4; //Arg2.hi32 138 | return ; 139 | } 140 | r2_offset = len; 141 | len += 4; 142 | 143 | //process Arg3 144 | if((arg = next_none_floating_arg(str_shorty, len)) == '\0') return; 145 | isLong = (arg == 'J'); 146 | if (!isLong) { //if Arg3 is long, r3 ignore 147 | r3_offset = len; 148 | } 149 | } 150 | 151 | #endif 152 | 153 | static bool calcArgSize(MethodItem* methodItem) { 154 | const char* shorty = methodItem->shorty; 155 | 156 | int size = 0; 157 | int obj_arg_count = 0; 158 | 159 | if (!methodItem->isStatic) { 160 | size += 4; 161 | } 162 | 163 | for (int i = 1; shorty[i]; i++) { 164 | switch(shorty[i]) { 165 | case 'D': 166 | case 'J': 167 | size += 8; 168 | break; 169 | case 'L': case '[': 170 | methodItem->object_arg_offsets[obj_arg_count] = size/4; 171 | obj_arg_count ++; 172 | default: 173 | size += 4; 174 | break; 175 | } 176 | } 177 | 178 | methodItem->argSize = size; 179 | methodItem->object_arg_count = obj_arg_count; 180 | 181 | #ifdef __arm__ 182 | 183 | #if PLATFORM_SDK_VERSION >= 23 184 | init_r0_r3(methodItem); 185 | #else 186 | pr0_r3[1] = 0; 187 | pr0_r3[2] = 4; 188 | pr0_r3[3] = 8; 189 | #endif 190 | 191 | //process s0-s15 192 | uint8_t* ps0_s15 = methodItem->off_s0_s15; 193 | uint8_t float_offset = methodItem->isStatic ? 0 : 4; 194 | int float_idx = 0; 195 | for (int i = 1; shorty[i] && float_idx < 16; i++) { 196 | if (shorty[i] == 'F') { 197 | ps0_s15[float_idx ++] = float_offset; 198 | float_offset += 4; 199 | } else if (shorty[i] =='D') { 200 | if (float_idx % 2 != 0) { 201 | ps0_s15[float_idx++] = 0xfe; //skip the register 202 | } 203 | if (float_idx > 15) break; 204 | ps0_s15[float_idx++] = float_offset; 205 | ps0_s15[float_idx++] = float_offset + 4; 206 | float_offset += 8; 207 | } else if (shorty[i] == 'J') { 208 | float_offset += 8; 209 | } else { 210 | float_offset += 4; 211 | } 212 | } 213 | 214 | #endif 215 | return true; 216 | } 217 | 218 | 219 | static bool init_method(JNIEnv* env, MethodItem* methodItem) { 220 | bool isStatic = false; 221 | jclass clazz = env->FindClass(methodItem->className); 222 | 223 | if (clazz == NULL) { 224 | ALOGE("Try load class %s failed", methodItem->className); 225 | return false; 226 | } 227 | 228 | jmethodID methodID= env->GetMethodID(clazz, methodItem->methodName, methodItem->methodSignature); 229 | if (methodID == 0) { 230 | methodID = env->GetStaticMethodID(clazz, methodItem->methodName, methodItem->methodSignature); 231 | isStatic = true; 232 | } 233 | if (methodID == 0) { 234 | ALOGE("Try get method %s.%s %s faield", methodItem->className, 235 | methodItem->methodName, methodItem->methodSignature); 236 | return false; 237 | } 238 | 239 | methodItem->ownerClass = (jclass)env->NewGlobalRef(clazz); 240 | methodItem->methodId = methodID; 241 | methodItem->originalEntry = get_method_entry(methodID); 242 | methodItem->isStatic = isStatic ? 1 : 0; 243 | 244 | if(!createShorty(methodItem)) { 245 | return false; 246 | } 247 | 248 | //calc the size 249 | if(!calcArgSize(methodItem)) { 250 | return false; 251 | } 252 | 253 | //set the hookentry 254 | set_method_entry(methodID, methodItem->hookEntry); 255 | 256 | show_method_item(methodItem); 257 | 258 | 259 | return true; 260 | } 261 | 262 | bool init_method_list(JNIEnv* env) { 263 | for (int i = 0; i < MethodItem_Index_Max; i ++) { 264 | if(!init_method(env, &g_method_items[i])) { 265 | return false; 266 | } 267 | } 268 | return true; 269 | } 270 | 271 | extern "C" MethodItem* get_method_item(int idx) { 272 | return &g_method_items[idx]; 273 | } 274 | 275 | 276 | #ifdef ENABLE_DEBUG 277 | 278 | #define _REG(x) (void*)(x), (unsigned int)(x) 279 | 280 | extern "C" void print_reg(int idx, intptr_t p) { 281 | DEBUG("show reg:%d:%p(%u)", idx, _REG(p)); 282 | } 283 | 284 | #ifdef __aarch64__ 285 | extern "C" void show_r0_7(intptr_t x0, intptr_t x1, intptr_t x2, intptr_t x3, intptr_t x4, intptr_t x5, intptr_t x6, intptr_t x7) { 286 | DEBUG("R0-R7:%p(%u), %p(%u), %p(%u), %p(%u), %p(%u), %p(%u), %p(%u), %p(%u)", 287 | _REG(x0), _REG(x1), _REG(x2), _REG(x3), _REG(x4), _REG(x5), _REG(x6), _REG(x7)); 288 | } 289 | #elif __arm__ 290 | extern "C" void show_r0_7(intptr_t x0, intptr_t x1, intptr_t x2, intptr_t x3) { 291 | DEBUG("R0-R7:%p(%u), %p(%u), %p(%u), %p(%u)", 292 | _REG(x0), _REG(x1), _REG(x2), _REG(x3)); 293 | } 294 | #else 295 | extern "C" void show_r0_7() { 296 | } 297 | #endif 298 | 299 | extern "C" void show_sp_values(void** sp, int count) { 300 | DEBUG("Show sp %p, %d", sp, count); 301 | char szbuf[1024]; 302 | int idx = 0; 303 | for (int i = 0; i < count; i++) { 304 | if (i % 4 == 0) { 305 | idx += sprintf(szbuf + idx, "%s%p:", i == 0 ? "" : "\n", sp + i); 306 | } 307 | idx += sprintf(szbuf + idx, "%p ", sp[i]); 308 | } 309 | 310 | szbuf[idx] = 0; 311 | DEBUG("%s", szbuf); 312 | } 313 | 314 | extern "C" void show_mem_values(void** sp, int count, int reg) { 315 | DEBUG("Show REG %d MEM(%p) count %d", reg, sp, count); 316 | char szbuf[1024]; 317 | int idx = 0; 318 | for (int i = 0; i < count; i++) { 319 | if (i % 4 == 0) { 320 | idx += sprintf(szbuf + idx, "%s%p:", i == 0 ? "" : "\n", sp + i); 321 | } 322 | idx += sprintf(szbuf + idx, "%p ", sp[i]); 323 | } 324 | 325 | szbuf[idx] = 0; 326 | DEBUG("%s", szbuf); 327 | } 328 | 329 | static const char* call_type[] = { 330 | "hook_entry", 331 | "call_original_entry", 332 | }; 333 | 334 | extern "C" void entry_call(MethodItem* pMethod, int type) { 335 | DEBUG(">>>>>>>>>> enter:%s %p>>>>>>", call_type[type], pMethod); 336 | } 337 | 338 | extern "C" void exit_call(MethodItem* pMethod, int type) { 339 | DEBUG("<<<<<<<<<< exit:%s %p <<<<<<", call_type[type], pMethod); 340 | } 341 | 342 | void show_method_item(MethodItem* methodItem) { 343 | DEBUG("MethodItem(%p) %s.%s%s with:", methodItem, methodItem->className, methodItem->methodName, methodItem->methodSignature); 344 | DEBUG(" methodID=%p, callback=%p, hookEntry=%p, originalEntry=%p", 345 | methodItem->methodId, methodItem->callback, methodItem->hookEntry, methodItem->originalEntry); 346 | DEBUG(" ownerclass=%p, argSize=%d, isStatic=%d, shorty=%s", 347 | methodItem->ownerClass, methodItem->argSize, methodItem->isStatic, methodItem->shorty); 348 | #ifdef __arm__ 349 | DEBUG(" offset r0: %d, r1:%d, r2:%d, r3:%d", 350 | methodItem->off_r0_r3[0], 351 | methodItem->off_r0_r3[1], 352 | methodItem->off_r0_r3[2], 353 | methodItem->off_r0_r3[3]); 354 | #ifdef ENABLE_DEBUG 355 | for (int i = 0; i < 16 && methodItem->off_s0_s15[i] != 0xff; i++) { 356 | DEBUG(" offset s[%d] = %d", i, methodItem->off_s0_s15[i]); 357 | } 358 | #endif 359 | #endif 360 | } 361 | 362 | #endif 363 | 364 | 365 | -------------------------------------------------------------------------------- /jnihelp.h: -------------------------------------------------------------------------------- 1 | #ifndef JNI_HELP_H 2 | #define JNI_HELP_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | JNIEnv* AttachCurrentThread(); 9 | 10 | struct JClass { 11 | jclass clazz; 12 | 13 | JClass() : clazz(0) { } 14 | JClass(JNIEnv* env, const char* className) { 15 | init(env, className); 16 | } 17 | 18 | bool init(JNIEnv* env, const char* className) { 19 | jclass clz = env->FindClass(className); 20 | if (clz == NULL) return false; 21 | clazz = (jclass)(env->NewGlobalRef(clz)); 22 | return true; 23 | } 24 | 25 | ~JClass() { 26 | if (clazz != 0) { 27 | JNIEnv* env = AttachCurrentThread(); 28 | if (env != NULL) { 29 | env->DeleteGlobalRef(clazz); 30 | } 31 | } 32 | } 33 | }; 34 | 35 | template 36 | struct JString { 37 | JNIEnv *env; 38 | jstring value; 39 | 40 | static jstring newString(JNIEnv* env, const char* str) { 41 | return env->NewStringUTF(str); 42 | } 43 | static jstring newString(JNIEnv* env, const wchar_t *str) { 44 | return env->NewString((const jchar*)str, wcslen(str)); 45 | } 46 | 47 | JString(JNIEnv *e, const TChar* str) { 48 | this->env = e; 49 | value = newString(env, str); 50 | } 51 | 52 | ~JString() { 53 | if (env && value) { 54 | env->DeleteLocalRef(value); 55 | } 56 | } 57 | }; 58 | 59 | 60 | template struct JType { 61 | T _value; 62 | JType(JNIEnv*, T t) : _value(t) { } 63 | T value() { return _value; } 64 | }; 65 | 66 | template<> struct JType { jint _value; JType(JNIEnv*, int v) : _value(v) { } jint value() { return _value; }}; 67 | template<> struct JType { jlong _value; JType(JNIEnv*, long v) : _value(v) { } jlong value() { return _value; }}; 68 | template<> struct JType { jchar _value; JType(JNIEnv*, char v) : _value(v) { } jchar value() { return _value; }}; 69 | template<> struct JType { jbyte _value; JType(JNIEnv*, uint8_t v) : _value(v) { } jbyte value() { return _value; } }; 70 | template<> struct JType { jshort _value; JType(JNIEnv*, short v) : _value(v) { } jshort value() { return _value; }}; 71 | template<> struct JType { jfloat _value; JType(JNIEnv*, float v) : _value(v) { } jfloat value() { return _value; }}; 72 | template<> struct JType { jdouble _value; JType(JNIEnv*, double v) : _value(v) { } jdouble value() { return _value; }}; 73 | template<> struct JType { 74 | JString str; 75 | JType(JNIEnv *env, const char* value): str(env, value) { } 76 | jstring value() { return str.value; } 77 | }; 78 | template<> struct JType { 79 | JString str; 80 | JType(JNIEnv *env, const wchar_t* value): str(env, value) { } 81 | jstring value() { return str.value; } 82 | }; 83 | template<> struct JType { 84 | JString str; 85 | JType(JNIEnv *env, const char* value): str(env, value) { } 86 | jstring value() { return str.value; } 87 | }; 88 | template<> struct JType { 89 | JString str; 90 | JType(JNIEnv *env, const wchar_t* value): str(env, value) { } 91 | jstring value() { return str.value; } 92 | }; 93 | 94 | #define ARGS_1(X, SP) X(A1, a1) 95 | #define ARGS_2(X, SP) X(A1, a1) SP X(A2, a2) 96 | #define ARGS_3(X, SP) X(A1, a1) SP X(A2, a2) SP X(A3, a3) 97 | #define ARGS_4(X, SP) X(A1, a1) SP X(A2, a2) SP X(A3, a3) SP X(A4, a4) 98 | #define ARGS_5(X, SP) X(A1, a1) SP X(A2, a2) SP X(A3, a3) SP X(A4, a4) SP X(A5, a5) 99 | #define ARGS_6(X, SP) X(A1, a1) SP X(A2, a2) SP X(A3, a3) SP X(A4, a4) SP X(A5, a5) SP X(A6, a6) 100 | #define ARGS_7(X, SP) X(A1, a1) SP X(A2, a2) SP X(A3, a3) SP X(A4, a4) SP X(A5, a5) SP X(A6, a6) SP X(A7, a7) 101 | #define ARGS_8(X, SP) X(A1, a1) SP X(A2, a2) SP X(A3, a3) SP X(A4, a4) SP X(A5, a5) SP X(A6, a6) SP X(A7, a7) SP X(A8, a8) 102 | #define ARGS_9(X, SP) X(A1, a1) SP X(A2, a2) SP X(A3, a3) SP X(A4, a4) SP X(A5, a5) SP X(A6, a6) SP X(A7, a7) SP X(A8, a8) SP X(A9, a9) 103 | #define ARGS_10(X, SP) X(A1, a1) SP X(A2, a2) SP X(A3, a3) SP X(A4, a4) SP X(A5, a5) SP X(A6, a6) SP X(A7, a7) SP X(A8, a8) SP X(A9, a9) SP X(A10, a10) 104 | #define ARGS_11(X, SP) X(A1, a1) SP X(A2, a2) SP X(A3, a3) SP X(A4, a4) SP X(A5, a5) SP X(A6, a6) SP X(A7, a7) SP X(A8, a8) SP X(A9, a9) SP X(A10, a10) SP X(A11, a11) 105 | #define ARGS_12(X, SP) X(A1, a1) SP X(A2, a2) SP X(A3, a3) SP X(A4, a4) SP X(A5, a5) SP X(A6, a6) SP X(A7, a7) SP X(A8, a8) SP X(A9, a9) SP X(A10, a10) SP X(A11, a11) SP X(A12, a12) 106 | #define ARGS_13(X, SP) X(A1, a1) SP X(A2, a2) SP X(A3, a3) SP X(A4, a4) SP X(A5, a5) SP X(A6, a6) SP X(A7, a7) SP X(A8, a8) SP X(A9, a9) SP X(A10, a10) SP X(A11, a11) SP X(A12, a12) SP X(A13, a13) 107 | #define ARGS_14(X, SP) X(A1, a1) SP X(A2, a2) SP X(A3, a3) SP X(A4, a4) SP X(A5, a5) SP X(A6, a6) SP X(A7, a7) SP X(A8, a8) SP X(A9, a9) SP X(A10, a10) SP X(A11, a11) SP X(A12, a12) SP X(A13, a13) SP X(A14, a14) 108 | #define ARGS_15(X, SP) X(A1, a1) SP X(A2, a2) SP X(A3, a3) SP X(A4, a4) SP X(A5, a5) SP X(A6, a6) SP X(A7, a7) SP X(A8, a8) SP X(A9, a9) SP X(A10, a10) SP X(A11, a11) SP X(A12, a12) SP X(A13, a13) SP X(A14, a14) SP X(A15, a15) 109 | 110 | #define TYPE_ARG(A, a) typename A 111 | #define DEFINE_ARG(A, a) A a 112 | #define JTYPE_ARG(A, a) JType _##a(env, a) 113 | #define CALL_ARG(A, a) _##a.value() 114 | #define NORMAL_CALL_ARG(A, a) a 115 | #define COMMA , 116 | 117 | #define DEFINE_ALL_METHODS(Define, ret, retName) \ 118 | Define(ARGS_1, ret, retName) \ 119 | Define(ARGS_2, ret, retName) \ 120 | Define(ARGS_3, ret, retName) \ 121 | Define(ARGS_4, ret, retName) \ 122 | Define(ARGS_5, ret, retName) \ 123 | Define(ARGS_6, ret, retName) \ 124 | Define(ARGS_7, ret, retName) \ 125 | Define(ARGS_8, ret, retName) \ 126 | Define(ARGS_9, ret, retName) \ 127 | Define(ARGS_10, ret, retName) \ 128 | Define(ARGS_11, ret, retName) \ 129 | Define(ARGS_12, ret, retName) \ 130 | Define(ARGS_13, ret, retName) \ 131 | Define(ARGS_14, ret, retName) \ 132 | Define(ARGS_15, ret, retName) 133 | 134 | 135 | #define DEFINE_CALL_METHOD_ARGN(ARGS_N, ret, retName) \ 136 | template static ret call(JNIEnv* env, jobject self, jmethodID methodID, ARGS_N(DEFINE_ARG, COMMA)){ \ 137 | ARGS_N(JTYPE_ARG, ;) ;\ 138 | return (ret)(env->Call##retName##Method(self, methodID, ARGS_N(CALL_ARG, COMMA))); } \ 139 | 140 | #define DEFINE_CALL_STATIC_METHOD_ARGN(ARGS_N, ret, retName) \ 141 | template static ret call(JNIEnv* env, jclass clazz, jmethodID methodID, ARGS_N(DEFINE_ARG, COMMA)){ \ 142 | ARGS_N(JTYPE_ARG, ;) ;\ 143 | return (ret)(env->CallStatic##retName##Method(clazz, methodID, ARGS_N(CALL_ARG, COMMA))); } \ 144 | 145 | #define CallInstanceMethod(ret, retName) \ 146 | static ret call(JNIEnv* env, jobject self, jmethodID methodID) { return (ret)(env->Call##retName##Method(self, methodID)); } \ 147 | DEFINE_ALL_METHODS(DEFINE_CALL_METHOD_ARGN, ret, retName) 148 | 149 | #define CallStaticMethod(ret, retName) \ 150 | static ret call(JNIEnv* env, jclass clazz, jmethodID methodID) { return (ret)(env->CallStatic##retName##Method(clazz, methodID)); } \ 151 | DEFINE_ALL_METHODS(DEFINE_CALL_STATIC_METHOD_ARGN, ret, retName) 152 | 153 | #define DEFINE_CALL_METHOD(ret, retName) \ 154 | template<> struct CallJMethod { \ 155 | CallInstanceMethod(ret, retName) }; \ 156 | template<> struct CallJMethod { \ 157 | CallStaticMethod(ret, retName) }; 158 | 159 | template struct CallJMethod{ }; 160 | 161 | DEFINE_CALL_METHOD(void, Void) 162 | DEFINE_CALL_METHOD(jint, Int) 163 | DEFINE_CALL_METHOD(jlong, Long) 164 | DEFINE_CALL_METHOD(jshort, Short) 165 | DEFINE_CALL_METHOD(jfloat, Float) 166 | DEFINE_CALL_METHOD(jdouble, Double) 167 | DEFINE_CALL_METHOD(jchar, Char) 168 | DEFINE_CALL_METHOD(jbyte, Byte) 169 | DEFINE_CALL_METHOD(jobject, Object) 170 | 171 | #undef DEFINE_CALL_METHOD 172 | 173 | 174 | template struct _owner_type { }; 175 | template<> struct _owner_type { typedef jclass type; }; 176 | template<> struct _owner_type { typedef jobject type; }; 177 | 178 | template 179 | struct JMethod { 180 | typedef TRet return_type; 181 | typedef typename _owner_type::type owner_type; 182 | 183 | jmethodID methodID; 184 | 185 | 186 | JMethod() : methodID(0) { } 187 | JMethod(JNIEnv* env, JClass& clazz, const char* name, const char* signature) { 188 | init(env, clazz, name, signature); 189 | } 190 | JMethod(JNIEnv* env, jclass clazz, const char* name, const char* signature) { 191 | init(env, clazz, name, signature); 192 | } 193 | 194 | bool init(JNIEnv* env, jclass clazz, const char* name, const char* signature) { 195 | if (isStatic()) 196 | methodID = env->GetStaticMethodID(clazz, name, signature); 197 | else 198 | methodID = env->GetMethodID(clazz, name, signature); 199 | return methodID != 0; 200 | } 201 | 202 | bool init(JNIEnv* env, JClass& clazz, const char* name, const char* signature) { 203 | return init(env, clazz.clazz, name, signature); 204 | } 205 | 206 | static bool isStatic() { return STATIC; } 207 | 208 | return_type operator()(JNIEnv* env, owner_type type) { 209 | return (return_type)(CallJMethod::call(env, type, methodID)); 210 | } 211 | 212 | #define DEFINE_CALL_JAVA_METHOD(ARGS_N, ret, retName) \ 213 | template \ 214 | return_type operator()(JNIEnv* env, owner_type type, ARGS_N(DEFINE_ARG, COMMA)) { \ 215 | return (return_type)(CallJMethod::call(env, type, methodID, ARGS_N(NORMAL_CALL_ARG, COMMA))); } 216 | 217 | DEFINE_ALL_METHODS(DEFINE_CALL_JAVA_METHOD, x, y) 218 | #undef DEFINE_CALL_JAVA_METHOD 219 | 220 | }; 221 | 222 | struct JConstructor { 223 | jmethodID methodID; 224 | jclass clazz; 225 | 226 | JConstructor(JNIEnv* env, JClass& clz, const char* signature) { 227 | init(env, clz, signature); 228 | } 229 | JConstructor(JNIEnv* env, jclass clz, const char* signature) { 230 | init(env, clz, signature); 231 | } 232 | 233 | bool init(JNIEnv *env, JClass& clz, const char* signature) { 234 | return init (env, clz.clazz, signature); 235 | } 236 | 237 | bool init(JNIEnv* env, jclass clz, const char* signature) { 238 | clazz = clz; 239 | methodID = env->GetMethodID(clazz, "", signature); 240 | return methodID != 0; 241 | } 242 | 243 | #define DEFINE_NEW_ARGN(ARGS_N, ret, retName) \ 244 | template jobject operator()(JNIEnv* env, ARGS_N(DEFINE_ARG, COMMA)) { \ 245 | ARGS_N(JTYPE_ARG, ;) ; \ 246 | return (jobject)(env->NewObject(clazz, methodID, ARGS_N(CALL_ARG, COMMA))); } 247 | 248 | jobject operator()(JNIEnv* env) { 249 | return (jobject)(env->NewObject(clazz, methodID)); 250 | } 251 | 252 | DEFINE_ALL_METHODS(DEFINE_NEW_ARGN, x, y) 253 | 254 | }; 255 | 256 | 257 | #undef TYPE_ARG 258 | #undef DEFINE_ARG 259 | #undef JTYPE_ARG 260 | #undef CALL_ARG 261 | #undef COMMA 262 | #undef DEFINE_CALL_METHOD_ARGN 263 | #undef DEFINE_CALL_STATIC_METHOD_ARGN 264 | #undef DEFINE_ALL_METHODS 265 | 266 | 267 | template 268 | struct JLazyInstance { 269 | pthread_mutex_t mutex; 270 | TJ * instance; 271 | JLazyInstance() : instance(NULL) { 272 | pthread_mutex_init(&mutex, NULL); 273 | } 274 | 275 | void ensureInitialized(JNIEnv *env) { 276 | if (!instance) { 277 | pthread_mutex_lock(&mutex); 278 | if (!instance) 279 | instance = new TJ(env); 280 | pthread_mutex_unlock(&mutex); 281 | } 282 | } 283 | 284 | TJ* operator -> () { return instance; } 285 | }; 286 | 287 | 288 | #endif 289 | 290 | -------------------------------------------------------------------------------- /log.h: -------------------------------------------------------------------------------- 1 | #ifndef ZYGOTE_HOOK_LOG_H 2 | #define ZYGOTE_HOOK_LOG_H 3 | 4 | #include 5 | 6 | #ifdef ENABLE_DEBUG 7 | #define DEBUG(format, ...) ALOGD("ZYGOTEHOOK:" format, __VA_ARGS__) 8 | #else 9 | #define DEBUG(...) 10 | #endif 11 | 12 | #endif 13 | 14 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "hook.h" 6 | #include "jnihelp.h" 7 | 8 | bool init_method_list(JNIEnv* env); 9 | 10 | JavaVM *g_jvm; 11 | 12 | #define MEMBER_OFFSET(s, m) __builtin_offsetof(s, m) 13 | 14 | #define ERROR ALOGE 15 | 16 | //check the 17 | static bool setup_check() { 18 | bool check_ok = true; 19 | #define DEFINE(name, class_name, method_name, signature, callback, index) \ 20 | if (MethodItem_Index_##name != index) { \ 21 | check_ok = false; \ 22 | ERROR("%s.%s %s index is error(except %d, but get %d)", \ 23 | class_name, method_name, signature, \ 24 | MethodItem_Index_##name, index); \ 25 | } 26 | 27 | #include "method-list.def" 28 | #undef DEFINE 29 | 30 | #define CHECK_MEMBER_OFFSET(s,m,off) \ 31 | if (MEMBER_OFFSET(s, m) != off) { \ 32 | ERROR("member offset %s.%s (%d) is not equal %s (%d)", \ 33 | #s, #m, (int)MEMBER_OFFSET(s, m), #off, off); \ 34 | check_ok = false; \ 35 | } 36 | 37 | CHECK_MEMBER_OFFSET(MethodItem, callback, METHOD_ITEM_CALLBACK) 38 | CHECK_MEMBER_OFFSET(MethodItem, originalEntry, METHOD_ITEM_ORIGINAL_ENTRY) 39 | CHECK_MEMBER_OFFSET(MethodItem, argSize, METHOD_ITEM_ARG_SIZE) 40 | CHECK_MEMBER_OFFSET(MethodItem, shorty, METHOD_ITEM_SHORTY) 41 | CHECK_MEMBER_OFFSET(MethodItem, methodId, METHOD_ITEM_ORIGINAL_METHOD) 42 | CHECK_MEMBER_OFFSET(MethodItem, isStatic, METHOD_ITEM_ISSTATIC) 43 | #ifdef __arm__ 44 | CHECK_MEMBER_OFFSET(MethodItem, off_r0_r3, METHOD_ITEM_R0_R3_OFFSET) 45 | CHECK_MEMBER_OFFSET(MethodItem, off_s0_s15, METHOD_ITEM_S0_S15_OFFSET) 46 | #endif 47 | if (sizeof(MethodItem) != METHOD_ITEM_SIZE) { 48 | ERROR("sizeof(MethodItem) (%d) is not equal %s (%d)", 49 | (int)sizeof(MethodItem), "METHOD_ITEM_SIZE", METHOD_ITEM_SIZE); 50 | check_ok = false; 51 | } 52 | 53 | 54 | return check_ok; 55 | } 56 | 57 | //define the hook method 58 | #define DEFINE(name, class_name, method_name, siganture, callback, index) \ 59 | extern "C" void HOOK_METHOD_ENTRY(name) (); 60 | #include "method-list.def" 61 | #undef DEFINE 62 | 63 | #ifdef __arm__ 64 | #define REG_OFFSETS_DEFAULT , {0xff, 0xff, 0xff, 0xff}, \ 65 | {0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff} 66 | #else 67 | #define REG_OFFSETS_DEFAULT 68 | #endif 69 | 70 | 71 | 72 | void log_callback(JNIEnv* env, MethodItem *methodItem, void* context, const jvalue* args, int count, void* pthread, void* result); 73 | 74 | MethodItem g_method_items[] = { 75 | #define DEFINE(name, class_name, method_name, signature, callback, index) \ 76 | {class_name, method_name, signature, callback, (void*)(HOOK_METHOD_ENTRY(name)), NULL, NULL, 0, NULL, 0, false \ 77 | REG_OFFSETS_DEFAULT, 0, {0}}, 78 | #include "method-list.def" 79 | #undef DEFINE 80 | }; 81 | 82 | jint JNI_OnLoad(JavaVM* vm, void*) { 83 | g_jvm = vm; 84 | 85 | if (!setup_check()) { 86 | ALOGE("check failed"); 87 | return -1; 88 | } 89 | 90 | JNIEnv* env = NULL; 91 | 92 | if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) { 93 | ALOGE("GetEnv failed"); 94 | return -1; 95 | } 96 | 97 | 98 | if (!init_method_list(env)) { 99 | ALOGE("init method failed"); 100 | return -1; 101 | } 102 | 103 | return JNI_VERSION_1_4; 104 | } 105 | 106 | 107 | JNIEnv* AttachCurrentThread() 108 | { 109 | JNIEnv* env = NULL; 110 | g_jvm->AttachCurrentThread(&env, NULL); 111 | return env; 112 | } 113 | 114 | 115 | static jclass get_object_class(JNIEnv* env) { 116 | static jclass object_class = NULL; 117 | if (object_class == NULL) { 118 | object_class = (jclass)env->NewGlobalRef(env->FindClass("java/lang/Object")); 119 | } 120 | return object_class; 121 | } 122 | 123 | template 124 | struct PrimitypeValueOf { 125 | typedef typename TPrimitype::base_type base_type; 126 | JClass clazz; 127 | JMethod valueOf; 128 | 129 | PrimitypeValueOf(JNIEnv* env) 130 | : clazz(env, TPrimitype::getClassName()) 131 | , valueOf(env, clazz, "valueOf", TPrimitype::getSignature()) 132 | { } 133 | 134 | static jobject of(JNIEnv* env, base_type value) { 135 | instance.ensureInitialized(env); 136 | return instance->valueOf(env, instance->clazz.clazz, value); 137 | } 138 | 139 | static JLazyInstance > instance; 140 | }; 141 | template 142 | JLazyInstance > PrimitypeValueOf::instance; 143 | 144 | #define DEF_PRIMITYPE(Name, BaseType, Sig) \ 145 | struct Name { \ 146 | typedef BaseType base_type; \ 147 | static const char* getClassName() { return "java/lang/" #Name; } \ 148 | static const char* getSignature() { return "(" #Sig ")Ljava/lang/" #Name ";"; } \ 149 | }; \ 150 | typedef PrimitypeValueOf Name##Value; 151 | 152 | DEF_PRIMITYPE(Integer, jint, I) 153 | DEF_PRIMITYPE(Char, jchar, C) 154 | DEF_PRIMITYPE(Byte, jbyte, B) 155 | DEF_PRIMITYPE(Boolean, jboolean, Z) 156 | DEF_PRIMITYPE(Float, jfloat, F) 157 | DEF_PRIMITYPE(Double, jdouble, D) 158 | DEF_PRIMITYPE(Short, jshort, S) 159 | DEF_PRIMITYPE(Long, jlong, J) 160 | 161 | #undef DEF_PRIMITYPE 162 | 163 | 164 | 165 | 166 | static jobject toObject(JNIEnv* env, char type, jvalue value) { 167 | switch(type) { 168 | case 'L' : case '[': return value.l; 169 | case 'D': return DoubleValue::of(env, value.d); 170 | case 'J': return LongValue::of(env, value.j); 171 | case 'F': return FloatValue::of(env, value.f); 172 | case 'B': return ByteValue::of(env, value.b); 173 | case 'I': return IntegerValue::of(env, value.i); 174 | case 'S': return ShortValue::of(env, value.s); 175 | case 'C': return CharValue::of(env, value.c); 176 | default: return 0; 177 | } 178 | } 179 | 180 | 181 | MethodArgs::MethodArgs(JNIEnv* e, MethodItem* pMethodItem, const jvalue* values, int arg_count) { 182 | env = e; 183 | //create method object 184 | method = env->ToReflectedMethod(pMethodItem->ownerClass, pMethodItem->methodId, pMethodItem->isStatic); 185 | 186 | if (values == NULL) { 187 | args = NULL; 188 | } else { 189 | args = env->NewObjectArray(arg_count, get_object_class(e), 0); 190 | const char* shorty = pMethodItem->shorty; 191 | int i = 0; 192 | if (!pMethodItem->isStatic) { 193 | env->SetObjectArrayElement(args, 0, values[0].l); 194 | i ++; 195 | } else { 196 | shorty ++; //skip the result 197 | } 198 | for (; i < arg_count; i++) { 199 | env->SetObjectArrayElement(args, i, toObject(e, shorty[i], values[i])); 200 | } 201 | } 202 | } 203 | 204 | MethodArgs::~MethodArgs() { 205 | if (env) { 206 | if (method) 207 | env->DeleteLocalRef(method); 208 | if (args) 209 | env->DeleteLocalRef(args); 210 | } 211 | } 212 | 213 | 214 | -------------------------------------------------------------------------------- /method-entries.asm: -------------------------------------------------------------------------------- 1 | 2 | #include "hook-asm.h" 3 | 4 | #define DEFINE(name, class_name, method_name, signature, callback, index) \ 5 | AsmHookEntry HOOK_METHOD_ENTRY(name), index 6 | 7 | #include "method-list.def" 8 | 9 | #undef DEFINE 10 | 11 | -------------------------------------------------------------------------------- /method-list.def: -------------------------------------------------------------------------------- 1 | 2 | //DEFINE(name, class_name, method_name, siganture, callback, index) 3 | 4 | DEFINE(app_Activity_onCreate, "android/app/Activity", "onCreate", "(Landroid/os/Bundle;)V", log_callback, 0) 5 | DEFINE(app_Activity_onDestroy, "android/app/Activity", "onDestroy", "()V", log_callback, 1) 6 | 7 | -------------------------------------------------------------------------------- /stub.cpp: -------------------------------------------------------------------------------- 1 | #include "hook.h" 2 | 3 | #include "dex_file.h" 4 | #include "runtime.h" 5 | #include "thread.h" 6 | #include "stack.h" 7 | #include "class_linker.h" 8 | #include "mirror/object.h" 9 | #include "mirror/object-inl.h" 10 | #include "mirror/dex_cache.h" 11 | #include "handle_scope.h" 12 | #if PLATFORM_SDK_VERSION >= 23 //android M 13 | #include "art_method.h" 14 | #else 15 | #include "mirror/art_method.h" 16 | #endif 17 | #include "oat.h" 18 | #include "oat_file.h" 19 | #include "entrypoints/quick/quick_entrypoints.h" 20 | #if PLATFORM_SDK_VERSION < 24 21 | #include "entrypoints/interpreter/interpreter_entrypoints.h" 22 | #endif 23 | #include "interpreter/interpreter.h" 24 | 25 | using art::ManagedStack; 26 | using art::Thread; 27 | using art::Runtime; 28 | using art::JValue; 29 | using art::ShadowFrame; 30 | using art::HandleScope; 31 | #if PLATFORM_SDK_VERSION >= 23 //android M 32 | using art::ArtMethod; 33 | #if PLATFORM_SDK_VERSION < 24 34 | using art::EntryPointFromInterpreter; 35 | #endif 36 | using art::StackedShadowFrameType; 37 | #else 38 | using art::mirror::ArtMethod; 39 | using art::mirror::EntryPointFromInterpreter; 40 | #endif 41 | using art::StackReference; 42 | #if PLATFORM_SDK_VERSION < 24 43 | using art::InterpreterEntryPoints; 44 | #endif 45 | using art::JNIEnvExt; 46 | 47 | #define CALLEE_SAVE_METHOD_TYPE Runtime::kSaveAll 48 | 49 | static void handle_deoptimize_exception(Thread *thread) { 50 | #if PLATFORM_SDK_VERSION >= 23 51 | if(thread->GetException() == Thread::GetDeoptimizationException()) { 52 | #else 53 | if(thread->GetException(nullptr) == Thread::GetDeoptimizationException()) { 54 | #endif 55 | thread->ClearException(); 56 | JValue* result = new JValue; 57 | #if PLATFORM_SDK_VERSION >= 23 58 | ShadowFrame* shadow_frame = thread->PopStackedShadowFrame(StackedShadowFrameType::kDeoptimizationShadowFrame); 59 | thread->SetTopOfStack(nullptr); 60 | #else 61 | ShadowFrame* shadow_frame = thread->GetAndClearDeoptimizationShadowFrame(result); 62 | thread->SetTopOfStack(nullptr, 0); 63 | #endif 64 | thread->SetTopOfShadowStack(shadow_frame); 65 | #if PLATFORM_SDK_VERSION >= 24 66 | ::art::interpreter::EnterInterpreterFromDeoptimize(thread, shadow_frame, true, result); 67 | #else 68 | ::art::interpreter::EnterInterpreterFromDeoptimize(thread, shadow_frame, result); 69 | #endif 70 | } 71 | } 72 | 73 | #ifdef ENABLE_DEBUG 74 | extern "C" void show_mem_values(void** sp, int count, int reg); 75 | #endif 76 | 77 | /*extern "C" void log_managed_stack(const char* pref, Thread* pThread) { 78 | const ManagedStack* managedStack = pThread->GetManagedStack(); 79 | for(int i = 0; managedStack && i < 20; i++) { 80 | ALOGD("==DJJ %s managedStack=%p, topqucikFrame=%p", pref, managedStack, managedStack->GetTopQuickFrame()); 81 | managedStack = managedStack->GetLink(); 82 | } 83 | }*/ 84 | 85 | struct CallbackContext { 86 | void *args; 87 | HandleScope * top_handle_scope; 88 | }; 89 | 90 | extern "C" void call_user_callback(MethodItem* pMethodItem, 91 | void* args, void **caller_sp, Thread *pThread, void * result) { 92 | 93 | DEBUG("call_user_callback, PMethodItem=%p, pThread=%p, args=%p, caller_sp=%p, result=%p", 94 | pMethodItem, pThread, args, caller_sp, result); 95 | //add the managed stack 96 | //log_managed_stack("top", pThread); 97 | ManagedStack& managed_stack = *(const_cast(pThread->GetManagedStack())); 98 | 99 | DEBUG("call_user_callback, PMethod:%s.%s%s", pMethodItem->className, pMethodItem->methodName, pMethodItem->methodSignature); 100 | 101 | show_method_item(pMethodItem); 102 | 103 | #if PLATFORM_SDK_VERSION >= 21 104 | if (caller_sp != NULL) { 105 | Runtime * runtime = Runtime::Current(); 106 | caller_sp[0] = (void*) runtime->GetCalleeSaveMethod(CALLEE_SAVE_METHOD_TYPE); 107 | //ALOGD("==DJJ caller_sp=%p, aller_sp[0]=%p manageedstack=%p", caller_sp, caller_sp[0], &managed_stack); 108 | } 109 | #endif 110 | 111 | #if PLATFORM_SDK_VERSION >= 23 112 | managed_stack.SetTopQuickFrame((ArtMethod**)caller_sp); 113 | #else 114 | managed_stack.SetTopQuickFrame((StackReference*)caller_sp); 115 | #endif 116 | 117 | //procted the value 118 | 119 | //push handle scope 120 | HandleScope * top_handle_scope = NULL; 121 | top_handle_scope = HandleScope::Create(alloca( 122 | sizeof(HandleScope) + (pMethodItem->object_arg_count + 1) * 4), 123 | pThread->GetTopHandleScope(), pMethodItem->object_arg_count + 1); 124 | 125 | uint32_t *reference_args = (uint32_t*)args; 126 | uint32_t *top_handles = (uint32_t*)(top_handle_scope + 1); 127 | if (pMethodItem->isStatic) { 128 | //push the class 129 | ArtMethod * method = (ArtMethod*)(pMethodItem->methodId); 130 | top_handles[0] = (uint32_t)(reinterpret_cast(method->GetDeclaringClass())); 131 | } else { 132 | top_handles[0] = reference_args[0]; 133 | } 134 | 135 | for (int i = 0; i < pMethodItem->object_arg_count; i++) { 136 | top_handles[i+1] = *(reference_args + pMethodItem->object_arg_offsets[i]);//skip this 137 | } 138 | 139 | 140 | pThread->PushHandleScope(top_handle_scope); 141 | //push the local ref cookie 142 | JNIEnvExt * env = pThread->GetJniEnv(); 143 | uint32_t saved_local_ref_cookie = env->local_ref_cookie; 144 | env->local_ref_cookie = env->locals.GetSegmentState(); 145 | 146 | if (pMethodItem->callback) { 147 | int arg_count = strlen(pMethodItem->shorty) - 1; 148 | if (!pMethodItem->isStatic) { 149 | arg_count += 1; 150 | } 151 | jvalue* arg_values = NULL; 152 | if (arg_count > 0) { 153 | arg_values = (jvalue*)alloca(sizeof(jvalue) * arg_count); 154 | uint32_t * ref_args = (uint32_t*)args; 155 | 156 | int idx = 0; 157 | int obj_ref_idx = 0; 158 | if (!pMethodItem->isStatic) { 159 | arg_values[0].l = (jobject)top_handles; 160 | ref_args ++; 161 | idx ++; 162 | } 163 | 164 | const char* shorty = pMethodItem->shorty + 1; 165 | for (int i = 0; shorty[i]; i++) { 166 | switch(shorty[i]) { 167 | case 'D': 168 | case 'J': 169 | arg_values[idx++].j = *((jlong*)ref_args); 170 | ref_args += 2; 171 | break; 172 | case 'L': case '[': 173 | if (top_handles[1 + obj_ref_idx] == 0) { 174 | arg_values[idx++].l = 0; 175 | } else { 176 | arg_values[idx++].l = (jobject)(top_handles + 1 + obj_ref_idx); 177 | } 178 | obj_ref_idx ++; 179 | ref_args ++; 180 | break; 181 | default: 182 | arg_values[idx++].i = *((jint*)ref_args); 183 | ref_args ++; 184 | break; 185 | } 186 | } 187 | } 188 | //log_managed_stack("after", pThread); 189 | 190 | //call the userdata 191 | PMethodCallback cb = pMethodItem->callback; 192 | 193 | CallbackContext context; 194 | context.args = args; 195 | context.top_handle_scope = top_handle_scope; 196 | 197 | cb(env, pMethodItem, &context, arg_values, arg_count, (void*)pThread, result); 198 | } 199 | 200 | env->locals.SetSegmentState(env->local_ref_cookie); 201 | env->local_ref_cookie = saved_local_ref_cookie; 202 | pThread->PopHandleScope(); 203 | 204 | handle_deoptimize_exception(pThread); 205 | 206 | } 207 | 208 | 209 | extern "C" void call_original_method(MethodItem* pMethodItem, const void* args, void* pthread, void* result); 210 | 211 | void call_original_entry(MethodItem* pMethodItem, void* context, void* pthread, void* result) { 212 | ManagedStack managed_stack; 213 | Thread* pThread = (Thread*)pthread; 214 | CallbackContext* ctx = (CallbackContext*) context; 215 | //log_managed_stack("before call original", pThread); 216 | 217 | //update the context to args, avoid gc 218 | uint32_t * ref_args = (uint32_t*)(ctx->args); 219 | int ref_count = ctx->top_handle_scope->NumberOfReferences(); 220 | uint32_t * top_handle = (uint32_t*)(ctx->top_handle_scope + 1); 221 | if (!pMethodItem->isStatic) { 222 | ref_args[0] = top_handle[0]; 223 | } 224 | for (int i = 1; i < ref_count; i++) { 225 | ref_args[pMethodItem->object_arg_offsets[i-1]] 226 | = top_handle[i]; 227 | } 228 | 229 | pThread->PushManagedStackFragment(&managed_stack); 230 | call_original_method(pMethodItem, ctx->args, pthread, result); 231 | pThread->PopManagedStackFragment(managed_stack); 232 | //log_managed_stack("after pop call original", pThread); 233 | } 234 | 235 | 236 | void * get_method_entry(jmethodID methodId) { 237 | ArtMethod* method = (ArtMethod*)methodId; 238 | if (method != NULL) { 239 | return const_cast(method->GetEntryPointFromQuickCompiledCode()); 240 | } 241 | return NULL; 242 | } 243 | 244 | void set_method_entry(jmethodID methodId, void* callback) { 245 | ArtMethod* method = (ArtMethod*)methodId; 246 | if (method != NULL) { 247 | method->SetEntryPointFromQuickCompiledCode(callback); 248 | } 249 | } 250 | 251 | 252 | -------------------------------------------------------------------------------- /user-callback.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "hook.h" 6 | #include "jnihelp.h" 7 | 8 | struct AndroidUtilLog { 9 | JClass clazz; 10 | JMethod i; 11 | JMethod d; 12 | 13 | AndroidUtilLog(JNIEnv* env) 14 | : clazz(env, "android/util/Log") 15 | , i(env, clazz, "i", "(Ljava/lang/String;Ljava/lang/String;)I") 16 | , d(env, clazz, "d", "(Ljava/lang/String;Ljava/lang/String;)I") 17 | { } 18 | }; 19 | 20 | struct JavaLangException { 21 | JClass clazz; 22 | JConstructor ctr; 23 | JMethod printStackTrace; 24 | 25 | JavaLangException(JNIEnv* env) 26 | : clazz(env, "java/lang/Exception") 27 | , ctr(env, clazz, "()V") 28 | , printStackTrace(env, clazz, "printStackTrace", "()V") 29 | {} 30 | }; 31 | 32 | struct ZygoteInit { 33 | JClass clazz; 34 | JMethod myLog; 35 | 36 | ZygoteInit(JNIEnv* env) 37 | : clazz(env, "com/android/internal/os/ZygoteInit") 38 | , myLog(env, clazz, "myLog", "(Ljava/lang/reflect/Method;[Ljava/lang/Object;)V") 39 | {} 40 | }; 41 | 42 | 43 | static JLazyInstance android_util_Log; 44 | static JLazyInstance java_lang_Exception; 45 | static JLazyInstance zygoteInit; 46 | 47 | extern "C" int _enable_log_stack; 48 | extern "C" void log_managed_stack(const char*, void*); 49 | 50 | void log_callback(JNIEnv* env, MethodItem *methodItem, void* context, const jvalue* args, int count, void* pthread, void* result) { 51 | struct timeval tv_start, tv_end; 52 | 53 | android_util_Log.ensureInitialized(env); 54 | java_lang_Exception.ensureInitialized(env); 55 | zygoteInit.ensureInitialized(env); 56 | 57 | gettimeofday(&tv_start, NULL); 58 | 59 | //call old method 60 | call_original_entry(methodItem, context, pthread, result); 61 | 62 | //log_managed_stack("after original", pthread); 63 | 64 | MethodArgs mthArgs(env, methodItem, args, count); 65 | zygoteInit->myLog(env, zygoteInit->clazz.clazz, mthArgs.method, mthArgs.args); 66 | 67 | 68 | gettimeofday(&tv_end, NULL); 69 | 70 | 71 | char szbuff[512]; 72 | sprintf(szbuff, "%s.%s.%s took %dms", methodItem->className, methodItem->methodName, methodItem->methodSignature, 73 | (int)((tv_end.tv_sec - tv_start.tv_sec) * 1000 + (tv_end.tv_usec - tv_start.tv_usec) / 1000)); 74 | android_util_Log->i(env, android_util_Log->clazz.clazz, "MYLOG", (const char*)szbuff); 75 | //log_managed_stack("after log", pthread); 76 | 77 | jobject exception = java_lang_Exception->ctr(env); 78 | //ALOGD("==DJJ create exception=%p", exception); 79 | java_lang_Exception->printStackTrace(env, exception); 80 | //ALOGD("==DJJ printStackTrace exception=%p", exception); 81 | env->DeleteLocalRef(exception); 82 | //jobject obj = java_lang_Object->ctr(env); 83 | //env->DeleteLocalRef(obj); 84 | } 85 | 86 | 87 | --------------------------------------------------------------------------------