JS逆向异步跟栈学习——XHR断点

前言

        今天开始打算做一些逆向的学习,以下内容可能不够专业,仅供大家参考。话不多说,开干!

一、说明

        目标地址:aHR0cHM6Ly93d3cuaml6aHkuY29tLw==

        任务:在搜索栏输入省份,即可获得网站提供的一些大学和专业列表

二、网站分析

        首先在搜索栏输入目标省份上海后,开始找网站提供的数据来源于哪个接口。通过观察调试工具可以发现,提供数据的接口为search?search_key......

        

        并且可以看到接口返回的数据直接为明文,这意味着不需要做接口返回值的解密了,同时可以看到接口的传入值确实是我们输入的上海,说明就是这个接口。

        然后观察接口的参数,通过多次输入其他省份调用接口发现,其中search_type、app_id、platform、v是不变的;而search_key很明显就是我们输入的省份,ts看起来就是一个时间戳,只有sign不清楚是如何加密得到的。

三、断点跟栈

        可以看到接口类型为xhr,我们添加XHR断点来进行跟栈。

        在调试工具点击源代码(Source),右侧「XHR/提取断点」处右键添加新的断点,框内输入接口域名之后到参数之前的这段就够了(/searchcenter/search)。添加完成后,刷新页面重新打开调试工具,在网页顶部搜索栏输入省份后,即可触发XHR断点。

        如下图所示,可以看到断点断在了_.send(y)这里了,在右侧「调用堆栈」中可以看到这是最顶部的一个栈,而经过这个栈执行函数完毕之后,页面就请求完毕了。所以我们需要从上到下,逆向地,一个一个来跟踪。

        这时我们可以在右侧「作用域」找一找,看哪个变量里有我们想要的sign,这里在t中parmas已经看到之前接口中所有的参数了,包括sign。这是肯定的,因为这已经是最后一个栈了,所以接下来我们开始看倒数第二个栈。

        可以看到,倒数第二个栈,断点在了return new Promise。这时我们继续看右侧「作用域」,参数t中params,sign也已经生成了。这说明生成sign的函数还在前面的栈中。根据这种思路,一步一步往下跟进调用栈。

        正常情况下,这样就能够找出加密sign的函数,但是这个网站使用了异步(axios),所以需要花更多的时间来断点调试。针对异步请求,一般加密都是在请求拦截器中,通常定位到加密的有两种方式,找到响应拦截器,或者找到请求拦截器,不过响应拦截器一般和请求拦截器在一起。而这次运气比较好,在第一个异步栈里就能找到。

        点击下图所示的异步栈,可以看到XHR断点在了n = n.then(e.shift(), e.shift());,我们手动添加一个断点,将当前的请求执行完后,再次进行搜索调用接口,新的断点断在了此处,点击右上角向下箭头的「进入下一个函数调用」,还是停在了这里,因为这里面有多个回调函数。

        在多次点击「进入下一个函数调用」,将这里的回调函数全部执行完后,下一步正好就到了响应拦截器这里,而请求拦截器就在上面,所以再对请求拦截器的返回添加断点。

        再次重新请求接口,在触发新的断点后,继续点击「进入下一个函数调用」,来到了一个新的函数内部。重复操作后,可以看到在经过"get" === e ? t.params = Qt(t.params) : "post" === e && (t.data = Qt(t.data))后,sign值被加密出来了,观察这行代码,加密函数应该就在Qt中。接下来就是还原代码的过程了。

四、还原加密

        这里没有太多需要注意的地方,按惯例缺什么补什么即可,不过有几个地方需要改一下才能运行。首先是这里,这个函数可以跳过,它的功能是判断请求的类型来决定如何调用Qt函数,我们直接从Qt开始还原。

function(t) {
                var e = t.method;
                "get" === e ? t.params = Qt(t.params) : "post" === e && (t.data = Qt(t.data))
            }

        可以看到Qt是一个匿名函数,我们在本地调用的话,对它稍微改写一下。首先重新定义变量t,其次在定义函数时添加传入的变量sign。

function Qt(sign) {
        var t = sign || {} // 如果没有传入参数,默认设置为一个空对象
            // t = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}
          , e = {
            app_id: F,
            ts: +new Date,
            platform: "desktop",
            v: 210
        };
        Object.assign(t, e);
        var n = Wt(t);
        return n += "&key=" + U,
        t.sign = Xt(n),
        Object.assign(t, e)
    }

        另外一个需要改的地方是Wt函数,这里的Object(ht.a)(data)像是某种依赖模块的方式,把它替换成JS中Object.prototype.toString.call(data)来处理。

function Wt(t) {
    var e = Object.prototype.toString.call(t)
      , n = Object.keys(t);
    n.sort((function(a, b) {
        return "[object Array]" === e && (a = +a,
        b = +b),
        a < b ? -1 : a > b ? 1 : 0
    }
    ));
    for (var r, param = [], o = 0, c = n; o < c.length; o++) {
        var l = c[o]
          , data = t[l];
        null == data && (t[l] = data = ""),
        // (data || 0 === data) && ("object" === Object(ht.a)(data) && (data = Wt(data)),
        (data || 0 === data) && ("object" === Object.prototype.toString.call(data) && (data = Wt(data)),
        param.push("".concat(l, "=").concat(data)))
    }
    return "[object Object]" === e ? (r = param.join("&"),
    r = "{".concat(r, "}")) : "[object Array]" === e ? (r = param.join(","),
    r = "[".concat(r, "]")) : r = param.join("&"),
    r
}

五、请求成功

        在加密函数Qt执行完成后,会直接返回插入了sign的params,但是里面却丢失了app_id,这里懒得看什么原因了,直接重新补一个app_id插入进params。最后将新的params一并传入请求参数中,即可成功获得数据。

完整代码

        Python部分:以下代码中url需使用base64解密

import requests
import time
# 后面三行代码防止运行js文件出现编码错误,需放在导入execjs前
import subprocess
from functools import partial
subprocess.Popen = partial(subprocess.Popen, encoding='utf-8')
import execjs


headers = {
    "accept": "application/json, text/plain, */*",
    "accept-language": "zh-CN,zh;q=0.9",
    "content-type": "application/json;charset=UTF-8",
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
}

url = "aHR0cHM6Ly93d3cuaml6aHkuY29tL3NlYXJjaGNlbnRlci9zZWFyY2g="
ts = str(int(time.time() * 1000))
search_place = input("请输入搜索内容:")

params = {
    "search_key": search_place,
    "search_type": "3",
    "app_id": "98357f659cf8fb6001cff80f7c6b85f2",
    "ts": ts,
    "platform": "desktop",
    "v": "210",
}

with open('加密参数sign.js', 'r', encoding='utf-8') as fp:
    sign = execjs.compile(fp.read()).call('Qt', params)
    print(sign)
sign.update({"app_id": "98357f659cf8fb6001cff80f7c6b85f2"})
print(sign)

response = requests.get(url, headers=headers, params=sign)
res_text = response.text
print(response)
print(res_text)

sch_list = response.json()['data']['sch_list']
for i in sch_list:
    print(i['sch_name'])

JavaScripts部分:与py文件在同一目录下创建js即可


// function sign(t) {
//                 var e = t.method;
//                 "get" === e ? t.params = Qt(t.params) : "post" === e && (t.data = Qt(t.data))
//             }

var ht = n(13), U = "146fd1e66513611ac7af69f21f1d7725",
    Vt = Et, St = 0;


function Qt(sign) {
        var t = sign || {} // 如果没有传入参数,默认设置为一个空对象
            // t = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}
          , e = {
            app_id: F,
            ts: +new Date,
            platform: "desktop",
            v: 210
        };
        Object.assign(t, e);
        var n = Wt(t);
        return n += "&key=" + U,
        t.sign = Xt(n),
        Object.assign(t, e)
    }

function Xt(t) {
            var e = Vt(t);
            return e = e.toUpperCase()
        }

function Wt(t) {
    var e = Object.prototype.toString.call(t)
      , n = Object.keys(t);
    n.sort((function(a, b) {
        return "[object Array]" === e && (a = +a,
        b = +b),
        a < b ? -1 : a > b ? 1 : 0
    }
    ));
    for (var r, param = [], o = 0, c = n; o < c.length; o++) {
        var l = c[o]
          , data = t[l];
        null == data && (t[l] = data = ""),
        // (data || 0 === data) && ("object" === Object(ht.a)(data) && (data = Wt(data)),
        (data || 0 === data) && ("object" === Object.prototype.toString.call(data) && (data = Wt(data)),
        param.push("".concat(l, "=").concat(data)))
    }
    return "[object Object]" === e ? (r = param.join("&"),
    r = "{".concat(r, "}")) : "[object Array]" === e ? (r = param.join(","),
    r = "[".concat(r, "]")) : r = param.join("&"),
    r
}

function F(t) {
            var e;
            if (t.message || "string" == typeof t)
                e = t.message || t;
            else
                try {
                    e = JSON.stringify(t, null, 2)
                } catch (n) {
                    e = "[".concat(t.constructor.name, "]")
                }
            return m(m({}, t), {}, {
                message: e,
                statusCode: t.statusCode || t.status || t.response && t.response.status || 500
            })
        }

function n(t, n, r) {
    return n && e(t.prototype, n),
    r && e(t, r),
    t
}

function Et(s) {
            return Tt(At(Rt(s)))
        }

function Tt(input) {
            for (var t, e = St ? "0123456789ABCDEF" : "0123456789abcdef", output = "", i = 0; i < input.length; i++)
                t = input.charCodeAt(i),
                output += e.charAt(t >>> 4 & 15) + e.charAt(15 & t);
            return output
        }

function At(s) {
            return Dt(Nt(Lt(s), 8 * s.length))
        }

function Rt(input) {
            for (var t, e, output = "", i = -1; ++i < input.length; )
                t = input.charCodeAt(i),
                e = i + 1 < input.length ? input.charCodeAt(i + 1) : 0,
                55296 <= t && t <= 56319 && 56320 <= e && e <= 57343 && (t = 65536 + ((1023 & t) << 10) + (1023 & e),
                i++),
                t <= 127 ? output += String.fromCharCode(t) : t <= 2047 ? output += String.fromCharCode(192 | t >>> 6 & 31, 128 | 63 & t) : t <= 65535 ? output += String.fromCharCode(224 | t >>> 12 & 15, 128 | t >>> 6 & 63, 128 | 63 & t) : t <= 2097151 && (output += String.fromCharCode(240 | t >>> 18 & 7, 128 | t >>> 12 & 63, 128 | t >>> 6 & 63, 128 | 63 & t));
            return output
        }

function Dt(input) {
            for (var output = "", i = 0; i < 32 * input.length; i += 8)
                output += String.fromCharCode(input[i >> 5] >>> i % 32 & 255);
            return output
        }

function Nt(t, e) {
            t[e >> 5] |= 128 << e % 32,
            t[14 + (e + 64 >>> 9 << 4)] = e;
            for (var a = 1732584193, b = -271733879, n = -1732584194, r = 271733878, i = 0; i < t.length; i += 16) {
                var o = a
                  , c = b
                  , l = n
                  , f = r;
                a = qt(a, b, n, r, t[i + 0], 7, -680876936),
                r = qt(r, a, b, n, t[i + 1], 12, -389564586),
                n = qt(n, r, a, b, t[i + 2], 17, 606105819),
                b = qt(b, n, r, a, t[i + 3], 22, -1044525330),
                a = qt(a, b, n, r, t[i + 4], 7, -176418897),
                r = qt(r, a, b, n, t[i + 5], 12, 1200080426),
                n = qt(n, r, a, b, t[i + 6], 17, -1473231341),
                b = qt(b, n, r, a, t[i + 7], 22, -45705983),
                a = qt(a, b, n, r, t[i + 8], 7, 1770035416),
                r = qt(r, a, b, n, t[i + 9], 12, -1958414417),
                n = qt(n, r, a, b, t[i + 10], 17, -42063),
                b = qt(b, n, r, a, t[i + 11], 22, -1990404162),
                a = qt(a, b, n, r, t[i + 12], 7, 1804603682),
                r = qt(r, a, b, n, t[i + 13], 12, -40341101),
                n = qt(n, r, a, b, t[i + 14], 17, -1502002290),
                a = Ft(a, b = qt(b, n, r, a, t[i + 15], 22, 1236535329), n, r, t[i + 1], 5, -165796510),
                r = Ft(r, a, b, n, t[i + 6], 9, -1069501632),
                n = Ft(n, r, a, b, t[i + 11], 14, 643717713),
                b = Ft(b, n, r, a, t[i + 0], 20, -373897302),
                a = Ft(a, b, n, r, t[i + 5], 5, -701558691),
                r = Ft(r, a, b, n, t[i + 10], 9, 38016083),
                n = Ft(n, r, a, b, t[i + 15], 14, -660478335),
                b = Ft(b, n, r, a, t[i + 4], 20, -405537848),
                a = Ft(a, b, n, r, t[i + 9], 5, 568446438),
                r = Ft(r, a, b, n, t[i + 14], 9, -1019803690),
                n = Ft(n, r, a, b, t[i + 3], 14, -187363961),
                b = Ft(b, n, r, a, t[i + 8], 20, 1163531501),
                a = Ft(a, b, n, r, t[i + 13], 5, -1444681467),
                r = Ft(r, a, b, n, t[i + 2], 9, -51403784),
                n = Ft(n, r, a, b, t[i + 7], 14, 1735328473),
                a = Ut(a, b = Ft(b, n, r, a, t[i + 12], 20, -1926607734), n, r, t[i + 5], 4, -378558),
                r = Ut(r, a, b, n, t[i + 8], 11, -2022574463),
                n = Ut(n, r, a, b, t[i + 11], 16, 1839030562),
                b = Ut(b, n, r, a, t[i + 14], 23, -35309556),
                a = Ut(a, b, n, r, t[i + 1], 4, -1530992060),
                r = Ut(r, a, b, n, t[i + 4], 11, 1272893353),
                n = Ut(n, r, a, b, t[i + 7], 16, -155497632),
                b = Ut(b, n, r, a, t[i + 10], 23, -1094730640),
                a = Ut(a, b, n, r, t[i + 13], 4, 681279174),
                r = Ut(r, a, b, n, t[i + 0], 11, -358537222),
                n = Ut(n, r, a, b, t[i + 3], 16, -722521979),
                b = Ut(b, n, r, a, t[i + 6], 23, 76029189),
                a = Ut(a, b, n, r, t[i + 9], 4, -640364487),
                r = Ut(r, a, b, n, t[i + 12], 11, -421815835),
                n = Ut(n, r, a, b, t[i + 15], 16, 530742520),
                a = Bt(a, b = Ut(b, n, r, a, t[i + 2], 23, -995338651), n, r, t[i + 0], 6, -198630844),
                r = Bt(r, a, b, n, t[i + 7], 10, 1126891415),
                n = Bt(n, r, a, b, t[i + 14], 15, -1416354905),
                b = Bt(b, n, r, a, t[i + 5], 21, -57434055),
                a = Bt(a, b, n, r, t[i + 12], 6, 1700485571),
                r = Bt(r, a, b, n, t[i + 3], 10, -1894986606),
                n = Bt(n, r, a, b, t[i + 10], 15, -1051523),
                b = Bt(b, n, r, a, t[i + 1], 21, -2054922799),
                a = Bt(a, b, n, r, t[i + 8], 6, 1873313359),
                r = Bt(r, a, b, n, t[i + 15], 10, -30611744),
                n = Bt(n, r, a, b, t[i + 6], 15, -1560198380),
                b = Bt(b, n, r, a, t[i + 13], 21, 1309151649),
                a = Bt(a, b, n, r, t[i + 4], 6, -145523070),
                r = Bt(r, a, b, n, t[i + 11], 10, -1120210379),
                n = Bt(n, r, a, b, t[i + 2], 15, 718787259),
                b = Bt(b, n, r, a, t[i + 9], 21, -343485551),
                a = zt(a, o),
                b = zt(b, c),
                n = zt(n, l),
                r = zt(r, f)
            }
            return Array(a, b, n, r)
        }

function Lt(input) {
            for (var output = Array(input.length >> 2), i = 0; i < output.length; i++)
                output[i] = 0;
            for (i = 0; i < 8 * input.length; i += 8)
                output[i >> 5] |= (255 & input.charCodeAt(i / 8)) << i % 32;
            return output
        }

function qt(a, b, t, e, n, s, r) {
            return Mt(b & t | ~b & e, a, b, n, s, r)
        }

function Mt(q, a, b, t, s, e) {
            return zt((n = zt(zt(a, q), zt(t, e))) << (r = s) | n >>> 32 - r, b);
            var n, r
        }

function zt(t, e) {
            var n = (65535 & t) + (65535 & e);
            return (t >> 16) + (e >> 16) + (n >> 16) << 16 | 65535 & n
        }

function Ft(a, b, t, e, n, s, r) {
            return Mt(b & e | t & ~e, a, b, n, s, r)
        }

function Ut(a, b, t, e, n, s, r) {
            return Mt(b ^ t ^ e, a, b, n, s, r)
        }

function Bt(a, b, t, e, n, s, r) {
            return Mt(t ^ (b | ~e), a, b, n, s, r)
        }


        最后分享一个网站,能解码加密。地址:BASE64编码解码工具 - 编码转换工具 - W3Cschool

        到此结束了,感谢观看。如果有错误的地方,望大佬指正!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值