Android apk加固实现原理(二)

在上一篇文章中已经分享了,apk加固的整体思路和具体的实现,在上一篇文章中还有一个问题没有分享完,在apk加固后,主App的Application是已经没有作用了,所以这次主要是分享如果让主App的Application可以正常的使用。
https://www.jianshu.com/p/e836428d61b9 apk加固实现原理(一)

Application的创建

要解决主App的Application不生效的问题,首先得先搞清楚Application是在哪里创建的。main方法是所有程序的入口;在Android的应用中main方法是在ActivityThread中,所以Application就是在ActivityThread中被创建出来的。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
      ...............................
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.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);
           .................................
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to start activity " + component
                    + ": " + e.toString(), e);
            }
        }
        return activity;
    }

每一个应用的Application都是在performLaunchActivity的方法中被创建出来的,在上面的源码中也可以看到,先是Activity被创建出来后;Application才被创建出来,这里也是有一个面试点:是Activity被创建先还是Application被创建先,很多没有看过源码的人都是觉得是Application先创建出来,其实不是这样的,Activity被创建出来只是还没有走生命周期,Application的生命周期先调用。

Application的生命周期与Application替换

Applicationt的生命周期主要有两个,一个是onCreate、另外一个是attachBaseContext,其它的几个很少用得上,这里先不描述。在Application被创建出来后第一个走的生命周期是:attachBaseContext然后再走onCreate这样一个App的Application就创建完成了。
在attachBaseContext中已经有将解密出来的dex交给系统处理了,在第一篇文章中已经分享过,这里就不具体描述了,ProxyApplication在onCreate的时候要将主App的Application创建出来,并替换系统中的Application引用,如果不替换则还是ProxyApplication的引用,则在App调用Application是就会出错,最后回调主App的Application的onCreate方法完成替换。
通过对源码的查看,可以看到ActivityThread中有两个全局变量需要要替换。一个是Application的成员变量、另外一个是集合中的。

 Application mInitialApplication;
 final ArrayList mAllApplications
            = new ArrayList();

以上是ActivityThread中要替换成主App的Application。
接下来就是LoadedApk,LoadedApk里面有一个全局变量的Application也是需要替换

 private Application mApplication;

以上这些替换的Application需要使用反射来进行替换,当前替换完成后再主动调用主 App的Application这样就达到目的了

 private void replaceApplication() throws Exception{
        //得到attachBaseContext的上下文
        Log.d(TAG,"app_name:"+app_name);
        Context baseContext = getBaseContext();
        //创建真实的Application
        Class delegateClass = Class.forName(app_name);
        delegate = (Application) delegateClass.newInstance();
        Method attach = Application.class.getDeclaredMethod("attach",Context.class);
        attach.setAccessible(true);
        attach.invoke(delegate,baseContext);
        //设置ContextImp中的setOuterContext
        Class contextImpClass = Class.forName("android.app.ContextImpl");
        Field mOuterContextFileId = contextImpClass.getDeclaredField("mOuterContext");
        mOuterContextFileId.setAccessible(true);
        mOuterContextFileId.set(baseContext, delegate);

        Field mMainThreadFieId = contextImpClass.getDeclaredField("mMainThread");
        mMainThreadFieId.setAccessible(true);
        Object mMainThread = mMainThreadFieId.get(baseContext);

        Class activityThreadClass = Class.forName("android.app.ActivityThread");
        Field mInitialApplicationField = activityThreadClass.getDeclaredField("mInitialApplication");
        mInitialApplicationField.setAccessible(true);
        mInitialApplicationField.set(mMainThread, delegate);

        Field mAllApplicationsFieId = activityThreadClass.getDeclaredField("mAllApplications");
        mAllApplicationsFieId.setAccessible(true);
        ArrayList mAllApplications = (ArrayList) mAllApplicationsFieId.get(mMainThread);
        mAllApplications.remove(this);//把代理的Application移除
        mAllApplications.add(delegate);

        Field mPackageInfoFieId = contextImpClass.getDeclaredField("mPackageInfo");
        mPackageInfoFieId.setAccessible(true);
        Object mPackageInfo = mPackageInfoFieId.get(baseContext);


        Class loadApkClass = Class.forName("android.app.LoadedApk");
        Field mApplicationFieId = loadApkClass.getDeclaredField("mApplication");
        mApplicationFieId.setAccessible(true);
        mApplicationFieId.set(mPackageInfo, delegate);

        //修改className
        Field mApplicationInfoFieId = loadApkClass.getDeclaredField("mApplicationInfo");
        mApplicationInfoFieId.setAccessible(true);
        ApplicationInfo mApplicationInfo = (ApplicationInfo) mApplicationInfoFieId.get(mPackageInfo);
        mApplicationInfo.className = app_name;
        delegate.onCreate();
        isReplace = true;
    }

以上就是Apk加固的原理分析全部过程了,如果在大家看到有哪里搞错了欢迎指正,毕竟个人能力有限。

你可能感兴趣的:(Android apk加固实现原理(二))