内存壳的保护方式主要是通过动态加载的方式保护源dex。
DexShellS的主要功能是解密释放源dex,并移交控制权,开始源dex的生命周期。
1、重写attachBaseContext,替换LoadedApk中的mClassLoader
加载App的classloader被保存在LoadedApk类的mClassLoader属性中,所以首先要找到这个mClassLoader。每一个App都有的ActivityThread对象,可以通过静态方法currentActivityThread获得,ActivityThread类有一个ArrayMap类型的变量mPackages,保存了packageName到LoadedApk对象的映射
1 | Object currentActivityThread = RefInvoke.invokeStaticMethod("android.app.ActivityThread", |
获得LoadedApk进而可以获得源classloader,源classloader保存了原先加载的资源,由于双亲委派机制,我们不能放弃源classloader
1 | ClassLoader mClassLoader = (ClassLoader)RefInvoke.getFieldObject("android.app.LoadedApk",wr.get(),"mClassLoader"); |
加载释放后的源dex,并继承于源classloader
1 | DexClassLoader dexClassLoader = new DexClassLoader(apkFileName, odexPath, libPath, mClassLoader); |
这个新的classloader是通过DexClassLoader加载得到的,并不能拥有onCreate等生命周期,所以还要将其赋值给LoadedApk的mClassLoader属性
1 | RefInvoke.setFieldObject("android.app.LoadedApk", "mClassLoader",wr.get(),dexClassLoader); |
以上就完成了替换LoadedApk中的mClassLoader的替换。因为application等属性还是DexShellS的,所以此时加载的源Dex还不能拥有正常的生命周期,需要做进一步屏蔽。
2、重写onCreate
因为此时application还是DexShellS的,所以要调用LoadedApk#makeApplication创建源Dex的application,通过查看源码可以知道:
1)LoadedApk的mApplication属性要为空,不然将返回DexShellS的application
2)修改LoadedApk#mApplicationInfo#appClass为源Dex的Applicaiton的Class
3)创建applicaiton后会新添加在mActivityThread.mAllApplications中,所以要删除DexShellS的application
1 | public Application makeApplication(boolean forceDefaultAppClass,Instrumentation instrumentation) { |
根据以上3步,具体代码实现如下:
1)LoadedApk#mApplication==NULL
1 | //---set LoadedApk#mApplication = null |
2)修改LoadedApk#mApplicationInfo#className
1 | //---set LoadedApk#mApplicationInfo ActivityThread$AppBindData#appInfo |
3)修改ActivityThread$AppBindData#appInfo#className
1 | ApplicationInfo appInfoAT$AppBindData = (ApplicationInfo) RefInvoke.getFieldObject("android.app.ActivityThread$AppBindData", |
4)移除ActivityThread#mAllApplications中DexShellS的application
1 | //---remove the old application in the ActivityThread#mAllApplications |
5)创建新application
1 | Application app = (Application)RefInvoke.invokeMethod("android.app.LoadedApk","makeApplication", |
6)替换ActivityThread#mInitialApplication为新application
1 | RefInvoke.setFieldObject("android.app.ActivityThread","mInitialApplication", |
7)替换mProviderMap中的application
1 | ArrayMap mProviderMap = (ArrayMap) RefInvoke.getFieldObject("android.app.ActivityThread", |
之后新的applicaiton就可以开始新的生命周期了
主要学习了app的启动流程,这种内存壳已经被完全破解了,下面的指令抽取壳也可以通过主动调用类方法去实现脱壳,将核心算法放入so文件,并混淆可以有效加强代码的保护强度,java2c不稳定,和系统版本强相关,现在vmp壳只是对部分函数做虚拟化,比如onCreate