欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android對so進行簡單hook思路解析

 更新時間:2023年04月10日 10:04:17   作者:Yocn  
這篇文章主要為大家介紹了Android對so進行簡單hook思路解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

1、什么是Hook

Hook 技術(shù)又叫做鉤子函數(shù),在系統(tǒng)沒有調(diào)用該函數(shù)之前,鉤子程序就先捕獲該消息,鉤子函數(shù)先得到控制權(quán),這時鉤子函數(shù)既可以加工處理(改變)該函數(shù)的執(zhí)行行為,還可以強制結(jié)束消息的傳遞。簡單來說,就是把系統(tǒng)的程序拉出來變成我們自己執(zhí)行代碼片段。

2、對App的so進行Hook的一種思路

我們知道現(xiàn)在JNI在Android開發(fā)中是特別重要的,使用JNI有什么好處呢?

  • Preference,C/C++在運行性能上面甩Java幾條GAI
  • Security,更多的加密解密還是放在Native上。

優(yōu)點不止這兩點,比如在Native里面開辟空間并不受JVM管理,JVM怎么使用native memory。這里不再贅述。

本文提供一種對Android上so庫進行Hook的一種思路,不涉及ELF的查看修改,不改動對方的調(diào)用方式。 思路就是一招偷梁換柱,用自己的so替換App的so,讓對象調(diào)用自己的so的時候調(diào)用我們自己寫的so,我們再調(diào)用原來的so,這樣就可以獲得對方so方法的輸入輸出。

可以應(yīng)用在想獲取對方App的數(shù)據(jù)傳遞格式或者無法破解對方的加解密,但是可以通過hook獲取對方的數(shù)據(jù)格式再調(diào)用對方的加解密方法得到自己想要的結(jié)果。

3、一個最基本的JNI sample程序代表目標(biāo)宿主Apk

這里我們自己準(zhǔn)備一個宿主Apk,直接用AndroidStudio新建一個支持JNI的工程,勾選Include C++ support,默認(rèn)生成一個Android工程。

默認(rèn)的MainActivity.java和native-lib.cpp分別長這樣子:

//MainActivity
public class MainActivity extends Activity {
    static {
        System.loadLibrary("native-lib");
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());
    }
    public native String stringFromJNI(String param);
}
//native-lib.cpp
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_hook_yocn_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

我們?yōu)榱诉壿嬊逦晕⒆鲆稽c修改,邏輯是希望傳入一個字符串,然后在C++里面對字符串的每個字符都+1操作,也就是 java -> kbwb。改完之后代碼長這個樣子:

//MainActivity.java
public class MainActivity extends Activity {
    static {
        System.loadLibrary("native-lib");
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(stringFromJNI("java"));
    }
    public native String stringFromJNI(String param);
}
//native-lib.cpp
#include <jni.h>
#include <string>
#include <android/log.h>
#define LOG_TAG "hook"
#define LOGV(...)  __android_log_print(ANDROID_LOG_VERBOSE,LOG_TAG,__VA_ARGS__)
#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
extern "C" JNIEXPORT jstring JNICALL
Java_com_hook_yocn_MainActivity_stringFromJNI(JNIEnv *env, jobject obj, jstring param) {
    int length = (env)->GetStringLength(param);
    const char *nativeString = (env)->GetStringUTFChars(param, 0);
    char *resultChars = new char[length + 1];
    for (int i = 0; i < length; ++i) {
        resultChars[i] = nativeString[i] + 1;
    }
    resultChars[length] = '\0';
    std::string par = resultChars;
    LOGE("輸入?yún)?shù)->%s,長度:%d", nativeString, length);
    LOGE("輸出結(jié)果->%s", resultChars);
    return env->NewStringUTF(resultChars);
}

我們跑一下,如果正常的話,輸出應(yīng)該是:

現(xiàn)在準(zhǔn)備工作就做完了,我們有了一個目標(biāo)宿主Apk,下面開始著手進行Hook。

4、開始Hook

我們需要一臺root了的手機或者一個Android模擬器,這里我用模擬器演示。
先理一下思路,按照我們的理解,需要以下幾步:

  • 4.1、 編寫Hook的代碼,并打包成so
  • 4.2、 找到目標(biāo)app我們要替換的so的存放目錄,把對方的so復(fù)制一份,用我們自己寫的so替換掉對方的so。
  • 4.3、 重新運行app查看調(diào)用結(jié)果

我們按照思路一步步往下走:

4.1、編寫Hook代碼

因為上面的宿主Apk是我們自己寫的,所以我們知道調(diào)用的方法名字,而面對一個我們并不了解的陌生Apk的時候,Apk尤其是so對我們來說完全是一個黑盒,這個時候我們怎么知道要怎么編寫Hook的代碼呢?

  • 使用IDA查看對方的so文件,這個我也不熟悉,大牛隨便用。
  • 換個思路,so我們看不了,但是我們可以查看java代碼,可以從java代碼中找思路。
  • 什么都不用,直接運行,哪個方法報錯我們就準(zhǔn)備神呢么方法。我們用這個方法講解。

所以我們先編一個空的so出來,命名為libhook.so,或者隨便找個so直接到第4.2步,找到app的so存放目錄。這個目錄一般在data/data/packageName/lib/xxx.so,比如我現(xiàn)在宿主包名是com.ahook.yocn,需要hook的so叫做libnative-lib.so,所以目錄應(yīng)該是在/data/data/com.ahook.yocn/lib/native-lib.so。
我們直接在terminal里面執(zhí)行:

// 我們自己的libhook.so push到內(nèi)存卡并且重命名為libnative-lib.so
adb push libhook.so /mnt/sdcard/libnative-lib.so
// 進到手機目錄
adb shell
// 獲取root權(quán)限
su
// 到so存放目錄
cd /data/data/com.ahook.yocn/lib
// 宿主的so拷貝一份,因為我們還要調(diào)用,起個別名,我們還要用
cp libnative-lib.so libnative-lib-src.so
// 將我們push進來的so拷貝到當(dāng)前目錄并覆蓋宿主apk的so
cp /mnt/sdcard/libnative-lib.so .

su是為了獲取root權(quán)限,因為data/data/目錄需要root權(quán)限才可以進。我們退出app(如果沒有退出的話),重新打開app,不出意料會報錯,如果沒有報錯可能是上面的so拷貝沒有生效,需要double check。

2019-07-06 21:49:45.531 12052-12052/com.ahook.yocn E/com.ahook.yocn: No implementation found for java.lang.String com.hook.yocn.MainActivity.stringFromJNI(java.lang.String) (tried Java_com_hook_yocn_MainActivity_stringFromJNI and Java_com_hook_yocn_MainActivity_stringFromJNI__Ljava_lang_String_2)
2019-07-06 21:49:45.533 12052-12052/com.ahook.yocn E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.ahook.yocn, PID: 12052
    java.lang.UnsatisfiedLinkError: No implementation found for java.lang.String com.hook.yocn.MainActivity.stringFromJNI(java.lang.String) (tried Java_com_hook_yocn_MainActivity_stringFromJNI and Java_com_hook_yocn_MainActivity_stringFromJNI__Ljava_lang_String_2)
        at com.hook.yocn.MainActivity.stringFromJNI(Native Method)
        at com.hook.yocn.MainActivity.onCreate(MainActivity.java:21)
 ......

報錯告訴我們有一個全限定名為com.hook.yocn.MainActivity.stringFromJNI的方法沒有找到,這個方法接受一個String參數(shù),并且有一個String返回值~
So,我們找到了宿主Apk調(diào)用的第一個方法的名字,并且知道它的參數(shù)和返回值,我們可以開始干活了。

// hook.cpp
#include <jni.h>
#include <string.h>
#include <vector>
#include <stdio.h>
#include <android/log.h>
#include <android/bitmap.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string>
#define LOG_TAG "hook"
#define LOGV(...)  __android_log_print(ANDROID_LOG_VERBOSE,LOG_TAG,__VA_ARGS__)
#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
using namespace std;
extern "C" {
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
//宿主動態(tài)鏈接庫路徑
#define LIB_CACULATE_PATH "/data/data/com.ahook.yocn/lib/libnative-lib-src.so"
//函數(shù)指針
typedef jstring (*CAC_FUNC)(JNIEnv *env, jobject thiz, jstring param);
jstring callFunc(JNIEnv *env, jobject thiz, jstring param) {
    void *handle;
    char *error;
    CAC_FUNC cac_func = NULL;
    //打開動態(tài)鏈接庫
    handle = dlopen(LIB_CACULATE_PATH, RTLD_LAZY);
    if (!handle) {
        LOGV("dlopen: %s\n", dlerror());
    }
    //清除之前存在的錯誤
    dlerror();
    //獲取一個函數(shù)
    *(void **) (&cac_func) = dlsym(handle, "Java_com_hook_yocn_MainActivity_stringFromJNI");
    if ((error = const_cast<char *>(dlerror())) != NULL) {
        LOGV("dlsym: %s\n", error);
    }
    jstring ret = (*cac_func)(env, thiz, param);
//    printfJstring(env, thiz, ret);
    //關(guān)閉動態(tài)鏈接庫
//    dlclose(handle);
    return ret;
}
JNIEXPORT jstring JNICALL
Java_com_hook_yocn_MainActivity_stringFromJNI(JNIEnv *env, jobject thiz, jstring param) {
    LOGE("Java_com_hook_yocn_MainActivity_stringFromJNI");
    string hookPre = "Hook_Head ";
    string paramString = (env)->GetStringUTFChars(param, 0) + hookPre;
    string modifyString = hookPre + paramString;
    jstring modifyParam = env->NewStringUTF(modifyString.c_str());
    jstring ss = callFunc(env, thiz, modifyParam);
    string rawResult = (env)->GetStringUTFChars(ss, 0);
    string hookEndString = rawResult + " Hook_End";
    return env->NewStringUTF(hookEndString.c_str());
}
} //extern "C"
//Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_LDLIBS    := -llog -ljnigraphics
LOCAL_MODULE    := hook
APP_PROJECT_PATH:= LOCAL_PATH
LOCAL_SRC_FILES := hook.cpp
LOCAL_CFLAGS    =  -ffast-math -O3 -funroll-loops
include $(BUILD_SHARED_LIBRARY)
//Application.mk
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := x86
APP_PLATFORM := android-28

代碼很簡單

  • 實現(xiàn)一個Java_com_hook_yocn_MainActivity_stringFromJNI的JNI方法接受一個jstring,返回一個jstring。
  • 找到宿主原來的so,存到LIB_CACULATE_PATH里,使用dlopen方法打開并且調(diào)用它自己的stringFromJNI方法并且得到一個jstring返回值。這里我們可以對方法的輸入輸出任意修改

我本身更熟悉ndk-build,如果熟悉makefile,也可以用makefile。因為我用的是模擬器所以Application.mk里面APP_ABI數(shù)x86,如果用真機的是arm架構(gòu)的可以修改成armeabi-v7a,編寫完之后目錄結(jié)構(gòu)差不多這樣子的,然后進到j(luò)ni目錄執(zhí)行ndk-build可以得到libhook.so。

得到了so之后我們執(zhí)行

adb push libhook.so /mnt/sdcard/libnative-lib.so
...

然后重復(fù)上面的代碼替換掉宿主的so。執(zhí)行完之后重新打開app,應(yīng)該能看到下面這樣的輸出。

如果能夠得到結(jié)果就說明我們hook成功了。可能宿主apk的方法不止一個,我們成功模擬了第一個方法后后面的還會報錯,所以我們需要一直重復(fù)上面的步驟直到運行正?;蛘叩玫轿覀兿胍臄?shù)據(jù)為止。

我們整理一下思路:

  • 找到目標(biāo)app我們要替換的so的存放目錄,把對方的so復(fù)制一份。
  • 打個空的so包后者隨便找個so,用我們自己的so替換掉對方的so。
  • 重新運行app查看調(diào)用結(jié)果,這時候肯定會出錯,根據(jù)出錯的全限定方法名編寫我們的hook代碼
  • 根據(jù)全限定名和輸入輸出編寫宿主需要的代碼,用輸入調(diào)用宿主的so得到輸出,對輸出加工后返回回去,相當(dāng)于一個代理模式
  • 重復(fù)1234步直到宿主app運行正常或者得到我們想要的數(shù)據(jù)。

如果有問題可以去看源碼,本文源碼:github.com/yocn/HookSo…

以上就是Android對so進行簡單hook思路解析的詳細(xì)內(nèi)容,更多關(guān)于Android so hook的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論