您好,登錄后才能下訂單哦!
這篇“Android怎么對so進行簡單hook”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Android怎么對so進行簡單hook”文章吧。
Hook 技術又叫做鉤子函數,在系統(tǒng)沒有調用該函數之前,鉤子程序就先捕獲該消息,鉤子函數先得到控制權,這時鉤子函數既可以加工處理(改變)該函數的執(zhí)行行為,還可以強制結束消息的傳遞。簡單來說,就是把系統(tǒng)的程序拉出來變成我們自己執(zhí)行代碼片段。
我們知道現在JNI在Android開發(fā)中是特別重要的,使用JNI有什么好處呢?
Preference,C/C++在運行性能上面甩Java幾條GAI
Security,更多的加密解密還是放在Native上。
優(yōu)點不止這兩點,比如在Native里面開辟空間并不受JVM管理,JVM怎么使用native memory。這里不再贅述。
本文提供一種對Android上so庫進行Hook的一種思路,不涉及ELF的查看修改,不改動對方的調用方式。 思路就是一招偷梁換柱,用自己的so替換App的so,讓對象調用自己的so的時候調用我們自己寫的so,我們再調用原來的so,這樣就可以獲得對方so方法的輸入輸出。
可以應用在想獲取對方App的數據傳遞格式或者無法破解對方的加解密,但是可以通過hook獲取對方的數據格式再調用對方的加解密方法得到自己想要的結果。
這里我們自己準備一個宿主Apk,直接用AndroidStudio新建一個支持JNI的工程,勾選Include C++ support,默認生成一個Android工程。
默認的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("輸入參數->%s,長度:%d", nativeString, length); LOGE("輸出結果->%s", resultChars); return env->NewStringUTF(resultChars); }
我們跑一下,如果正常的話,輸出應該是:
現在準備工作就做完了,我們有了一個目標宿主Apk,下面開始著手進行Hook。
我們需要一臺root了的手機或者一個Android模擬器,這里我用模擬器演示。
先理一下思路,按照我們的理解,需要以下幾步:
4.1、 編寫Hook的代碼,并打包成so
4.2、 找到目標app我們要替換的so的存放目錄,把對方的so復制一份,用我們自己寫的so替換掉對方的so。
4.3、 重新運行app查看調用結果
我們按照思路一步步往下走:
因為上面的宿主Apk是我們自己寫的,所以我們知道調用的方法名字,而面對一個我們并不了解的陌生Apk的時候,Apk尤其是so對我們來說完全是一個黑盒,這個時候我們怎么知道要怎么編寫Hook的代碼呢?
使用IDA查看對方的so文件,這個我也不熟悉,大牛隨便用。
換個思路,so我們看不了,但是我們可以查看java代碼,可以從java代碼中找思路。
什么都不用,直接運行,哪個方法報錯我們就準備神呢么方法。我們用這個方法講解。
所以我們先編一個空的so出來,命名為libhook.so
,或者隨便找個so直接到第4.2
步,找到app的so存放目錄。這個目錄一般在data/data/packageName/lib/xxx.so
,比如我現在宿主包名是com.ahook.yocn
,需要hook的so叫做libnative-lib.so
,所以目錄應該是在/data/data/com.ahook.yocn/lib/native-lib.so
。
我們直接在terminal里面執(zhí)行:
// 我們自己的libhook.so push到內存卡并且重命名為libnative-lib.so adb push libhook.so /mnt/sdcard/libnative-lib.so // 進到手機目錄 adb shell // 獲取root權限 su // 到so存放目錄 cd /data/data/com.ahook.yocn/lib // 宿主的so拷貝一份,因為我們還要調用,起個別名,我們還要用 cp libnative-lib.so libnative-lib-src.so // 將我們push進來的so拷貝到當前目錄并覆蓋宿主apk的so cp /mnt/sdcard/libnative-lib.so .
su是為了獲取root權限,因為data/data/目錄需要root權限才可以進。我們退出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參數,并且有一個String返回值~
So,我們找到了宿主Apk調用的第一個方法的名字,并且知道它的參數和返回值,我們可以開始干活了。
// 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" //函數指針 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(); //獲取一個函數 *(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); //關閉動態(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
代碼很簡單
實現一個Java_com_hook_yocn_MainActivity_stringFromJNI的JNI方法接受一個jstring,返回一個jstring。
找到宿主原來的so,存到LIB_CACULATE_PATH里,使用dlopen方法打開并且調用它自己的stringFromJNI方法并且得到一個jstring返回值。這里我們可以對方法的輸入輸出任意修改
我本身更熟悉ndk-build,如果熟悉makefile,也可以用makefile。因為我用的是模擬器所以Application.mk里面APP_ABI數x86,如果用真機的是arm架構的可以修改成armeabi-v7a
,編寫完之后目錄結構差不多這樣子的,然后進到jni目錄執(zhí)行ndk-build可以得到libhook.so。
得到了so之后我們執(zhí)行
adb push libhook.so /mnt/sdcard/libnative-lib.so ...
然后重復上面的代碼替換掉宿主的so。執(zhí)行完之后重新打開app,應該能看到下面這樣的輸出。
如果能夠得到結果就說明我們hook成功了。可能宿主apk的方法不止一個,我們成功模擬了第一個方法后后面的還會報錯,所以我們需要一直重復上面的步驟直到運行正?;蛘叩玫轿覀兿胍臄祿橹埂?/p>
我們整理一下思路:
找到目標app我們要替換的so的存放目錄,把對方的so復制一份。
打個空的so包后者隨便找個so,用我們自己的so替換掉對方的so。
重新運行app查看調用結果,這時候肯定會出錯,根據出錯的全限定方法名編寫我們的hook代碼
根據全限定名和輸入輸出編寫宿主需要的代碼,用輸入調用宿主的so得到輸出,對輸出加工后返回回去,相當于一個代理模式
重復1234步直到宿主app運行正?;蛘叩玫轿覀兿胍臄祿?。
以上就是關于“Android怎么對so進行簡單hook”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業(yè)資訊頻道。
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。