Android收藏集JS破解&&Android逆向面试题

APK加固之动态替换Application

2020-05-27  本文已影响0人  Simplelove_f033

想要替换 ProxyApplication首先对 Application 启动源码很熟悉才能对它进行操作,下面由我来带着大家一起进入源码的世界吧。

Application 绑定过程

今天主要从 ActivityThread => main() 开始,下面以一个流程图来说明一下:

image

XML 中如何解析我们的 Application

反射需要替换的内容

反射开始替换 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();
            //创建用户真实的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回调参数获取
            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;
        }

现在重新签名打包完成,启动我们的 APK 看下 Log

image
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()

这里已经替换成我们自己的 MyApplication , 而且 Activity 和 Service 获取上下文也已经是我们替换成功的 Applicaton。但是...也许有的眼神比较好的已经看出问题了,为什么内容提供者 Context 还是代理的 Application 而且比我们自己的应用还要先执行,那么我们带着这个问题去看 Application onCreate 之前做了什么事儿。

image

我们点击 installlContentProviders(app,providers);

注意这里传进去的还是 代理 Context

image

重点在最后

image

注意看我 勾画 的圈里面的逻辑判断,判断当前应用的包名是否跟 XML 中的包名一致,如果一致我们就赋值,再次提醒下 这里的 context 是我们代理的 context ,那么我们怎么做勒,我们在代理中重写 PackageName 只要都不等 那么就会走 else 会根据包名创建一个 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;

    }

  1. 首先判断我们自己 XML 中的 app_name 是否为空
  2. 如果不为空,我们传入一个 空包
  3. SDK 会判断是否跟 XML 中的 pck 一样,最后走 else 我们在重写 createPackageContext 传入我们自己应用的包名。会生成一个 Context

最后我们来验证一下:

image

<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 是系统的以外,所有的 Context 都是我们替换的 Application Context。完美解决。不过这里有一个隐藏 BUG ,据说面试题会问那么是什么勒?

可以在广播中使用 context 在开启一个广播或者绑定一个服务吗?

我们其实可以带着这个问题看下源码

H -> RECEIVER 消息

image

<figcaption></figcaption>

image

果然注册广播和绑定服务会抛一个异常。

总结

image

到这里我们的加固已经讲完了,从 dex 分包 -> 加密 -> 对齐 > 签名 - > 打包压缩成 APK 。一套完整的流程和代码都已经写完了。跟市面上的加固流程原理都几乎一样。懂了原理再去使用第三方就轻车熟路了。

上一篇下一篇

猜你喜欢

热点阅读