悟夜叉个人博客 技术专题 某一个网站注册接口的网站参数加解密分析

某一个网站注册接口的网站参数加解密分析

最近一直在弄“电子数据取证百科”的一个 WIKI 内容,精力有限已经一个多月没更新文章了。以后还是坚持每月至少更新几篇技术文章。

这一次来做一个网站参数加解密分析,以注册接口为例:

抓包结果:

POST /TQL?Entry=PUL.register_account&RI= HTTP/1.1
Host: pul.tdx.com.cn
Connection: keep-alive
Content-Length: 148
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="102", "Google Chrome";v="102"
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36
sec-ch-ua-platform: "Windows"
Content-Type: text/plain
Accept: */*
Origin: https://pul.tdx.com.cn
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://pul.tdx.com.cn/site/app/pul/register.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: LST=10; ASPSessionID=12689960779600960594
 
[{"ATYPE":340,"AID":"@ec0:PvhToe2pW6FL0v1tQru05UKrc5uZJHC5","YZM":"1234","AUTOLOGIN":2,"APASD":"MD5@25d55ad283aa400af464c76d713c07ad","encFlg":"1"}]

返回如下:

HTTP/1.1 200 OK
Set-Cookie: ASPSessionID=12689961020119129188;path=/;HttpOnly
Set-Cookie: LST=10;path=/
Content-Length: 77
Content-Type: application/octet-stream
Pragma: no-cache
Cache-Control: no-cache
Connection: close
 
[[-12543,"éªŒè¯ç ä¸æ­£ç¡®<==(S7102)PUL.register_account",0,"",0],[],[],[]]

请求参数中,AID与APASD均被加密,AID是填写的手机号,YZM是手机验证码,APASD是注册密码。

观察特点,手机号加密有前缀@ec0:,密码加密有前缀,MD5@,可以得到的结论是,至少有两种加密,密码大概率可能是md5的签名算法。

YhtRegister: function(o, i, s, r, a) {
                    var c = this
                      , u = this;
                    return new Promise(function(e, t) {
                        var n = {
                            ATYPE: 340,
                            AID: Object(m.b)(o),
                            YZM: i,
                            AUTOLOGIN: s || 1
                        };
                        r && (n.APASD = r),
                        a && (n.LABLESIGN = a),
                        n.encFlg = "1",
                        c.tdxYhtRegister([n], function(t) {
                            0 == t.ErrorCode && ("tdxios" != u.platform() && "tdxandroid" != u.platform() || 0 < Object.keys(t.tables[0][0]).length && t.tables[0][0].TDXID && (u.isNewRegister = !0),
                            e(t.Cookies))
                        })
                    }
                    )
                },

在这里,参数o是手机号明文,r是密码加密后的值。先看手机号加密逻辑,在控制台输出加密函数Object(m.b),回车,定位到加密代码:

var o = function(t) {
            return window.tdxEncrypt(t)
        }

在控制台输出加密函数window.tdxEncrypt,回车,定位到window.tdxEncrypt函数代码:

window.tdxEncrypt = function(r) {
        if (0 === r.indexOf("@ec0"))
            return r;
        r = e.encrypt(r += "@_bfstr");
        return "@ec0:" + e.base64Encode(r)
    }

再继续定位到e.encrypt函数代码:

encrypt: function(r, t) {
            if ("ecb" === this.mode)
                return this.encryptECB(r);
            if ("cbc" === this.mode)
                return this.encryptCBC(r, t);
            throw new Error("Неизвестный режим шифрования.")
        },

通过打断点进去,可以看见,this.mode=ecb,因此手机号是encryptECB加密函数:

encryptECB: function(r) {
            r = this.utf8Decode(r);
            for (var t = Math.ceil(r.length / 8), o = "", e = 0; e < t; e++) {
                var n = r.substr(8 * e, 8);
                if (n.length < 8)
                    for (var i = 8 - n.length; 0 < i--; )
                        n += "\0";
                var h = this.split64by32(n)
                  , s = h[0]
                  , u = h[1];
                s = (h = this.encipherEX(s, u))[0],
                u = h[1],
                o += this.num2block32(u) + this.num2block32(s)
            }
            return o
        },

再把涉及到的一些调用函数抠出来:

split64by32: function(r) {
            var t = r.substring(0, 4)
              , r = r.substring(4, 8);
            return [this.block32toNum(t), this.block32toNum(r)]
        },
        utf8Decode: function(r) {
            for (var t = "", o = 0; o < r.length; o++) {
                var e = r.charCodeAt(o);
                e < 128 ? t += String.fromCharCode(e) : (127 < e && e < 2048 ? t += String.fromCharCode(e >> 6 | 192) : (t += String.fromCharCode(e >> 12 | 224),
                t += String.fromCharCode(e >> 6 & 63 | 128)),
                t += String.fromCharCode(63 & e | 128))
            }
            return t
        },
 
encipherEX: function(r, t) {
            var o = this
              , t = t
              , r = o.xor(r = r, o.pArray[0])
              , t = o.round(t, r, 1);
            return r = o.round(r, t, 2),
            t = o.round(t, r, 3),
            r = o.round(r, t, 4),
            t = o.round(t, r, 5),
            r = o.round(r, t, 6),
            t = o.round(t, r, 7),
            r = o.round(r, t, 8),
            t = o.round(t, r, 9),
            r = o.round(r, t, 10),
            t = o.round(t, r, 11),
            r = o.round(r, t, 12),
            t = o.round(t, r, 13),
            r = o.round(r, t, 14),
            t = o.round(t, r, 15),
            [r = o.round(r, t, 16), t = o.xor(t, o.pArray[17])]
        },
 
xor: function(r, t) {
            return this.fixNegative(r ^ t)
        },
 trimZeros: function(r) {
            return r.replace(/\0+$/g, "")
        },
        wordbyte0: function(r) {
            return Math.floor(Math.floor(Math.floor(r / 256) / 256) / 256) % 256
        },
        wordbyte1: function(r) {
            return Math.floor(Math.floor(r / 256) / 256) % 256
        },
        wordbyte2: function(r) {
            return Math.floor(r / 256) % 256
        },
        wordbyte3: function(r) {
            return r % 256
        },
        round: function(r, t, o) {
            var e = this;
            return e.xor(r, e.xor(e.xor(e.sBox0[e.wordbyte0(t)] + e.sBox1[e.wordbyte1(t)], e.sBox2[e.wordbyte2(t)]) + e.sBox3[e.wordbyte3(t)], e.pArray[o]))
        },
 
o对象的值:
{
    "key": "w83c5~%%30nm5-+",
    "sBox0": [
        1401335157,
        2656414153,
        1572114622,
       这里还有,省略掉,太长了,
        839348932,
        2117317013,
        1753590868
    ],
    "sBox1": [
        2944530774,
        611268753,
        2480642903,
       这里还有,省略掉,太长了,
        1125867267
    ],
    "sBox2": [
        117595124,
        794470125,
        1939519107,
       这里还有,省略掉,太长了,
        1016397799,
        2504754504
    ],
    "sBox3": [
        898774924,
      这里还有,省略掉,太长了,
        35895393,
        1829467942
    ],
    "pArray": [
        4293367326,
        2903747409,
      这里还有,省略掉,太长了,
        1985119492
    ]
}
 
num2block32: function(r) {
            return String.fromCharCode(r << 24 >>> 24) + String.fromCharCode(r << 16 >>> 24) + String.fromCharCode(r << 8 >>> 24) + String.fromCharCode(r >>> 24)
        },
 
block32toNum: function(r) {
            return this.fixNegative(r.charCodeAt(3) << 24 | r.charCodeAt(2) << 16 | r.charCodeAt(1) << 8 | r.charCodeAt(0))
        },
 
fixNegative: function(r) {
            return r >>> 0
        },
 
 
 
base64Encode: function(r) {
            for (var t, o, e, n, i, h, s = "", u = 0; u < r.length; )
                e = (h = r.charCodeAt(u++)) >> 2,
                n = (3 & h) << 4 | (t = r.charCodeAt(u++)) >> 4,
                i = (15 & t) << 2 | (o = r.charCodeAt(u++)) >> 6,
                h = 63 & o,
                isNaN(t) ? i = h = 64 : isNaN(o) && (h = 64),
                s = s + this.keyStr.charAt(e) + this.keyStr.charAt(n) + this.keyStr.charAt(i) + this.keyStr.charAt(h);
            return s
        },
 
keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

把流程串起来之后得知手机号加密方式:”@ec0:” + base64(encrypt(手机号+@_bfstr))

将js流程代码组装起来后验证如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>
<script type="text/javascript">
function num2block32(r) {
        return String.fromCharCode(r << 24 >>> 24) + String.fromCharCode(r << 16 >>> 24) + String.fromCharCode(r << 8 >>> 24) + String.fromCharCode(r >>> 24)
}
 
var o_data ={
    "key": "w83c5~%%30nm5-+",
    "sBox0": [
        1401335157,
        2656414153,
       这里还有,省略掉,太长了,
        839348932,
        2117317013,
        1753590868
    ],
    "sBox1": [
        2944530774,
        611268753,
        这里还有,省略掉,太长了,
        1410597869,
        1965163803,
        1125867267
    ],
    "sBox2": [
        117595124,
        794470125,
        这里还有,省略掉,太长了,
        1016397799,
        2504754504
    ],
    "sBox3": [
        898774924,
        3133324463,
        3959680370,
      这里还有,省略掉,太长了,
        35895393,
        1829467942
    ],
    "pArray": [
        4293367326,
        2903747409,
        2513112869,
       这里还有,省略掉,太长了,
        1985119492
    ]
}
 
function xor(r, t) {
        return fixNegative(r ^ t)
};
 
 
function trimZeros (r) {
        return r.replace(/\0+$/g, "")
};
function wordbyte0(r) {
        return Math.floor(Math.floor(Math.floor(r / 256) / 256) / 256) % 256
};
function wordbyte1(r) {
        return Math.floor(Math.floor(r / 256) / 256) % 256
};
function wordbyte2(r) {
        return Math.floor(r / 256) % 256
};
function wordbyte3 (r) {
        return r % 256
};
function round(r, t, o) {
        return xor(r, xor(xor(o_data.sBox0[wordbyte0(t)] + o_data.sBox1[wordbyte1(t)], o_data.sBox2[wordbyte2(t)]) + o_data.sBox3[wordbyte3(t)], o_data.pArray[o]))
};
 
function encipherEX(r, t) {
           t = t
          , r = xor(r = r, o_data.pArray[0])
          , t = round(t, r, 1);
        return r = round(r, t, 2),
        t = round(t, r, 3),
        r = round(r, t, 4),
        t = round(t, r, 5),
        r = round(r, t, 6),
        t = round(t, r, 7),
        r = round(r, t, 8),
        t = round(t, r, 9),
        r = round(r, t, 10),
        t = round(t, r, 11),
        r = round(r, t, 12),
        t = round(t, r, 13),
        r = round(r, t, 14),
        t = round(t, r, 15),
        [r = round(r, t, 16), t = xor(t, o_data.pArray[17])]
};
function fixNegative(r) {
        return r >>> 0
};
function block32toNum(r) {
        return fixNegative(r.charCodeAt(3) << 24 | r.charCodeAt(2) << 16 | r.charCodeAt(1) << 8 | r.charCodeAt(0))
};
function split64by32(r) {
        var t = r.substring(0, 4)
          , r = r.substring(4, 8);
        return [block32toNum(t), block32toNum(r)]
};
 
function utf8Decode(r) {
        for (var t = "", o = 0; o < r.length; o++) {
                var e = r.charCodeAt(o);
                e < 128 ? t += String.fromCharCode(e) : (127 < e && e < 2048 ? t += String.fromCharCode(e >> 6 | 192) : (t += String.fromCharCode(e >> 12 | 224),
                t += String.fromCharCode(e >> 6 & 63 | 128)),
                t += String.fromCharCode(63 & e | 128))
        }
        return t
};
 
function encryptECB(r) {
        r = utf8Decode(r);
        for (var t = Math.ceil(r.length / 8), o = "", e = 0; e < t; e++) {
                var n = r.substr(8 * e, 8);
                if (n.length < 8)
                        for (var i = 8 - n.length; 0 < i--; )
                                n += "\0";
                var h = split64by32(n)
                  , s = h[0]
                  , u = h[1];
                s = (h = encipherEX(s, u))[0],
                u = h[1],
                o += num2block32(u) + num2block32(s)
        }
        return o
};
 
var keyStr ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
 
function base64Encode(r) {
        for (var t, o, e, n, i, h, s = "", u = 0; u < r.length; )
                e = (h = r.charCodeAt(u++)) >> 2,
                n = (3 & h) << 4 | (t = r.charCodeAt(u++)) >> 4,
                i = (15 & t) << 2 | (o = r.charCodeAt(u++)) >> 6,
                h = 63 & o,
                isNaN(t) ? i = h = 64 : isNaN(o) && (h = 64),
                s = s + keyStr.charAt(e) + keyStr.charAt(n) + keyStr.charAt(i) + keyStr.charAt(h);
        return s
};
 
 
debugger;
 
var aa = '15512345678'+'@_bfstr';
var bb = encryptECB(aa);
var cc = base64Encode(bb);
var dd = "@ec0:" + cc;
console.log(dd); //输出结果:@ec0:PvhToe2pW6FL0v1tQru05UKrc5uZJHC5,与抓包数据一致

</script>                
</body>
</html>

密码的加密方式、以及返回值的解密方式,也能通过相同的方法获取到对应的处理方案。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

评论列表(1)

联系我们

联系我们

站长QQ/VX:82794

在线咨询: QQ交谈

邮箱: 82794@qq.com

任何技术问题请联系QQ,非特殊行业请勿加微信!龙信小伙伴请联系微信群找我。
关注微信
非商务合作请勿添加

非商务合作请勿添加微信

返回顶部