在前面的文章中咱們介紹了DroidPlugin的Hook機制,也就是代理方式和Binder Hook;插件框架經過AOP實現了插件使用和開發的透明性。在講述DroidPlugin如何實現四大組件的插件化以前,有必要說明一下它對ActivityManagerServiche以及PackageManagerService的Hook方式(如下簡稱AMS,PMS)。java
ActivityManagerService對於FrameWork層的重要性不言而喻,Android的四大組件無一不與它打交道:android
startActivity
最終調用了AMS的startActivity
系列方法,實現了Activity的啓動;Activity的生命週期回調,也在AMS中完成;startService,bindService
最終調用到AMS的startService和bindService方法;AMS
中完成(靜態廣播在PMS
中完成)getContentResolver
最終從AMS
的getContentProvider
獲取到ContentProvider而PMS
則完成了諸如權限校撿(checkPermission,checkUidPermission
),Apk meta信息獲取(getApplicationInfo
等),四大組件信息獲取(query
系列方法)等重要功能。git
在上文Android插件化原理解析——Hook機制之Binder Hook中講述了DroidPlugin的Binder Hook機制;咱們知道AMS
和PMS
就是以Binder方式提供給應用程序使用的系統服務,理論上咱們也能夠採用這種方式Hook掉它們。可是因爲這二者使用得如此頻繁,Framework給他們了一些「特別優待」,這也給了咱們相對於Binder Hook更加穩定可靠的hook方式。github
閱讀本文以前,能夠先clone一份 understand-plugin-framework,參考此項目的ams-pms-hook
模塊。另外,插件框架原理解析系列文章見索引。編程
前文提到Android的四大組件無一不與AMS
相關,也許讀者還有些許疑惑;這裏我就挑一個例子,依據Android源碼來講明,一個簡單的startActivity
是如何調用AMS
最終經過IPC到system_server的。segmentfault
不論讀者是否知道,咱們使用startActivity
有兩種形式:app
Context
類的startActivity
方法;這種方式啓動的Activity沒有Activity棧,所以不能以standard方式啓動,必須加上FLAG_ACTIVITY_NEW_TASK
這個Flag。Activity
類重載過的startActivity
方法,一般在咱們的Activity中直接調用這個方法就是這種形式;咱們查看Context
類的startActivity
方法,發現這居然是一個抽象類;查看Context
的類繼承關係圖以下:框架
咱們看到諸如Activity
,Service
等並無直接繼承Context
,而是繼承了ContextWrapper
;繼續查看ContextWrapper
的實現:ide
1 2 3 4 |
@Override public void startActivity(Intent intent) { mBase.startActivity(intent); } |
WTF!! 果真人如其名,只是一個wrapper而已;這個mBase
是什麼呢?這裏我先直接告訴你,它的真正實現是ContextImpl
類;至於爲何,有一條思路:mBase是在ContextWrapper構造的時候傳遞進來的,那麼在ContextWrapper構造的時候能夠找到答案
何時會構造ContextWrapper呢?它的子類Application
,Service
等被建立的時候。工具
能夠在App的主線程AcitivityThread
的performLaunchActivit
方法裏面找到答案;更詳細的解析能夠參考老羅的 Android應用程序啓動過程源代碼分析
好了,咱們姑且看成已經知道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); } |
代碼至關簡單;咱們知道了兩件事:
FLAG_ACTIVITY_NEW_TASK
;startActivity
使用了Instrumentation
類的execStartActivity
方法;繼續跟蹤: 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) { // ... 省略無關代碼 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; } |
到這裏咱們發現真正調用的是ActivityManagerNative
的startActivity
方法;若是你不清楚ActivityManager
,ActivityManagerService
以及ActivityManagerNative
之間的關係;建議先仔細閱讀我以前關於Binder的文章 Binder學習指南。
Activity類的startActivity
方法相比Context而言直觀了不少;這個startActivity
經過若干次調用展轉到達startActivityForResult
這個方法,在這個方法內部有以下代碼:
1 2 3 4 |
Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options); |
能夠看到,其實經過Activity和ContextImpl類啓動Activity並沒有本質不一樣,他們都經過Instrumentation
這個輔助類調用到了ActivityManagerNative
的方法。
OK,咱們到如今知道;其實startActivity
最終經過ActivityManagerNative
這個方法遠程調用了AMS
的startActivity
方法。那麼這個ActivityManagerNative
是什麼呢?
ActivityManagerNative實際上就是ActivityManagerService
這個遠程對象的Binder代理對象;每次須要與AMS打交道的時候,須要藉助這個代理對象經過驅動進而完成IPC調用。
咱們繼續看ActivityManagerNative
的getDefault()
方法作了什麼:
1 2 3 |
static public IActivityManager getDefault() { return gDefault.get(); } |
gDefault
這個靜態變量的定義以下:
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調用,獲取這個單例便可。這是AMS
這個系統服務與其餘普通服務的不一樣之處,也是咱們不經過Binder Hook的緣由——咱們只須要簡單地Hook掉這個單例便可。
這裏還有一點小麻煩:Android不一樣版本之間對於如何保存這個單例的代理對象是不一樣的;Android 2.x系統直接使用了一個簡單的靜態變量存儲,Android 4.x以上抽象出了一個Singleton類;具體的差別可使用grepcode
進行比較:差別
咱們以4.x以上的代碼爲例說明如何Hook掉AMS
;方法使用的動態代理,若是有不理解的,能夠參考以前的系列文章Android插件化原理解析——Hook機制之動態代理
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對象; 咱們取出這個單例裏面的字段 Class<?> singleton = Class.forName("android.util.Singleton"); Field mInstanceField = singleton.getDeclaredField("mInstance"); mInstanceField.setAccessible(true); // ActivityManagerNative 的gDefault對象裏面原始的 IActivityManager對象 Object rawIActivityManager = mInstanceField.get(gDefault); // 建立一個這個對象的代理對象, 而後替換這個字段, 讓咱們的代理對象幫忙幹活 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看看會發生什麼:
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
已經被咱們徹底劫持了!! 至於劫持了能幹什麼,本身發揮想象吧~
DroidPlugin關於AMS
的Hook,能夠查看IActivityManagerHook
這個類,它處理了我上述所說的兼容性問題,其餘原理相同。另外,也許有童鞋有疑問了,你用startActivity
爲例怎麼能確保Hook掉這個靜態變量以後就能保證全部使用AMS
的入口都被Hook了呢?
答曰:無他,惟手熟爾。
Android Framewrok層對於四大組件的處理,調用AMS
服務的時候,所有都是經過使用這種方式;如有疑問能夠自行查看源碼。你能夠從Context
類的startActivity, startService,bindService, registerBroadcastReceiver, getContentResolver 等等入口進行跟蹤,最終都會發現它們都會使用ActivityManagerNative的這個AMS
代理對象來完成對遠程AMS的訪問。
PMS
的獲取也是經過Context完成的,具體就是getPackageManager
這個方法;咱們姑且看成已經知道了Context的實如今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
對它還進行了一層包裝咱們繼續查看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代理對象也是一個全局變量存放在一個靜態字段中;咱們能夠如法炮製,Hook掉PMS。
如今咱們的目的很明切,若是須要Hook PMS
有兩個地方須要Hook掉:
ActivityThread
的靜態字段sPackageManager
getPackageManager
方法獲取到的ApplicationPackageManager
對象裏面的mPM
字段。如今使用代理Hook應該是輕車熟路了吧,經過上面的分析,咱們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); // 準備好代理對象, 用來替換原始的對象 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完畢咱們驗證如下結論;調用一下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
查看。
在結束講解PackageManager的Hook以前,咱們須要說明一點;那就是Context
的實現類裏面沒有使用靜態全局變量來保存PMS
的代理對象,而是每擁有一個Context
的實例就持有了一個PMS
代理對象的引用;因此這裏有個很蛋疼的事情,那就是咱們若是想要徹底Hook住PMS
,須要精確控制整個進程內部建立的Context
對象;所幸,插件框架中,插件的Activity,Service,ContentProvider,Broadcast等全部使用到Context的地方,都是由框架控制建立的;所以咱們要當心翼翼地替換掉全部這些對象持有的PMS
代理對象。
我前面也提到過,靜態變量和單例都是良好的Hook點,這裏很好地反證了這句話:想要Hook掉一個實例變量該是多麼麻煩!
寫到這裏,關於DroidPlugin的Hook技術的講解已經完結了;我相信讀者或多或少地認識到,其實Hook並非一項神祕的技術;一個乾淨,透明的框架少不了AOP,而AOP也少不了Hook。
我所講解的Hook僅僅使用反射和動態代理技術,更增強大的Hook機制能夠進行字節碼編織,好比J2EE普遍使用了cglib和asm進行AOP編程;而Android上現有的插件框架仍是加載編譯時代碼,採用動態生成類的技術理論上也是可行的;以前有一篇文章Android動態加載黑科技 動態建立Activity模式,就講述了這種方式;如今全球的互聯網公司不排除有用這種技術實現插件框架的可能 ;我相信不遠的將來,這種技術也會在Android上大放異彩。
瞭解完Hook技術以後,接下來的系列文章會講述DroidPlugin對Android四大組件在插件系統上的處理,插件框架對於這一部分的實現是DroidPlugin的精髓,Hook只不過是工具而已。學習這部份內容須要對於Activity,Service,Broadcast以及ContentProvider的工做機制有必定的瞭解,所以我也會在必要的時候穿插講解一些Android Framework的知識;我相信這必定會對讀者大有裨益。