http://bbs.pediy.com/showthread.php?t=200398, 很早就有大大給出方法了.
記得看雪已經有人發過一份源碼:用來hook jni 用來打印日誌的,http://bbs.pediy.com/showthread.php?t=209914.,使用的是ELF-ARM-HOOK-Library庫,
因爲我的緣由常用模擬器測試app,目前比較好用的模擬器都是基於x86的,上面兩位提供的都是基於arm的.
Cydia Substrate支持arm和x86 hook
參考上面兩位的方法,和dvm源碼
JNINativeInterface 具體實現方法,如GetMethodIDphp
static jmethodID GetMethodID(JNIEnv* env, jclass jclazz, const char* name, const char* sig) { ScopedJniThreadState ts(env){ .... }
編譯事後沒有導出函數.沒法經過dlsym拿到函數地址.想要hook這個函數就要拿到這個函數的地址.
查看jni.h 函數頭.
JNIEnv結構:android
typedef _JNIEnv JNIEnv;
_JNIEnv結構:c++
struct _JNIEnv { /* do not rename this; it does not seem to be entirely opaque */ const struct JNINativeInterface* functions; #if defined(__cplusplus) jint GetVersion() { return functions->GetVersion(this); } ....
JNINativeInterface結構:git
struct JNINativeInterface { void* reserved0; void* reserved1; void* reserved2; void* reserved3; jint (*GetVersion)(JNIEnv *); jclass (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*, jsize); jclass (*FindClass)(JNIEnv*, const char*); ....
能夠看到JNINativeInterfac結構全是函數的指針:拿到這個結構體中函數的指針,就拿到具體函數的指針.
這些結構體中的方法初始化實現,能夠參考:JNIEnv*dvmCreateJNIEnv(Thread* self);github
關鍵部分代碼:hook RegisterNatives方法app
void Jnimethod::startHookJni(JNIEnv * env) { #if defined(__i386__) MSHookFunction((void*)*(unsigned int*)((*(unsigned int*)env) + 0x35C), (void*)&myRegisterNatives, (void**)&oldRegisterNatives); return; #else ....
其中關鍵部分:函數
(void*)*(unsigned int*)((*(unsigned int*)env) + 0x35C)
解析:*(unsigned int*)env+ 0x35C 先取JNINativeInterface結構體的地址,0x35C爲RegisterNatives在結構體中的偏移.即拿到:測試
jint (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*, jint);
這個函數指針this
*(unsigned int*)((*(unsigned int*)env) + 0x35C)//拿到RegisterNatives函數的地址
拿到函數指針便可實現hook.指針
jint(*oldRegisterNatives)(JNIEnv* env, jclass c, const JNINativeMethod* m, jint n); jint myRegisterNatives(JNIEnv* env, jclass c, const JNINativeMethod* m, jint n) { const char*clazzName = getClassName(env, c); __android_log_print(ANDROID_LOG_INFO, "RegisterNatives", "start! jclass[%s] methods[0x%x]%d ", clazzName, (unsigned int)m, n); for (int i = 0; i < n; i++) { __android_log_print(ANDROID_LOG_INFO, "RegisterNatives", "method[%s%s] fnPtr[0x%x]", m[i].name, m[i].signature, (unsigned int)m[i].fnPtr); } __android_log_print(ANDROID_LOG_INFO, "RegisterNatives", "end! jclass[%s] ", clazzName); return oldRegisterNatives(env, c, m, n); } void Jnimethod::startHookJni(JNIEnv * env) { MSHookFunction((void*)*(unsigned int*)((*(unsigned int*)env) + 0x35C), (void*)&myRegisterNatives, (void**)&oldRegisterNatives); return;
之前,打印jni日誌老是在手機上打印.如今能夠在模擬器上打印了,也能夠丟掉:ELF-ARM-HOOK-Library,這個庫了(主要是cydia之處arm和x86,我的比較喜歡cydia).