下你所需,载你所想!
IT技术源码资料下载网站

DEX动态加载分析与Frida进阶hook

:其他软件 2019-08-30 12:34:14

DEX动态加载分析与Frida进阶hook教程

1.静态分析:
分别用AK,JADX,JEB加载文件看看
(1)JADX
DEX动态加载分析与Frida进阶hook教程
(2)AK
DEX动态加载分析与Frida进阶hook教程
(3)JEB
jadx和AK明显分析难度很高,果断放弃,选用JEB查看
2.分析oncreat函数

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,[size=13.475px]runrobust;其中[size=13.475px]runrobust有lmp标识,这个是动态加载的特征
3.分析runrobust

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(),我们跟进去看看;
DEX动态加载分析与Frida进阶hook教程
看几个函数,应该可以看到fetchpatchlist,fetch就是取的意思,我们就可以怀疑这个函数就是取出动态加载的dex
我们继续找到有什么文件
DEX动态加载分析与Frida进阶hook教程
看到了一个 v10 = arg17.getAssets().open("GeekTan.BMP");,奇怪怎么是BMP?
找到这个文件放进010看看
DEX动态加载分析与Frida进阶hook教程看到抬头是PK...就知道是压缩文件,在看后面的lasses.dex,就可以确定这个实际就是dex的压缩文件
Robust是美团出的一款热修复框架,可以在github上面它的最新的源码,地址:https://github.com/Meituan-Dianping/Robust
Robust为每个class增加了个类型为ChangeQuickRedirect的静态成员,而在每个方法前都插入了使用changeQuickRedirect相关的逻辑,当changeQuickRedirect不为null时,会执行到accessDispatch方法从而替换掉之前老的逻辑,达到修复的目的
4.分析PatchExecutor
上一步我们大体知道了PatchManipulateImp这个对象,接下来我们回到mainactivity看下一步实例对象PatchExecutor 
DEX动态加载分析与Frida进阶hook教程 
我们看看run函数

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());就是打补丁的操作,跟进去看看

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;
        }
    }

再定位到载入的位置

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函数,点进去看看

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看看
DEX动态加载分析与Frida进阶hook教程 
结构就长这样了,我们看一下PatchesInfoImpl看一下什么类是需要被修复的
就是这两个
cn.chaitin.geektan.crackme.MainActivityPatchControl
cn.chaitin.geektan.crackme.MainActivity$1PatchControl 
先分析一下MainActivityPatchControl
 DEX动态加载分析与Frida进阶hook教程DEX动态加载分析与Frida进阶hook教程 
我们可以看到onCreate,Joseph,onclick会被重载
我们点进onclick看看
DEX动态加载分析与Frida进阶hook教程
一堆的invokeReflectStaticMethod,invokeReflectMethod,我们找到重点

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来得到结果

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

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()

DEX动态加载分析与Frida进阶hook教程
结果就是DDCTF{2936012829362176}