Android中如何Hook住JNI方法

背景

最近在研究插件技術時遇到一個問題劳较,用插件技術調起應用驹止,應用里面的攝像頭無法打開浩聋,我就查看了攝像頭相關的源碼,發(fā)現(xiàn)問題出在了android.hardware.Camera類的初始化里面臊恋。具體來說衣洁,當我們初始化一個Camera類對象時,在初始化過程中調用了一個native的方法:

private void final int native_setup(Object camera_this,int cameraId, int halVersion, String packageName);

這個方法在JNI層通過Binder通信請求Server端的ICameraService去初始化攝像頭抖仅。在Server端ICameraService會檢查Client端傳過來的包名坊夫,然后去PackageManagerService那邊請求該包名對應的應用是否聲明了Camera相關的權限,如果有岸售,則打開攝像頭践樱,如果沒有在界面上就會提示去設置里面打開相應的權限。我們的問題就出在這里凸丸,當我們的插件調起初始化Camera類時拷邢,Camera類獲取的包名是插件的包名,而插件沒有安裝屎慢,所以在Server端進行權限校驗時就會失敗瞭稼,最終導致攝像頭無法打開

解決問題的思路

其實這個問題我們也好解,只要我們在宿主應用里面申請Camera相關的權限腻惠,然后Hook住Camera類里面的native_setup方法在調用該方法時傳入宿主的包名环肘,這樣權限校驗就能通過,攝像頭就能打開集灌。我們解決問題的具體的思路如下:

  1. 對于Java里面的native方法悔雹,在JNI層肯定有一個對應的函數(shù)

  2. 我們在JNI層找到保存這個函數(shù)地址的指針,先把指針保存起來

  3. 我們自己寫一個代理函數(shù)欣喧,然后替換上面的那個指針

  4. 在我們的代理函數(shù)里面對傳進來的參數(shù)做修改(針對我們的這個Case只需要替換包名腌零,把“com.baidu.browser.apps”替換成我們的宿主包名),然后再調用我們在上面保存的函數(shù)指針唆阿,傳進我們修改后的參數(shù)完成相應的操作

    針對上面解決問題的思路益涧,我們的目標就很清晰了,首先并且最重要的是:我們得在JNI層找到android.hardware.Camera.native_setup的對應的函數(shù)驯鳖。

JNI中如何去調用Java方法

// 這是調用Java中的static方法
jmethodID  method = env->GetStaticMethodID(g_jclass,        JAVA_CALLBACK__ON_KILL_PROCESS, JAVA_CALLBACK__ON_KILL_PROCESS_SIGNATURE);
env->CallStaticVoidMethod(g_jclass, method, pid, sig);

// 這是調用Java中的非static方法
jmethodID  method = env->GetMethodID(g_jclass, JAVA_CALLBACK__ON_KILL_PROCESS, JAVA_CALLBACK__ON_KILL_PROCESS_SIGNATURE);
env->CallVoidMethod(g_jclass, method, pid, sig);

從上面的代碼我們看到闲询,不管是調用靜態(tài)方法還是非靜態(tài)方法,我們都得找到代表這個方法的jmethodID浅辙,然后通過這個jmethodID去調用JNIEnv的相應的CallXXXXMethod;

jmethodID到底是什么呢扭弧?從jni.h文件里面可以看到下面這段定義:

struct _jmethodID;                      /* opaque structure */
typedef struct _jmethodID* jmethodID;   /* method IDs */

其實可以看到jmethodID其實就是一個結構體指針,這個結構體叫__jmethodID是如何定義的呢摔握?其定義如下:

struct Method {

    ClassObject*    clazz;  // 代表這個方法所屬的類

    u4  accessFlags;     // 訪問標識符寄狼, 定義是public? native氨淌?synchronized泊愧?

    u2  methodIndex;   // index,在ClassObject中一個保存Method對象的數(shù)組的位置盛正。

    u2  registersSize;  // 僅對native方法有用删咱,保存方法的參數(shù)的個數(shù)。

    u2  outsSize;   // unknown豪筝, 不重要

    u2  insSize;    // unknown痰滋, 不重要

    const char*   name;   // 方法名

    DexProto  prototype;   // 我們可以理解這是一個代表該方法簽名的字符串(包含返回值以及參數(shù)類型)

    const char*  shorty; // 跟上面的意義,方法的簽名续崖,like:(ILjava/lang/String)V

    const u2*  insns;  // 方法的指令代碼

    int  jniArgInfo;  // jni參數(shù)信息敲街,好像沒啥用

    DalvikBridgeFunc nativeFunc;   // native函數(shù)指針,可能是真正的native函數(shù)严望,也可能是JNI橋接函數(shù)

    bool fastJni;   // unknown多艇, 不重要

    bool noRef;     // unknown, 不重要

    bool shouldTrace;   // unknown像吻, 不重要

    const RegisterMap* registerMap;   // unknown峻黍, 不重要

    bool   inProfile;   // unknown, 不重要
};

針對上面的這些參數(shù)拨匆,我們只挑幾個重要的下面可能會用到的說一下:

  1. accessFlags: 在Dalvik虛擬機中定義了一系列的值用來指示這個方法是public的還是private的姆涩,是static的還是非static的,是native 的還是非native的惭每,有沒有final骨饿、synchronized、absttact等等關鍵字台腥。 我們可以修改這個值來修改一個方法的屬性宏赘,比如說下面這段代碼,可以講一個非native的方法修改成native的方法:

     public static final int ACC_NATIVE = 0x0100;
    
     (method->accessFlags & ACC_NATIVE) != 0
    
  2. registersSize:這個保存方法的參數(shù)個數(shù)

  3. nativeFuc:這個最重要览爵,它是一個函數(shù)指針置鼻,這個指針可能指向該方法對應的native函數(shù),也可能指向一個橋接函數(shù)蜓竹。簡單的說就是箕母,在Dalvik虛擬機下面,這個指向的是橋接函數(shù)俱济;在Art虛擬機下嘶是,指向的是native函數(shù)。至于這個橋接函數(shù)蛛碌,它的聲明如下:

     typedef void (* DalvikBridgeFunc)(const u4* args, JValue* pResult,
         const Method* method, struct Thread* self);
    

這個橋接函數(shù)的參數(shù)我解釋一下:

args: 存儲的是調研native方法傳過來的參數(shù)聂喇,對于非static方法,args[0]存儲的是this對象,args[1]存儲的是第一個參數(shù)值希太,args[2]存儲的是第二個參數(shù)值克饶,以此類推。對于static對象誊辉,args[0]存儲的是第一個參數(shù)值矾湃,args[1]存儲的是第二個參數(shù)值, 舉個例子,比如說對于我自己定一個一個方法getSubString(String aString, int aStart, int aLength), 這是一個非static方法堕澄。 我現(xiàn)在調用該方法邀跃,getString("This is a sentence", 5, 6); 當方法走到我們的橋接函數(shù)中時,我們可以通過args[1]獲取到字符串"This is a sentence", 通過args[2]獲取到5蛙紫,通過args[3]獲取到6拍屑。

pResult: 存儲函數(shù)的返回值

method: 存儲與該橋接函數(shù)對應的method對象。

self: 抱歉坑傅,這個我也沒搞明白僵驰。-_-

對于native函數(shù),這個就簡單了裁蚁,看下面的代碼就一目了然了:

    static JNINativeMethod gMethods[] = {
        {"hookNativeMethod", "(Ljava/lang/Object;Ljava/lang/String;Z)Z", (void *) native_hook}

    };
    
    void native_hook(JNIEnv *env, jclass clazz, jobject method, jstring pkg,            
        jboolean isArt)

    env->RegisterNatives(javaClass, gMethods, 1)

上面的"hookNativeMethod"對應的是java里面的聲明為native的方法矢渊, native_hook代表的是native函數(shù),我們通過JNIEnv 的RegisterNatives將這他們關聯(lián)起來枉证。 java層調用hookNativeMethod最終回轉化成native層調用native_hook函數(shù)矮男。在Art虛擬機下,Method結構體中存儲的nativeFuc指針指向的就是這個native_hook函數(shù)室谚。

上面已經(jīng)解釋清楚jmethodID代表啥之后毡鉴,下面我們就來著手Hook住Camara的native_setup方法:我們分步來實現(xiàn)Hook:

第一步: 獲取native_setup方法的jmethodID值:

我們直接看代碼:

private static native boolean hookNativeMethod(Object method, String packageName);

public static void fixCamera() {
    if (!sNativeFixed) {
        try {
            Method native_setup;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                native_setup = Reflect.on(Camera.class).exactMethod("native_setup",
                        new Class[]{Object.class, int.class, int.class, String.class});
            } else {
                native_setup = Reflect.on(Camera.class).exactMethod("native_setup",
                        new Class[]{Object.class, int.class, String.class});
            }
            hookNativeMethod(native_setup, VirtualCore.getCore().getHostPkg());
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        sNativeFixed = true;
    }
}

這是Java層代碼,我們通過反射獲取到Camera類里面native_setup方法對應的Method對象(注意秒赤,這是Java里面的Method類猪瞬,不是我們上面說的Method結構體);然后我們通過一個native方法hookNativeMethod將這個Method傳到JNI層入篮。

下面是JNI層的代碼:

jmethodID mtd_openDexNative =  env->FromReflectedMethod(javaMethod);

在JNI層陈瘦,我們調用JNIEnv的FromReflectedMethod方法,傳入java層傳過來的代表Method對象的jobject對象潮售,得到了native_setup在native層的jmethodID對象痊项。

第二步:我們找到jmethodID之后,接下來要找到這個結構體里面的nativeFuc指針

正如我們上面所說酥诽,這個jmethodID其實就是上面的我們解釋的Method結構體了鞍泉,我們現(xiàn)在就是要找到這個結構體里面的nativeFunc指針,然后把這個指針改成我們自己定義的一個函數(shù)肮帐,這樣就完成了Hook咖驮。那么我們怎么去找到這個nativeFuc指針呢,一種比較簡單的方法就是將jmethodID強制轉換成結構題Method的指針,但是我們目前沒這么做托修,原因很簡單忘巧,上面的Method結構體里面還包含了另外一些結構題,如DexProto诀黍、RegisterMap等等袋坑,這兩個結構題里面可能還包含了其他的結構體仗处,我要么得把這些結構體都拷過來眯勾,要么我就得引用定義這些結構體的頭文件,這個比較麻煩婆誓。我們換用另一種方法:

我們在java類里面定義一個mark()的native方法吃环,在JNI里面也定義一個nativeMark()函數(shù),然后通過JNIEnv的RegisterNatives函數(shù)將他們兩個關聯(lián)起來洋幻。然后我們通過JNIEnv的GetMethodId函數(shù)獲取到mark方法的jmethodID郁轻,jmethodID是Method結構體的指針(其實就是這個結構體的內存起始地址),我們知道這個指針之后文留,下面要分兩種情況來分析了:

  • Art虛擬機下
    我們上面說了Art虛擬機下面結構體Method里面的nativeFunc其實指向的就是native函數(shù)好唯,在我們這個案例中指向的就是nativeMark函數(shù)。那下面就好辦了:我們用一個while循環(huán)燥翅,從jmethodID所代表的內存地址開始骑篙,4個字節(jié)4個字節(jié)的往后尋址,然后通過“*”取出所指地址中存儲的值森书,用這個值跟nativeHook指針比較靶端,當相等時,我們就找到了結構體Method里面的nativeFuc的偏移量凛膏。

  • Dalvik虛擬機下
    還是上面說的杨名,在Dalvik虛擬機下結構體Method里面的nativeFunc指向的是一個橋接函數(shù),那么我們如何去找到保存這個橋接函數(shù)的指針呢猖毫?可以通過下面這段代碼找到:

char vmSoName[15] = {0};
__system_property_get("persist.sys.dalvik.vm.lib", vmSoName);
LOGD("Find the so name : %s.", strlen(vmSoName) == 0 ? "<EMPTY>" : vmSoName);

void *vmHandle = dlopen(vmSoName, 0);
if (!vmHandle) {
    LOGE("Unable to open the %s.", vmSoName);
    vmHandle = RTLD_DEFAULT;
}

gOffset.art_work_around_app_jni_bugs = dlsym(vmHandle, "art_work_around_app_jni_bugs");

上面這段代碼可以找到橋接函數(shù)的指針台谍,同樣,我們從jmethodID代表的內存地址開始吁断,4個字節(jié)4個字節(jié)的往后尋址趁蕊,然后通過“*”取出所指地址中存儲的值,用這個值跟我們剛剛找到的橋接函數(shù)的指針比較胯府,當相等時介衔,我們就找到了結構體Method里面的nativeFuc的偏移量。
下面這段代碼就是尋址和比較的代碼:

void mark() {
     // Do nothing
};

void searchJniOffset(JNIEnv *env, bool isArt) {

    jclass g_class = env->FindClass(JAVA_CLASS);
    jmethodID mtd_nativeHook = env->GetStaticMethodID(g_class, "mark", "()V");

    size_t startAddress = (size_t) mtd_nativeHook;
    size_t targetAddress = (size_t) nativeMark;
    if (isArt && gOffset.art_work_around_app_jni_bugs) {
        targetAddress = (size_t) gOffset.art_work_around_app_jni_bugs;
    }

    int offset = 0;
    bool found = false;
    while (true) {
        if (*((size_t *) (startAddress + offset)) == targetAddress) {
            found = true;
            break;
        }
        
        offset += 4;
        if (offset >= 100) {
            LOGE("Ops: Unable to find the jni function.");
            break;
        }
    }
    
    if (found) {
        gOffset.nativeOffset = offset;
        if (!isArt) {
            gOffset.nativeOffset += (sizeof(int) + sizeof(void *));
        }
        LOGD("Hoho, Get the offset : %d.", gOffset.nativeOffset);
    }
}

上面我們找到nativeFunc偏移量之后,我們前面又找到了Camera類的native_setup對應的jmethodID骂因,jmethodID加上這個偏移量就是native_setup對應的Method結構體里的nativeFuc地址炎咖,這個地址里面存儲了一個C函數(shù)指針,在Dalvik虛擬機下,這個是橋接函數(shù)的指針乘盼,在Art虛擬機下這個指針就是native函數(shù)的指針升熊。

第三步:找到C函數(shù)指針之后,把指針替換掉绸栅,換成我們定義的Hook函數(shù)的函數(shù)指針

這一步就很簡單了级野,直接看代碼:

inline void replaceImplementation(JNIEnv *env, jobject javaMethod, jboolean isArt) {

    size_t mtd_openDexNative = (size_t) env->FromReflectedMethod(javaMethod);
    int nativeFuncOffset = gOffset.nativeOffset;
    void **jniFuncPtr = (void **) (mtd_openDexNative + nativeFuncOffset);

    if (!isArt) {
        LOGD("The vm is dalvik");
        gOffset.orig_DalvikBridgeFunc = (Bridge_DalvikBridgeFunc) (*jniFuncPtr);
        *jniFuncPtr = (void *) new_bridge_nativeSetUpFunc;
    } else {
        char vmSoName[4] = {0};
        __system_property_get("ro.build.version.sdk", vmSoName);
        int sdk;
        sscanf(vmSoName, "%d", &sdk);
        LOGD("The vm is art and the sdk int is %d", sdk);

        if (sdk < 21) {
            gOffset.orig_native_openDexNativeDalvikFunc = (Native_nativeSetUpDalvikFunc) (*jniFuncPtr);
            *jniFuncPtr = (void *) new_nativeSetUpDalvikFunc;
        } else {
            gOffset.orig_native_openDexNativeFunc = (Native_nativeSetUpFunc) (*jniFuncPtr);
            *jniFuncPtr = (void *) new_nativeSetUpFunc;
        }

    }

}

這里我們解釋一下,我們區(qū)分了Dalvik虛擬機和Art虛擬機兩種情形粹胯,Art虛擬機下直接替換的就是native函數(shù)蓖柔,而Dalvik虛擬機下我們替換的是橋接函數(shù)。其實更通俗的講就是风纠,Art虛擬機下面我們Hook的是真正的native函數(shù)况鸣,在Dalvik虛擬機下面我們要Hook的是橋接函數(shù)。

第四步: 定義Hook函數(shù)竹观,在第三步中會用我們定義的Hook函數(shù)替換掉原來的函數(shù)

直接看代碼镐捧,分Dalvik虛擬機和Art虛擬機:

// 這個函數(shù)是Art虛擬機下的hook函數(shù)
static jobject new_nativeSetUpArtFunc(JNIEnv *env, jclass jclazz,
                                     jobject weak_this, jint halVersion,
                                     jstring clientPackageName) {
    jstring newPkg = env->NewStringUTF(newPkgName);

    return gOffset.orig_native_openArtNativeDalvikFunc(env, jclazz, weak_this, halVersion, newPkg);
}

// 這個是Dalvik虛擬機下面的hook函數(shù),用該函數(shù)去Hook橋接函數(shù)
static void new_bridge_nativeSetUpFunc(const void **args, void *pResult, const void *method, void *self) {
    JNIEnv *env = NULL;
    g_vm->GetEnv((void **) &env, JNI_VERSION_1_6);
    g_vm->AttachCurrentThread(&env, NULL);

    typedef char* (*GetCstrFromString)(void *);
    typedef void* (*GetStringFromCstr)(const char*);

    const char* origPkg3 = args[3] == NULL ? NULL : gOffset.GetCstrFromString((void*) args[3]);
    LOGD("The original package3 is: %s", origPkg3);

    args[3] = gOffset.GetStringFromCstr(newPkgName);
    gOffset.orig_DalvikBridgeFunc(args, pResult, method, self);
}

至此臭增,我們分四步就已經(jīng)完成了Camera類的native_setup的Hook懂酱。總結起來就是:

  1. 找到Camera.native_setup方法在jni層的jmethodID
  2. 在jmethodID中找到nativeFuc誊抛,將這個值改成我們自己定義的Hook函數(shù)的地址
  3. 自己寫Hook函數(shù)列牺,要分Dalvik虛擬機和Art虛擬機

進階(如果你想,你就能)

其實上面的代碼可以實現(xiàn)所有Java方法的Hook芍锚;
對于native的方法上面的代碼稍微把方法名昔园,方法簽名修改一下,實現(xiàn)一下對應的Hook函數(shù)就可以了并炮。
對于非native的方法默刚,我們可以把Method結構體中的accessFlags改一下,就可以把它變成native方法逃魄,然后將nativeFuc賦值成我們寫的Hook函數(shù)荤西,然后再改一下registersSize和insSize的值,這就是保存參數(shù)個數(shù)的地方伍俘,最后再該一下jniArgsInfo值邪锌,具體的改法可以參照git上的代碼AllHookInOne

下面是我寫的完整版的代碼

這是Java層的代碼

<!-- lang:java -->
public class CameraFixer {
    
    private static native boolean hookNativeMethod(
            Object method, String packageName, boolean isArt);
    
    private static native void hook();
}

這是C代碼:

camera_hook.h

#ifndef VIRTUALAPP_CAMERA_HOOK_H
#define VIRTUALAPP_CAMERA_HOOK_H

#include <jni.h>
#include <stdlib.h>
#include <android/log.h>
#include <dlfcn.h>
#include <stddef.h>
#include <fcntl.h>
#include <sys/system_properties.h>
#include <stdio.h>

#define TAG "HOOKCAMERA"
#define JAVA_CLASS "com/baidu/freeinstall/client/ContextFixer"


#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,  TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,  TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,  TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)

__BEGIN_DECLS
    JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved);
    JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved);
__END_DECLS

#endif //VIRTUALAPP_CAMERA_HOOK_H

camera_hook.c

#include "camera_hook.h"

JavaVM *g_vm;

typedef void (*Bridge_DalvikBridgeFunc)(const void **, void *, const void *,        void *);

typedef jobject (*Native_nativeSetUpFunc)(JNIEnv *, jclass, jobject weak_this,      jint cameraId, jint halVersion, jstring clientPackageName);

typedef jobject (*Native_nativeSetUpDalvikFunc)(JNIEnv *, jclass, 
    jobject weak_this,  jint halVersion, jstring clientPackageName);


void mark() {
    // Do nothing
};


static struct {
    bool isArt;
    int nativeOffset;
    void *art_work_around_app_jni_bugs;
    char *(*GetCstrFromString)(void *);
    void *(*GetStringFromCstr)(const char *);
    Bridge_DalvikBridgeFunc orig_DalvikBridgeFunc;
    Native_nativeSetUpFunc orig_native_openDexNativeFunc;
    Native_nativeSetUpDalvikFunc orig_native_openDexNativeDalvikFunc;
} gOffset;

char *newPkgName;
static jobject new_nativeSetUpFunc(JNIEnv *env, jclass jclazz,
     jobject weak_this, jint cameraId, jint halVersion, jstring clientPackageName) {
    
    jstring newPkg = env->NewStringUTF(newPkgName);
    return gOffset.orig_native_openDexNativeFunc(env, jclazz, weak_this, cameraId, halVersion, newPkg);

}

static jobject new_nativeSetUpDalvikFunc(JNIEnv *env, jclass jclazz,
    jobject weak_this, jint halVersion, jstring clientPackageName) {

    jstring newPkg = env->NewStringUTF(newPkgName);

    return gOffset.orig_native_openDexNativeDalvikFunc(env, jclazz, weak_this, halVersion, newPkg);
    
}

static void new_bridge_nativeSetUpFunc(const void **args, void *pResult, const      void *method,  void *self) {

    JNIEnv *env = NULL;
    g_vm->GetEnv((void **) &env, JNI_VERSION_1_6);
    g_vm->AttachCurrentThread(&env, NULL);

    typedef char* (*GetCstrFromString)(void *);
    typedef void* (*GetStringFromCstr)(const char*);

    const char* origPkg0 = args[0] == NULL ? NULL : gOffset.GetCstrFromString((void*) args[0]);
    LOGD("The original package0 is: %s", origPkg0);

    const char* origPkg = args[2] == NULL ? NULL : gOffset.GetCstrFromString((void*) args[2]);
    LOGD("The original package2 is: %s", origPkg);

    const char* origPkg3 = args[3] == NULL ? NULL : gOffset.GetCstrFromString((void*) args[3]);
    LOGD("The original package3 is: %s", origPkg3);

    args[3] = gOffset.GetStringFromCstr(newPkgName);
    gOffset.orig_DalvikBridgeFunc(args, pResult, method, self);
}


void searchJniOffset(JNIEnv *env, bool isArt) {

    jclass g_class = env->FindClass(JAVA_CLASS);
    jmethodID mtd_nativeHook = env->GetStaticMethodID(g_class, "nativeMark", "()V");

    size_t startAddress = (size_t) mtd_nativeHook;
    size_t targetAddress = (size_t) mark;
    if (isArt && gOffset.art_work_around_app_jni_bugs) {
        targetAddress = (size_t) gOffset.art_work_around_app_jni_bugs;
    }

    int offset = 0;
    bool found = false;
    while (true) {
        if (*((size_t *) (startAddress + offset)) == targetAddress) {
            found = true;
            break;
        }
        offset += 4;
        if (offset >= 100) {
            LOGE("Ops: Unable to find the jni function.");
            break;
        }
    }
    
    if (found) {
        gOffset.nativeOffset = offset;
        if (!isArt) {
            gOffset.nativeOffset += (sizeof(int) + sizeof(void *));
        }
        LOGD("Hoho, Get the offset : %d.", gOffset.nativeOffset);
    }
}

inline void replaceImplementation(JNIEnv *env, jobject javaMethod, jboolean isArt) {

    size_t mtd_openDexNative = (size_t) env->FromReflectedMethod(javaMethod);
    int nativeFuncOffset = gOffset.nativeOffset;
    void **jniFuncPtr = (void **) (mtd_openDexNative + nativeFuncOffset);

    if (!isArt) {
        LOGD("The vm is dalvik");
        gOffset.orig_DalvikBridgeFunc = (Bridge_DalvikBridgeFunc) (*jniFuncPtr);
        *jniFuncPtr = (void *) new_bridge_nativeSetUpFunc;
    } else {
        char vmSoName[4] = {0};
        __system_property_get("ro.build.version.sdk", vmSoName);
        int sdk;
        sscanf(vmSoName, "%d", &sdk);
        LOGD("The vm is art and the sdk int is %d", sdk);

        if (sdk < 21) {
            gOffset.orig_native_openDexNativeDalvikFunc = (Native_nativeSetUpDalvikFunc) (*jniFuncPtr);
            *jniFuncPtr = (void *) new_nativeSetUpDalvikFunc;
        } else {
            gOffset.orig_native_openDexNativeFunc = (Native_nativeSetUpFunc) (*jniFuncPtr);
            *jniFuncPtr = (void *) new_nativeSetUpFunc;
        }

    }

}

static JNINativeMethod gMarkMethods[] = {
        {"nativeMark", "()V", (void *) mark}
};

void native_hook(JNIEnv *env, jclass clazz, jobject method, jstring pkg, jboolean isArt) {

    newPkgName = (char *) env->GetStringUTFChars(pkg, NULL);
    g_vm->GetEnv((void **) &env, JNI_VERSION_1_6);
    g_vm->AttachCurrentThread(&env, NULL);

    jclass g_class = env->FindClass(JAVA_CLASS);
    if (env->RegisterNatives(g_class, gMarkMethods, 1) < 0) {
        return;
    }

    gOffset.isArt = isArt;

    char vmSoName[15] = {0};
    __system_property_get("persist.sys.dalvik.vm.lib", vmSoName);
    LOGD("Find the so name : %s.", strlen(vmSoName) == 0 ? "<EMPTY>" : vmSoName);

    void *vmHandle = dlopen(vmSoName, 0);
    if (!vmHandle) {
        LOGE("Unable to open the %s.", vmSoName);
        vmHandle = RTLD_DEFAULT;
    }


    if (isArt) {
        gOffset.art_work_around_app_jni_bugs = dlsym(vmHandle, "art_work_around_app_jni_bugs");
    } else {
        gOffset.GetCstrFromString = (char *(*)(void *)) dlsym(vmHandle,                                                 "_Z23dvmCreateCstrFromStringPK12StringObject");
        if (!gOffset.GetCstrFromString) {
            gOffset.GetCstrFromString = (char *(*)(void *)) dlsym(vmHandle,
                     "dvmCreateCstrFromString");
        }
        
        gOffset.GetStringFromCstr = (void *(*)(const char *)) dlsym(vmHandle,
                "_Z23dvmCreateStringFromCstrPKc");
        if (!gOffset.GetStringFromCstr) {
            gOffset.GetStringFromCstr = (void *(*)(const char *)) dlsym(vmHandle,  "dvmCreateStringFromCstr");
        }
    }
    
    searchJniOffset(env, isArt);
    replaceImplementation(env, method, isArt);
}


static JNINativeMethod gMethods[] = {
    { "hookNativeMethod", "(Ljava/lang/Object;Ljava/lang/String;Z)Z",
         (void *) native_hook }
};

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    g_vm = vm;
    JNIEnv *env;

    LOGE("JNI_Onload start");
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        LOGE("GetEnv() FAILED!!!");
        return JNI_ERR;
    }
    
    jclass javaClass = env->FindClass(JAVA_CLASS);
    LOGE("we have found the class: %s", JAVA_CLASS);
    if (javaClass == NULL) {
        LOGE("unable to find class: %s", JAVA_CLASS);
        return JNI_ERR;
    }
    
    env->UnregisterNatives(javaClass);
    if (env->RegisterNatives(javaClass, gMethods, 1) < 0) {
        LOGE("register methods FAILED!!!");
        return JNI_ERR;
    }

    env->DeleteLocalRef(javaClass);
    LOGI("JavaVM::GetEnv() SUCCESS!");
    return JNI_VERSION_1_6;
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末癌瘾,一起剝皮案震驚了整個濱河市觅丰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌妨退,老刑警劉巖妇萄,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜕企,死亡現(xiàn)場離奇詭異,居然都是意外死亡冠句,警方通過查閱死者的電腦和手機轻掩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來懦底,“玉大人唇牧,你說我怎么就攤上這事【厶疲” “怎么了丐重?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長拱层。 經(jīng)常有香客問我弥臼,道長,這世上最難降的妖魔是什么根灯? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮掺栅,結果婚禮上烙肺,老公的妹妹穿的比我還像新娘。我一直安慰自己氧卧,他們只是感情好桃笙,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著沙绝,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上麸拄,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天氛悬,我揣著相機與錄音,去河邊找鬼粗悯。 笑死虚循,一個胖子當著我的面吹牛,可吹牛的內容都是我干的样傍。 我是一名探鬼主播横缔,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼衫哥!你這毒婦竟也來了茎刚?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤撤逢,失蹤者是張志新(化名)和其女友劉穎膛锭,沒想到半個月后捌斧,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡泉沾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年捞蚂,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片跷究。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡姓迅,死狀恐怖,靈堂內的尸體忽然破棺而出俊马,到底是詐尸還是另有隱情丁存,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布柴我,位于F島的核電站解寝,受9級特大地震影響,放射性物質發(fā)生泄漏艘儒。R本人自食惡果不足惜聋伦,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望界睁。 院中可真熱鬧觉增,春花似錦、人聲如沸翻斟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽访惜。三九已至嘹履,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間债热,已是汗流浹背砾嫉。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留阳柔,地道東北人焰枢。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像舌剂,于是被迫代替她去往敵國和親济锄。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內容