[TOC]
1.(Launcher应用程序中执行)点击图标,Launcher应用程序通知ActivityManagerService开启activity
2.(ActivityManagerService组件中执行)ActivityManagerService通过IBinder向Launcher组件发送SCHEDULE_PAUSE_ACTIVITY_TRANSACTION
3.(Launcher应用程序中执行)Launcher的ActivityThread来处理发送来的消息,暂停Launcher组件,接着发送ACTIVITY_PAUSED_TRANSACTION消息给ActivityManagerService组件
4.(ActivityManagerService组件中执行)ActivityManagerService处理Launcher发送来的消息,接着创建应用程序进程,主要通过zygoto来创建新的应用进程和新的ActivityThread
5.(应用进程内部执行)应用进程向ActivityManagerService发送进程通信请求ATTACH_APPLICATION_TRANSACTION(表示应用启动完成)
6.(ActivityManagerService组件中执行)处理应用进程发送来的请求,既然应用进程已经创建,现在开始应用进程对象ProcessRecord的初始化操作,并且向应用进程发送进程通信请求 。
7.(ActivityThread中执行)这里的初始化操作中比较重要的一项就是bindApplication,发送BIND_APPLICATION消息给H类,调用handleBindApplication方法进行Application类的创建和初始化
8.后面就是MainActivity组件的启动过程,在正式启动Activity前也会执行makeApplication方法来查看application是否创建
应用程序启动过程中,先实例化application来初始化全局变量,接着通过全局变量来启动Activity组件
类加载器,起源于JAVA,实现java类可以被动态加载进虚拟机
java.lang.ClassLoader
。ClassLoader.getSystemClassLoader()
来获取它。如上面加载器树状组织结构所示,子类加载器加载类时(查找类字节码并定义它),会代理给父类加载器(不是父类对象),父类加载器尝试加载该类,如果父类加载器不能加载,再由子类加载器加载
因为代理模式的存在,启动这个类的加载过程的类加载器(子类加载器)和真正完成类加载工作的类加载器(父类加载器)不是同一个类加载器。
默认使用系统类加载器。
解决引导类加载器无法加载找到系统类加载器需要加载的类
【1】Java虚拟机对于两个类判断是否相同,出了名称是否一样还要判断类的定义加载器是否一样
【2】不同类加载器加载的类是不兼容的,也就是Java虚拟机中存在一个个相互隔离的类空间。
1.设置父类加载器
2.优化并加载dex文件到内存中(本地方法)
默认类加载器是PathClassLoader
在开始启动Activity组件前的一步,有ActivityThread内执行bindApplication方法中创建Application组件,performLaunchActivity启动Activity类中也会调用makeApplication检查是否创建applicaiton
动态加载Dex文件,防止反编译
创建壳Application
使用DexClassLoader加载原始Dex
源码地址:https://github.com/xiongchaochao/ApkProtect/tree/master/v1.0
根据预知识Activity启动流程:application类实例化在开启MainActivity之前,所以我们将动态加载工作放在application中,也就是我们自定义一个壳application并生成壳Dex文件让系统启动,在这个壳application中去开启原始Dex文件的application
public class shell extends Application { ..... }
根据预知识DexClassLoader原理:类加载器可以加载dex文件到内存中,但是不能启动里面的Application、Activity等组件启动原始dex文件的执行流程。这是我们下一步的方向,开启类加载器加载的Dex文件的组件
/* 2. 加载Dex文件。 * 通过替换ActivityThread对象内loadedapk的mClassLoader值,完成了类加载器的替换和Dex文件的加载*/ String classActivityThread = "android.app.ActivityThread"; String classLoadedApk = "android.app.LoadedApk"; DexClassLoader loader; File nativeLib = new File(FileUtils.getParent(LIBS), "lib"); Object activityThread = RefInvoke.invokeStaticMethod(classActivityThread, "currentActivityThread", new Class[]{}, new Object[]{}); String packageName = this.getPackageName();//当前apk的包名 //获取currentActivityThread的mPackages字段值,是一个包名对应loadedapk对象的map值 Map<?,?> mPackage = (Map<?,?>)RefInvoke.getField(activityThread, classActivityThread, "mPackages"); //获取loadedapk对象的弱引用 WeakReference<?> wr = (WeakReference<?>) mPackage.get(packageName); //获取DexClassLoader类加载器,准备加载原始dex文件,并且设置父类加载器为当前主线程上下文中loadedApk中保存的mClassLoader对象 loader = new DexClassLoader(dexPathList.toString(), mApp.getCacheDir().getAbsolutePath(), nativeLib.getAbsolutePath(), (ClassLoader) RefInvoke.getField(wr.get(), "android.app.LoadedApk", "mClassLoader")); //更改当前loadedapk对象的类加载器为DexClassLoader类加载器 RefInvoke.setField(wr.get(), classLoadedApk, "mClassLoader", loader);
用原始Dex文件的application类重新实例化一个对象,并且替换掉ActivityThread和LoadedApk对象内部的applicaiton对象,这样后面开始之后Activity的时候就会调用新的application类的内部属性。
/* 2. 执行application*/ //原始Dex文件的application地址 String main = "com.scoreloop.games.gearedub.GearApplication"; String applicationName = main; //ActivityThread.currentActivityThread().mBoundApplication.info.mApplication = null; Object currentActivityThread = RefInvoke.invokeStaticMethod(classActivityThread, "currentActivityThread", new Class[]{}, new Object[]{}); Object mBoundApplication = RefInvoke.getField(currentActivityThread, classActivityThread, "mBoundApplication"); //info字段代表的是loadedapk对象 Object loadedApkInfo = RefInvoke.getField(mBoundApplication, classActivityThread+"$AppBindData", "info"); //将loadedapk的mApplication属性置空,在后面makeApplication方法中用原始Dex文件的Application对象赋值 RefInvoke.setField(loadedApkInfo, classLoadedApk, "mApplication", null); //移除ActivityThread对象内的mInitialApplication,后面重新赋值原始Dex文件的Application对象赋值 Object mInitApplication = RefInvoke.getField(currentActivityThread, classActivityThread, "mInitialApplication"); List<Application> mAllApplications = (List<Application>) RefInvoke.getField(currentActivityThread, classActivityThread, "mAllApplications"); mAllApplications.remove(mInitApplication); //将原始Dex文件的application路径名分别赋值给loadedapk和ActivityThread$AppBindData的className //(LoadedApk) loadedApkInfo.mApplicationInfo.className = applicationName ((ApplicationInfo) RefInvoke.getField(loadedApkInfo, classLoadedApk, "mApplicationInfo")).className = applicationName; //(ActivityThread$AppBindData) mBoundApplication.appInfo.className = applicationName ((ApplicationInfo) RefInvoke.getField(mBoundApplication, classActivityThread+"$AppBindData", "appInfo")).className = applicationName; //给ActivityThread的mInitialApplication赋值makeApplication对象 //currentActivityThread.mInitApplication = loadedApkInfo.makeApplication(false, null) Application makeApplication = (Application) RefInvoke.invokeMethod(loadedApkInfo, classLoadedApk, "makeApplication", new Class[]{boolean.class, Instrumentation.class}, new Object[]{false, null}); RefInvoke.setField(currentActivityThread, classActivityThread, "mInitialApplication", makeApplication); //currentActivityThread.mProviderMap Map<?,?> mProviderMap = (Map<?,?>) RefInvoke.getField(currentActivityThread, classActivityThread, "mProviderMap"); for (Map.Entry<?, ?> entry : mProviderMap.entrySet()) { Object providerClientRecord = entry.getValue(); Object mLocalProvider = RefInvoke.getField(providerClientRecord, classActivityThread+"$ProviderClientRecord", "mLocalProvider"); RefInvoke.setField(mLocalProvider, "android.content.ContentProvider", "mContext", makeApplication); } makeApplication.onCreate();
【1】DexClassLoader 加载器加载dex文件字节码到虚拟机中,但是不能没有生命周期,只是不同类
【2】在ActivityThread内部是通过LoadedApk类来存储APK安装包所有数据
【1】深入探讨 Java 类加载器 https://www.ibm.com/developerworks/cn/java/j-lo-classloader/index.html
【2】从源码分析 Android dexClassLoader 加载机制原理 https://blog.csdn.net/nanzhiwen666/article/details/50515895