丁香公开课请求 sign(签名) 分析过程讲解

Sep 17, 2020 11:58:33 PM 15441

本教程仅供学习。如有侵权请联系本站删除。

背景分析

1.网站链接:https://class.dxy.cn/

2.所有异步请求都会带上 sign=xxxx,并且每次sign只能用一次。

3.目的:解决sign的算法,得到正确的值。

4.初步定位算法js为:https://assets.dxycdn.com/gitrepo/dxycourse-pc/dist/index.e8a8a63d2fc74a69.js 格式化一看发现有6万多行的JS。有点头大,但是JS没有加密,只是打包了。

0x0、定位 sign 具体位置

发现只有8处位置,通过经验+第六感得出,大概在43895这行这个sign可能是最终算法。估计你们看到说凭借经验+第六感,就慌了。接下来我们来验证吧。

使用 charles 工具,Mapping Local 到本地格式化的 js 中,添加一处debugger

return {
    sign: function () {
        debugger;//增加debug
        console.log('arguments',arguments);
        var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {},
            t = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : "省略长串",
            r = n({}, e), i = {appSignKey: t};
        r.timestamp = Date.now(), r.noncestr = a(8, "number");
        var o = Object.keys(r).filter(function (e) {
            return void 0 != r[e] && "" !== r[e] || (delete r[e], !1)
        }).concat("appSignKey").sort().map(function (e) {
            var t = i[e] || (void 0 == r[e] ? "" : r[e]);
            return "".concat(e, "=").concat(t)
        }).join("&");
        return r.sign = u(o), r
    }
}

然后刷新页面,果然是此处。

0x1、提取JS代码

既然是这里,那么就提取此处代码用于单独调用,毕竟6万多行的js,并且是打包的,外部是无法调用的。

//提取算法代码
var CORE = (function () {
    function e(e, t, n) {
        return t in e ? Object.defineProperty(e, t, {
            value: n,
            enumerable: !0,
            configurable: !0,
            writable: !0
        }) : e[t] = n, e
    }

    function n(t) {
        for (var n = 1; n < arguments.length; n++) {
            var r = null != arguments[n] ? arguments[n] : {}, a = Object.keys(r);
            "function" === typeof Object.getOwnPropertySymbols && (a = a.concat(Object.getOwnPropertySymbols(r).filter(function (e) {
                return Object.getOwnPropertyDescriptor(r, e).enumerable
            }))), a.forEach(function (n) {
                e(t, n, r[n])
            })
        }
        return t
    }

    function r(e, t) {
        return t = {exports: {}}, e(t, t.exports), t.exports
    }

    function a() {
        for (var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : 8, t = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : "alphabet", n = "", r = {
            alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
            number: "0123456789"
        }[t], a = 0; a < e; a++) n += r.charAt(Math.floor(Math.random() * r.length));
        return n
    }

    var i = r(function (e) {
        !function () {
            var t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", n = {
                rotl: function (e, t) {
                    return e << t | e >>> 32 - t
                }, rotr: function (e, t) {
                    return e << 32 - t | e >>> t
                }, endian: function (e) {
                    if (e.constructor == Number) return 16711935 & n.rotl(e, 8) | 4278255360 & n.rotl(e, 24);
                    for (var t = 0; t < e.length; t++) e[t] = n.endian(e[t]);
                    return e
                }, randomBytes: function (e) {
                    for (var t = []; e > 0; e--) t.push(Math.floor(256 * Math.random()));
                    return t
                }, bytesToWords: function (e) {
                    for (var t = [], n = 0, r = 0; n < e.length; n++, r += 8) t[r >>> 5] |= e[n] << 24 - r % 32;
                    return t
                }, wordsToBytes: function (e) {
                    for (var t = [], n = 0; n < 32 * e.length; n += 8) t.push(e[n >>> 5] >>> 24 - n % 32 & 255);
                    return t
                }, bytesToHex: function (e) {
                    for (var t = [], n = 0; n < e.length; n++) t.push((e[n] >>> 4).toString(16)), t.push((15 & e[n]).toString(16));
                    return t.join("")
                }, hexToBytes: function (e) {
                    for (var t = [], n = 0; n < e.length; n += 2) t.push(parseInt(e.substr(n, 2), 16));
                    return t
                }, bytesToBase64: function (e) {
                    for (var n = [], r = 0; r < e.length; r += 3) for (var a = e[r] << 16 | e[r + 1] << 8 | e[r + 2], i = 0; i < 4; i++) 8 * r + 6 * i <= 8 * e.length ? n.push(t.charAt(a >>> 6 * (3 - i) & 63)) : n.push("=");
                    return n.join("")
                }, base64ToBytes: function (e) {
                    e = e.replace(/[^A-Z0-9+\/]/gi, "");
                    for (var n = [], r = 0, a = 0; r < e.length; a = ++r % 4) 0 != a && n.push((t.indexOf(e.charAt(r - 1)) & Math.pow(2, -2 * a + 8) - 1) << 2 * a | t.indexOf(e.charAt(r)) >>> 6 - 2 * a);
                    return n
                }
            };
            e.exports = n
        }()
    }), o = {
        utf8: {
            stringToBytes: function (e) {
                return o.bin.stringToBytes(unescape(encodeURIComponent(e)))
            }, bytesToString: function (e) {
                return decodeURIComponent(escape(o.bin.bytesToString(e)))
            }
        }, bin: {
            stringToBytes: function (e) {
                for (var t = [], n = 0; n < e.length; n++) t.push(255 & e.charCodeAt(n));
                return t
            }, bytesToString: function (e) {
                for (var t = [], n = 0; n < e.length; n++) t.push(String.fromCharCode(e[n]));
                return t.join("")
            }
        }
    }, s = o, u = r(function (e) {
        !function () {
            var n = i, r = s.utf8, a = s.bin, o = function (e) {
                e.constructor == String ? e = r.stringToBytes(e) : "undefined" !== typeof t && "function" == typeof t.isBuffer && t.isBuffer(e) ? e = Array.prototype.slice.call(e, 0) : Array.isArray(e) || (e = e.toString());
                var a = n.bytesToWords(e), i = 8 * e.length, o = [], s = 1732584193, u = -271733879,
                    l = -1732584194, c = 271733878, d = -1009589776;
                a[i >> 5] |= 128 << 24 - i % 32, a[15 + (i + 64 >>> 9 << 4)] = i;
                for (var f = 0; f < a.length; f += 16) {
                    for (var p = s, h = u, m = l, v = c, y = d, g = 0; g < 80; g++) {
                        if (g < 16) o[g] = a[f + g]; else {
                            var _ = o[g - 3] ^ o[g - 8] ^ o[g - 14] ^ o[g - 16];
                            o[g] = _ << 1 | _ >>> 31
                        }
                        var b = (s << 5 | s >>> 27) + d + (o[g] >>> 0) + (g < 20 ? 1518500249 + (u & l | ~u & c) : g < 40 ? 1859775393 + (u ^ l ^ c) : g < 60 ? (u & l | u & c | l & c) - 1894007588 : (u ^ l ^ c) - 899497514);
                        d = c, c = l, l = u << 30 | u >>> 2, u = s, s = b
                    }
                    s += p, u += h, l += m, c += v, d += y
                }
                return [s, u, l, c, d]
            }, u = function (e, t) {
                var r = n.wordsToBytes(o(e));
                return t && t.asBytes ? r : t && t.asString ? a.bytesToString(r) : n.bytesToHex(r)
            };
            u._blocksize = 16, u._digestsize = 20, e.exports = u
        }()
    });
    return {
        sign: function () {
            var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {},
                t = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : "...",
                r = n({}, e), i = {appSignKey: t};
            var mt = Date.now();
            r.timestamp =mt, r.noncestr = a(8, "number");
            var o = Object.keys(r).filter(function (e) {
                return void 0 != r[e] && "" !== r[e] || (delete r[e], !1)
            }).concat("appSignKey").sort().map(function (e) {
                var t = i[e] || (void 0 == r[e] ? "" : r[e]);
                return "".concat(e, "=").concat(t)
            }).join("&");
            return r.sign = u(o), r
        }
    }
})(n("EuP9").Buffer);

在提取代码的过程中,发现依赖了一个外部调用 n("EuP9").Buffer 然后跟踪下,发现里面还有调用,一坨很大,然后我们分析下当前 sign 算法是否使用了,发现没有明确的地方使用,直接去掉先。

接着我们开始来测试。先找了一个无参数的   Get  请求试一下。

var res = CORE.sign();
//得到 {timestamp: 1600356565232, noncestr: "51070119", sign: "c2d89f55ca8c4e1da93002e274739e70e43fdf89"}

好像成功了,然后尝试一下。

拼接无参数链接请求:

https://class.dxy.cn/pcweb/user/info?timestamp=1600356565232&noncestr=51070119&sign=c2d89f55ca8c4e1da93002e274739e70e43fdf89

返回签名错误。

这就有点蛋疼了,刚刚的喜悦被当头一棒。冷静冷静。仔细分析下。

调用sign获取签名是不是有参数?看下面部分代码。

var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {},
    t = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : "太长省略",
    r = n({}, e), i = {appSignKey: t};

这里sign直接读取了参数判断,然后去取值,看语义分析下,第一个应该是一个 json 对象格式的参数,第二个是一个字符串,并且是个key,如果得不到就给一个默认的值。咱们直接在js里输出 “arguments”看看。

return {
    sign: function () {
        debugger;//增加debug
        //输出参数
        console.log('arguments',arguments);
        var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {},
            t = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : "...",
            r = n({}, e), i = {appSignKey: t};
        r.timestamp = Date.now(), r.noncestr = a(8, "number");
        var o = Object.keys(r).filter(function (e) {
            return void 0 != r[e] && "" !== r[e] || (delete r[e], !1)
        }).concat("appSignKey").sort().map(function (e) {
            var t = i[e] || (void 0 == r[e] ? "" : r[e]);
            return "".concat(e, "=").concat(t)
        }).join("&");
        return r.sign = u(o), r
    }
}


看到了吧,第一个是 {} ,第二个参数是一个key ,而且这个key和代码里默认的key不一样。

那我们就也带上这个key吧。或者把这个key写死在代码里,然后不传第二个参数也可以。

这里观察了多个请求并 console.log('arguments',arguments);后发现第一个参数是提交到后台的参数值。

好,然后优化下代码结果是:

//有参数,data就给参数。
var data = {courseId: 402,courseType: 2};
var res = s.sign(data,"695e7084696bf4a6b4c73dc67da4f5df41405763ef4a2a854e3af96b141254b0050986ccf2b8cd9a6ab5cfabc6e4bb30");
var url = "https://class.dxy.cn/pcweb/user/course/like/status?courseId=402&courseType=2&timestamp="+res.timestamp+"&noncestr="+res.noncestr+"&sign="+res.sign;

获取一个分页信息:

var data = {pageNum:1,pageSize:4,courseId:402,courseType:2};
var res = CORE.sign(data,"695e7084696bf4a6b4c73dc67da4f5df41405763ef4a2a854e3af96b141254b0050986ccf2b8cd9a6ab5cfabc6e4bb30");
console.log("签名",res.sign);
console.log("timestamp",res.timestamp);
console.log("noncestr",res.noncestr);
var url = "https://class.dxy.cn/pcweb/user/pack/comment/list?pageNum=1&pageSize=4&courseId=402&courseType=2&timestamp="+res.timestamp+"&noncestr="+res.noncestr+"&sign="+res.sign;
console.log("请求连接",url);

通过在线JS运行工具:https://www.sojson.com/runjs.html


得到链接,直接浏览器打开(因为这个是个get请求),然后就得到如下内容


结果拿到了。好了,分析到此结束,其实 JS 算法也好,只要是重要的部分,最好还是加密一下,使用本站的JS最牛加密,或者先用JS方法加密,加密后在用JS最牛加密加密JS,这样整个逻辑就打乱了,可以使分析者第一步就很难。

申明:当前内容只能用于学习,不能用于其他。


版权所属:JavaScript加密

原文地址:https://www.jsjiami.com/article/dxy-sign.html

转载时必须以链接形式注明原始出处及本声明。


本文标签:
最新文章
Can "Upgrade" only to "WebSocket".是为什么? 5301
JS setTimeout 方法,JavaScript Hook 劫持setTimeout 7767
文件(文本)不乱码,保存后发给对方,对方打开乱码,怎么处理文件乱码? 11719
MacBook安装Nginx最有效方法 3310
模仿支付宝输入支付密码的UI,带JavaScript 密码输入校验功能 25205
Chrome浏览器模拟微信客户端访问网址,方法图文讲解模拟微信 47258
JavaScript在线加密,最牛加密按次加密计费方式讲解。 53034
丁香公开课请求 sign(签名) 分析过程讲解 15441
JavaScript优雅的跳转,作弊跳转方式,JS防检测跳转。 26099
JS代码书写注意事项,JavaScript代码压缩后为什么报错? 15082

      商务合作:1#vfan.cn(把#换成@),或者联系QQ:84034666

      业务咨询:i#itboy.net(把#换成@),或者联系QQ:8446666

再次输入密码