一 原理
要点:替换整个类
- 网络下载dex
- 将下载在sd卡的dex拷贝到私有路径下,获取对应的File[]
- 反射获取dalvik.system.BaseDexClassLoader的成员pathList,再次反射获取pathList的成员dexElements
- 获取系统的dexElements
- 将自己的dexElements和系统的dexElements拼成一个新的数组,保证自己的再签名,这样类在加载的时候找到自己修复的对象就会返回
二 实现
FixManager修复
package com.cn.ray.tinker_ray;
import android.content.Context;
import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.HashSet;
import dalvik.system.DexClassLoader;
import dalvik.system.PathClassLoader;
public class FixManager {
private static final FixManager ourInstance = new FixManager();
public static FixManager getInstance() {
return ourInstance;
}
private HashSet<File> loaded = new HashSet<>();
private FixManager() {
}
public void loadDex(Context context) {
if (context == null) {
return;
}
File filesDir = context.getDir("odex", Context.MODE_PRIVATE);
File[] listFiles = filesDir.listFiles();
for (File file : listFiles) {
// 如果是classes开到 。dex
if (file.getName().endsWith(".dex")) {
loaded.add(file);
}
}
//修复
String optimizeDir = filesDir.getAbsolutePath() + File.separator + "opt_dex";
File fopt = new File(optimizeDir);
if(!fopt.exists()){
fopt.mkdirs();
}
for(File dex:loaded){
// private final Element[] dexElements;
// // 对象成员变量 对象 还原 ----数组 反射调用
// ------------------------------我们的--------------------------------------------------------
DexClassLoader dexClassloader = new DexClassLoader(dex.getAbsolutePath(),optimizeDir,null,context.getClassLoader());
// BaseClassLoader ----dexPathList ---->dexElements;
try {
Class myDexClazzLoader = Class.forName("dalvik.system.BaseDexClassLoader");
Field myPathListFiled = myDexClazzLoader.getDeclaredField("pathList");
myPathListFiled.setAccessible(true);
//dexPathList
Object myPathListObject = myPathListFiled.get(dexClassloader);
Class myPathClazz = myPathListObject.getClass();
//dexElements
Field myElementsField = myPathClazz.getDeclaredField("dexElements");
myElementsField.setAccessible(true);
Object myElements = myElementsField.get(myPathListObject);
// myElements -----》 private final Element[] dexElements;
//----------------------------------系统的--------------------------------------------------------
// PathClassLoader PathList Element
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);
Object systemElements = systemElementsField.get(pathListObject);
// 其他地方 新建留个房间
// dalvik.system.Element[] dexElements;
Class elemnt = systemElements.getClass();
int systemLength = Array.getLength(systemElements);
int myLength = Array.getLength(myElements);
// 生成一个新的 数组 类型为Element类型
Object newElementArray = Array.newInstance(elemnt, systemLength + myLength);
for(int i=0;i<myLength+systemLength;i++) {
if (i < myLength) {
// 先插入修复包的dex yes 1
Array.set(newElementArray, i, Array.get(myElements,i));
}else {
// 系统的 no 2 索引错了 systemElements 5
Array.set(newElementArray, i, Array.get(systemElements,i-myLength));
}
}
Field elementsField=pathListObject.getClass().getDeclaredField("dexElements");;
elementsField.setAccessible(true);
elementsField.set(pathListObject, newElementArray);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
手动修复和再次启动加载
MainActivity
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//外置卡 - dex 指定目录 copy
public void fix(View view) {
File filesDir = getDir("odex", Context.MODE_PRIVATE);
String name = "fix.dex";
String filePath = new File(filesDir, name).getAbsolutePath();
File file = new File(filePath);
if (file.exists()) {
file.delete();
}
InputStream is = null;
FileOutputStream os = null;
try {
Log.i(TAG, "fixBug: " + new File(Environment.getExternalStorageDirectory(), name).getAbsolutePath());
is = new FileInputStream(new File(Environment.getExternalStorageDirectory(), name));
os = new FileOutputStream(filePath);
int len = 0;
byte[] buffer = new byte[1024];
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
File f = new File(filePath);
FixManager.getInstance().loadDex(this);
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
os.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void test(View view) {
Test test = new Test();
test.testFix(this);
}
}
MyAplication
public class MyAplication extends Application {
@Override
protected void attachBaseContext(Context base) {
FixManager.getInstance().loadDex(base);
super.attachBaseContext(base);
}
}
三 注意
- SD卡的读写权限
- 因为应用第一次进入,修复的dex还没下载,而类已经被加载使用了,因此需要再次启动的时候,才能完全修复