吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4515|回复: 20
收起左侧

[Web逆向] 【某点数据】榜单数据抓取

  [复制链接]
xinjun_ying 发表于 2023-8-8 17:11
本帖最后由 xinjun_ying 于 2023-8-15 14:50 编辑

一直在搞其他事情,好久没搞js加密分析了,正好遇到一个很简单的数据要搞。想着也很久没在论坛发帖了,这次就发下。【某点数据】的榜单数据抓取。

[Java] 纯文本查看 复制代码
列表页接口:【https://xxx/pc/app/v1/rank?market_id=11&genre_id=4&country_id=24&device_id=0&page=1&time=1691424000&rank_type=1&brand_id=1&k=AVUJVF8GAh5QBQVWDFVeBwEcXRwXQhFUXgQGHlkBAVcOTUgRGkwNHBdCEUoYBhxaCFpa】
打开列表页链接清空下请求,点击下其他的分类,一下子就看到数据返回的接口了。除了k是根据动态计算的,其他都是按照分类或者时间戳写死的。

2.png
[Asm] 纯文本查看 复制代码
老规矩先全局搜下接口路径,发现没有,好家伙,藏的那么深,从接口执行堆栈流程看到有个getAppRank 过于明显果断从这里开始debug。

3.png
[Asm] 纯文本查看 复制代码
debug过程中发现接口路径是从【/web/api/rank】替换到【/app/v1/rank】

6.png

[Asm] 纯文本查看 复制代码
最后我们顺着debug看下k的生成规则是啥,发现k是由请求参数+basse64生成这就好弄了,分析下这块逻辑。

5.png

简单带你直接把这块js扣下看看缺啥补啥。具体看代码上的注释,已经把原代码和修改的备注了。
[Asm] 纯文本查看 复制代码
20230810 【'1d8en73(i4'】 本以为是固定参数,后发现是每天虽则签名更改,一天都不会变动,等有空再更新上来 


[JavaScript] 纯文本查看 复制代码
function getToken(e, path, n, r) {
        function u8ArrZBase64(u8Arr) {
          try{
                         let CHUNK_SIZE = 0x8000;
                         let index = 0;
                         let length = u8Arr.length;
                         let result = '';
                         let slice;
                         while (index < length) {
                                 slice = u8Arr.subarray(index, Math.min(index + CHUNK_SIZE, length));
                                 result += String.fromCharCode.apply(null, slice);
                                 index += CHUNK_SIZE;
                         }
                         return btoa(result);
                 }
                 catch(e) {
                         throw e;
                 }
        }
        var s = n.s
          , d = n.k
          , f = n.l
          , v = n.d
          , l = n.sort
          , k = n.num
          , y = function(content, t, e) {
                for (var a = Array.from(content), n = Array.from(t), r = a.length, o = n.length, d = String.fromCodePoint, i = 0; i < r; i++)
                        a[i] = d(a[i].codePointAt(0) ^ n[(i + e) % o].codePointAt(0));
                return a.join("")
        }(function(s, t, path, e) {
                return [s, t, e, path].join("(&&)")
        }(function(t, e) {
                // 原代码var n = c()(t); 缺c()(t) debug后发现只是简单校验t,我们可以直接把t复制给n
                var n = t;
                // 原代码
                //if (!_()(n)) {
                //        var r = [];
                //        for (var d in n)
                //                m()(n[d]) && "get" === e && (n[d] = n[d].join("")),
                //                "post" === e && (m()(n[d]) || o()(n[d])) && (n[d] = JSON.stringify(n[d])),
                //                r.push(n[d]);
                //        return r.sort(),
                //        r.join("")
                //}
                // debug并删除无用代码,就是简单拼接
                var r = [];
                for (var d in n){
                        r.push(n[d])
                }
                return r.sort(),r.join("");
                // Object(h.b)(s, d, f) 方法主要针对入参n中s,k的Uint8Array,并转成字符串,进去发现只是可以是写死的字符串1d8en73(i4
        }(e, r), parseInt((new Date).getTime() / 1e3) - 655876800 - v, path, l), '1d8en73(i4', k);
        // 最后就是u8Arr转Base64
        return u8ArrZBase64(new TextEncoder().encode(y));
}

var path = '/v1/rank';

var eData = {
    "market_id": 11,
    "genre_id": 6,
    "country_id": 24,
    "device_id": 0,
    "page": 1,
    "time": 1690732800,
    "rank_type": 1,
    "brand_id": 0
}

// s,k,l  均会过期,定时从列表页解析即可,https://xxx/rank/googleplay/11-1-14-24-0?time=1690387200000
var nData = {
    "s": "7f260473b50b9a6beb9a384d3adc2027",
    "k": "d8462b23fc155996",
    "l": "4e226e4686f23bc3",
    "d": 0,
    "sort": "dd",
    "num": 10
}

var rData = "get";

getToken(eData, path,nData, rData)


[Asm] 纯文本查看 复制代码
前几天没来得及更新,次日发现某点还有个需要签名会每天变动,因此以下补充签名生成规则。对应上述代码中 【Object(h.b)(s, d, f)】,开始debug进去查看,着急搬砖直接上解析后的加密规则,在代码里都注释了




[JavaScript] 纯文本查看 复制代码
function readUInt32BE(tt, t) {
        return 16777216 * tt[t] + (tt[t + 1] << 16 | tt[t + 2] << 8 | tt[t + 3])
}

function oFunction(t) {
        for (var e = t.length / 4 | 0, r = new Array(e), i = 0; i < e; i++)
                r[i] = readUInt32BE(t, 4 * i);
        return r
}

// 后续至关重用
var f = [0, 1, 2, 4, 8, 16, 32, 64, 128, 27, 54]
  , d = function() {
        for (var t = new Array(256), e = 0; e < 256; e++)
                t[e] = e < 128 ? e << 1 : e << 1 ^ 283;
        for (var r = [], n = [], o = [[], [], [], []], h = [[], [], [], []], l = 0, f = 0, i = 0; i < 256; ++i) {
                var d = f ^ f << 1 ^ f << 2 ^ f << 3 ^ f << 4;
                d = d >>> 8 ^ 255 & d ^ 99,
                r[l] = d,
                n[d] = l;
                var c = t[l]
                  , m = t[c]
                  , v = t[m]
                  , y = 257 * t[d] ^ 16843008 * d;
                o[0][l] = y << 24 | y >>> 8,
                o[1][l] = y << 16 | y >>> 16,
                o[2][l] = y << 8 | y >>> 24,
                o[3][l] = y,
                y = 16843009 * v ^ 65537 * m ^ 257 * c ^ 16843008 * l,
                h[0][d] = y << 24 | y >>> 8,
                h[1][d] = y << 16 | y >>> 16,
                h[2][d] = y << 8 | y >>> 24,
                h[3][d] = y,
                0 === l ? l = f = 1 : (l = c ^ t[t[t[v ^ c]]],
                f ^= t[t[f]])
        }
        return {
                SBOX: r,
                INV_SBOX: n,
                SUB_MIX: o,
                INV_SUB_MIX: h
        }
}();

function _reset(tt) {
        for (var t = tt, e = t.length, r = e + 6, n = 4 * (r + 1), o = [], h = 0; h < e; h++)
                o[h] = t[h];
        for (h = e; h < n; h++) {
                var l = o[h - 1];
                h % e == 0 ? (l = l << 8 | l >>> 24,
                l = d.SBOX[l >>> 24] << 24 | d.SBOX[l >>> 16 & 255] << 16 | d.SBOX[l >>> 8 & 255] << 8 | d.SBOX[255 & l],
                l ^= f[h / e | 0] << 24) : e > 6 && h % e == 4 && (l = d.SBOX[l >>> 24] << 24 | d.SBOX[l >>> 16 & 255] << 16 | d.SBOX[l >>> 8 & 255] << 8 | d.SBOX[255 & l]),
                o[h] = o[h - e] ^ l
        }
        for (var c = [], m = 0; m < n; m++) {
                var v = n - m
                  , y = o[v - (m % 4 ? 0 : 4)];
                c[m] = m < 4 || v <= 4 ? y : d.INV_SUB_MIX[0][d.SBOX[y >>> 24]] ^ d.INV_SUB_MIX[1][d.SBOX[y >>> 16 & 255]] ^ d.INV_SUB_MIX[2][d.SBOX[y >>> 8 & 255]] ^ d.INV_SUB_MIX[3][d.SBOX[255 & y]]
        }

        return {
                '_nRounds' : r,
                '_keySchedule' : o,
                '_invKeySchedule' : c
        }
}

function DAES(t) {
        var _key = oFunction(t);
        var rs = _reset(_key);
        return {
                '_key' : _key,
                '_nRounds' : rs._nRounds,
                '_keySchedule' : rs._keySchedule,
                '_invKeySchedule' : rs._invKeySchedule
        }
}

function dFrom(sData,sP) {
        k(sData,sP,0,16);
        return sData;
}

function k(t, e, r, n) {
        r = Number(r) || 0;
        var f = t.length - r;
        n ? (n = Number(n)) > f && (n = f) : n = f;
        var o = e.length;
        if (o % 2 != 0)
                throw new TypeError("Invalid hex string");
        n > o / 2 && (n = o / 2);
        for (var i = 0; i < n; ++i) {
                var c = parseInt(e.substr(2 * i, 2), 16);
                if (isNaN(c))
                        return i;
                t[r + i] = c
        }
        return i
}

function l(t, e, r, n, o) {
        for (var h, l, f, d, c = r[0], m = r[1], v = r[2], y = r[3], w = t[0] ^ e[0], _ = t[1] ^ e[1], M = t[2] ^ e[2], S = t[3] ^ e[3], E = 4, k = 1; k < o; k++)
                h = c[w >>> 24] ^ m[_ >>> 16 & 255] ^ v[M >>> 8 & 255] ^ y[255 & S] ^ e[E++],
                l = c[_ >>> 24] ^ m[M >>> 16 & 255] ^ v[S >>> 8 & 255] ^ y[255 & w] ^ e[E++],
                f = c[M >>> 24] ^ m[S >>> 16 & 255] ^ v[w >>> 8 & 255] ^ y[255 & _] ^ e[E++],
                d = c[S >>> 24] ^ m[w >>> 16 & 255] ^ v[_ >>> 8 & 255] ^ y[255 & M] ^ e[E++],
                w = h,
                _ = l,
                M = f,
                S = d;
        return h = (n[w >>> 24] << 24 | n[_ >>> 16 & 255] << 16 | n[M >>> 8 & 255] << 8 | n[255 & S]) ^ e[E++],
        l = (n[_ >>> 24] << 24 | n[M >>> 16 & 255] << 16 | n[S >>> 8 & 255] << 8 | n[255 & w]) ^ e[E++],
        f = (n[M >>> 24] << 24 | n[S >>> 16 & 255] << 16 | n[w >>> 8 & 255] << 8 | n[255 & _]) ^ e[E++],
        d = (n[S >>> 24] << 24 | n[w >>> 16 & 255] << 16 | n[_ >>> 8 & 255] << 8 | n[255 & M]) ^ e[E++],
        [h >>>= 0, l >>>= 0, f >>>= 0, d >>>= 0]
}

function N(t, e, r, n) {
        e < 0 && (e = 4294967295 + e + 1);
        for (var i = 0, f = Math.min(t.length - r, 4); i < f; ++i)
                t[r + i] = e >>> 8 * (n ? i : 3 - i) & 255
}

function writeUInt32BE(tData, t, e,r) {
        return t = +t,
        e |= 0,
        true ? (tData[e] = t >>> 24,
        tData[e + 1] = t >>> 16,
        tData[e + 2] = t >>> 8,
        tData[e + 3] = 255 & t) : N(tData, t, e, !1),
        e + 4
}

function decryptBlock(t,kData){
        var e = (t = oFunction(t))[1];
        t[1] = t[3],
        t[3] = e;
        var thisData = DAES(new TextEncoder().encode(kData));
        var tData = new Uint8Array(16);
        var r = l(t, thisData._invKeySchedule, d.INV_SUB_MIX, d.INV_SBOX, thisData._nRounds)
          , h = new Uint8Array(16);
        // console.log('e:'+e);
        // console.log('h:'+h);
        // console.log('r:'+r);
        // console.log('t:'+t);
        writeUInt32BE(tData, r[0], 0),
        writeUInt32BE(tData, r[3], 4),
        writeUInt32BE(tData, r[2], 8),
        writeUInt32BE(tData, r[1], 12),
        h;
        return tData;
}

function exports(a, b) {
        for (var t = Math.min(a.length, b.length), r = new Uint8Array(16), i = 0; i < t; ++i)
                r[i] = a[i] ^ b[i];
        return r
}
function slice(tData, t, e) {
        var r, n = tData.length;
        if ((t = ~~t) < 0 ? (t += n) < 0 && (t = 0) : t > n && (t = n),
        (e = void 0 === e ? n : ~~e) < 0 ? (e += n) < 0 && (e = 0) : e > n && (e = n),
        e < t && (e = t),true)
                (r = tData.subarray(t, e)).__proto__ = d.prototype;
        return r
}

function ttt(t) {
        var e = t[15];
        if (e < 1 || e > 16)
                throw new Error("unable to decrypt data");
        var i = -1;
        for (; ++i < e; )
                if (t[i + (16 - e)] !== e)
                        throw new Error("unable to decrypt data");
        if (16 === e)
                return;
        return slice(t, 0, 16 - e)
}

function getSign(sData,kData,lData){
        // 按s字符串生成 对应 16位数组
        var dFromResult = dFrom(new Uint8Array(16),sData);
        console.log(dFromResult);
        // 将k转16为数组通过 DAES方法 生成_key、_nRounds、_keySchedule、_invKeySchedule,等签名所需参数,并返回执行结果
        var decryptBlockResult= decryptBlock(dFromResult,kData);
        console.log(decryptBlockResult);
        var exportsResult = exports(decryptBlockResult,new TextEncoder().encode(lData));
        console.log(exportsResult);
        // 按数组切片重组并转成字符串得到签名
        return new TextDecoder("utf-8").decode(ttt(exportsResult));
}

var params = process.argv.splice(2);

process.stdout.write(getSign(params[0],params[1],params[2]));





[Asm] 纯文本查看 复制代码
最后先调用getSign 再根据参数调用getToken即可


7.png

免费评分

参与人数 10威望 +1 吾爱币 +26 热心值 +9 收起 理由
涛之雨 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
笙若 + 1 + 1 谢谢@Thanks!
s3rious74 + 1 我很赞同!
学习使我快乐1 + 1 + 1 我很赞同!
yanecc + 1 + 1 我很赞同!
2snfks29fais + 1 谢谢@Thanks!
jingchangdenglu + 1 谢谢@Thanks!
空竹 + 1 + 1 用心讨论,共获提升!
wellionx + 1 鼓励转贴优秀软件安全工具和文档!
Xiongjy + 1 + 1 鼓励转贴优秀软件安全工具和文档!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

空竹 发表于 2023-8-9 10:54
https://static.diandian.com/_app/app~f69643ec.f0b6bc0.js

1:400756 这个e(n)好像也能生成k这个参数
YC5201314 发表于 2023-8-8 19:08
dychjyfgfda 发表于 2023-8-8 19:10
kkoo 发表于 2023-8-8 20:44
感谢分享,其他的我也试试看行不行
xiaoshan208 发表于 2023-8-8 22:16
教程很详细,学习了
aonima 发表于 2023-8-8 23:34
这个容易黑号
zd53011 发表于 2023-8-9 07:54
值得学习的文章
qxt29680874 发表于 2023-8-9 10:23
这个感觉还阔以
空竹 发表于 2023-8-9 11:10
请问楼主,如果要扣这里的话,怎么扣和补环境?
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 14:14

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表