插件化方式(hook)
2019-02-20 本文已影响0人
爬行的蚂蚁2725
主要原理:将插件的dex和宿主的dex融为一体
这里涉及到连个classloader,DexClassLoader和PathClassLoader他们都继承了BaseDexClassLoader,
DexClassLoader可以加载任何目录的dex,PathClassLoader只能加载系统安装的dex.
dex在内存中的表现形式为Element,在BasedexClassLoader下有个成员变量pathList,
PathList类中有个成员变量dexElements,是Element的集合。也就是所有的dex在内存中会被加载成Element集合,于是我们可以将插件中的dexElements 融合到系统的dexElents数组。
public class HookUtil {
public void injectPluginClass() {
String cachePath = context.getCacheDir().getAbsolutePath();
String apkPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/plugin.apk";
DexClassLoader dexClassLoader = new DexClassLoader(apkPath, cachePath, cachePath, context.getClassLoader());
// 第一步 找到 插件的Elements数组 dexPathlist ----?dexElement
try {
Class myDexClazzLoader=Class.forName("dalvik.system.BaseDexClassLoader");
Field myPathListFiled=myDexClazzLoader.getDeclaredField("pathList");
myPathListFiled.setAccessible(true);
Object myPathListObject =myPathListFiled.get(dexClassLoader);
Class myPathClazz=myPathListObject.getClass();
Field myElementsField = myPathClazz.getDeclaredField("dexElements");
myElementsField.setAccessible(true);
// 自己插件的 dexElements[]
Object myElements=myElementsField.get(myPathListObject);
// 第二步 找到 系统的Elements数组 dexElements
PathClassLoader pathClassLoader= (PathClassLoader) context.getClassLoader();
Class baseDexClazzLoader = Class.forName("dalvik.system.BaseDexClassLoader");
Field pathListFiled = baseDexClazzLoader.getDeclaredField("pathList");
pathListFiled.setAccessible(true);
Object pathListObject = pathListFiled.get(pathClassLoader);
Class systemPathClazz = pathListObject.getClass();
Field systemElementsField = systemPathClazz.getDeclaredField("dexElements");
systemElementsField.setAccessible(true);
//系统的 dexElements[]
Object systemElements = systemElementsField.get(pathListObject);
// 第三步 上面的dexElements 数组 合并成新的 dexElements 然后通过反射重新注入系统的Field (dexElements )变量中
// 新的 Element[] 对象
// dalvik.system.Element
int systemLength = Array.getLength(systemElements);
int myLength = Array.getLength(myElements);
// 找到 Element 的Class类型 数组 每一个成员的类型
Class<?> sigleElementClazz = systemElements.getClass().getComponentType();
int newSysteLength = myLength + systemLength;
Object newElementsArray=Array.newInstance(sigleElementClazz, newSysteLength);
//融合
for (int i = 0; i < newSysteLength; i++) {
// 先融合 插件的Elements
if (i < myLength) {
Array.set(newElementsArray, i, Array.get(myElements, i));
}else {
Array.set(newElementsArray,i,Array.get(systemElements,i-myLength));
}
}
Field elementsField=pathListObject.getClass().getDeclaredField("dexElements");;
elementsField.setAccessible(true);
// 将新生成的EleMents数组对象重新放到系统中去
elementsField.set( pathListObject,newElementsArray);
} catch (Exception e) {
e.printStackTrace();
}
}
}
application中注册hook,并重写Resources 和AssetManager
public class MyApplication extends Application {
private static Context sContext;
private static final String TAG = "xxx";
private AssetManager assetManager;
private Resources newResource;
private Resources.Theme mTheme;
@Override
public void onCreate() {
super.onCreate();
HookUtil hookUtil = new HookUtil();
hookUtil.hookStartActivity(this);
hookUtil.hookHookMh(this);
hookUtil.injectPluginClass();
// 重构
String apkPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/plugin.apk";
try {
assetManager = AssetManager.class.newInstance();
Method addAssetPathMethod = assetManager.getClass().getDeclaredMethod("addAssetPath", String.class);
addAssetPathMethod.setAccessible(true);
addAssetPathMethod.invoke(assetManager, apkPath);
// 手动实例化
Method ensureStringBlocks = AssetManager.class.getDeclaredMethod("ensureStringBlocks");
ensureStringBlocks.setAccessible(true);
ensureStringBlocks.invoke(assetManager);
// 插件的StringBloac被实例化了
Resources supResource = getResources();
newResource = new Resources(assetManager, supResource.getDisplayMetrics(), supResource.getConfiguration());
} catch (Exception e) {
e.printStackTrace();
}
}
public AssetManager getAssetManager() {
return assetManager==null?super.getAssets():assetManager;
}
@Override
public Resources getResources() {
return newResource==null?super.getResources():newResource;
}
// resource
}
在插件中创建baseActivity 获取新的AssetManager和Resources
public class BaseActivity extends Activity{
@Override
public Resources getResources() {
if (getApplication() != null && getApplication().getResources() != null) {
return getApplication().getResources();
}
return super.getResources();
}
@Override
public AssetManager getAssets() {
if (getApplication() != null && getApplication().getAssets() != null) {
return getApplication().getAssets();
}
return super.getAssets();
}
// @Override
// public Resources.Theme getTheme() {
// if(getApplication() != null && getApplication().getTheme() != null){
// return getApplication().getTheme();
// }
// return super.getTheme();
// }
}