1. 程式人生 > >Android Hook程式,對庫函式進行HOOK

Android Hook程式,對庫函式進行HOOK

1、Hook原理
Hook技術,其本質就是劫持函式的呼叫,但是由於處於Linux使用者態,每個程序都有自己獨立的程序空間,所以必須先注入到所要Hook的程序空間,修改其記憶體中的程序程式碼,替換其過程表的符號地址。在Android中,一般是通過ptrace函式附加程序,然後向程序注入so庫,從而達到監控以及遠端程序關鍵函式掛鉤。
Hook技術的難點,並不在Hook技術,如何找到函式的入口點、替換函式,這就涉及了理解函式的連線與載入機制。
2、HOOK小程式
在寫HOOK例子前,需要對先寫JNI小程式,可以參考上篇部落格:JNI小程式
下面的hook例子是我對C語言庫函式進行的hook操作。
2.1:在安卓程式中設定了Button按鈕來產生事件,事件中呼叫hook函式,及readHook函式

btnHook.setOnClickListener(new OnClickListener(){
            public void onClick(View v){
                hookFunc();
                Toast.makeText(MainActivity.this,"Hook成功"+hook("str1","str2"),Toast.LENGTH_SHORT).show();
            }
        });

下面是宣告的兩個native函式,在C程式中進行實現;

/**
     * 該方法為native方法,hook內容
     * @return
*/
public static native String hook(String str1,String str2); /** * 該方法為native ,hookFun() */ public static native void hookFunc();

載入so檔案

/**
     * 載入JNI生成so庫檔案
     */
    static{
        System.loadLibrary("hookFun");
    }

下面是我在hook.cpp檔案中實現的hook關鍵程式碼,將C庫函式strcpy()進行hook,修改為自定義的函式功能

extern "C" {
JNIEXPORT jstring JNICALL hook(JNIEnv *env, jclass tis, jstring str1,
        jstring str2) {

    char *str1_ch = jstringTostring(env,str1); //這個函式是將jstring轉化為char*
    char *str2_ch = jstringTostring(env,str2);

    char *str = strcpy(str1_ch, str2_ch); //hook後進行的呼叫

    //將char *轉化為java字串
    jclass clsstr = env->FindClass("java/lang/String");
    jmethodID ctorID = env->GetMethodID(clsstr, "<init>", "([B)V");
    jbyteArray bytes = env->NewByteArray(strlen(str));

    env->SetByteArrayRegion(bytes, 0, strlen(str), (jbyte*) str);
    jstring jdes = (jstring) env->NewObject(clsstr, ctorID, bytes);

    return jdes;
}
}

將jstring轉化為指標的函式jstringTostring()

char* jstringTostring(JNIEnv* env, jstring jstr) { //將jstring轉換為char*
    char* rtn = NULL;
    jclass clsstring = env->FindClass("java/lang/String");
    jstring strencode = env->NewStringUTF("utf-8");
    jmethodID mid = env->GetMethodID(clsstring, "getBytes",
            "(Ljava/lang/String;)[B");
    jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);
    jsize alen = env->GetArrayLength(barr);
    jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
    if (alen > 0) {
        rtn = (char*) malloc(alen + 1);

        memcpy(rtn, ba, alen);
        rtn[alen] = 0;
    }
    env->ReleaseByteArrayElements(barr, ba, 0);
    return rtn;
}

下面是對hookFun()函式進行的實現,

typedef char* (*fnread)(char *str1,char *str2);
fnread ori_strcpy = NULL;
char *hook_read(char *str1, char *str2) { //定義為自己設定的轉化規則
    char *str_hook = NULL;
    str_hook = ori_strcpy(str2,str1);
    return str_hook;
}
extern "C" void hookFunc(JNIEnv *env, jclass tis) {//中的getTargetSI,replaceFunc可以看後面
    void* libcom_hook = getTargetSI("libHookFun.so");
    if (libcom_hook == NULL) {
        LOGI("-----------hook-----------NULL------");
    }
    LOGI("%p, %d", libcom_hook, sdkVersion);
    replaceFunc(libcom_hook, "strcpy", (void*)hook_read,(void **)&ori_strcpy,
            sdkVersion);
    if (ori_strcpy == NULL) {
        LOGI("-------------ori_strcpy--------NULL--------");
    }
}

後面的是JNI的註冊函式

static JNINativeMethod getMethods[] = {
        { "hook", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
                (void*) hook } ,
                {"hookFunc","()V",(void*) hookFunc}
};

static int registerNativeMethods(JNIEnv* env, const char* className,
        JNINativeMethod* gMethods, int numMethods) {
    jclass clazz;
    jclass temp = env->FindClass(className);
    clazz = (jclass) env->NewGlobalRef(temp);
    if (clazz == NULL) {
        return 0;
    }
    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
        return 0;
    }
    return 1;
}

static int registerNatives(JNIEnv* env) {
    if (!registerNativeMethods(env, "com/example/jnihook/MainActivity",
            getMethods, sizeof(getMethods) / sizeof(getMethods[0])))
        return JNI_FALSE;
    return JNI_TRUE;
}

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void *reserved) {
    jint result = -1;
    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK)
        return -1;
    if (!registerNatives(env))
        return -1;

    char sdkchar[16];
    memset(sdkchar, 0, 16);
    __system_property_get("ro.build.version.sdk", sdkchar);
    sdkVersion = atoi(sdkchar);

    return JNI_VERSION_1_4;
}

HookUtil.cpp可以參考下面這個檔案;
hookutil.cpp
文中還有好多標頭檔案,配置資訊Android.mk,及安卓源程式沒有列出來,重要的點都列出了。