FDex2 源码学习

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,001评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,210评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,874评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,001评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,022评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,005评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,929评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,742评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,193评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,427评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,583评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,305评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,911评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,564评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,731评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,581评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,478评论 2 352

推荐阅读更多精彩内容