Android脱壳4-Application创建过程分析

youncyb 发布于 20 天前 121 次阅读 reverse


该文章主要讲述了Android系统中Application创建过程的分析,具体涵盖了Application在系统启动时的加载与初始化步骤、进程间通信机制以及相关的内存管理。文章通过深入剖析源码和执行过程,展示了Application创建的核心要点,包括生命周期的管理及与其他系统组件的交互。

1.目录

2.简介

在 Android 应用中,每一个app都对应一个Application对象,Application对象的生命周期等同于app的生命周期。Android可以分为两种应用创建Application对象:system_server进程和app进程。

理解Application的创建过程,有助于理解一代壳的加固原理,一代壳在dex外围新加了一个用于启动的ShellApplication,在启动的过程中,通过ShellApplication代理RealApplication,最终使app的启动顺序、生命周期都回归到RealApplication,同时对getContext,getApplicationContext函数返回RealApplication的上下文。

3.创建system_server

3.1创建SystemContext

创建system_server的代码位于java\com\android\server\SystemServer.java的run方法,首先会调用createSystemContext()创建系统上下文。

private void run() {
    ...
    // Initialize the system context.
    createSystemContext();
    // Create the system service manager.
    mSystemServiceManager = new SystemServiceManager(mSystemContext);
    ...
}

3.2创建ActivityThread对象

createSystemContext方法如下,会创建一个ActivityThread对象,ActivityThread 是 Android 应用程序的主线程,负责管理应用程序的生命周期和处理UI操作。

system_server不仅是一个后台进程,其同时运行多个组件的Service进程,与Service进行交互的对话框是由system_server进程提供的,所以system_server也需要一个APK应用的上下文环境。

private void createSystemContext() {
    ActivityThread activityThread = ActivityThread.systemMain();
    mSystemContext = activityThread.getSystemContext();
    ...
}

systemMain方法会调用attach方法,attach方法会创建系统上下文和app上下文。在获取了SystemContext后,通过ContextImpl.createAppContext方法创建appContext。再调用context.mPackageInfo.makeApplication创建Application对象。

public static ActivityThread systemMain() {
    ...
    ActivityThread thread = new ActivityThread();
    thread.attach(true, 0);
    return thread;
}

private void attach(boolean system, long startSeq) {
    sCurrentActivityThread = this;
    mSystemThread = system;
    if (!system) {
        ...
    } else {
        ...
        try {
            mInstrumentation = new Instrumentation();
            mInstrumentation.basicInit(this);
            ContextImpl context = ContextImpl.createAppContext(
                    this, getSystemContext().mPackageInfo);
            mInitialApplication = context.mPackageInfo.makeApplication(true, null);
            mInitialApplication.onCreate();
        }
    }

其中getSystemContext方法会创建LoadedApk对象,其包名是"android",指向了framework-res.apk。

LoadedApk(ActivityThread activityThread) {
    mActivityThread = activityThread;
    mApplicationInfo = new ApplicationInfo();
    mApplicationInfo.packageName = "android";
    ...
}
static ContextImpl createSystemContext(ActivityThread mainThread) {
    LoadedApk packageInfo = new LoadedApk(mainThread);
    ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
            null, null);
    context.setResources(packageInfo.getResources());
    context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
            context.mResourcesManager.getDisplayMetrics());
    return context;
}

3.3 创建Application对象

首先判断mApplication对象是否是null,不是则表明application已经生成了。否则,调用mActivityThread.mInstrumentation.newApplication创建application。

    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 = ContextImpl.createAppContext(mActivityThread, this);
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            appContext.setOuterContext(app);
        } 
        ...
        mActivityThread.mAllApplications.add(app);
        mApplication = app;
        if (instrumentation != null) {
    		try {
        		instrumentation.callApplicationOnCreate(app);
            }...
        }
        return app;
    }

跟入newApplication方法,发现在创建application时会调用app的attach方法,跟进后发现调用的是attachBaseContext。所以该方法优先于onCreate方法执行。

public Application newApplication(ClassLoader cl, String className, Context context) {
    Application app = getFactory(context.getPackageName())
            .instantiateApplication(cl, className);
    app.attach(context);
    return app;
}
/* package */ final void attach(Context context) {
    attachBaseContext(context);
    mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}

4.创建用户级的application流程

与创建system_server相似,创建用户级的application通过ActivityThread.main方法,此时attach进入if逻辑,然后通过AMS(ActivityManagerService)进行创建。

private void attach(boolean system, long startSeq) {
    sCurrentActivityThread = this;
    mSystemThread = system;
    if (!system) {
        android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
                                                UserHandle.myUserId());
        RuntimeInit.setApplicationObject(mAppThread.asBinder());
        final IActivityManager mgr = ActivityManager.getService();
        mgr.attachApplication(mAppThread, startSeq);

AMS调用ActivityThread.bindApplication方法,通过Binder发送BIND_APPLICATION消息调用ActivityThread创建application。

public final void attachApplication(IApplicationThread thread, long startSeq) {
    ...
    synchronized (this) {
        int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        attachApplicationLocked(thread, callingPid, callingUid, startSeq);
        Binder.restoreCallingIdentity(origId);
    }
}
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
        int pid, int callingUid, long startSeq) {
    ...
    thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
            null, null, null, testMode,
            mBinderTransactionTrackingEnabled, enableTrackAllocation,
            isRestrictedBackupMode || !normalMode, app.isPersistent(),
            new Configuration(app.getWindowProcessController().getConfiguration()),
            app.compat, getCommonServicesLocked(app.isolated),
            mCoreSettingsObserver.getCoreSettingsLocked(),
            buildSerial, autofillOptions, contentCaptureOptions);
}
public final void bindApplication(...){
    ...
    AppBindData data = new AppBindData();
    data.processName = processName;
    data.appInfo = appInfo;
    data.providers = providers;
    ...
    sendMessage(H.BIND_APPLICATION, data);
    ...
}

具体创建函数是handleBindApplication,后面的流程则和system_server进程一致。

public void handleMessage(Message msg) {
    switch (msg.what) {
        case BIND_APPLICATION:
            AppBindData data = (AppBindData)msg.obj;
            handleBindApplication(data);
}
private void handleBindApplication(AppBindData data) {
    data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
    final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
    ...
    app = data.info.makeApplication(data.restrictedBackupMode, null);
    mInitialApplication = app;
    if (!data.restrictedBackupMode) {
        if (!ArrayUtils.isEmpty(data.providers)) {
            installContentProviders(app, data.providers);
        }
    }
    ...
    try {
        mInstrumentation.callApplicationOnCreate(app);
}

5.加壳思路

首先需要理清楚用户的Application创建过程,如图5-1所示。根据途中app的创建过程,ShellApplication同样可以调用makeApplication创建RealApplication。

  1. 修改AndroidManifest.xml,将ShellApplication作为MainApplication。

  2. 系统创建ShellApplication时,在attachBaseContext方法中动手。

  3. 由于makeApplication需要满足以下条件,才会创建RealApplication,即mApplication=null,且mApplicationInfo.className由我们控制。

    public final class LoadedApk {
        private ApplicationInfo mApplicationInfo;
        private Application mApplication;
    	...
        if (mApplication != null) {
            return mApplication;
        }
    
        Application app = null;
    
        String appClass = mApplicationInfo.className;
        if (forceDefaultAppClass || (appClass == null)) {
            appClass = "android.app.Application";
        }
        ...
    }
    
  4. 为了满足3的要求,首先看到ActivityThread类的关键成员属性,其中mBoundApplication类型是AppBindData,而其成员属性包含了LoadedApk,ApplicationInfo,providers。

    public final class ActivityThread extends ClientTransactionHandler {
        AppBindData mBoundApplication;
        Application mInitialApplication;
        final ArrayList<Application> mAllApplications
            = new ArrayList<Application>();
    }
    static final class AppBindData {
        @UnsupportedAppUsage
        LoadedApk info;
        @UnsupportedAppUsage
        String processName;
        @UnsupportedAppUsage
        ApplicationInfo appInfo;
        @UnsupportedAppUsage
        List<ProviderInfo> providers;
    }
    
    • 反射获取ActivityThread对象,进而得到LoadedApk对象、AppBindData对象、ApplicationInfo对象。

    • 修改LoadedApk.mApplication=null。

    • 修改AppBindData.appInfo.className和LoadedApk.mApplicationInfo.className为RealApplication的类名。

    • 修改mAllApplications,将ShellApplication从中移除。

    • 反射调用makeApplication方法,生成RealApplication。

    此时对于Context.getContextContext.getApplicationContext方法返回的都是RealApp的上下文。但ContentProvider中也有getContext,如下文所示,mContext有两处可以赋值,分别是构造方法和attachInfo方法。

    根据图5-1,attachInfo是通过ActivityThread.installProvider方法的localProvider.attachInfo(c, info);调用,而installProvider的context参数则是由handleBindApplication传入,即context=app。

    同时根据图5-1,ContentPrivider的初始化是在attachBaseContext之后的,此时看到installProvider类,如果控制了ActivityThread.mInitialApplication的值为RealApplication并且绕过第一个if条件,但仍然会被handleBindApplication覆盖为ShellApplication。

    1. 但可以注意到data.providers is empty,则可以避免系统自动创建ContentProvider,此时则将data.providers置空,由attachBaseContext手动创建ContentProvider。
    2. 在Application.onCreate中恢复data.providers。
    private void handleBindApplication(AppBindData data) {
        data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
        final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
        ...
        app = data.info.makeApplication(data.restrictedBackupMode, null);
        mInitialApplication = app;
        if (!data.restrictedBackupMode) {
            if (!ArrayUtils.isEmpty(data.providers)) {
                installContentProviders(app, data.providers);
            }
        }
        ...
        try {
            mInstrumentation.callApplicationOnCreate(app);
    }
    
    private ContentProviderHolder installProvider(Context context,
        ContentProviderHolder holder, ProviderInfo info,
        boolean noisy, boolean noReleaseNeeded, boolean stable) {
            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) {
                    // Ignore
                }
            }
            localProvider.attachInfo(c, info);
    }
    
    public abstract class ContentProvider implements ContentInterface, ComponentCallbacks2 {
        private Context mContext = null;
        public ContentProvider(
                Context context,
                String readPermission,
                String writePermission,
                PathPermission[] pathPermissions) {
            mContext = context;
            mReadPermission = readPermission;
            mWritePermission = writePermission;
            mPathPermissions = pathPermissions;
        }
        public final @Nullable Context getContext() {
        	return mContext;
    	}
        private void attachInfo(Context context, ...) {
        if (mContext == null) {
            mContext = context;
        }
    }
    

Application启动过程

图5-1

6. 参考

  1. 枯燥的源码分析—Application创建过程
  2. 浅谈 Zygote 和 SystemServer
  3. SystemServer进程浅析
  4. Android中动态替换Application的实现
  5. Android ContentProvider 启动分析