1.插件化
關于插件化的原理和插件化框架之前的優(yōu)缺點對比,已經(jīng)有很多的文章,這里不再贅述。
2.Activity的啟動
APK在安裝的時候,PMS(PackageManagerService)解析Apk中的AndroidManifest.xml文件,根據(jù)Apk包路徑創(chuàng)建一個對應的資源管理器對象AssetManager,通過該對象來訪問Apk包中的資源信息,包括AndroidManifest.xml文件。
Activity的啟動由AMS管理,Launcher通知AMS啟動Activity,AMS通過PMS去查是否存在該Activity,若不存在,則停止了。若存在,再判斷是否已經(jīng)創(chuàng)建了進程。若未創(chuàng)建,則請求Zygote創(chuàng)建應用進程,然后啟動新的進程,在進程中創(chuàng)建ActivityThread對象,執(zhí)行其中的main函數(shù)方法,這里會創(chuàng)建啟動主線程,再通知AMS,傳入applicationThread以便通訊,AMS再通知應用啟動Application,創(chuàng)建啟動Activity。
3.插樁Activity
要啟動一個Activity必須在AndroidManifest.xml中注冊,如果我們要啟動一個新的Activity,則必須先在發(fā)布的APK內(nèi)占坑注冊。這樣,在啟動Activity的時候,可以通過AMS的校驗。然后將插件里的新Activity來代替之前占坑的Activity,去執(zhí)行代碼。
那么問題來了,hook技術應該hook什么,才能替換Activity。這考驗對Android系統(tǒng)工作原理的熟悉程度。
此外,為了保證hook的穩(wěn)定性,hook點一般找不容易變化的對象,比如單例、靜態(tài)變量。
3.1Activity的啟動分析
啟動Activity調(diào)用startActivity,最終會調(diào)用startActivityForResult。
在內(nèi)部調(diào)用Instrumentation的execStartActivity方法.
Instrumentation:用戶監(jiān)控應用程序與系統(tǒng)的交互。

Instrumentation的startActivityForResult在Android的7.0和8.0中代碼邏輯是不同的。先看Android7.0。
Android7.0
啟動Activity需先通過AMS的校驗,則需要獲取AMS對象。Android7.0通過
ActivityManagerNative的getDefault來獲取AMS的代理對象。

getDefault通過ServiceManager得到“activity”的Service引用,也就是IBinder類型的AMS的引用。

將其封裝成ActivityManagerProxy類型對象。它用來與AMS進行進程間通信。

getDefault借助Singleton類來實現(xiàn)單例,且它又是靜態(tài)的,因此hook IActivityManager。
Android8.0
ActivityManager的getService方法,該方法得到名為“activity的”Servcie的引用,也是IBinder類型,將其轉(zhuǎn)化成IActivityManager類型對象,這里使用了AIDL的方式,IActivityManager類是由AIDL工具在編譯時自動生成,與AMS通信,只要集成IActivityManager.Stub并實現(xiàn)對象的方法就可以。因此IActivityManager就是AMS在本地的代理。因此startActivityAsUser實際上調(diào)用的是AMS的方法。

IActivityManager借助Singleton類實現(xiàn)單例,因此也選擇IActivityManager為hook點。

4.Hook Activity的具體實現(xiàn)(Kotlin)
先寫一個插樁Activity(OldActivity),在AndroidManifest內(nèi)注冊。

再寫一個新的Activity(PluginActivity),不注冊。
最后寫一個測試類AcTestActivity,點擊啟動PluginActivity。

下面開始寫核心代碼
IActivityManager的代理類:
IActivityManagerProxy繼承自InvocationHandler
class IActivityManagerProxy :InvocationHandler {
var mActivityManager:Any
companion object {
private val mMethod:String="startActivity"
private val OLD_INTENT="OLD_ACTIVITY_INTENT"
}
constructor(activityManager: Any){
this.mActivityManager=activityManager
}
@Throws(Throwable::class)
override fun invoke(proxy: Any?, method: Method?, args: Array<Any>): Any ?{
//判斷是否為"startActivity"方法
if (mMethod == method?.name){
val intent:Intent
var index=0
//獲取intent
for (i in args.indices){
if (args[i] is Intent){
index=i
break
}
}
intent=args[index] as Intent
//替換intent類要啟動的Activity為在AndroidManifest內(nèi)已經(jīng)注冊的Activity
val newIntent=Intent()
val packageName=BuildConfig.APPLICATION_ID
//指定插樁Activity
val componentName=ComponentName(packageName,OldActivity::javaClass.name)
newIntent.component = componentName
//獲取到的intent先保存到新的intent內(nèi),后面會用到。
newIntent.putExtra(OLD_INTENT,intent)
//將新的intent賦值
args[index]=newIntent
}
return method?.invoke(mActivityManager,*(args))
}
}
利用反射,將系統(tǒng)原定的IActivityManager對象替換成我們自定義的代理類。
class HookUtil {
companion object {
@Throws(Throwable::class)
fun hookAMS(){
var defaultSingleton:Any
if (Build.VERSION.SDK_INT>=26){
//android8.0以上,獲取hook的IActivityManagerSingleton字段
val activityManageClass=Class.forName("android.app.ActivityManager")
val filed=activityManageClass.getDeclaredField("IActivityManagerSingleton")
filed.isAccessible=true
defaultSingleton= filed.get(null)
}else{
//android 7.0以下,獲取hook的gDefault字段
val activityManagerClass=Class.forName("android.app.ActivityManagerNative")
val filed=activityManagerClass.getDeclaredField("gDefault")
filed.isAccessible=true
defaultSingleton=filed.get(null)
}
//獲取Singleton中的即將被替換的字段的值--mInstance
val singletonClass=Class.forName("android.util.Singleton")
val singletonClassFiled=singletonClass.getDeclaredField("mInstance")
singletonClassFiled.isAccessible=true
//通過反射,得到即將被代理的對象(即mInstance的實例)--iActivityManager
val iActivityManager=singletonClassFiled.get(defaultSingleton)
//創(chuàng)建代理類對象,該對象持有被代理的對象 :IActivityManagerProxy(iActivityManager)
// 我們可以在IActivityManagerProxy做一些自定義的操作,其內(nèi)部持有原來的iActivityManager,
val iActivityManagerClass=Class.forName("android.app.IActivityManager")
val interfaces= arrayOf(iActivityManagerClass)
val proxy=Proxy.newProxyInstance(Thread.currentThread().contextClassLoader, interfaces,
IActivityManagerProxy(iActivityManager)) as Any
//用創(chuàng)建的代理類proxy對象來替換Singleton中的mInstance字段原來的對象
singletonClassFiled.set(defaultSingleton,proxy)
}
}
}
自定義Application類(記得替換AndroidManifest內(nèi)的Application)
class ActivityApplication: Application() {
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
try {
HookUtil.hookAMS()
} catch (e:Exception){
e.printStackTrace()
}
}
}
最后點擊AcTestActivity內(nèi)的按鈕,啟動一個未注冊的PluginActivity最終會啟動插樁OldActivity。那么就成功“騙過”AMS啟動了一個已注冊的Activity。
啟動新的Activity
上面講到啟動了已注冊的Activity,但整個是空的,我們真正想啟動的是新Activity而不是插樁Activity。那么這就要找到真正啟動Activity的位置,對其進行替換改造。我們知道通過AMS校驗后,AMS會通知應用可以啟動Activity。AMS支持ApplicationThread的IBinder類IApplicationThread對象,用來進程間通信。
ApplicationThread的scheduleLaunchActivity方法,會調(diào)用ActivityThread的sendMessage(H.LAUNCH_ACTIVITY, r)方法。H是Activity的內(nèi)部類,繼承自Handler,是應用程序進程中主線程的消息管理類。Activity的生命周期都是在主線程中執(zhí)行,所以這里會通過H來切換。

其中handleLaunchActivity方法最終會調(diào)到Activity的onCreate方法。
我們將H的handleMessage的msg進行替換,讓其攜帶的消息為新的Activity。(即從代理類IActivityManagerProxy保存的intent取出來。還記得之前我們將intent內(nèi)的Activity做了一次替換嗎?現(xiàn)在再換回來?。?/p>
====未完,待續(xù)~~