插桩方式实现
- 通过
dexClassLoader
将第三方的apk加载到ClassLoader中。 - 打开第三方apk时,实际上打开的是父应用的可以空壳activity。先用packageManager加载apk,拿到LauncherActivity的全类名,通过前面的
ClassLoader
反射实例化apk中的LauncherActivity
。 - 父app的空壳activity里面所有的生命周期方法都调用反射拿到的
LauncherActivity
对应的方法。把父app的空壳activity保存到第三方activity中备用(that
)。 - 由于反射实例化的
LauncherActivity
是没有上下文的,所以任何使用到了this
的方法都要重写,用父app传进来的that
代替this
。 - 第三方app中会使用到自己的资源文件,所以父空壳activity还需要重写
getResources
方法,利用AssetManager
加载apk得到Resources
。 - 这种方法要求了父app的空壳activity在注册时,
launchMode
必须是standard
。 - 第三方appk的activity不需要在自己的Manifest中注册。
- Service的方式类似Activity。
- 第三方apk中的动态广播类似Activity。
插件的静态广播接收器必须转换为宿主的动态广播接收器
- 插件apk里面的静态广播接收器可以在宿主app中以动态广播接收器的方式注册,核心是如何获取插件apk的Manifest中定义的BoradCastReceiver全类名以及对应的Intent-Filter。
- 借鉴系统如何安装apk,实现静态广播的注册方式。
- Android系统安装apk:1.把apk文件复制到data/app 目录下。2.开辟存放应用文件的数据 data/data/包名。3.将apk中的dex文件安装到data/dalvik-cache目录下。
- 真正加载广播接收器,是发生在系统启动时。1.
PackageManagerService
的main方法被SystemService
调用。2.main
方法会去扫描data/app目录,拿到apk文件后new一个PackageParser
,调用PackageParser.parsePackage(apkFile)
方法得到实体PackageParser.Package
,里面就包含了解析apk得到的所有四大组件信息。3.PackageParser.parsePackage(apkFile)
实现原理:通过AssetManager.addAssetPath(apkPath)
拿到AssetManager
,然后作为参数拿到apk对应的Resources
,可以从Resources
获取到AndroidManifest.xml
的xml信息。然后就是解析xml信息得到任何四大组件的内容。 - 拿到了插件apk里面所谓的静态广播接收器的全类名和intent-filter后,就按照动态广播的方式在宿主app中注册一样intent-filter的动态广播接收器,通过反射的方式实例化插件的静态广播接收器,宿主把所有的广播转发给插件。
Hook绕过AMS方式实现
- 将插件apk的dexElements导入到系统ClassLoader中。
- 使用
DexClassLoader
加载一个插件apk,拿到DexClassLoader
。这时它的父类BaseDexClassLoader
里面有一个 DexPathList pathList 里面有一个成员Element[] dexElements 就是用来存放dex资源的。 - 同样,系统Context的ClassLoader是PathClassLoader,它也继承自
BaseDexClassLoader
,所以它也有pathList对象。把插件包的ClassLoader中的dexElements取出,放入到系统Context的ClassLoader中,之后系统就可以直接找到插件包中的class资源了。
双亲委派机制
- ClassLoader的双亲委派机制:ClassLoader的loadClass方法会优先查找parent:ClassLoader的loadClass方法,parent为空时就到BootstrapClassLoader中查找,如果还是没找到自己的pathList中找。
- pathList查找的方式是:遍历里面的dexElements,从Element元素中取出dexFile,然后调用到native层找到Class。
- 所以,dexElements这个数组里面加入插件的dexElements是有讲究的,如果把插件的dexElements插到前面,系统就会优先使用插件里面定义的类,后面的dexFile即使定义了也不会用到它。
- 有两个Hook点:1. ActivityManager.startActivity,修改Intent的目标地址为宿主APP的一个空壳Activity,骗过AMS的检查。2. 给ActivityThread的sMainThreadHandler指定mCallback,当msg是startActivity类型时还原msg中Intent参数的目标地址为插件中的Activity地址。
插件包中资源文件的加载问题
- 参考动态换肤,需要重写插件activity的getResources、getAssets两个方法,还需要重写Application的getAssetManager、getResources两个方法。
网友评论