使用Cydia Substrate Hook Android JNINativeInterface.md


Hook JNINativeInterface

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 具體實現方法,如GetMethodID

static jmethodID GetMethodID(JNIEnv* env, jclass jclazz, const char* name, const char* sig) {
ScopedJniThreadState ts(env){
....
}

編譯過后沒有導出函數.無法通過dlsym拿到函數地址.想要hook這個函數就要拿到這個函數的地址.
查看jni.h 函數頭.
JNIEnv結構:

typedef _JNIEnv JNIEnv;

_JNIEnv結構:

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結構:

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);

實現

關鍵部分代碼:hook RegisterNatives方法

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);

這個函數指針

*(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).

測試


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM