想要替換 ProxyApplication首先對(duì) Application 啟動(dòng)源碼很熟悉才能對(duì)它進(jìn)行操作躏救,下面由我來帶著大家一起進(jìn)入源碼的世界吧颖杏。
Application 綁定過程
今天主要從 ActivityThread => main() 開始,下面以一個(gè)流程圖來說明一下:
XML 中如何解析我們的 Application
-
ActivityThread.java
mian() -> thread.attach() -> attachApplication() -> 接收 AMS 發(fā)過來的參數(shù)之后 sendMessage(H.BIND_APPLICATION)-> 處理 BIND_APPLICATION -> handleBindApplication() 在這里準(zhǔn)備好 application - > Application app = data.info.makeApplication() - > mInitialApplication = app;
-
LoadedApk.java
這個(gè)類就是 APK 在內(nèi)存中的表示政溃,可以得到如代碼趾访,資料,功能清單等信息
- 通過 mApplicationInfo.className 得到我們注冊(cè)的全類名
- app = mActivityThread.mInstrumentation.newApplication () 創(chuàng)建 application
- 接下來會(huì)使用 appContext.setOuterContext(app)
- mApplication = app
反射需要替換的內(nèi)容
- ContextImpl -> mOuterContext(app) 通過 Application 的 attachBaseContext 回調(diào)參數(shù)獲取
- ActivityThread -> mAllApplication(arrayList) 通過 ContextImpl 的 mMainThread 屬性獲取
- LoadedApk -> mApplication 通過 ContextImpl 的 mPackageInfo 屬性獲取
反射開始替換 Application
boolean isBindReal;
Application delegate;
private void bindRealApplicatin() throws Exception {
if (isBindReal) {
return;
}
if (TextUtils.isEmpty(app_name)) {
return;
}
//得到attachBaseContext(context) 傳入的上下文 ContextImpl
Context baseContext = getBaseContext();
//創(chuàng)建用戶真實(shí)的application (MyApplication)
Class<?> delegateClass = Class.forName(app_name);
delegate = (Application) delegateClass.newInstance();
//得到attach()方法
Method attach = Application.class.getDeclaredMethod("attach", Context.class);
attach.setAccessible(true);
attach.invoke(delegate, baseContext);
// ContextImpl---->mOuterContext(app) 通過Application的attachBaseContext回調(diào)參數(shù)獲取
Class<?> contextImplClass = Class.forName("android.app.ContextImpl");
//獲取mOuterContext屬性
Field mOuterContextField = contextImplClass.getDeclaredField("mOuterContext");
mOuterContextField.setAccessible(true);
mOuterContextField.set(baseContext, delegate);
// ActivityThread--->mAllApplications(ArrayList) ContextImpl的mMainThread屬性
Field mMainThreadField = contextImplClass.getDeclaredField("mMainThread");
mMainThreadField.setAccessible(true);
Object mMainThread = mMainThreadField.get(baseContext);
// ActivityThread--->>mInitialApplication
Class<?> activityThreadClass=Class.forName("android.app.ActivityThread");
Field mInitialApplicationField = activityThreadClass.getDeclaredField("mInitialApplication");
mInitialApplicationField.setAccessible(true);
mInitialApplicationField.set(mMainThread,delegate);
// ActivityThread--->mAllApplications(ArrayList) ContextImpl的mMainThread屬性
Field mAllApplicationsField = activityThreadClass.getDeclaredField("mAllApplications");
mAllApplicationsField.setAccessible(true);
ArrayList<Application> mAllApplications =(ArrayList<Application>) mAllApplicationsField.get(mMainThread);
mAllApplications.remove(this);
mAllApplications.add(delegate);
// LoadedApk------->mApplication ContextImpl的mPackageInfo屬性
Field mPackageInfoField = contextImplClass.getDeclaredField("mPackageInfo");
mPackageInfoField.setAccessible(true);
Object mPackageInfo=mPackageInfoField.get(baseContext);
Class<?> loadedApkClass=Class.forName("android.app.LoadedApk");
Field mApplicationField = loadedApkClass.getDeclaredField("mApplication");
mApplicationField.setAccessible(true);
mApplicationField.set(mPackageInfo,delegate);
//修改ApplicationInfo className LooadedApk
Field mApplicationInfoField = loadedApkClass.getDeclaredField("mApplicationInfo");
mApplicationInfoField.setAccessible(true);
ApplicationInfo mApplicationInfo = (ApplicationInfo)mApplicationInfoField.get(mPackageInfo);
mApplicationInfo.className=app_name;
delegate.onCreate();
isBindReal = true;
}
現(xiàn)在重新簽名打包完成董虱,啟動(dòng)我們的 APK 看下 Log
2019-06-04 23:17:30.892 6064-6064/com.yk.dexdeapplication I/DevYK: provider onCreate:com.example.proxy_core.ProxyApplication@1ec3c70
2019-06-04 23:17:30.892 6064-6064/com.yk.dexdeapplication I/DevYK: provider onCreate:com.example.proxy_core.ProxyApplication@1ec3c70
2019-06-04 23:17:30.892 6064-6064/com.yk.dexdeapplication I/DevYK: provider onCreate:com.example.proxy_core.ProxyApplication
2019-06-04 23:17:30.895 6064-6064/com.yk.dexdeapplication I/DevYK: MyApplication onCreate()
2019-06-04 23:17:30.995 6064-6064/com.yk.dexdeapplication I/DevYK: activity:com.yk.dexdeapplication.App@300b5f6
2019-06-04 23:17:30.995 6064-6064/com.yk.dexdeapplication I/DevYK: activity:com.yk.dexdeapplication.App@300b5f6
2019-06-04 23:17:30.995 6064-6064/com.yk.dexdeapplication I/DevYK: activity:com.yk.dexdeapplication.App
2019-06-04 23:17:31.001 6064-6064/com.yk.dexdeapplication I/DevYK: provider delete:com.example.proxy_core.ProxyApplication@1ec3c70
2019-06-04 23:17:31.021 6064-6064/com.yk.dexdeapplication I/DevYK: service:com.yk.dexdeapplication.App@300b5f6
2019-06-04 23:17:31.021 6064-6064/com.yk.dexdeapplication I/DevYK: service:com.yk.dexdeapplication.App@300b5f6
2019-06-04 23:17:31.021 6064-6064/com.yk.dexdeapplication I/DevYK: service:com.yk.dexdeapplication.App
2019-06-04 23:17:31.022 6064-6064/com.yk.dexdeapplication I/DevYK: reciver:android.app.ReceiverRestrictedContext@9b92293
2019-06-04 23:17:31.022 6064-6064/com.yk.dexdeapplication I/DevYK: reciver:com.yk.dexdeapplication.App@300b5f6
2019-06-04 23:17:31.022 6064-6064/com.yk.dexdeapplication I/DevYK: reciver:com.yk.dexdeapplication.App
注意看 LOG
MyApplication onCreate()
這里已經(jīng)替換成我們自己的 MyApplication , 而且 Activity 和 Service 獲取上下文也已經(jīng)是我們替換成功的 Applicaton扼鞋。但是...也許有的眼神比較好的已經(jīng)看出問題了,為什么內(nèi)容提供者 Context 還是代理的 Application 而且比我們自己的應(yīng)用還要先執(zhí)行,那么我們帶著這個(gè)問題去看 Application onCreate 之前做了什么事兒云头。
我們點(diǎn)擊 installlContentProviders(app,providers);
注意這里傳進(jìn)去的還是 代理 Context
重點(diǎn)在最后
注意看我 勾畫 的圈里面的邏輯判斷捐友,判斷當(dāng)前應(yīng)用的包名是否跟 XML 中的包名一致,如果一致我們就賦值盘寡,再次提醒下 這里的 context 是我們代理的 context ,那么我們?cè)趺醋隼粘睿覀冊(cè)诖碇兄貙?PackageName 只要都不等 那么就會(huì)走 else 會(huì)根據(jù)包名創(chuàng)建一個(gè) Context
/**
* 讓代碼走入if中的第三段中
* @return
*/
@Override
public String getPackageName() {
if(!TextUtils.isEmpty(app_name)){
return "";
}
return super.getPackageName();
}
@Override
public Context createPackageContext(String packageName, int flags) throws PackageManager.NameNotFoundException {
if(TextUtils.isEmpty(app_name)){
return super.createPackageContext(packageName, flags);
}
try {
bindRealApplicatin();
} catch (Exception e) {
e.printStackTrace();
}
return delegate;
}
- 首先判斷我們自己 XML 中的 app_name 是否為空
- 如果不為空,我們傳入一個(gè) 空包
- SDK 會(huì)判斷是否跟 XML 中的 pck 一樣竿痰,最后走 else 我們?cè)谥貙?createPackageContext 傳入我們自己應(yīng)用的包名脆粥。會(huì)生成一個(gè) Context
最后我們來驗(yàn)證一下:
<figcaption></figcaption>
2019-06-05 00:12:30.271 7570-7570/com.yk.dexdeapplication I/DevYK: MyApplication onCreate()
2019-06-05 00:12:30.273 7570-7570/com.yk.dexdeapplication I/DevYK: provider onCreate:com.yk.dexdeapplication.App@1ec3c70
2019-06-05 00:12:30.273 7570-7570/com.yk.dexdeapplication I/DevYK: provider onCreate:com.yk.dexdeapplication.App@1ec3c70
2019-06-05 00:12:30.273 7570-7570/com.yk.dexdeapplication I/DevYK: provider onCreate:com.yk.dexdeapplication.App
2019-06-05 00:12:30.381 7570-7570/com.yk.dexdeapplication I/DevYK: activity:com.yk.dexdeapplication.App@1ec3c70
2019-06-05 00:12:30.381 7570-7570/com.yk.dexdeapplication I/DevYK: activity:com.yk.dexdeapplication.App@1ec3c70
2019-06-05 00:12:30.381 7570-7570/com.yk.dexdeapplication I/DevYK: activity:com.yk.dexdeapplication.App
2019-06-05 00:12:30.387 7570-7570/com.yk.dexdeapplication I/DevYK: provider delete:com.yk.dexdeapplication.App@1ec3c70
2019-06-05 00:12:30.406 7570-7570/com.yk.dexdeapplication I/DevYK: service:com.yk.dexdeapplication.App@1ec3c70
2019-06-05 00:12:30.406 7570-7570/com.yk.dexdeapplication I/DevYK: service:com.yk.dexdeapplication.App@1ec3c70
2019-06-05 00:12:30.406 7570-7570/com.yk.dexdeapplication I/DevYK: service:com.yk.dexdeapplication.App
2019-06-05 00:12:30.408 7570-7570/com.yk.dexdeapplication I/DevYK: reciver:android.app.ReceiverRestrictedContext@b7a3b82
2019-06-05 00:12:30.408 7570-7570/com.yk.dexdeapplication I/DevYK: reciver:com.yk.dexdeapplication.App@1ec3c70
2019-06-05 00:12:30.408 7570-7570/com.yk.dexdeapplication I/DevYK: reciver:com.yk.dexdeapplication.App
日志中除了 BroadCase Context 是系統(tǒng)的以外,所有的 Context 都是我們替換的 Application Context影涉。完美解決变隔。不過這里有一個(gè)隱藏 BUG ,據(jù)說面試題會(huì)問那么是什么勒蟹倾?
可以在廣播中使用 context 在開啟一個(gè)廣播或者綁定一個(gè)服務(wù)嗎匣缘?
我們其實(shí)可以帶著這個(gè)問題看下源碼
H -> RECEIVER 消息
<figcaption></figcaption>
果然注冊(cè)廣播和綁定服務(wù)會(huì)拋一個(gè)異常荆残。
總結(jié)
到這里我們的加固已經(jīng)講完了党觅,從 dex 分包 -> 加密 -> 對(duì)齊 > 簽名 - > 打包壓縮成 APK 。一套完整的流程和代碼都已經(jīng)寫完了凰狞。跟市面上的加固流程原理都幾乎一樣豁陆。懂了原理再去使用第三方就輕車熟路了柑爸。