1.插件化
關(guān)于插件化的原理和插件化框架之前的優(yōu)缺點(diǎn)對(duì)比扫尺,已經(jīng)有很多的文章,這里不再贅述。
2.Activity的啟動(dòng)
APK在安裝的時(shí)候展融,PMS(PackageManagerService)解析Apk中的AndroidManifest.xml文件,根據(jù)Apk包路徑創(chuàng)建一個(gè)對(duì)應(yīng)的資源管理器對(duì)象AssetManager,通過(guò)該對(duì)象來(lái)訪(fǎng)問(wèn)Apk包中的資源信息豫柬,包括AndroidManifest.xml文件告希。
Activity的啟動(dòng)由AMS管理扑浸,Launcher通知AMS啟動(dòng)Activity,AMS通過(guò)PMS去查是否存在該Activity燕偶,若不存在喝噪,則停止了。若存在指么,再判斷是否已經(jīng)創(chuàng)建了進(jìn)程酝惧。若未創(chuàng)建,則請(qǐng)求Zygote創(chuàng)建應(yīng)用進(jìn)程伯诬,然后啟動(dòng)新的進(jìn)程晚唇,在進(jìn)程中創(chuàng)建ActivityThread對(duì)象,執(zhí)行其中的main函數(shù)方法盗似,這里會(huì)創(chuàng)建啟動(dòng)主線(xiàn)程缺亮,再通知AMS,傳入applicationThread以便通訊桥言,AMS再通知應(yīng)用啟動(dòng)Application萌踱,創(chuàng)建啟動(dòng)Activity。
3.插樁Activity
要啟動(dòng)一個(gè)Activity必須在AndroidManifest.xml中注冊(cè)号阿,如果我們要啟動(dòng)一個(gè)新的Activity并鸵,則必須先在發(fā)布的APK內(nèi)占坑注冊(cè)。這樣扔涧,在啟動(dòng)Activity的時(shí)候园担,可以通過(guò)AMS的校驗(yàn)。然后將插件里的新Activity來(lái)代替之前占坑的Activity枯夜,去執(zhí)行代碼弯汰。
那么問(wèn)題來(lái)了,hook技術(shù)應(yīng)該hook什么湖雹,才能替換Activity咏闪。這考驗(yàn)對(duì)Android系統(tǒng)工作原理的熟悉程度。
此外摔吏,為了保證hook的穩(wěn)定性鸽嫂,hook點(diǎn)一般找不容易變化的對(duì)象,比如單例征讲、靜態(tài)變量据某。
3.1Activity的啟動(dòng)分析
啟動(dòng)Activity調(diào)用startActivity,最終會(huì)調(diào)用startActivityForResult诗箍。
在內(nèi)部調(diào)用Instrumentation的execStartActivity方法.
Instrumentation:用戶(hù)監(jiān)控應(yīng)用程序與系統(tǒng)的交互癣籽。
Instrumentation的startActivityForResult在Android的7.0和8.0中代碼邏輯是不同的。先看Android7.0。
Android7.0
啟動(dòng)Activity需先通過(guò)AMS的校驗(yàn)筷狼,則需要獲取AMS對(duì)象橱夭。Android7.0通過(guò)
ActivityManagerNative的getDefault來(lái)獲取AMS的代理對(duì)象。
getDefault通過(guò)ServiceManager得到“activity”的Service引用桑逝,也就是IBinder類(lèi)型的AMS的引用棘劣。
將其封裝成ActivityManagerProxy類(lèi)型對(duì)象。它用來(lái)與AMS進(jìn)行進(jìn)程間通信楞遏。
getDefault借助Singleton類(lèi)來(lái)實(shí)現(xiàn)單例茬暇,且它又是靜態(tài)的,因此hook IActivityManager寡喝。
Android8.0
ActivityManager的getService方法糙俗,該方法得到名為“activity的”Servcie的引用,也是IBinder類(lèi)型预鬓,將其轉(zhuǎn)化成IActivityManager類(lèi)型對(duì)象巧骚,這里使用了AIDL的方式,IActivityManager類(lèi)是由AIDL工具在編譯時(shí)自動(dòng)生成格二,與AMS通信劈彪,只要集成IActivityManager.Stub并實(shí)現(xiàn)對(duì)象的方法就可以。因此IActivityManager就是AMS在本地的代理顶猜。因此startActivityAsUser實(shí)際上調(diào)用的是AMS的方法沧奴。
IActivityManager借助Singleton類(lèi)實(shí)現(xiàn)單例,因此也選擇IActivityManager為hook點(diǎn)长窄。
4.Hook Activity的具體實(shí)現(xiàn)(Kotlin)
先寫(xiě)一個(gè)插樁Activity(OldActivity)滔吠,在AndroidManifest內(nèi)注冊(cè)。
再寫(xiě)一個(gè)新的Activity(PluginActivity)挠日,不注冊(cè)疮绷。
最后寫(xiě)一個(gè)測(cè)試類(lèi)AcTestActivity,點(diǎn)擊啟動(dòng)PluginActivity嚣潜。
下面開(kāi)始寫(xiě)核心代碼
IActivityManager的代理類(lèi):
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類(lèi)要啟動(dòng)的Activity為在AndroidManifest內(nèi)已經(jīng)注冊(cè)的Activity
val newIntent=Intent()
val packageName=BuildConfig.APPLICATION_ID
//指定插樁Activity
val componentName=ComponentName(packageName,OldActivity::javaClass.name)
newIntent.component = componentName
//獲取到的intent先保存到新的intent內(nèi)冬骚,后面會(huì)用到。
newIntent.putExtra(OLD_INTENT,intent)
//將新的intent賦值
args[index]=newIntent
}
return method?.invoke(mActivityManager,*(args))
}
}
利用反射郑原,將系統(tǒng)原定的IActivityManager對(duì)象替換成我們自定義的代理類(lèi)唉韭。
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
//通過(guò)反射,得到即將被代理的對(duì)象(即mInstance的實(shí)例)--iActivityManager
val iActivityManager=singletonClassFiled.get(defaultSingleton)
//創(chuàng)建代理類(lèi)對(duì)象女器,該對(duì)象持有被代理的對(duì)象 :IActivityManagerProxy(iActivityManager)
// 我們可以在IActivityManagerProxy做一些自定義的操作酸役,其內(nèi)部持有原來(lá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)建的代理類(lèi)proxy對(duì)象來(lái)替換Singleton中的mInstance字段原來(lái)的對(duì)象
singletonClassFiled.set(defaultSingleton,proxy)
}
}
}
自定義Application類(lèi)(記得替換AndroidManifest內(nèi)的Application)
class ActivityApplication: Application() {
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
try {
HookUtil.hookAMS()
} catch (e:Exception){
e.printStackTrace()
}
}
}
最后點(diǎn)擊AcTestActivity內(nèi)的按鈕,啟動(dòng)一個(gè)未注冊(cè)的PluginActivity最終會(huì)啟動(dòng)插樁OldActivity涣澡。那么就成功“騙過(guò)”AMS啟動(dòng)了一個(gè)已注冊(cè)的Activity贱呐。
啟動(dòng)新的Activity
上面講到啟動(dòng)了已注冊(cè)的Activity,但整個(gè)是空的入桂,我們真正想啟動(dòng)的是新Activity而不是插樁Activity奄薇。那么這就要找到真正啟動(dòng)Activity的位置,對(duì)其進(jìn)行替換改造抗愁。我們知道通過(guò)AMS校驗(yàn)后馁蒂,AMS會(huì)通知應(yīng)用可以啟動(dòng)Activity。AMS支持ApplicationThread的IBinder類(lèi)IApplicationThread對(duì)象蜘腌,用來(lái)進(jìn)程間通信沫屡。
ApplicationThread的scheduleLaunchActivity方法,會(huì)調(diào)用ActivityThread的sendMessage(H.LAUNCH_ACTIVITY, r)方法撮珠。H是Activity的內(nèi)部類(lèi)沮脖,繼承自Handler,是應(yīng)用程序進(jìn)程中主線(xiàn)程的消息管理類(lèi)芯急。Activity的生命周期都是在主線(xiàn)程中執(zhí)行勺届,所以這里會(huì)通過(guò)H來(lái)切換。
其中handleLaunchActivity方法最終會(huì)調(diào)到Activity的onCreate方法娶耍。
我們將H的handleMessage的msg進(jìn)行替換涮因,讓其攜帶的消息為新的Activity。(即從代理類(lèi)IActivityManagerProxy保存的intent取出來(lái)伺绽。還記得之前我們將intent內(nèi)的Activity做了一次替換嗎养泡?現(xiàn)在再換回來(lái)!)
====未完奈应,待續(xù)~~