前言
在《從底層分析PathClassLoader和DexClassLoader的區(qū)別,基于Android4.4》中分析了Android虛擬機(jī)的兩種類加載器匣沼,利用動(dòng)態(tài)加載技術(shù)實(shí)現(xiàn)APK安全加固依賴于DexClassLoader的使用贴彼。
為了更好的理解本文的內(nèi)容昙楚,建議讀《深入理解Java虛擬機(jī)》中的 “”虛擬機(jī)類加載機(jī)制“”,老羅的《Android系統(tǒng)源代碼情景分析》中Application和四大組件的啟動(dòng)過程枉氮。
本人在剛開始做APK加固時(shí)也是參考了別人的文章危尿,但是只有深入到源碼分析并且親手實(shí)踐才知道有些代碼為什么是這樣寫的。
虛擬機(jī)中的類加載分析
根據(jù)《深入理解Java虛擬機(jī)》描述渗磅,類從被加載到虛擬機(jī)內(nèi)存開始,到卸載出內(nèi)存為止,它的整個(gè)生命周期包括:加載始鱼、驗(yàn)證仔掸、準(zhǔn)備、解析医清、初始化起暮、使用的卸載。其中驗(yàn)證会烙、準(zhǔn)備负懦、解析統(tǒng)稱為連接。其中解析和初始化沒有先后順序柏腻。
Dex相當(dāng)于Class字節(jié)碼文件的集合纸厉,符合Java虛擬機(jī)規(guī)范。根據(jù)Java類加載描述五嫂,類加載的時(shí)機(jī)沒有強(qiáng)行約束颗品,但當(dāng)遇到以下情況必須進(jìn)行初始化:
- 遇到new、putstatic沃缘、getstatic躯枢、invokestatic字節(jié)碼指令時(shí);
- 使用java.lang.reflect的方法對(duì)類進(jìn)行反射調(diào)用時(shí)槐臀;
- 初始化一個(gè)類時(shí)锄蹂,先初始化其父類;
- 虛擬機(jī)啟動(dòng)時(shí)峰档,需要初始化包含main函數(shù)的類败匹;
- 使用JDK1.7的動(dòng)態(tài)語言支持時(shí)寨昙;
類加載器
虛擬機(jī)設(shè)計(jì)團(tuán)隊(duì)把類加載階段中的“通過一個(gè)類的全限定名來獲取描述此類的二進(jìn)制字節(jié)碼流”這個(gè)動(dòng)作放到Java虛擬機(jī)外部實(shí)現(xiàn)讥巡,以便讓程序自己決定如何獲取所需的類,實(shí)現(xiàn)這個(gè)動(dòng)作的模塊稱為“類加載器”舔哪。
類加載器除了具有加載類的功能欢顷,另一個(gè)意義是每一個(gè)類加載器都有一個(gè)獨(dú)立的類命名空間,比較兩個(gè)類是否相等只對(duì)同一個(gè)類加載器才有意義捉蚤。假設(shè)同一個(gè)Class文件抬驴,一個(gè)由系統(tǒng)的類加載器加載,另一個(gè)由程序自定義的類加載器加載缆巧,雖然是同一Class文件卻對(duì)應(yīng)不同類加載器的兩個(gè)實(shí)例布持,在虛擬機(jī)中是不相等的。
雙親委派模型
前文說到用戶可以自定義類加載器陕悬,并且每個(gè)類加載器加載的同一Class并不相等题暖。但對(duì)于某些公共的類,比如在Java SDK中定義的Class,我們沒有必要因?yàn)樽远x類加載器的不同而加載出不同的Class對(duì)象胧卤。因此在JDK1.2期間引入并被廣泛應(yīng)用于以后的所有Java虛擬機(jī)中唯绍。
雙親委派模型的工作原理是:如果一個(gè)類加載器收到了類加載的請(qǐng)求,它首先不會(huì)自己去嘗試加載這個(gè)類枝誊,而是把這個(gè)請(qǐng)求委派給父類加載器去完成况芒,每一個(gè)層次的類加載器都是如此,因此所有的加載請(qǐng)求最終都應(yīng)該傳送到頂層的啟動(dòng)類加載器中叶撒,只有當(dāng)父加載器反饋?zhàn)约簾o法完成這個(gè)加載請(qǐng)求(它的搜索范圍中沒有找到所需的類)時(shí)绝骚,子加載器才會(huì)嘗試自己去加載。
因此雙親委派模型能夠保證例如Java SDK中的公共Class在不同自定義加載器中也是相等的祠够,因?yàn)椴煌淖远x類加載器都有相同的父類加載器皮壁。保證了公共類的一致性。
雙親委派模型對(duì)于保證Java程序的穩(wěn)定運(yùn)作很重要哪审,但它的實(shí)現(xiàn)卻非常簡(jiǎn)單蛾魄,實(shí)現(xiàn)雙親委派的代碼都集中在java.lang.ClassLoader的loadClass()方法之中:先檢查是否已經(jīng)被加載過,若沒有加載則調(diào)用父加載器的loadClass()方法湿滓,若父加載器為空則默認(rèn)使用啟動(dòng)類加載器作為父加載器滴须。如果父類加載失敗,拋出ClassNotFoundException異常后叽奥,再調(diào)用自己的findClass()方法進(jìn)行加載扔水。
分析Anrdoid類加載器的創(chuàng)建及Application的初始化
什么時(shí)候Android程序被啟動(dòng)?
大多數(shù)人都知道在桌面點(diǎn)擊一個(gè)應(yīng)用的圖標(biāo)可以啟動(dòng)該應(yīng)用的Activity,其實(shí)不光是Activity朝氓,通過遠(yuǎn)程調(diào)用Service魔市、ContentProvider,乃至發(fā)送一個(gè)廣播都會(huì)啟動(dòng)目標(biāo)進(jìn)程赵哲。當(dāng)然根據(jù)廠家的適配,通過靜態(tài)廣播喚醒進(jìn)程并不一定可行待德。實(shí)測(cè)在Android 4.1 虛擬機(jī)上是可行的。
Android進(jìn)程如何啟動(dòng)?
ActivityThread中的main函數(shù)是Android進(jìn)程的唯一入口枫夺,當(dāng)啟動(dòng)四大組件會(huì)先檢查目標(biāo)的進(jìn)程有沒有啟動(dòng)将宪,若沒有啟動(dòng)會(huì)通過Zygote孵化一個(gè)Java進(jìn)程并執(zhí)行ActivityThread類的main函數(shù)。
此函數(shù)中會(huì)執(zhí)行attach函數(shù)橡庞,通過Binder機(jī)制與ActivityManagerService(簡(jiǎn)稱AMS)通信较坛,將自己attach到AMS的ProcessRecord對(duì)象,反過來在由AMS調(diào)用ActivityThread的handleBindApplication函數(shù):
private void handleBindApplication(AppBindData data) {
mBoundApplication = data;
...
data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
...
}
這里調(diào)用了getPackageInfoNoCheck函數(shù)給data.info賦值扒最,而data.info實(shí)際是LoadedApk對(duì)象丑勤,再來看該函數(shù):
public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
CompatibilityInfo compatInfo) {
return getPackageInfo(ai, compatInfo, null, false, true);
}
private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
ClassLoader baseLoader, boolean securityViolation, boolean includeCode) {
synchronized (mResourcesManager) {
WeakReference<LoadedApk> ref;
if (includeCode) {
ref = mPackages.get(aInfo.packageName);
} else {
ref = mResourcePackages.get(aInfo.packageName);
}
LoadedApk packageInfo = ref != null ? ref.get() : null;
if (packageInfo == null || (packageInfo.mResources != null
&& !packageInfo.mResources.getAssets().isUpToDate())) {
if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package "
: "Loading resource-only package ") + aInfo.packageName
+ " (in " + (mBoundApplication != null
? mBoundApplication.processName : null)
+ ")");
packageInfo =
new LoadedApk(this, aInfo, compatInfo, baseLoader,
securityViolation, includeCode &&
(aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);
...
}
}
LoadedApk對(duì)象會(huì)先從mPackages(API19及以下是HashMap類型,以上是ArrayMap類型)中嘗試獲取一個(gè)弱引用吧趣,若找不到該引用則會(huì)調(diào)用構(gòu)造方法創(chuàng)建一個(gè)新的法竞。
而觀察getPackageInfoNoCheck除呵,發(fā)現(xiàn)傳入的baseLoader參數(shù)為空,這意味著默認(rèn)類加載器沒有父加載器,LoadedApk 構(gòu)造源碼如下:
public final class LoadedApk {
...
private final ClassLoader mBaseClassLoader;
private ClassLoader mClassLoader;
...
public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,
CompatibilityInfo compatInfo, ClassLoader baseLoader,
boolean securityViolation, boolean includeCode, boolean registerPackage) {
...
mBaseClassLoader = baseLoader;
...
}
public ClassLoader getClassLoader() {
synchronized (this) {
if (mClassLoader != null) {
return mClassLoader;
}
if (mIncludeCode && !mPackageName.equals("android")) {
...
mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, lib,
mBaseClassLoader);
...
} else {
if (mBaseClassLoader == null) {
mClassLoader = ClassLoader.getSystemClassLoader();
} else {
mClassLoader = mBaseClassLoader;
}
}
return mClassLoader;
}
}
}
ApplicationLoaders.getDefault().getClassLoader(zip,lib, mBaseClassLoader)正是構(gòu)造默認(rèn)類加載器的函數(shù):
class ApplicationLoaders
{
public static ApplicationLoaders getDefault()
{
return gApplicationLoaders;
}
public ClassLoader getClassLoader(String zip, String libPath, ClassLoader parent)
{
ClassLoader baseParent = ClassLoader.getSystemClassLoader().getParent();
synchronized (mLoaders) {
if (parent == null) {
parent = baseParent;
}
if (parent == baseParent) {
ClassLoader loader = mLoaders.get(zip);
if (loader != null) {
return loader;
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
PathClassLoader pathClassloader =
new PathClassLoader(zip, libPath, parent);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
mLoaders.put(zip, pathClassloader);
return pathClassloader;
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
PathClassLoader pathClassloader = new PathClassLoader(zip, parent);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return pathClassloader;
}
}
private final ArrayMap<String, ClassLoader> mLoaders = new ArrayMap<String, ClassLoader>();
private static final ApplicationLoaders gApplicationLoaders
= new ApplicationLoaders();
}
到這里我們大體能明白Android Framework是如何創(chuàng)建默認(rèn)類加載器的爪喘,系統(tǒng)由ApplicaionLoaders.getDefault().getClassLoader創(chuàng)建PathClassLoader作為默認(rèn)的類加載器颜曾。然后由這個(gè)ClassLoader去加載DEX中的各個(gè)類。
接著回到handleBindApplication函數(shù):
private void handleBindApplication(AppBindData data) {
...
try {
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
if (!data.restrictedBackupMode) {
List<ProviderInfo> providers = data.providers;
if (providers != null) {
installContentProviders(app, providers);
mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
}
}
try {
mInstrumentation.onCreate(data.instrumentationArgs);
}
catch (Exception e) {
throw new RuntimeException(
"Exception thrown in onCreate() of "
+ data.instrumentationName + ": " + e.toString(), e);
}
try {
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (!mInstrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
} finally {
StrictMode.setThreadPolicy(savedPolicy);
}
}
在創(chuàng)建完LoadedApk對(duì)象后通過執(zhí)行makeApplication函數(shù)創(chuàng)建Application對(duì)象的實(shí)例秉剑。
創(chuàng)建完Application對(duì)象后通過mInstrumentation的callApplicationOnCreate函數(shù)啟動(dòng)Application泛豪。
我們先來看makeApplication函數(shù)的代碼:
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if (mApplication != null) {
return mApplication;
}
Application app = null;
String appClass = mApplicationInfo.className;
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
}
try {
java.lang.ClassLoader cl = getClassLoader();
ContextImpl appContext = new ContextImpl();
appContext.init(this, null, mActivityThread);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
if (!mActivityThread.mInstrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to instantiate application " + appClass
+ ": " + e.toString(), e);
}
}
mActivityThread.mAllApplications.add(app);
mApplication = app;
if (instrumentation != null) {
try {
instrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (!instrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
}
return app;
}
如果LoadedApk中已存在mApplication的引用則直接返回,如果沒有就用前文提到的類加載器去創(chuàng)建它,這里的具體實(shí)現(xiàn)是mInstrumentation.newApplication(cl, appClass, appContext)侦鹏。
之后將LoadedApk中的mApplication賦值以便重復(fù)使用诡曙,并將這個(gè)引用添加到mActivityThread.mAllApplications這個(gè)數(shù)組。因?yàn)閭魅氲膇nstrumentation為空所以不執(zhí)行Application對(duì)象的生命周期函數(shù)onCreate略水。
接下來分android.app.Instrumentation這個(gè)類价卤,上文創(chuàng)建Application對(duì)象用到了newApplication函數(shù),執(zhí)行Application的生命周期用到了callApplicationOnCreate函數(shù):
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
return newApplication(cl.loadClass(className), context);
}
static public Application newApplication(Class<?> clazz, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = (Application)clazz.newInstance();
app.attach(context);
return app;
}
public void callApplicationOnCreate(Application app) {
app.onCreate();
分析得知,Application對(duì)象實(shí)際是由Class的newInstance函數(shù)創(chuàng)建的渊涝,這種語法經(jīng)常見于Java反射機(jī)制的調(diào)用慎璧。而callApplicationOnCreate實(shí)際調(diào)用了onCreate函數(shù)。
下面通過分析Android4.4(API 19)源碼得到ActivityThread對(duì)象關(guān)系圖:
到此我們知道Android默認(rèn)的類加載器是如何創(chuàng)建的跨释,并且如何用這個(gè)類加載器創(chuàng)建了Application對(duì)象胸私。
分析Android四大組件的類加載過程
上文分析了Application對(duì)象的創(chuàng)建過程,以及PathClassLoader的創(chuàng)建過程鳖谈。下文中還需要分析四大組件是如何創(chuàng)建及啟動(dòng)的岁疼。為什么不是只分析Activity?除了Activity以外缆娃,Android程序還可以以后臺(tái)Service捷绒、數(shù)據(jù)共享提供器ContentProvider、廣播接收者BroadCastReceiver的形式運(yùn)行贯要。我們需要知道動(dòng)態(tài)加載技術(shù)對(duì)四大組件的創(chuàng)建及生命周期是否可行暖侨。
Activity
以下時(shí)序圖來自于老羅的《Android系統(tǒng)源代碼情景分析》
從一個(gè)Android進(jìn)程啟動(dòng)另一個(gè)Android進(jìn)程的Activity需要先初始化ActivtyThread并創(chuàng)建Application,如上文所述郭毕。然后由AMS通過Binder機(jī)制通知ActivityThread執(zhí)行scheduleLaunchActivity函數(shù):
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state,
PersistableBundle persistentState, List<ResultInfo> pendingResults,
List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward,
ProfilerInfo profilerInfo) {
updateProcessState(procState, false);
ActivityClientRecord r = new ActivityClientRecord();
r.token = token;
r.ident = ident;
r.intent = intent;
r.referrer = referrer;
r.voiceInteractor = voiceInteractor;
r.activityInfo = info;
r.compatInfo = compatInfo;
r.state = state;
r.persistentState = persistentState;
r.pendingResults = pendingResults;
r.pendingIntents = pendingNewIntents;
r.startsNotResumed = notResumed;
r.isForward = isForward;
r.profilerInfo = profilerInfo;
updatePendingConfiguration(curConfig);
sendMessage(H.LAUNCH_ACTIVITY, r);
}
接著在handleMessage函數(shù)接收Message它碎,根據(jù)msg.what繼續(xù)執(zhí)行handleLaunchActivity函數(shù):
public void handleMessage(Message msg) {
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
...
}
}
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed);
...
} else {
// If there was an error, for any reason, tell the activity
// manager to stop us.
try {
ActivityManagerNative.getDefault()
.finishActivity(r.token, Activity.RESULT_CANCELED, null, false);
} catch (RemoteException ex) {
// Ignore
}
}
}
可以看到performLaunchActivity函數(shù)創(chuàng)建了Activity實(shí)例,handleResumeActivity函數(shù)調(diào)用了Activity的onResume函數(shù):
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
...
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (activity != null) {
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor);
...
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onCreate()");
}
r.activity = activity;
r.stopped = true;
if (!r.activity.mFinished) {
activity.performStart();
r.stopped = false;
}
if (!r.activity.mFinished) {
if (r.isPersistable()) {
if (r.state != null || r.persistentState != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
r.persistentState);
}
} else if (r.state != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
}
}
if (!r.activity.mFinished) {
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnPostCreate(activity, r.state,
r.persistentState);
} else {
mInstrumentation.callActivityOnPostCreate(activity, r.state);
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onPostCreate()");
}
}
}
r.paused = true;
mActivities.put(r.token, r);
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to start activity " + component
+ ": " + e.toString(), e);
}
}
return activity;
}
這里我們只關(guān)注Activity的創(chuàng)建過程和Activity的生命周期函數(shù)显押。可以看到在Activity對(duì)象創(chuàng)建后傻挂,由mInstrumentation調(diào)用了callActivityOnCreate乘碑、callActivityOnRestoreInstanceState函數(shù),間接調(diào)用了Activity的onCreate、onRestoreInstanceState函數(shù)金拒。
r.packageInfo.getClassLoader()是Activity的類加載器兽肤,newActivity是Activity對(duì)象的創(chuàng)建函數(shù):
public Activity newActivity(ClassLoader cl, String className,
Intent intent)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
return (Activity)cl.loadClass(className).newInstance();
}
再來看r.packageInfo.getClassLoader()是怎么來的,r是ActivityClientRecord 對(duì)象,最初是由scheduleLaunchActivity函數(shù)創(chuàng)建的,r.packageInfo是在handleMessage函數(shù)中調(diào)用getPackageInfoNoCheck函數(shù)得到的套腹,再看getPackageInfoNoCheck函數(shù):
public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
CompatibilityInfo compatInfo) {
return getPackageInfo(ai, compatInfo, null, false, true);
}
到這一步跟上文分析創(chuàng)建Application獲取類加載器似曾相識(shí),因?yàn)槭峭瑯拥暮瘮?shù):
private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
ClassLoader baseLoader, boolean securityViolation, boolean includeCode) {
synchronized (mPackages) {
WeakReference<LoadedApk> ref;
if (includeCode) {
ref = mPackages.get(aInfo.packageName);
} else {
ref = mResourcePackages.get(aInfo.packageName);
}
LoadedApk packageInfo = ref != null ? ref.get() : null;
if (packageInfo == null || (packageInfo.mResources != null
&& !packageInfo.mResources.getAssets().isUpToDate())) {
packageInfo =
new LoadedApk(this, aInfo, compatInfo, this, baseLoader,
securityViolation, includeCode &&
(aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0);
if (includeCode) {
mPackages.put(aInfo.packageName,
new WeakReference<LoadedApk>(packageInfo));
} else {
mResourcePackages.put(aInfo.packageName,
new WeakReference<LoadedApk>(packageInfo));
}
}
return packageInfo;
}
}
這里跟Application獲取ClassLoader實(shí)際上是同樣的原理资铡,首先從mPackages嘗試獲取WeakReference<LoadedApk> ref电禀,如果能取到就直接返回,如果取不到就重新創(chuàng)建一個(gè)LoadedApk笤休。
這里注意ref是一個(gè)弱引用尖飞,因此它不會(huì)存活到下一次虛擬機(jī)反生GC后。所以每次GC后都會(huì)重新創(chuàng)建LoadedApk并放到mPackages里店雅。但是在前文講述ActivityThread啟動(dòng)Application的過程中政基,mBoundApplication作為ActivtyThread中的字段引用了AppBindData對(duì)象,AppBindData對(duì)象的info(LoadedApk)也被間接的強(qiáng)引用了闹啦,所以對(duì)于執(zhí)行了bindApplication的Android應(yīng)用程序應(yīng)該不存在LoadedApk被回收的情況沮明,這一點(diǎn)通過DDMS模擬GC反射查看LoadedApk是否為NULL。我的測(cè)試結(jié)果中說明它是不會(huì)再GC后為NULL的印證了這點(diǎn)窍奋。
Service
startService函數(shù)定義在ContextWrapper中荐健,間接調(diào)用Context的startService函數(shù),這個(gè)函數(shù)是抽象函數(shù)琳袄,實(shí)際調(diào)用的是ContextImpl的startService函數(shù)摧扇。接著調(diào)用ContextImpl內(nèi)部的startServiceCommon函數(shù),最后執(zhí)行ActivityManagerNative.getDefault().startService函數(shù),實(shí)際是調(diào)用AMP(ActivityManagerProxy)的startService方法,在此函數(shù)中通過IBinder的transact函數(shù)發(fā)送START_SERVICE_TRANSACTION消息挚歧,最后由ActivityManagerNative在onTransact函數(shù)收到通知扛稽,并執(zhí)行startService函數(shù),該函數(shù)由AMS實(shí)現(xiàn)滑负。
在AMS中執(zhí)行startService函數(shù)在张,間接調(diào)用ActiveServices類的startServiceLocked、startServiceInnerLocked矮慕、bringUpServiceLocked函數(shù)帮匾,如果該Service所在進(jìn)程未啟動(dòng),則調(diào)用AMS類中的startProcessLocked函數(shù)去創(chuàng)建目標(biāo)進(jìn)程痴鳄。這里通過Process.start靜態(tài)函數(shù)創(chuàng)建目標(biāo)進(jìn)程并執(zhí)行ActivityThread瘟斜,于是又回到了上文中Android進(jìn)程的啟動(dòng)入口以及Application對(duì)象的創(chuàng)建。
在ActivityThread執(zhí)行attch后會(huì)通知AMS調(diào)用attachApplicationLocked痪寻,間接調(diào)用ActiveServices類的attachApplicationLocked函數(shù)螺句,此時(shí)目標(biāo)進(jìn)程已經(jīng)創(chuàng)建完畢。
在ActiveServices的attachApplicationLocked函數(shù)中會(huì)調(diào)用realStartServiceLocked函數(shù)來啟動(dòng)需要的Service橡类。此函數(shù)會(huì)調(diào)用app.thread.scheduleCreateService函數(shù)蛇尚,app.thread其實(shí)是ProcessRecord對(duì)象中一個(gè)ApplicationThreadProxy的代理(IApplicationThread),最后會(huì)通過Binder機(jī)制調(diào)用transact函數(shù)發(fā)送一個(gè)SCHEDULE_CREATE_SERVICE_TRANSACTION的消息,由ApplicationThread接收顾画,并向ActivityThread發(fā)送一個(gè)CREATE_SERVICE的消息取劫,最后在ActivityThread的handleMessage函數(shù)中執(zhí)行handleCreateService函數(shù)匆笤,同樣通過getPackageInfoNoCheck函數(shù)得到LoadedApk對(duì)象,然后通過LoaedApk的類加載器加載Service對(duì)象:
private void handleCreateService(CreateServiceData data) {
...
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = (Service) cl.loadClass(data.info.name).newInstance();
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
"Unable to instantiate service " + data.info.name
+ ": " + e.toString(), e);
}
}
try {
ContextImpl context = new ContextImpl();
context.init(packageInfo, null, this);
Application app = packageInfo.makeApplication(false, mInstrumentation);
context.setOuterContext(service);
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
service.onCreate();
mServices.put(data.token, service);
try {
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, 0, 0, 0);
} catch (RemoteException e) {
// nothing to do.
}
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
"Unable to create service " + data.info.name
+ ": " + e.toString(), e);
}
}
}
可以看到Service與Activity都是由getPackageInfoNoCheck函數(shù)創(chuàng)建LoadedApk對(duì)象得到類加載器創(chuàng)建的谱邪。
下圖描述了使用代理模式遠(yuǎn)程調(diào)用系統(tǒng)AMS的時(shí)序圖:
ContentProvider
可以看到ContentProvider跟Activity炮捧、Service的創(chuàng)建有些不同,在handleBindApplication函數(shù)內(nèi)部調(diào)用了installContentProviders函數(shù)來安裝ContentProvider惦银,此函數(shù)中循環(huán)調(diào)用了installProvider函數(shù):
private IContentProvider installProvider(Context context,
IContentProvider provider, ProviderInfo info, boolean noisy) {
ContentProvider localProvider = null;
if (provider == null) {
Context c = null;
ApplicationInfo ai = info.applicationInfo;
if (context.getPackageName().equals(ai.packageName)) {
c = context;
} else if (mInitialApplication != null &&
mInitialApplication.getPackageName().equals(ai.packageName)) {
c = mInitialApplication;
} else {
try {
c = context.createPackageContext(ai.packageName,
Context.CONTEXT_INCLUDE_CODE);
} catch (PackageManager.NameNotFoundException e) {
}
}
if (c == null) {
return null;
}
try {
final java.lang.ClassLoader cl = c.getClassLoader();
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
provider = localProvider.getIContentProvider();
if (provider == null) {
return null;
}
localProvider.attachInfo(c, info);
} catch (java.lang.Exception e) {
return null;
}
}
synchronized (mProviderMap) {
// Cache the pointer for the remote provider.
String names[] = PATTERN_SEMICOLON.split(info.authority);
for (int i=0; i<names.length; i++) {
ProviderClientRecord pr = new ProviderClientRecord(names[i], provider,
localProvider);
try {
provider.asBinder().linkToDeath(pr, 0);
mProviderMap.put(names[i], pr);
} catch (RemoteException e) {
return null;
}
}
if (localProvider != null) {
mLocalProviders.put(provider.asBinder(),
new ProviderClientRecord(null, provider, localProvider));
}
}
return provider;
}
這里的Context c其實(shí)就是我們創(chuàng)建的Application.因此ContentProvider是用Application對(duì)象的getClassLoader函數(shù)返回的類加載器創(chuàng)建的咆课。這里的getClassLoader函數(shù)實(shí)際執(zhí)行的是父類ContextImpl對(duì)象的getClassLoader函數(shù),通過調(diào)用傳入的LoadedApk的getClassLoader函數(shù)來實(shí)現(xiàn)璧函。因此ContentProvider的類加載與上述所述一致傀蚌。
有一點(diǎn)要注意的是ContentProvider是在Application對(duì)象創(chuàng)建以后就創(chuàng)建的,并且先于Application對(duì)象執(zhí)行onCreate函數(shù):
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
// don't bring up providers in restricted mode; they may depend on the
// app's custom Application class
if (!data.restrictedBackupMode){
List<ProviderInfo> providers = data.providers;
if (providers != null) {
installContentProviders(app, providers);
// For process that contains content providers, we want to
// ensure that the JIT is enabled "at some point".
mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
}
}
try {
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (!mInstrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
BroadCastReceiver
廣播的注冊(cè)分為靜態(tài)和動(dòng)態(tài)兩種蘸吓。無論哪一種最終目的都是在AMS注冊(cè)一個(gè)InnerReceiver對(duì)象善炫,這個(gè)對(duì)象與Android進(jìn)程內(nèi)的BroadCastReceiver向?qū)?yīng),如果AMS接收到廣播库继,首先經(jīng)過InnerReceiver列表的篩選箩艺,然后再通過Binder機(jī)制交給應(yīng)用本地的BroadCastReceiver對(duì)象處理。
靜態(tài)注冊(cè)指的是在AndroidManifest.xml中聲明的接收者宪萄,在系統(tǒng)啟動(dòng)的時(shí)候艺谆,會(huì)由PackageManagerService(以下簡(jiǎn)稱PMS)去解析。當(dāng)AMS調(diào)用PMS的接口來查詢廣播注冊(cè)的時(shí)候拜英,PMS會(huì)查詢記錄并且返回給AMS 静汤。
動(dòng)態(tài)注冊(cè)時(shí)值在Android應(yīng)用啟動(dòng)后由Context(實(shí)際是ContextImpl)的registerReceiver函數(shù)通過Binder遠(yuǎn)程調(diào)用AMS注冊(cè)廣播接收器。
當(dāng)AMS通知Android進(jìn)程處理廣播居凶,會(huì)在ActivityThread執(zhí)行scheduleReceiver函數(shù),接著通過 queueOrSendMessage(H.RECEIVER, r)調(diào)用handleReceiver函數(shù):
private void handleReceiver(ReceiverData data) {
...
String component = data.intent.getComponent().getClassName();
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
IActivityManager mgr = ActivityManagerNative.getDefault();
BroadcastReceiver receiver;
try {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
data.intent.setExtrasClassLoader(cl);
data.setExtrasClassLoader(cl);
receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
} catch (Exception e) {
if (DEBUG_BROADCAST) Slog.i(TAG,
"Finishing failed broadcast to " + data.intent.getComponent());
data.sendFinished(mgr);
throw new RuntimeException(
"Unable to instantiate receiver " + component
+ ": " + e.toString(), e);
}
try {
Application app = packageInfo.makeApplication(false, mInstrumentation);
ContextImpl context = (ContextImpl)app.getBaseContext();
sCurrentBroadcastIntent.set(data.intent);
receiver.setPendingResult(data);
receiver.onReceive(context.getReceiverRestrictedContext(),
data.intent);
} catch (Exception e) {
data.sendFinished(mgr);
if (!mInstrumentation.onException(receiver, e)) {
throw new RuntimeException(
"Unable to start receiver " + component
+ ": " + e.toString(), e);
}
} finally {
sCurrentBroadcastIntent.set(null);
}
if (receiver.getPendingResult() != null) {
data.finish();
}
}
可以看到這里跟上述組件的加載很相似虫给,依然用getPackageInfoNoCheck函數(shù)獲取類加載器然后實(shí)例化BroadCastReceiver對(duì)象。
在Android4.1上測(cè)試不管是靜態(tài)注冊(cè)還是動(dòng)態(tài)注冊(cè)侠碧,在創(chuàng)建BroadCastReceiver對(duì)象前一定先啟動(dòng)它所在的進(jìn)程,即執(zhí)行上述bindApplication函數(shù)創(chuàng)建Application對(duì)象抹估。
加固思路
上文中已經(jīng)分析了Android的類加載器和Application、Activity弄兜、Service药蜻、BroadCastReceiver、ContentProvider等組件的類加載過程替饿。
我們知道.dex是Android虛擬機(jī)能夠執(zhí)行的一個(gè)類集合文件语泽,默認(rèn)類加載器以apk包中的dex路徑為參數(shù)。要實(shí)現(xiàn)對(duì)原dex的加密盛垦,我們就只能通過動(dòng)態(tài)加載技術(shù)以插件的形式加載dex湿弦。
首先定義一個(gè)殼Application類,在此類中要實(shí)現(xiàn)對(duì)加密的原.dex文件的解密腾夯,并且動(dòng)態(tài)加載dex颊埃。
如何動(dòng)態(tài)加載原.dex文件?我們可以將原.dex文件解密到指定路徑,然后創(chuàng)建DexClassLoader對(duì)象蝶俱,用此對(duì)象去替換默認(rèn)的PathClassLoader班利。
而替換的時(shí)機(jī)一定要早于各個(gè)組件的創(chuàng)建過程,為此選擇Application的attachBaseContext函數(shù)榨呆,該函數(shù)定義于ContextWrapper類罗标,在Application執(zhí)行attach函數(shù)時(shí)調(diào)用:
final void attach(Context context) {
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
還記得Application對(duì)象是LoadedApk類中的makeApplication函數(shù)創(chuàng)建的,而在makeApplication函數(shù)中积蜻,實(shí)際調(diào)用Instrumentation類的newApplication函數(shù):
static public Application newApplication(Class<?> clazz, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = (Application)clazz.newInstance();
app.attach(context);
return app;
}
因此可以得出結(jié)論闯割,一旦通過LoadedApk的makeApplication函數(shù)創(chuàng)建Application對(duì)象,也會(huì)調(diào)用Application中的函數(shù)attachBaseContext函數(shù)竿拆。要在殼程序的Application類中重寫這個(gè)函數(shù)并且解密原.dex文件宙拉,并用DexClassLoader動(dòng)態(tài)加載,然后用反射技術(shù)替換掉LoadedApk對(duì)象中默認(rèn)的類加載器丙笋。這樣在加載其他組件時(shí)使用的就是替換的DexClassLoader谢澈,加載的是原.dex中的類。
根據(jù)分析ContentProvider是最早在attachBaseContext函數(shù)后被加載的御板。所以其他組件的加載也不會(huì)出現(xiàn)問題锥忿。
除了在殼Application類的attachBaseContext函數(shù)中替換原類加載器。還需要替換掉一些相關(guān)對(duì)象怠肋,例如用原Application對(duì)象代替殼Application對(duì)象敬鬓。在LoadedApk的makeApplication函數(shù)中可見:
if (mApplication != null) {
return mApplication;
}
mActivityThread.mAllApplications.add(app);
mApplication = app;
這里需要用反射技術(shù)將mApplication這個(gè)引用置空,這樣第二次執(zhí)行此函數(shù)就會(huì)新建一個(gè)Application對(duì)象并賦給mApplication笙各。并且需要從mActivityThread對(duì)象中的mAllApplications這個(gè)數(shù)組移除殼Application對(duì)象钉答。
在ActivityThread的handleBindApplication函數(shù)中調(diào)用完makeApplication函數(shù)可見:
mInitialApplication = app;
if (!data.restrictedBackupMode){
List<ProviderInfo> providers = data.providers;
if (providers != null) {
installContentProviders(app, providers);
// For process that contains content providers, we want to
// ensure that the JIT is enabled "at some point".
mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
}
}
try {
mInstrumentation.callApplicationOnCreate(app);
}...
這里的mInitialApplication也引用了殼Application對(duì)象,需要用原程序中的Application對(duì)象替換它酪惭。
注意到installContentProviders函數(shù)也引用了app,也需要替換它希痴。
注意到最后由Instrumentation對(duì)象執(zhí)行了callApplicationOnCreate函數(shù),間接調(diào)用了殼Application類的onCreate函數(shù)春感,因此反射替換將在殼onCreate函數(shù)完成砌创。
到此理清了加殼技術(shù)的大部分思路。具體的實(shí)現(xiàn)還需要針對(duì)不同API版本鲫懒,以及MultiDex的情況嫩实。
詳見《利用動(dòng)態(tài)加載技術(shù)實(shí)現(xiàn)APK安全加固》