美文网首页
唯品会登录edata字段分析

唯品会登录edata字段分析

作者: ever_hu | 来源:发表于2021-11-30 20:26 被阅读0次

    唯品会登录edata字段分析

    Java层

    image-20211130180456283

    jadx搜索"edata"

    image-20211130180651678

    EDATA查找用例

    image-20211130180736943

    com.achievo.vipshop.commons.api.middleware.ApiRequest -> getNewEncodeBodyMap

    image-20211130180830005

    com.achievo.vipshop.commons.config.GobalConfig -> encodeStr

    image-20211130180912901

    com.vip.vcsp.security.sign.VCSPSecurityConfig -> encodeStr

    image-20211130181000812

    com.vip.vcsp.security.sign.VCSPSecurityConfig -> es

    image-20211130181047965

    找到这里,发现没办法继续跳转了,不过这个类有个初始化函数initInstance

    image-20211130144003950

    从中可以看到clazz就是com.vip.vcsp.KeyInfo

    image-20211130190122519

    然后在com.vip.vcsp.KeyInfo,我们也找到了es函数,发现它是调用了esNav函数,而这个是libkeyinfo.so中的native函数。

    frida hook

    Java.perform(function() {
        var KeyInfo = Java.use("com.vip.vcsp.KeyInfo");
        KeyInfo.esNav.implementation = function(ctx, str1, str2, str3, i) {
            console.log(str1);
            console.log(str2);
            console.log(str3);
            console.log(i);
            var ret = this.esNav(ctx, str1, str2, str3, i);
            console.log(ret);
            return ret;
        }
    }
    
    image-20211130190458104

    由此可知str1是urlencode之后的参数

    so层

    ida打开libkeyinfo.so,函数窗口搜索Java

    image-20211130145030206

    说明函数是静态注册的,打开Java_com_vip_vcsp_KeyInfo_esNav

    image-20211130190900182

    跟进一下

    image-20211130190923390 image-20211130191106743

    从函数主体可以看出,so通过反射来调用Java层的AES加密方法。

    先将Functions_esa1参数类型设为JNIEnv*,在a1处按Y,设置为JNIEnv *a1

    image-20211130191501749 image-20211130191735911

    这样就方便许多了,我们知道AES/CBC最重要的3个参数是datakeyIV,然后我们找个Java的AES/CBC实现来和native反射调用对比一下

    private static final String key = "aesEncryptionKey";
    private static final String initVector = "encryptionIntVec";
     
    public static String encrypt(String value) {
        try {
            IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
     
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
     
            byte[] encrypted = cipher.doFinal(value.getBytes());
            return Base64.encodeBase64String(encrypted);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }
    
    key
    image-20211130192613358

    通过对比,可以看出v16就是key,而v16是由v15赋值的,v15j_getMD516的结果

    IV
    image-20211130192925782

    通过对比,v26就是IVv26v25赋值,v25v53赋值,v53看起来似乎是由全0构成的,但是在其下方有个函数j_rand16Str()看起来孤零零的,点进去看看,

    image-20211130193135099

    继续跟进

    image-20211130193201181

    看起来rand16Str的调用对不上,回到j_rand16Str处,F5一下

    image-20211130193352635 image-20211130193437571

    更新之后可以通过rand16Str的代码看出,v53就是一个长度16的16进制随机字符

    data
    image-20211130193752036

    要加密的数据,其实就是esNav传进来的参数

    base64
    image-20211130194159021

    接下来有个base64,可以看出v44是由v53v41拼接而成,而v53就是前面提到的IVv41则是由v37而来,也就是AES加密的结果。

    接下来,我们通过frida hook来验证上面的分析,hook rand16Str得到IV的值,hook getMD516得到key的值,hook base64_encode来得到AES加密后的结果,以及base64之后的结果

    function dump(name, addr, legnth) {
        console.log("======================== " + name + " ============");
        console.log(hexdump(addr, {length:legnth||32}));
    }
    
    Java.perform(function() {
        var KeyInfo = Java.use("com.vip.vcsp.KeyInfo");
        KeyInfo.esNav.implementation = function(ctx, str1, str2, str3, i) {
            console.log(str1);
            console.log(str2);
            console.log(str3);
            console.log(i);
            var ret = this.esNav(ctx, str1, str2, str3, i);
            console.log(ret);
            return ret;
        }
    
        var ptr_es = Module.findExportByName("libkeyinfo.so", "Functions_es");
        Interceptor.attach(ptr_es, {
            onEnter: function(args) {
                logvar = true;
                console.log("es-arg1", args[1]);
                console.log("es-arg2", args[2]);
                console.log("es-arg3", args[3]);
                console.log("es-arg4", args[4]);
            },
            onLeave: function(retval) {
                logvar = false;
            }
        })
    
        var ptr_getMD516 = Module.findExportByName("libkeyinfo.so", "getMD516");
        Interceptor.attach(ptr_getMD516, {
            onEnter: function(args) {
                console.log(args[1]);
                dump("md5", args[1], parseInt(args[2]));
            },
            onLeave: function(retval) {
                console.log("md5-ret", retval);
                dump("md5-ret", retval, 16);
            }
        })
    
        var ptr_rand = Module.findExportByName("libkeyinfo.so", "rand16Str");
        Interceptor.attach(ptr_rand, {
            onEnter: function(args){
                this.arg0 = args[0];
            },
            onLeave: function(retval) {
                console.log("\n\nrand-ret-arg0", this.arg0.readCString());
            }
        })
    
        var ptr_b64 = Module.findExportByName("libkeyinfo.so", "base64_encode");
        Interceptor.attach(ptr_b64, {
            onEnter: function(args){
                dump("b64", args[0], parseInt(args[1]));
            },
            onLeave: function(retval) {
                console.log("b64-ret", retval.readCString());
            }
        })
    });
    
    image-20211130200026249 image-20211130200331154

    接下来就是验证,打开CyberChef (gchq.github.io),配置如下

    image-20211130201113326

    完全对上了

    代码实现

    import base64
    
    from random import choice
    from urllib.parse import urlencode
    from Crypto.Cipher import AES
    from Crypto.Util.Padding import pad
    
    nhex = lambda n: ''.join(choice('0123456789abcdef') for _ in range(n))
    
    
    def calc_edata(params, iv=None):
        if isinstance(params, (list, tuple, dict)):
            if hasattr(params, 'items'):
                params = params.items()
            params = urlencode(sorted(params))
        if isinstance(params, str):
            params = params.encode()
        iv = iv or nhex(16)
        if isinstance(iv, str):
            iv = iv.encode()
        key = bytes.fromhex('3feeaf10550a61596331f8e5c7f77a97')
        data = pad(params, 16)
        cryptor = AES.new(key, mode=AES.MODE_CBC, iv=iv)
        content = cryptor.encrypt(data)
        edata = base64.b64encode(iv + content).decode()
        return edata
    

    验证

    image-20211130202323249

    代码仅供把玩。

    相关文章

      网友评论

          本文标题:唯品会登录edata字段分析

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