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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android 插件化原理解析——Hook机制之AMSPMS

發(fā)布時間:2025/3/15 Android 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android 插件化原理解析——Hook机制之AMSPMS 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

在前面的文章中我們介紹了DroidPlugin的Hook機制,也就是代理方式Binder Hook;插件框架通過AOP實現(xiàn)了插件使用和開發(fā)的透明性。在講述DroidPlugin如何實現(xiàn)四大組件的插件化之前,有必要說明一下它對ActivityManagerServiche以及PackageManagerService的Hook方式(以下簡稱AMS,PMS)。

ActivityManagerService對于FrameWork層的重要性不言而喻,Android的四大組件無一不與它打交道:

  • startActivity最終調(diào)用了AMS的startActivity系列方法,實現(xiàn)了Activity的啟動;Activity的生命周期回調(diào),也在AMS中完成;
  • startService,bindService最終調(diào)用到AMS的startService和bindService方法;
  • 動態(tài)廣播的注冊和接收在AMS中完成(靜態(tài)廣播在PMS中完成)
  • getContentResolver最終從AMS的getContentProvider獲取到ContentProvider
  • 而PMS則完成了諸如權(quán)限校撿(checkPermission,checkUidPermission),Apk meta信息獲取(getApplicationInfo等),四大組件信息獲取(query系列方法)等重要功能。

    在上文Android插件化原理解析——Hook機制之Binder Hook中講述了DroidPlugin的Binder Hook機制;我們知道AMS和PMS就是以Binder方式提供給應(yīng)用程序使用的系統(tǒng)服務(wù),理論上我們也可以采用這種方式Hook掉它們。但是由于這兩者使用得如此頻繁,Framework給他們了一些“特別優(yōu)待”,這也給了我們相對于Binder Hook更加穩(wěn)定可靠的hook方式。

    閱讀本文之前,可以先clone一份?understand-plugin-framework,參考此項目的ams-pms-hook模塊。另外,插件框架原理解析系列文章見索引。

    AMS獲取過程

    前文提到Android的四大組件無一不與AMS相關(guān),也許讀者還有些許疑惑;這里我就挑一個例子,依據(jù)Android源碼來說明,一個簡單的startActivity是如何調(diào)用AMS最終通過IPC到system_server的。

    不論讀者是否知道,我們使用startActivity有兩種形式:

  • 直接調(diào)用Context類的startActivity方法;這種方式啟動的Activity沒有Activity棧,因此不能以standard方式啟動,必須加上FLAG_ACTIVITY_NEW_TASK這個Flag。
  • 調(diào)用被Activity類重載過的startActivity方法,通常在我們的Activity中直接調(diào)用這個方法就是這種形式;
  • Context.startActivity

    我們查看Context類的startActivity方法,發(fā)現(xiàn)這竟然是一個抽象類;查看Context的類繼承關(guān)系圖如下:

    我們看到諸如Activity,Service等并沒有直接繼承Context,而是繼承了ContextWrapper;繼續(xù)查看ContextWrapper的實現(xiàn):

    1 2 3 4 @Override public void startActivity(Intent intent) { mBase.startActivity(intent); }

    WTF!! 果然人如其名,只是一個wrapper而已;這個mBase是什么呢?這里我先直接告訴你,它的真正實現(xiàn)是ContextImpl類;至于為什么,有一條思路:mBase是在ContextWrapper構(gòu)造的時候傳遞進來的,那么在ContextWrapper構(gòu)造的時候可以找到答案
    什么時候會構(gòu)造ContextWrapper呢?它的子類Application,Service等被創(chuàng)建的時候。

    可以在App的主線程AcitivityThread的performLaunchActivit方法里面找到答案;更詳細的解析可以參考老羅的?Android應(yīng)用程序啟動過程源代碼分析

    好了,我們姑且當(dāng)作已經(jīng)知道Context.startActivity最終使用了ContextImpl里面的方法,代碼如下:

    1 2 3 4 5 6 7 8 9 10 11 12 public void startActivity(Intent intent, Bundle options) { warnIfCallingFromSystemProcess(); if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) { throw new AndroidRuntimeException( "Calling startActivity() from outside of an Activity " + " context requires the FLAG_ACTIVITY_NEW_TASK flag." + " Is this really what you want?"); } mMainThread.getInstrumentation().execStartActivity( getOuterContext(), mMainThread.getApplicationThread(), null, (Activity)null, intent, -1, options); }

    代碼相當(dāng)簡單;我們知道了兩件事:

  • 其一,我們知道了在Service等非Activity的Context里面啟動Activity為什么需要添加FLAG_ACTIVITY_NEW_TASK;
  • 其二,真正的startActivity使用了Instrumentation類的execStartActivity方法;繼續(xù)跟蹤:
  • 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { // ... 省略無關(guān)代碼 try { intent.migrateExtraStreamToClipData(); intent.prepareToLeaveProcess(); // ----------------look here!!!!!!!!!!!!!!!!!!! int result = ActivityManagerNative.getDefault() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null ? target.mEmbeddedID : null, requestCode, 0, null, null, options); checkStartActivityResult(result, intent); } catch (RemoteException e) { } return null; }

    到這里我們發(fā)現(xiàn)真正調(diào)用的是ActivityManagerNative的startActivity方法;如果你不清楚ActivityManager,ActivityManagerService以及ActivityManagerNative之間的關(guān)系;建議先仔細閱讀我之前關(guān)于Binder的文章?Binder學(xué)習(xí)指南。

    Activity.startActivity

    Activity類的startActivity方法相比Context而言直觀了很多;這個startActivity通過若干次調(diào)用輾轉(zhuǎn)到達startActivityForResult這個方法,在這個方法內(nèi)部有如下代碼:

    1 2 3 4 Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options);

    可以看到,其實通過Activity和ContextImpl類啟動Activity并無本質(zhì)不同,他們都通過Instrumentation這個輔助類調(diào)用到了ActivityManagerNative的方法。

    Hook AMS

    OK,我們到現(xiàn)在知道;其實startActivity最終通過ActivityManagerNative這個方法遠程調(diào)用了AMS的startActivity方法。那么這個ActivityManagerNative是什么呢?

    ActivityManagerNative實際上就是ActivityManagerService這個遠程對象的Binder代理對象;每次需要與AMS打交道的時候,需要借助這個代理對象通過驅(qū)動進而完成IPC調(diào)用。

    我們繼續(xù)看ActivityManagerNative的getDefault()方法做了什么:

    1 2 3 static public IActivityManager getDefault() { return gDefault.get(); }

    gDefault這個靜態(tài)變量的定義如下:

    1 2 3 4 5 6 7 private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() { protected IActivityManager create() { IBinder b = ServiceManager.getService("activity IActivityManager am = asInterface( return am; } };

    由于整個Framework與AMS打交道是如此頻繁,framework使用了一個單例把這個AMS的代理對象保存了起來;這樣只要需要與AMS進行IPC調(diào)用,獲取這個單例即可。這是AMS這個系統(tǒng)服務(wù)與其他普通服務(wù)的不同之處,也是我們不通過Binder Hook的原因——我們只需要簡單地Hook掉這個單例即可。

    這里還有一點小麻煩:Android不同版本之間對于如何保存這個單例的代理對象是不同的;Android 2.x系統(tǒng)直接使用了一個簡單的靜態(tài)變量存儲,Android 4.x以上抽象出了一個Singleton類;具體的差異可以使用grepcode進行比較:差異

    我們以4.x以上的代碼為例說明如何Hook掉AMS;方法使用的動態(tài)代理,如果有不理解的,可以參考之前的系列文章Android插件化原理解析——Hook機制之動態(tài)代理

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Class<?> activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative"); // 獲取 gDefault 這個字段, 想辦法替換它 Field gDefaultField = activityManagerNativeClass.getDeclaredField("gDefault"); gDefaultField.setAccessible(true); Object gDefault = gDefaultField.get(null); // 4.x以上的gDefault是一個 android.util.Singleton對象; 我們?nèi)〕鲞@個單例里面的字段 Class<?> singleton = Class.forName("android.util.Singleton"); Field mInstanceField = singleton.getDeclaredField("mInstance"); mInstanceField.setAccessible(true); // ActivityManagerNative 的gDefault對象里面原始的 IActivityManager對象 Object rawIActivityManager = mInstanceField.get(gDefault); // 創(chuàng)建一個這個對象的代理對象, 然后替換這個字段, 讓我們的代理對象幫忙干活 Class<?> iActivityManagerInterface = Class.forName("android.app.IActivityManager"); Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[] { iActivityManagerInterface }, new IActivityManagerHandler(rawIActivityManager)); mInstanceField.set(gDefault, proxy);

    好了,我們hook成功之后啟動Activity看看會發(fā)生什么:

    1 2 3 4 5 6 7 8 D/HookHelper﹕ hey, baby; you are hook!! D/HookHelper﹕ method:activityResumed called with args:[android.os.BinderProxy@9bc71b2] D/HookHelper﹕ hey, baby; you are hook!! D/HookHelper﹕ method:activityIdle called with args:[android.os.BinderProxy@9bc71b2, null, false] D/HookHelper﹕ hey, baby; you are hook!! D/HookHelper﹕ method:startActivity called with args:[android.app.ActivityThread$ApplicationThread@17e750c, com.weishu.upf.ams_pms_hook.app, Intent { act=android.intent.action.VIEW dat=http://wwww.baidu.com/... }, null, android.os.BinderProxy@9bc71b2, null, -1, 0, null, null] D/HookHelper﹕ hey, baby; you are hook!! D/HookHelper﹕ method:activityPaused called with args:[android.os.BinderProxy@9bc71b2]

    可以看到,簡單的幾行代碼,AMS已經(jīng)被我們完全劫持了!! 至于劫持了能干什么,自己發(fā)揮想象吧~

    DroidPlugin關(guān)于AMS的Hook,可以查看IActivityManagerHook這個類,它處理了我上述所說的兼容性問題,其他原理相同。另外,也許有童鞋有疑問了,你用startActivity為例怎么能確保Hook掉這個靜態(tài)變量之后就能保證所有使用AMS的入口都被Hook了呢?

    答曰:無他,唯手熟爾。

    Android Framewrok層對于四大組件的處理,調(diào)用AMS服務(wù)的時候,全部都是通過使用這種方式;若有疑問可以自行查看源碼。你可以從Context類的startActivity, startService,bindService, registerBroadcastReceiver, getContentResolver 等等入口進行跟蹤,最終都會發(fā)現(xiàn)它們都會使用ActivityManagerNative的這個AMS代理對象來完成對遠程AMS的訪問。

    PMS獲取過程

    PMS的獲取也是通過Context完成的,具體就是getPackageManager這個方法;我們姑且當(dāng)作已經(jīng)知道了Context的實現(xiàn)在ContextImpl類里面,直奔ContextImpl類的getPackageManager方法:

    1 2 3 4 5 6 7 8 9 10 11 12 public PackageManager getPackageManager() { if (mPackageManager != null) { return mPackageManager; } IPackageManager pm = ActivityThread.getPackageManager(); if (pm != null) { // Doesn't matter if we make more than one instance. return (mPackageManager = new ApplicationPackageManager(this, pm)); } return null; }

    可以看到,這里干了兩件事:

  • 真正的PMS的代理對象在ActivityThread類里面
  • ContextImpl通過ApplicationPackageManager對它還進行了一層包裝
  • 我們繼續(xù)查看ActivityThread類的getPackageManager方法,源碼如下:

    1 2 3 4 5 6 7 8 public static IPackageManager getPackageManager() { if (sPackageManager != null) { return sPackageManager; } IBinder b = ServiceManager.getService("package"); sPackageManager = IPackageManager.Stub.asInterface(b); return sPackageManager; }

    可以看到,和AMS一樣,PMS的Binder代理對象也是一個全局變量存放在一個靜態(tài)字段中;我們可以如法炮制,Hook掉PMS。

    現(xiàn)在我們的目的很明切,如果需要Hook?PMS有兩個地方需要Hook掉:

  • ActivityThread的靜態(tài)字段sPackageManager
  • 通過Context類的getPackageManager方法獲取到的ApplicationPackageManager對象里面的mPM字段。
  • Hook PMS

    現(xiàn)在使用代理Hook應(yīng)該是輕車熟路了吧,通過上面的分析,我們Hook兩個地方;代碼信手拈來:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 // 獲取全局的ActivityThread對象 Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread"); Object currentActivityThread = currentActivityThreadMethod.invoke(null); // 獲取ActivityThread里面原始的 sPackageManager Field sPackageManagerField = activityThreadClass.getDeclaredField("sPackageManager"); sPackageManagerField.setAccessible(true); Object sPackageManager = sPackageManagerField.get(currentActivityThread); // 準(zhǔn)備好代理對象, 用來替換原始的對象 Class<?> iPackageManagerInterface = Class.forName("android.content.pm.IPackageManager"); Object proxy = Proxy.newProxyInstance(iPackageManagerInterface.getClassLoader(), new Class<?>[] { iPackageManagerInterface }, new HookHandler(sPackageManager)); // 1. 替換掉ActivityThread里面的 sPackageManager 字段 sPackageManagerField.set(currentActivityThread, proxy); // 2. 替換 ApplicationPackageManager里面的 mPM對象 PackageManager pm = context.getPackageManager(); Field mPmField = pm.getClass().getDeclaredField("mPM"); mPmField.setAccessible(true); mPmField.set(pm, proxy);

    好了,Hook完畢我們驗證以下結(jié)論;調(diào)用一下PMS的getInstalledApplications方法,打印日志如下:

    1 2 03-07 15:07:27.187 8306-8306/com.weishu.upf.ams_pms_hook.app D/IActivityManagerHandler﹕ hey, baby; you are hook!! 03-07 15:07:27.187 8306-8306/com.weishu.upf.ams_pms_hook.app D/IActivityManagerHandler﹕ method:getInstalledApplications called with args:[0, 0]

    OK,我們又成功劫持了PackageManager!!DroidPlugin 處理PMS的代碼可以在IPackageManagerHook查看。

    在結(jié)束講解PackageManager的Hook之前,我們需要說明一點;那就是Context的實現(xiàn)類里面沒有使用靜態(tài)全局變量來保存PMS的代理對象,而是每擁有一個Context的實例就持有了一個PMS代理對象的引用;所以這里有個很蛋疼的事情,那就是我們?nèi)绻胍耆獺ook住PMS,需要精確控制整個進程內(nèi)部創(chuàng)建的Context對象;所幸,插件框架中,插件的Activity,Service,ContentProvider,Broadcast等所有使用到Context的地方,都是由框架控制創(chuàng)建的;因此我們要小心翼翼地替換掉所有這些對象持有的PMS代理對象。

    我前面也提到過,靜態(tài)變量和單例都是良好的Hook點,這里很好地反證了這句話:想要Hook掉一個實例變量該是多么麻煩!

    小結(jié)

    寫到這里,關(guān)于DroidPlugin的Hook技術(shù)的講解已經(jīng)完結(jié)了;我相信讀者或多或少地認(rèn)識到,其實Hook并不是一項神秘的技術(shù);一個干凈,透明的框架少不了AOP,而AOP也少不了Hook。

    我所講解的Hook僅僅使用反射和動態(tài)代理技術(shù),更加強大的Hook機制可以進行字節(jié)碼編織,比如J2EE廣泛使用了cglib和asm進行AOP編程;而Android上現(xiàn)有的插件框架還是加載編譯時代碼,采用動態(tài)生成類的技術(shù)理論上也是可行的;之前有一篇文章Android動態(tài)加載黑科技 動態(tài)創(chuàng)建Activity模式,就講述了這種方式;現(xiàn)在全球的互聯(lián)網(wǎng)公司不排除有用這種技術(shù)實現(xiàn)插件框架的可能 ;我相信不遠的未來,這種技術(shù)也會在Android上大放異彩。

    了解完Hook技術(shù)之后,接下來的系列文章會講述DroidPlugin對Android四大組件在插件系統(tǒng)上的處理,插件框架對于這一部分的實現(xiàn)是DroidPlugin的精髓,Hook只不過是工具而已。學(xué)習(xí)這部分內(nèi)容需要對于Activity,Service,Broadcast以及ContentProvider的工作機制有一定的了解,因此我也會在必要的時候穿插講解一些Android Framework的知識;我相信這一定會對讀者大有裨益。

    喜歡就點個贊吧~持續(xù)更新,請關(guān)注github項目?understand-plugin-framework和我的?博客!

    原文地址:?http://weishu.me/2016/03/07/understand-plugin-framework-ams-pms-hook/


    總結(jié)

    以上是生活随笔為你收集整理的Android 插件化原理解析——Hook机制之AMSPMS的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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