修改了这三个值以后,壳dex文件的末尾就可以添加一些和dex格式定义中无关的信息,比如原始dex文件的个数,大小以及加密以后原始dex文件的内容。然后将壳dex替换掉原始的dex文件对Apk进行重打包即可。
问题2:
壳代码会将自己的Application类替换掉原来的Application类,因此壳的Application类的onCreate()
和attachBaseContext()
会在App进程启动的时候率先执行拿到控制权。而访问被隐藏加密的dex可以通过getApplicationInfo().sourceDir
来获取apk的路径,解压apk得到壳dex文件从而解密出壳dex文件里边附带的原始dex文件。
问题3:
通过Android中的类加载器动态加载解密后的dex文件,为了让四大组件可以正常走生命周期函数,还需要对类加载器进行修正,修正的方式大概有两种方式,下面的内容中会详细的描述。
四. Android类加载器:
类加载器在dex整体加壳与脱壳中起到了重要的作用。
网上有很多的文章介绍Java中的类加载器,因此我这里就不写了,这里主要说的是Android里边关于dex加载的"类"加载器。
类加载器可以在运行的时候加载在编译时未知的类,对于c/c++来说也有类似的功能dlopen与dlsym,而Android里边的BaseDexClassLoader
可以实现在运行的时候加载在编译时未知的dex文件,经过此加载器的加载,ART虚拟机内存中会形成相应的数据结构,对应的dex文件也会由mmap映射到虚拟内存当中,通过此加载器的loadClass(String className, boolean resolve)
方法就可以得到类的Class对象,从而可以使用该类。
对于壳来说,它解密出来的dex文件也是编译时未知的,因此需要通过类加载器来加载dex文件,系统提供的BaseDexClassLoader
可以满足要求,也可以自定义ClassLoader
来实现。
通过打印测试可以看到JDK API中的类如java.lang.String
,以及Android系统提供的核心类如android.app.Activity
(位于/system/framework/framework.jar中)都是由同一个类加载器java.lang.BootClassLoader
所加载的。
而由应用程序开发者自己所编写的代码由dalvik.system.PathClassLoader
加载器加载,打印出来的信息如下:
dalvik.system.PathClassLoader[DexPathList[[directory “.”],nativeLibraryDirectories=[/system/lib64, /vendor/lib64, /system/lib64, /vendor/lib64]]]
查看源码可以看到PathClassLoader
是继承自BaseDexClassLoader
的,而PathClassLoader
还有另外两个兄弟: InMemoryDexClassLoader
以及DexClassLoader
,而壳程序很多都使用了这两个类加载器来加载解密后的dex文件。其中InMemoryDexClassLoader
是Android8.0以后新增的类,可以实现所谓的"不落地加载"。
总结一下,主要有如下几个ClassLoader:
- BootClassLoader: 加载系统核心类的加载器,由系统创建
- BaseDexClassLoader: 加载dex文件的基类加载器
- PathClassLoader: 加载应用程序开发者自己编写的代码的加载器,比如四大组件类,它继承自BaseDexClassLoader
- DexClassLoader: 从源码中可以看到几乎和BaseDexClassLoader没有区别,它继承自BaseDexClassLoader,一般用于实现插件化和加壳
- InMemoryDexClassLoader: 通过ByteBuffer数组来加载dex的加载器,它继承自BaseDexClassLoader
- 自定义ClassLoader: 可以实现想实现的任何功能
前面说过壳加载完原始的dex以后还需要对ClassLoader
进行修正,否则加载组件类运行的时候会报ClassN