吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 9916|回复: 23
收起左侧

[Android 原创] 【申精】DEX动态加载分析与Frida进阶hook(一)

  [复制链接]
Richor 发表于 2019-8-29 11:07
本帖最后由 Richor 于 2019-8-29 11:12 编辑

最近分析YY直播TCP协议的时候发现不管用任何方法都找不到发送接收位置,用DDMS又看到一些不存在与反编译文件里的类,冥思苦想无解,最后看到ghostmazeW大佬的文章才知道有动态加载的,以下用demo分析动态加载过程
1.静态分析:
分别用AK,JADX,JEB加载文件看看
(1)JADX
image.png
(2)AK
image.png
(3)JEB

image.png
jadx和AK明显分析难度很高,果断放弃,选用JEB查看
2.分析oncreat函数
[Java] 纯文本查看 复制代码
 public void onCreate(Bundle arg13) {
        int v4 = 3;
        Object[] v0 = new Object[1];
        v0[0] = arg13;
        ChangeQuickRedirect v2 = MainActivity.changeQuickRedirect;
        Class[] v5 = new Class[1];
        Class v1 = Bundle.class;
        v5[0] = v1;
        Class v6 = Void.TYPE;
        MainActivity v1_1 = this;
        boolean v0_1 = PatchProxy.isSupport(v0, v1_1, v2, false, v4, v5, v6);
        if(v0_1) {
            v0 = new Object[1];
            v0[0] = arg13;
            v2 = MainActivity.changeQuickRedirect;
            v5 = new Class[1];
            v1 = Bundle.class;
            v5[0] = v1;
            v6 = Void.TYPE;
            v1_1 = this;
            PatchProxy.accessDispatch(v0, v1_1, v2, false, v4, v5, v6);
        }
        else {
            int v0_2 = 2;
            String v9 = this.Joseph(1, v0_2);
            super.onCreate(arg13);
            v0_2 = 0x7F09001B;
            this.setContentView(v0_2);
            this.runRobust();
            String v0_3 = "1B:D0:4A:9D:B5:A9:84:93:7E:79:27:9C:6C:C4:14:AB:DD:B0:75:7F";
            SignCheck v10 = new SignCheck(this, ((Context)this), v0_3);
            v10.check();
            Debug.isDebuggerConnected();
            v0_2 = 0x7F07003D;
            View v8 = this.findViewById(v0_2);
            v0_2 = 0x7F070026;
            View v7 = this.findViewById(v0_2);
            cn.chaitin.geektan.crackme.MainActivity$1 v0_4 = new View$OnClickListener(((EditText)v8), v9) {
                public static ChangeQuickRedirect changeQuickRedirect;

                public void onClick(View arg9) {
                    Toast v0_6;
                    MainActivity v0_5;
                    String v1_3;
                    int v4 = 18;
                    Object[] v0 = new Object[1];
                    v0[0] = arg9;
                    ChangeQuickRedirect v2 = cn.chaitin.geektan.crackme.MainActivity$1.changeQuickRedirect;
                    Class[] v5 = new Class[1];
                    Class v1 = View.class;
                    v5[0] = v1;
                    Class v6 = Void.TYPE;
                    cn.chaitin.geektan.crackme.MainActivity$1 v1_1 = this;
                    boolean v0_1 = PatchProxy.isSupport(v0, v1_1, v2, false, v4, v5, v6);
                    if(v0_1) {
                        v0 = new Object[1];
                        v0[0] = arg9;
                        v2 = cn.chaitin.geektan.crackme.MainActivity$1.changeQuickRedirect;
                        v5 = new Class[1];
                        v1 = View.class;
                        v5[0] = v1;
                        v6 = Void.TYPE;
                        v1_1 = this;
                        PatchProxy.accessDispatch(v0, v1_1, v2, false, v4, v5, v6);
                    }
                    else {
                        EditText v0_2 = this.val$input_text;
                        Editable v0_3 = v0_2.getText();
                        v0_1 = TextUtils.isEmpty(((CharSequence)v0_3));
                        if(!v0_1) {
                            v0_2 = this.val$input_text;
                            v0_3 = v0_2.getText();
                            String v0_4 = v0_3.toString();
                            StringBuilder v1_2 = new StringBuilder();
                            String v2_1 = "DDCTF{";
                            v1_2 = v1_2.append(v2_1);
                            v2_1 = this.val$result;
                            v1_2 = v1_2.append(v2_1);
                            v2_1 = "}";
                            v1_2 = v1_2.append(v2_1);
                            v1_3 = v1_2.toString();
                            v0_1 = v0_4.equals(v1_3);
                            if(v0_1) {
                                v0_5 = MainActivity.this;
                                v1_3 = "恭喜大佬!密码正确!";
                                v0_6 = Toast.makeText(((Context)v0_5), ((CharSequence)v1_3), 0);
                                v0_6.show();
                                return;
                            }
                        }

                        v0_5 = MainActivity.this;
                        v1_3 = "大佬莫急!再试试!";
                        v0_6 = Toast.makeText(((Context)v0_5), ((CharSequence)v1_3), 0);
                        v0_6.show();
                    }
                }
            };
            ((Button)v7).setOnClickListener(((View$OnClickListener)v0_4));
        }
    }

好的,我们发现了几个比较可疑的函数,PatchProxychangeQuickRedirect,runrobust;其中runrobust有lmp标识,这个是动态加载的特征
image.png
3.分析runrobust
[Java] 纯文本查看 复制代码
private void runRobust() {
        int v4 = 4;
        Object[] v0 = new Object[0];
        ChangeQuickRedirect v2 = MainActivity.changeQuickRedirect;
        Class[] v5 = new Class[0];
        Class v6 = Void.TYPE;
        MainActivity v1 = this;
        boolean v0_1 = PatchProxy.isSupport(v0, v1, v2, false, v4, v5, v6);
        if(v0_1) {
            v0 = new Object[0];
            v2 = MainActivity.changeQuickRedirect;
            v5 = new Class[0];
            v6 = Void.TYPE;
            v1 = this;
            PatchProxy.accessDispatch(v0, v1, v2, false, v4, v5, v6);
        }
        else {
            Context v1_1 = this.getApplicationContext();
            PatchManipulateImp v2_1 = new PatchManipulateImp();
            PatchExecutor v0_2 = new PatchExecutor(v1_1, ((PatchManipulate)v2_1), new GeekTanCallBack());
            v0_2.start();
        }
    }
}

可以看到此函数实例化了PatchManipulateImp(),我们跟进去看看;
image.png
看几个函数,应该可以看到fetchpatchlist,fetch就是取的意思,我们就可以怀疑这个函数就是取出动态加载的dex
我们继续找到有什么文件
image.png
看到了一个 v10 = arg17.getAssets().open("GeekTan.BMP");,奇怪怎么是BMP?
image.png
找到这个文件放进010看看
image.png
看到抬头是PK...就知道是压缩文件,在看后面的lasses.dex,就可以确定这个实际就是dex的压缩文件
Robust是美团出的一款热修复框架,可以在github上面它的最新的源码,地址:https://github.com/Meituan-Dianping/Robust
Robust为每个class增加了个类型为ChangeQuickRedirect的静态成员,而在每个方法前都插入了使用changeQuickRedirect相关的逻辑,当changeQuickRedirect不为null时,会执行到accessDispatch方法从而替换掉之前老的逻辑,达到修复的目的
4.分析PatchExecutor
上一步我们大体知道了PatchManipulateImp这个对象,接下来我们回到mainactivity看下一步实例对象PatchExecutor
image.png
我们看看run函数
[Java] 纯文本查看 复制代码
    public void run() {
        try {
            this.applyPatchList(this.fetchPatchList());
        }
        catch(Throwable v1) {
            String v2 = "robust";
            String v3 = "PatchExecutor run";
            Log.e(v2, v3, v1);
            RobustCallBack v2_1 = this.robustCallBack;
            v3 = "class:PatchExecutor,method:run,line:36";
            v2_1.exceptionNotify(v1, v3);
        }
    }

this.applyPatchList(this.fetchPatchList());就是打补丁的操作,跟进去看看
[Java] 纯文本查看 复制代码
protected void applyPatchList(List arg9) {
        RobustCallBack v4_4;
        boolean v0;
        String v6;
        StringBuilder v5_2;
        if(arg9 != null) {
            boolean v3 = arg9.isEmpty();
            if(v3) {
                return;
            }

            String v3_1 = "robust";
            StringBuilder v4 = new StringBuilder();
            String v5 = " patchManipulate list size is ";
            v4 = v4.append(v5);
            int v5_1 = arg9.size();
            v4 = v4.append(v5_1);
            String v4_1 = v4.toString();
            Log.d(v3_1, v4_1);
            Iterator v3_2 = arg9.iterator();
        label_98:
            boolean v4_2 = v3_2.hasNext();
            if(!v4_2) {
                return;
            }

            Object v1 = v3_2.next();
            v4_2 = ((Patch)v1).isAppliedSuccess();
            if(v4_2) {
                v4_1 = "robust";
                v5_2 = new StringBuilder();
                v6 = "p.isAppliedSuccess() skip ";
                v5_2 = v5_2.append(v6);
                v6 = ((Patch)v1).getLocalPath();
                v5_2 = v5_2.append(v6);
                v5 = v5_2.toString();
                Log.d(v4_1, v5);
                goto label_98;
            }

            PatchManipulate v4_3 = this.patchManipulate;
            v4_2 = v4_3.ensurePatchExist(((Patch)v1));
            if(!v4_2) {
                goto label_98;
            }

            try {
                v0 = this.patch(this.context, ((Patch)v1));
            }
            catch(Throwable v2) {
                v4_4 = this.robustCallBack;
                v5 = "class:PatchExecutor method:applyPatchList line:69";
                v4_4.exceptionNotify(v2, v5);
            }

            if(v0) {
                ((Patch)v1).setAppliedSuccess(true);
                v4_4 = this.robustCallBack;
                v4_4.onPatchApplied(true, ((Patch)v1));
            }
            else {
                v4_4 = this.robustCallBack;
                v4_4.onPatchApplied(false, ((Patch)v1));
            }

            v4_1 = "robust";
            v5_2 = new StringBuilder();
            v6 = "patch LocalPath:";
            v5_2 = v5_2.append(v6);
            v6 = ((Patch)v1).getLocalPath();
            v5_2 = v5_2.append(v6);
            v6 = ",apply result ";
            v5_2 = v5_2.append(v6);
            v5_2 = v5_2.append(v0);
            v5 = v5_2.toString();
            Log.d(v4_1, v5);
            goto label_98;
        }
    }

再定位到载入的位置
[Java] 纯文本查看 复制代码
try {
                v0 = this.patch(this.context, ((Patch)v1));
            }
            catch(Throwable v2) {
                v4_4 = this.robustCallBack;
                v5 = "class:PatchExecutor method:applyPatchList line:69";
                v4_4.exceptionNotify(v2, v5);
            }

有个path函数,点进去看看
[Java] 纯文本查看 复制代码
 String v18_3 = arg25.getTempPath();
        File v19_2 = arg24.getCacheDir();
        v19_1 = v19_2.getAbsolutePath();
        v20 = null;
        Class v21 = PatchExecutor.class;
        ClassLoader v21_1 = v21.getClassLoader();
        String v0_3 = v18_3;
        String v1_1 = v19_1;
        String v2_1 = v20;
        ClassLoader v3 = v21_1;
        DexClassLoader v5 = new DexClassLoader(v0_3, v1_1, v2_1, v3);
        v18_3 = arg25.getTempPath();
        Patch v0_4 = arg25;
        v1_1 = v18_3;
        v0_4.delete(v1_1);
        try {
            Log.d("robust", "PatchsInfoImpl name:" + arg25.getPatchesInfoImplClassFullName());
            v15 = v5.loadClass(arg25.getPatchesInfoImplClassFullName()).newInstance();
            Log.d("robust", "PatchsInfoImpl ok");
        }
        catch(Throwable v17) {
            v0 = this;
            v0_2 = v0.robustCallBack;
            v18_2 = v0_2;
            v19_1 = "class:PatchExecutor method:patch line:108";
            v0_2 = v18_2;
            v1_2 = v17;
            v2_1 = v19_1;
            v0_2.exceptionNotify(v1_2, v2_1);
            v18_3 = "robust";
            v19 = new StringBuilder();
            v20 = "PatchsInfoImpl failed,cause of";
            v19 = v19.append(v20);
            v20 = v17.toString();
            v19 = v19.append(v20);
            v19_1 = v19.toString();
            Log.e(v18_3, v19_1);
            v17.printStackTrace();
        }

然后可以看到DexClassLoader,这就是动态加载类了
5.分析动态加载的dex文件
我们刚才已经得到了dex文件,把文件名改一下GeekTan.jar,丢到jadx看看
image.png
结构就长这样了,我们看一下PatchesInfoImpl看一下什么类是需要被修复的
就是这两个
cn.chaitin.geektan.crackme.MainActivityPatchControl
cn.chaitin.geektan.crackme.MainActivity$1PatchControl
image.png
先分析一下MainActivityPatchControl
image.png
image.png
我们可以看到onCreate,Joseph,onclick会被重载
我们点进onclick看看
image.png
一堆的invokeReflectStaticMethod,invokeReflectMethod,我们找到重点
[Asm] 纯文本查看 复制代码
String str = "DDCTF{";
str = (String) EnhancedRobustUtils.invokeReflectMethod("Joseph", obj32, getRealParameter(new Object[]{new Integer(7), new Integer(8)}), new Class[]{Integer.TYPE, Integer.TYPE}, MainActivity.class);
            if (obj4 == this) {
                obj4 = ((MainActivity$1Patch) obj4).originClass;
            }

if (((Boolean) EnhancedRobustUtils.invokeReflectMethod("equals", obj2, getRealParameter(new Object[]{str2}), new Class[]{Object.class}, String.class)).booleanValue()) {
                if (this instanceof MainActivity$1Patch) {
                    obj2 = this.originClass;
                } else {
                    obj2 = this;
                }
                obj2 = (Toast) EnhancedRobustUtils.invokeReflectStaticMethod("makeText", Toast.class, getRealParameter(new Object[]{(MainActivity) EnhancedRobustUtils.getFieldValue("this$0", obj2, 1.class), "恭喜大佬!密码正确!", new Integer(0)}), new Class[]{Context.class, CharSequence.class, Integer.TYPE});
                if (obj2 == this) {
                    obj2 = ((MainActivity$1Patch) obj2).originClass;
                }
                EnhancedRobustUtils.invokeReflectMethod("show", obj2, new Object[0], null, Toast.class);
                return;
            }

结果就是Joseph(int,int)
我们可以通过HOOK EnhancedRobustUtils.invokeReflectMethod来得到结果
[Asm] 纯文本查看 复制代码
 public static Object invokeReflectMethod(String arg5, Object arg6, Object[] arg7, Class[] arg8, Class arg9) {
        Object v2;
        try {
            v2 = EnhancedRobustUtils.getDeclaredMethod(arg6, arg5, arg8, arg9).invoke(arg6, arg7);
        }
        catch(Exception v0) {
            v0.printStackTrace();
            boolean v2_1 = EnhancedRobustUtils.isThrowable;
            if(v2_1) {
                StringBuilder v3 = new StringBuilder();
                String v4 = "invokeReflectMethod error ";
                v3 = v3.append(v4);
                v3 = v3.append(arg5);
                v4 = "   parameter   ";
                v3 = v3.append(v4);
                v3 = v3.append(arg7);
                v4 = " targetObject ";
                v3 = v3.append(v4);
                v4 = arg6.toString();
                v3 = v3.append(v4);
                v4 = "  args  ";
                v3 = v3.append(v4);
                v3 = v3.append(arg8);
                String v3_1 = v3.toString();
                RuntimeException v2_2 = new RuntimeException(v3_1);
                throw v2_2;
            }

            v2 = null;
        }

        return v2;
    }

最后一步,HOOK
image.png
打开Pycharm:
image.png
image.png
[Python] 纯文本查看 复制代码
import frida, sys


def on_message(message, data):
    if message['type'] == 'send':
        print("[/i][i] {0}".format(message['payload']))
    else:
        print(message)


js_code = '''
    Java.perform(function(){
        var robust = Java.use("com.meituan.robust.utils.EnhancedRobustUtils");
        robust.invokeReflectMethod.implementation = function(v1,v2,v3,v4,v5){
            var result = this.invokeReflectMethod(v1,v2,v3,v4,v5);
            if(v1=="Joseph"){
                console.log("functionName:"+v1);
                console.log("functionArg3:"+v3);
                console.log("functionArg4:"+v4);
                send(v4);
                console.log("return:"+result);
                console.log("-----------------------------------------------------")
            }
            else if(v1=="equals"){
                console.log("functionName:"+v1);
                console.log("functionArg3:"+v3);
                console.log("functionArg4:"+v4);
                send(v4);
                console.log("return:"+result);
            }
            return result;
        }
});
'''

process = frida.get_usb_device().attach("cn.chaitin.geektan.crackme")
print("找到目标包")
script = process.create_script(js_code)
script.on('message', on_message)
script.load()
sys.stdin.read()

image.png


结果就是DDCTF{2936012829362176}
image.png
下一章学一下Frida HOOK动态DEX函数
image.png
image.png
image.png
image.png

apk下载地址.txt

91 Bytes, 下载次数: 12, 下载积分: 吾爱币 -1 CB

免费评分

参与人数 12威望 +2 吾爱币 +22 热心值 +10 收起 理由
白二宝 + 1 + 1 我很赞同!
hzzheyang + 1 + 1 用心讨论,共获提升!
这是追求不是梦 + 1 + 1 我很赞同!
笙若 + 1 + 1 谢谢@Thanks!
icode2019 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
茶叶ˇ蛋℡ + 1 + 1 热心回复!
LOLQAQ + 1 + 1 我很赞同!
独行风云 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
清炒藕片丶 + 1 热心回复!
qtfreet00 + 2 + 12 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
yanmingming + 1 + 1 我很赞同!
okxiaobo + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

头像被屏蔽
okxiaobo 发表于 2019-8-29 11:20
提示: 作者被禁止或删除 内容自动屏蔽
xixicoco 发表于 2019-8-29 11:24
S.K 发表于 2019-8-29 11:33
yanmingming 发表于 2019-8-29 11:34
感谢发布原创
yiwai2012 发表于 2019-8-29 11:48
精品分析 膜拜大佬
FleTime 发表于 2019-8-29 12:59
你这表情包用的真不少。。。
dyh8283221 发表于 2019-8-29 13:15
厉害👍,佩服佩服,虽然看不懂
清炒藕片丶 发表于 2019-8-29 14:36

感谢发布原创,谢谢楼主
mzycy 发表于 2019-8-29 15:26
完全看不懂嘤嘤嘤
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-26 20:22

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表