美文网首页安卓逆向
FDex2 源码学习

FDex2 源码学习

作者: 艾胖胖胖 | 来源:发表于2021-12-27 16:47 被阅读0次

今天来学习一下脱壳的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,写入文件

相关文章

网友评论

    本文标题:FDex2 源码学习

    本文链接:https://www.haomeiwen.com/subject/rfzjeltx.html