声明:本文章中所有内容仅供学习交流使用,不用于其他任何目的,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!
1,前言
在电商数据分析领域,京东平台的数据具有重要的研究价值。其中,京东 H5ST 相关的加密参数在数据获取过程中扮演着关键的角色。
当深入研究京东接口时,我们发现 H5ST 参数的生成和加密机制复杂而独特。对其接口进行细致分析以及对加密过程进行逆向推导是获取有效数据的重要途径
2,任选一接口分析![](https://i-blog.csdnimg.cn/direct/11efdadf1c7341828644982c51d8a5bc.png)
2.1,上图为评论接口参数,t(时间戳),body(url编码后的对象),这两个个也比较关键,会参与到h5st的算法中,x-api-eid-token跟环境相关。h5st看出是4.7版本,4.7版本又有小版本,目前是4.7.4。
2.2 ,首先把h5st解码下,可以看出有九部分组成,
20240910183448173;
gt99ytggtnmz56t1;
fb5df;
tk03wd0fc1cd218ndFyYrKIQ7yrMsUejJNSH0hfkMdkaDRaty0pBrXHRZnvAFWnd2rWKb1MExGlVDQZZd4gDhIKsZ9fW;
49f4d87c7aa42c3c71789d9bd8fc618d;
4.7;
1725964488173;
VOr4kxyDxBDQQSgbkK8NuLed7aP0kebnM0H4iviCO1P353wYJWBnUeSZBLIGT2zr1yyW1rMJlQGtLYOZCU-u7BhSG4KrJlTA2UsNE7Wddz_ztbjSrSlTRuVTMX81Cxv6DSnroFvfkb1ZouwtSkXNwTgs9TYI2lKbQ1-y9pv58zpgnks8v_YO3q4jjV6hqfECeeTHamie9Fsn9TGoysQG-L-dp_vEK01J5CLWuLuA0-NdSAS0tqvgfoQ-XaIMzvvx_xS9yD1tCsDMNi2mWo6kbPee2ItjaOBQhLh_GYhWcMaAQRu-qToZnCZ6xVwlakrzYp-FjBFvUoDzkg24bpjbIQZw-lPHTkVZr7YNFHPMWqbcWF854ZP1vlRB08xlzN42xf3q1JQsOskd5F5jkwwASX29dGUSITCAa9yuuGa8G1EU1kky2Mx_f2QYzHOJCTWPFwkVDVVK-20HUbttIa0VDRAOzBS9Bkol3tb9YuWdO27575n1vdKXRAhLtiZTYJ7EBKt-uUtkVwBK69r9yIo4lbwdi7VnhUaTeyPp2XQWLE8aDiCFeLpowmDSJ5bqTmNglN_LwfotsiwQ3VIfegficuH8fY_p5xIpUsrVxOLCu7nZggE7nDk8PeheJO0dl8zjLad9Prk3hGJ0DQIeqffFGvzEemLTD52YgeDqWQHLXbk3;
c9d09012cfdbb688fbf85012f5846799
这里我提前说下
第一部分,时间戳转化的时间字符串
第二部分,fp值,由一个初始的种子字符串,不断计算得出的16位字符串,末尾永远是数字
第三部分,appid值,接口不同值不同
第四部分,tk值,接口返回,tk 和随机算法绑定,有效期大约一天
第五部分,此部分值为MD5加密结果,但是再进行md5之前会有一系列操作,过程相对复杂
第六部分,版本号
第七部分,时间戳,与第一部分相关
第八部分,环境等参数经过AES加密后的结果
第九部分,MD5值,跟第五部分生成方式一致,过程字符串不一样
4.7版本的的算法,有的是加了盐,有的是进行了算法魔改,改动交大的是AES和Baes64
3, 定位分析
3.1,这个网站调试起来还是有一定难度,毕竟是大站。直接全局搜索h5st,发现有大量结果,过程还需要异步调试有些小伙伴说不会异步调试,这东西不难,但是不懂JS异步原理确实调试起来挺头疼大,多费些功夫最后定位到如下关键位置
这里断下可以看到,body参数经过处理后做了SHA256加密,加密值放进了u对象中, 在下一步就进入了异步,很多小伙伴卡在了这里,始终调试不到关键位置,还是需要点应验和耐心,加油吧
调试过程发现很多vmp化的代码
4,解决方案
4.1,遇到这种情况怎么办呢?RPC,补环境,插桩纯算还原。RPC还是有诸多不便,补环境的话,环境检测点很多,也频繁添加新的,补起来也很费时间,并且花费很大精补完,发现返回403,内心是崩溃的。本人采取纯算还原的方式
4.2,算法还原,需要插桩分析日志,那么如何插桩呢?这是很多小伙伴的疑点难点,网上有很多文章,很多原理介绍,有的小伙伴说看的云里雾里。这东西需要一边学习一边实战才能慢慢有所顿悟。
4.3,扣出fp算法部分,做个简单分析
e.generateVisitKey = function() {
for (var e, t, o, s, c, i, u, l, p, v, b, y, x = n, S = a, A = [], E = 0; ; )
switch (S[E++]) {
case 5:
A.push(p);
break;
case 6:
A[A.length - 1] = A[A.length - 1].length;
break;
case 8:
A.push(r[S[E++]]);
break;
case 10:
A.push(e);
break;
case 11:
A.push(s);
break;
case 14:
A.push(null);
break;
case 15:
A.push({});
break;
case 16:
i = A[A.length - 1];
break;
case 17:
A.push(m);
break;
case 18:
A.push(i);
break;
case 19:
A.push(void 0);
break;
case 20:
s = A[A.length - 1];
break;
case 22:
A.push(new Array(S[E++]));
break;
case 23:
A.push(b);
break;
case 24:
y = A.pop(),
A[A.length - 1] += y;
break;
case 29:
A.pop();
break;
case 30:
v = A[A.length - 1];
break;
case 31:
A.push(h);
break;
case 32:
A[A.length - 2][r[S[E++]]] = A[A.length - 1],
A.length--;
break;
case 34:
t = A[A.length - 1];
break;
case 35:
null != A[A.length - 1] ? A[A.length - 2] = x.call(A[A.length - 2], A[A.length - 1]) : (y = A[A.length - 2],
A[A.length - 2] = y()),
A.length--;
break;
case 37:
null != A[A.length - 2] ? (A[A.length - 3] = x.call(A[A.length - 3], A[A.length - 2], A[A.length - 1]),
A.length -= 2) : (y = A[A.length - 3],
A[A.length - 3] = y(A[A.length - 1]),
A.length -= 2);
break;
case 38:
A.push(o);
break;
case 39:
A.push(S[E++]);
break;
case 44:
A.push(f);
break;
case 45:
A.push(c);
break;
case 46:
y = A.pop(),
A[A.length - 1] = A[A.length - 1] > y;
break;
case 48:
E += S[E];
break;
case 50:
y = A.pop(),
A[A.length - 1] -= y;
break;
case 51:
A[A.length - 5] = x.call(A[A.length - 5], A[A.length - 4], A[A.length - 3], A[A.length - 2], A[A.length - 1]),
A.length -= 4;
break;
case 55:
A[A.length - 4] = x.call(A[A.length - 4], A[A.length - 3], A[A.length - 2], A[A.length - 1]),
A.length -= 3;
break;
case 57:
return;
case 59:
A.push(d);
break;
case 60:
A.push(v);
break;
case 64:
return A.pop();
case 65:
l = A[A.length - 1];
break;
case 69:
A.push(l);
break;
case 70:
A[A.length - 1] = A[A.length - 1][r[S[E++]]];
break;
case 73:
e = A[A.length - 1];
break;
case 74:
u = A[A.length - 1];
break;
case 77:
A.push(u);
break;
case 78:
A.push(A[A.length - 1]),
A[A.length - 2] = A[A.length - 2][r[S[E++]]];
break;
case 80:
A.push(_);
break;
case 83:
A.push(g);
break;
case 85:
A.push(t);
break;
case 86:
A.pop() ? E += S[E] : ++E;
break;
case 91:
o = A[A.length - 1];
break;
case 92:
c = A[A.length - 1];
break;
case 93:
A.push(0);
break;
case 94:
A.push(k);
break;
case 95:
p = A[A.length - 1];
break;
case 96:
A.push(w);
break;
case 98:
b = A[A.length - 1]
}
}
4.4,通过分析上面代码,发现先定义了一些变量,有个A数组,case部分,不断往A里面push东西,然后pop东西,进行不断的入栈出栈操作,A类似于寄存器是插桩的关键点,jsvmp代码中,call或者apply处是关键的插桩点,这里涉及加密过程函数调用,+,-等运算符号处是关键点
5, fp 算法还原
根据上面输出日志,很容易发现算法逻辑,有一个初始的字符转不断操作得出结果
还原后的算法如下,可以直接运行
function generateVisitKey() {
// 生成fp
function k(e, t) {
let n = [], a = e.length;
for (let i = 0; i < e.length; i++) {
let s = e[i];
if (Math.random() * a < t && (n.push(s), 0 === --t))
break;
a--
}
;
// console.log(n)
for (var c = "", i = 0; i < n.length; i++) {
let u = Math.random() * (n.length - i) | 0;
c += n[u],
n[u] = n[n.length - i - 1]
}
return c
};
function w(e) {
for (var t = e.size, r = e.num, n = ""; t--;)
n += r[Math.random() * r.length | 0];
return n
};
function d() {
return 10 * Math.random() | 0
};
let str1 = '1uct6d0jhq';
let t = 5;
let str2 = k(str1, t);
let str3 = str1.split('').filter(char => !str2.includes(char)).join('');
let str3Obj = {'size': d(), 'num': str3};
let str4 = w(str3Obj);
let str4Obj = {'size': 15 - (str3Obj.size + str2.length), 'num': str3};
let str5 = w(str4Obj)
let str6 = str4 + str2 + str5;
let atr6Array = str6.split('');
let lt = [];
for (let i = 1; i < atr6Array.length + 1; i++) {
let char = atr6Array[atr6Array.length - i];
let nu = 35 - parseInt(char, 36);
let newchar = nu.toString(36);
lt.push(newchar);
}
lt.push(str3Obj.size)
return lt.join('')
}
6,其他算法
h5st 生成过程有多个算法参与,接口返回的有(MD5, SHA256, SHA512, HamcSHAMD5, HamcSHA256, HamcSHA512),这些算法接口随机返回,跟返回的 tk fd是绑定的,也是有过期时间的,并且这些算法有人说魔改了,其实不算吧,就是加了盐值,对入参的时间戳末尾做了处理。。。还有AES加密算是魔改了,以及AES加密结果是Base64格式的,Base64不仅改了初始字符串,还修改了编码逻辑,
这些都可以通过插桩的方式,分析出具体逻辑,算法还原后如果不对,或者参数过期,接口会返回 403, 601
经过不懈努力算法分析还原正确,
就可以稳定的抓取目标数据
7,M端订单信息h5st
m端获取订单信息接口,目前 h5st 仅为4.2版本,算法没有魔改,结果也是只有八段组合而成,部分代码结构如下全部订单,待付款订单,待收货,已完成,这几个接口算法一致,入参不同,效果如下
最后感觉这篇文章有帮助,麻烦点个赞,有疑问可评论区见