日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

dex注入实现详解

發(fā)布時(shí)間:2025/3/15 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 dex注入实现详解 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

最近在研究Android綠色安全這一塊,具體到上層的業(yè)務(wù)就是“去第三方APP的廣告”。如果既想使用第三方APP,又不想看到一些無(wú)良的廣告,那dex注入基本無(wú)法避免。本文針對(duì)網(wǎng)上一些大牛分享的文章,進(jìn)行了一些簡(jiǎn)單的實(shí)現(xiàn),總結(jié)和分享自是不能少的。Ps:感謝金山毒霸實(shí)現(xiàn)了該功能,感謝大牛們破解之后的無(wú)私分享。

參考文章

金山手機(jī)毒霸工作原理【引用1】

【原創(chuàng)】手機(jī)毒霸去廣告功能分析一:總體分析【引用2】

【原創(chuàng)】手機(jī)毒霸去廣告功能分析三:java代碼(dex)注入

Android中的so注入(inject)和掛鉤(hook) - For both x86 and arm 【引用3】

源碼相關(guān)

android.os.Handler 自行eclipse 關(guān)聯(lián)即可;

android.app.ActivityThread 在線(xiàn)代碼;

實(shí)現(xiàn)目標(biāo)

系統(tǒng):Android 4.2.2 平板
功能:將一段dex代碼注入到HelloWord APP中,dex對(duì)應(yīng)的java代碼要求能夠攔截目標(biāo)APP中的onPause與onResume 回調(diào),輸出打印。

基本原理

其實(shí)原理在各路大牛的文章里面已經(jīng)解釋的很清楚了,這里再不厭其煩的絮叨絮叨,主要是捋一捋思路,別整亂嘍。

1.獲得root權(quán)限后,通過(guò)ptrace()注入到指定pid的進(jìn)程中;

Android下的注入都是從Linux下ptrace()函數(shù)繼承下來(lái)的,具體原理不便深入。網(wǎng)上大牛已有相關(guān)的開(kāi)源工具,這里采用【引用3】篇幅中博主開(kāi)源出的代碼,注意修改對(duì)應(yīng)參數(shù)即可。【引用3】中是以注入系統(tǒng)進(jìn)程/system/bin/surfaceflinger為例的,我們這里需要修改成目標(biāo)APP的包名。這部分拿到開(kāi)源代碼之后使用ndk編譯生成注入工具文件inject;

2.注入代碼調(diào)用功能庫(kù).so中的接口,它的作用是利用反射注入dex文件、并調(diào)用相應(yīng)的java代碼;

這里對(duì)應(yīng)的就是【引用3】中hello.c部分了,作為注入的功能代碼關(guān)鍵部分,這部分不能打印兩句草草了事。這里采用【引用1】中對(duì)金山毒霸分析結(jié)果得出的代碼拿出來(lái)來(lái)實(shí)現(xiàn)dex注入與java層代碼調(diào)用,具體實(shí)現(xiàn)與分析見(jiàn)后文。這里也是C代碼通過(guò)ndk編譯生成注入功能庫(kù),呃,libhelloTool.so;

3.生成dex的java源碼通過(guò)反射置換 ActivityThread 中mH屬性中的的mCallback回調(diào),來(lái)實(shí)現(xiàn)攔截Activity生命周期回調(diào)的HOOK功能;

通過(guò)上一步程序會(huì)執(zhí)行到j(luò)ava層中,既然要下鉤子,就要弄清楚我們要鉤在哪里才有效。很明顯的,既然要攔截界面的onPause、onResume消息,那就必須要了解Activity的生命周期回調(diào)在底層是如何實(shí)現(xiàn)消息分發(fā)的,知其所以然之后才好下手。有了前面提到的幾篇大牛博客的文章,我們可以很清晰的定位到android.app.ActivityThread 類(lèi)中的mH變量:

public final class ActivityThread { … final H mH = new H(); … private void queueOrSendMessage(int what, Object obj, int arg1, int arg2) {synchronized (this) {if (DEBUG_MESSAGES) Slog.v(TAG, "SCHEDULE " + what + " " + mH.codeToString(what)+ ": " + arg1 + " / " + obj);Message msg = Message.obtain();msg.what = what;msg.obj = obj;msg.arg1 = arg1;msg.arg2 = arg2;mH.sendMessage(msg);} } … private class H extends Handler {public static final int LAUNCH_ACTIVITY = 100;public static final int PAUSE_ACTIVITY = 101;public static final int PAUSE_ACTIVITY_FINISHING= 102;public static final int STOP_ACTIVITY_SHOW = 103;public static final int STOP_ACTIVITY_HIDE = 104;public static final int SHOW_WINDOW = 105;public static final int HIDE_WINDOW = 106;public static final int RESUME_ACTIVITY = 107;public static final int SEND_RESULT = 108;…public void handleMessage(Message msg) {if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));switch (msg.what) {case LAUNCH_ACTIVITY: {Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");ActivityClientRecord r = (ActivityClientRecord)msg.obj;r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);handleLaunchActivity(r, null);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);} break;…}}… }

從源碼上就能看出來(lái),底層消息派發(fā)都在內(nèi)部類(lèi)H中實(shí)現(xiàn),而H實(shí)際上是Handler的子類(lèi)。對(duì)應(yīng)的H本身是個(gè)final類(lèi)型的內(nèi)部私有類(lèi),做手腳不甚方便,考慮到要攔截的實(shí)際情況,伸手到其父類(lèi)中的屬性的mCallback回調(diào)就是個(gè)很好的選擇了。從前面所引博客中對(duì)金山毒霸的反編譯情況來(lái)看即是這個(gè)思路。引用【引用2】中的一句話(huà)即:

f) 替換當(dāng)前ActivityThread中的mH(Handler類(lèi)型)的mCallback,用金山自定義的一個(gè)callback對(duì)象來(lái)包裹過(guò)原callback并且替換原callback,從而起到hook作用。

實(shí)現(xiàn)流程

必備工具

  • root工具,想什么辦法把測(cè)試機(jī)器root掉,或者直接用虛擬機(jī);
  • NDK工具,各路注入工具都需要ndk來(lái)編譯,本人使用的版本是:android-ndk-r8b;
  • 基礎(chǔ)Android 開(kāi)發(fā)環(huán)境,具體就不用多說(shuō)了~
  • 注意:為了快速調(diào)通,這里所有參數(shù)都是寫(xiě)死的,這就限制了后續(xù)驗(yàn)證流程必須要實(shí)現(xiàn)過(guò)程中的代碼編寫(xiě)的一致。如果只是作為預(yù)研、測(cè)試是否可行的階段,這種做法無(wú)可厚非;相對(duì)的如果是正規(guī)的開(kāi)發(fā)流程中,在迭代周期里面做好通用性的設(shè)計(jì)是必要的。

    開(kāi)始實(shí)現(xiàn)

    建立目標(biāo)APP
    這一步最簡(jiǎn)單了,新建一個(gè)HelloWord Android工程,運(yùn)行安裝到測(cè)試機(jī)器中,我這里設(shè)置了包名為:com.inject.helloword 后面注入工具中需要用到;

    注入工具

    如【引用3】的方法,簡(jiǎn)歷文件夾填好配置文件。編輯injec.c文件更換參數(shù),主要在main函數(shù)中:

    int main(int argc, char** argv) {pid_t target_pid;//更換為目標(biāo)應(yīng)用的包名target_pid = find_pid_of("com.inject.helloword");if (-1 == target_pid) {printf("Can't find the process\n");return -1;}//設(shè)置注入代碼庫(kù)位置、調(diào)用接口與參數(shù)inject_remote_process(target_pid, "/data/libhelloTool.so", "hook_entry", "I'm parameter!hehe", strlen("I'm parameter! hehe "));return 0; }

    如果想深入研究注入的原理,可以仔細(xì)分析相關(guān)函數(shù)的實(shí)現(xiàn)即可,NDK編譯后生成注入工具inject文件;

    kf2lc@kf2lc-OptiPlex-3020:~/develop/inject/jni$ ndk-build

    Compile x86 : inject <= jni inject.c

    Executable : inject

    Install : inject =libs/x86/inject

    Compile thumb : inject <= jni inject.c

    Executable : inject

    Install : inject =libs/armeabi-v7a/inject

    注入代碼庫(kù)

    建立文件夾如【引用3】所述,修改注入接口方法hook_entry如【引用1】中的分析實(shí)現(xiàn),代碼并注釋如下:

    #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <android/log.h> #include <elf.h> #include <fcntl.h> #include <jni.h> #include <dlfcn.h>#define LOG_TAG "DEBUG" #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args) int invoke_dex_method(const char* dexPath, const char* dexOptDir, const char* className, const char* methodName, int argc, char *argv[]);int hook_entry(char * a){ LOGD("Hook success, pid = %d\n", getpid()); LOGD("Hello %s\n", a); //參數(shù)直接寫(xiě)死是個(gè)取巧的方法int ret = invoke_dex_method("/data/injects/DexInject.apk","/data/data/com.inject.helloword/cache","com/inject/dexinject/HookTool","dexInject",0,NULL);LOGD("Hello %d\n",ret);return 0; }JNIEnv* (*getJNIEnv)(); /** * PARAM: * dexPath要注入的apk/jar路徑 * dexOptDir 緩存路徑,注意需要目標(biāo)應(yīng)用進(jìn)程中可寫(xiě)的目錄 * className 執(zhí)行方法所在類(lèi)名 * methodName 執(zhí)行的方法名 * argc 參數(shù)之流這里沒(méi)有使用 * argv 參數(shù)之流這里沒(méi)有使用 */ int invoke_dex_method(const char* dexPath, const char* dexOptDir, const char* className, const char* methodName, int argc, char *argv[]) {//獲取JNIEnvvoid* handle = dlopen("/system/lib/libandroid_runtime.so", RTLD_NOW);getJNIEnv = dlsym(handle, "_ZN7android14AndroidRuntime9getJNIEnvEv");JNIEnv* env = getJNIEnv();//調(diào)用ClassLoader中的getSystemClassLoader方法獲取當(dāng)前進(jìn)程的ClassLoaderjclass classloaderClass = (*env)->FindClass(env,"java/lang/ClassLoader");jmethodID getsysloaderMethod = (*env)->GetStaticMethodID(env,classloaderClass, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");jobject loader = (*env)->CallStaticObjectMethod(env, classloaderClass, getsysloaderMethod);//以進(jìn)程現(xiàn)有的ClassLoader、要注入的dex路徑為參數(shù)構(gòu)造注入后的DexClassLoaderjstring dexpath = (*env)->NewStringUTF(env, dexPath);jstring dex_odex_path = (*env)->NewStringUTF(env,dexOptDir);jclass dexLoaderClass = (*env)->FindClass(env,"dalvik/system/DexClassLoader");jmethodID initDexLoaderMethod = (*env)->GetMethodID(env, dexLoaderClass, "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V");jobject dexLoader = (*env)->NewObject(env, dexLoaderClass, initDexLoaderMethod,dexpath,dex_odex_path,NULL,loader);//獲取新出爐的DexClassLoader中findClass方法加載dex中要執(zhí)行代碼所在類(lèi)jmethodID findclassMethod = (*env)->GetMethodID(env,dexLoaderClass,"findClass","(Ljava/lang/String;)Ljava/lang/Class;");jstring javaClassName = (*env)->NewStringUTF(env,className);jclass javaClientClass = (*env)->CallObjectMethod(env,dexLoader,findclassMethod,javaClassName);//獲取注入dex中要執(zhí)行的方法jmethodID start_inject_method = (*env)->GetStaticMethodID(env, javaClientClass, methodName, "()V");//執(zhí)行之注意目標(biāo)方法必須是靜態(tài)公有的(*env)->CallStaticVoidMethod(env,javaClientClass,start_inject_method); }

    原帖中倒數(shù)第二句GetStaticMethodID方法所給的參數(shù)有誤,這里修正一下,如此就完成了dex注入并調(diào)用了java代碼中的:com.inject.dexinject. HookTool. dexInject() 方法。同上采用NDK編譯,生成注入庫(kù):libhelloTool.so。

    生成dex

    說(shuō)是dex注入,實(shí)際上從上一段的代碼中可以知道最終采用的是DexClassLoader類(lèi)來(lái)實(shí)現(xiàn)注入。托之前研究過(guò)一段APK加殼的福,對(duì)這里還相對(duì)比較了解,DexClassLoader的主要參數(shù)路徑實(shí)際上應(yīng)該是一個(gè)apk/jar的路徑,具體可見(jiàn)相關(guān)的SDK文檔。這里直接編一個(gè)APK丟進(jìn)去就好。
    建立Android應(yīng)用工程DexInject,干掉無(wú)關(guān)的界面配置。建立如下類(lèi):

    1.自定義Callback,加入攔截操作代碼(打印);

    public class HookCallback implements Callback{public static final int RESUME_ACTIVITY = 107;public static final int PAUSE_ACTIVITY = 101;private Callback mParentCallback;public HookCallback(Callback parentCallback){mParentCallback = parentCallback;}@Overridepublic boolean handleMessage(Message msg) {switch (msg.what) {case RESUME_ACTIVITY:Log.d(HookTool.TAG, "hook activity resume!!!");break;case PAUSE_ACTIVITY:Log.d(HookTool.TAG, "hook activity pause!!!");default:Log.d(HookTool.TAG, "hook a " + msg.what);break;}if(mParentCallback != null){return mParentCallback.handleMessage(msg);}else{return false;}}}

    2.工具類(lèi),攔截實(shí)現(xiàn)代碼;

    public class HookTool {public static final String TAG = "Inject";public static void dexInject() {Log.d(TAG, "this is dex code,welcome to HookTool~");try { Object currentActivityThread = ReflectUtils.invokeStaticMethod("android.app.ActivityThread", "currentActivityThread",new Class[] {}, new Object[] {});Handler localHandler = (Handler) ReflectUtils.getFiled("android.app.ActivityThread","mH",currentActivityThread);HookCallback oriCallback = (HookCallback) ReflectUtils.getFieldObject(Handler.class, localHandler, "mCallback");HookCallback hookCallBack = new HookCallback(oriCallback);ReflectUtils.setFieldObject(Handler.class, localHandler, "mCallback", hookCallBack);} catch (IllegalArgumentException e) {e.printStackTrace();}}}

    3.反射工具類(lèi)

    這部分就不貼了,反射工具到處都是。
    最后編譯生成DexInject.apk

    驗(yàn)證結(jié)果

  • 測(cè)試機(jī)器上點(diǎn)擊目標(biāo)APP HelloWord;
  • Push各路工具和數(shù)據(jù)到測(cè)試機(jī)器:

    adb push DexInject.apk /data/injects
    adb push inject /data/
    adb push libhelloTool.so /data/

  • 運(yùn)行注入工具:

  • 這是按home退出APP再進(jìn)入,通過(guò)日志過(guò)濾器可以得到:


    原文地址: http://taoyuanxiaoqi.com/2015/03/16/dexinject/

    總結(jié)

    以上是生活随笔為你收集整理的dex注入实现详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。