今天来学习一下脱壳的xposed模块:Fdex2
一 反编译
使用jadx反编译fdex2.apk
image.png
二 源码学习
入口
xposed插件的入口在assets的xposed_init,内容如下:
formatfa.xposed.Fdex2.MainHook
-
流程分析
首先实现了IXposedHookLoadPackage类
image.png
接口IXposedHookLoadPackage(在一个应用启动的时候,会被调用,loadPackageParam中的是当前应用的信息)
接口IXposedHookZygoteInit (安卓系统启动时)
接口IXposedHookInitPackageResources (资源被初始化时)
核心代码
这里复制了formatfa.xposed.Fdex2.MainHook中的核心代码
package formatfa.xposed.Fdex2;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XSharedPreferences;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MainHook implements IXposedHookLoadPackage {
Class Dex;
Method Dex_getBytes;
Method getDex = null;
XSharedPreferences shared;
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) {
this.shared = new XSharedPreferences("formatfa.xposed.Fdex2", "package");
this.shared.reload();
initRefect();
String string = this.shared.getString(MainActivity.KEY, (String) null);
if (string == null) {
XposedBridge.log("没有指定apk,请打开模块选择要脱dex的apk");
} else if (loadPackageParam.packageName.equals(string)) {
XposedBridge.log(new StringBuffer().append(string).append(" has hook").toString());
ClassLoader classLoader = loadPackageParam.classLoader;
Object[] objArr = new Object[3];
try {
objArr[0] = Class.forName("java.lang.String");
objArr[1] = Boolean.TYPE;
objArr[2] = new XC_MethodHook(this, string, loadPackageParam) {
/* class formatfa.xposed.Fdex2.MainHook.AnonymousClass100000000 */
private final MainHook this$0;
private final String val$aim;
private final XC_LoadPackage.LoadPackageParam val$p1;
{
this.this$0 = r8;
this.val$aim = r9;
this.val$p1 = r10;
}
static MainHook access$0(AnonymousClass100000000 r4) {
return r4.this$0;
}
/* access modifiers changed from: protected */
@Override
public void afterHookedMethod(XC_MethodHook.MethodHookParam methodHookParam) {
XposedBridge.log(" after hook : ");
boolean z = true;
Class cls = (Class) methodHookParam.getResult();
if (cls != null) {
try {
String name = cls.getName();
try {
Class.forName("formatfa.xposed.Fdex2.MainHook").getClassLoader();
Class.forName(name, false, ClassLoader.getSystemClassLoader());
} catch (ClassNotFoundException e) {
throw new NoClassDefFoundError(e.getMessage());
}
} catch (ClassNotFoundException e2) {
z = false;
}
if (!z) {
try {
// 2. this.this$0.getDex.invoke(cls, new Object[0]), new Object[0]
// 3.this.this$0.Dex_getBytes.invoke
byte[] bArr = (byte[]) this.this$0.Dex_getBytes.invoke(this.this$0.getDex.invoke(cls, new Object[0]), new Object[0]);
if (bArr != null) {
new StringBuffer().append(new StringBuffer().append(new StringBuffer().append(new StringBuffer().append(new StringBuffer().append("/data/data/").append(this.val$aim).toString()).append("/").toString()).append(this.val$aim).toString()).append(bArr.length).toString()).append(".dex").toString();
File file = new File(this.this$0.shared.getString(MainActivity.DIR, "/sdcard"), new StringBuffer().append(new StringBuffer().append(this.val$p1.packageName).append(bArr.length).toString()).append(".dex").toString());
if (!file.exists()) {
FIO.writeByte(bArr, file.getAbsolutePath());
}
}
} catch (Exception e3) {
XposedBridge.log(e3.toString());
}
}
}
}
/* access modifiers changed from: protected */
@Override
public void beforeHookedMethod(XC_MethodHook.MethodHookParam methodHookParam) {
}
};
// 1.hook ClassLoader中的loadClass方法
XposedHelpers.findAndHookMethod("java.lang.ClassLoader", classLoader, "loadClass", objArr);
} catch (ClassNotFoundException e) {
throw new NoClassDefFoundError(e.getMessage());
}
}
}
public void initRefect() {
try {
// 4.获取这个dex对象的字节流
this.Dex = Class.forName("com.android.dex.Dex");
this.Dex_getBytes = this.Dex.getDeclaredMethod("getBytes", new Class[0]);
if (MainActivity.h.endsWith("0") && MainActivity.s.length() == 91) {
try {
// 3.获取这个Class对象的Dex对象
this.getDex = Class.forName("java.lang.Class").getDeclaredMethod("getDex", new Class[0]);
} catch (ClassNotFoundException e) {
throw new NoClassDefFoundError(e.getMessage());
}
}
} catch (Exception e2) {
XposedBridge.log(e2.toString());
}
}
/* access modifiers changed from: package-private */
public void writeDex(String str, Object obj) {
try {
byte[] bArr = (byte[]) this.Dex_getBytes.invoke(this.getDex.invoke(obj.getClass(), new Object[0]), new Object[0]);
if (bArr != null) {
File file = new File(this.shared.getString(MainActivity.DIR, "/sdcard"), new StringBuffer().append(new StringBuffer().append(str).append(bArr.length).toString()).append(".dex").toString());
if (!file.exists()) {
FIO.writeByte(bArr, file.getAbsolutePath());
}
}
} catch (InvocationTargetException e) {
} catch (IllegalAccessException e2) {
} catch (IllegalArgumentException e3) {
}
}
}
所有的类都是通过ClassLoader的loadClass方法加载的,所以hook住loadClass我们就可以得到所有的类的对象。
再通过这个Class对象来获取到其所属的dex对象,以及com.android.dex.Dex类中的getBytes函数,用于通过一个dex对象来获取到该dex对象在内存中的字节流
分别是java.lang.Class类中的getDex和com.android.dex.Dex类中的getBytes,注意两个API只有在Andriod6和Andriod7中有
总结
其实就是hook了ClaasLoader中的loadClass,然后再通过安卓原生的两个方法获取到了dex,写入文件
网友评论