FDex2 源碼學(xué)習(xí)

今天來學(xué)習(xí)一下脫殼的xposed模塊:Fdex2

一 反編譯

使用jadx反編譯fdex2.apk


image.png

二 源碼學(xué)習(xí)

入口

xposed插件的入口在assets的xposed_init,內(nèi)容如下:

formatfa.xposed.Fdex2.MainHook
  • 流程分析
    首先實現(xiàn)了IXposedHookLoadPackage類


    image.png

接口IXposedHookLoadPackage(在一個應(yīng)用啟動的時候,會被調(diào)用,loadPackageParam中的是當(dāng)前應(yīng)用的信息)
接口IXposedHookZygoteInit (安卓系統(tǒng)啟動時)
接口IXposedHookInitPackageResources (資源被初始化時)

核心代碼

這里復(fù)制了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對象的字節(jié)流
            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函數(shù),用于通過一個dex對象來獲取到該dex對象在內(nèi)存中的字節(jié)流

分別是java.lang.Class類中的getDex和com.android.dex.Dex類中的getBytes,注意兩個API只有在Andriod6和Andriod7中有

總結(jié)

其實就是hook了ClaasLoader中的loadClass,然后再通過安卓原生的兩個方法獲取到了dex,寫入文件

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容