mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
1002 字
3 分钟
某东h5st逆向全流程
2026-05-16

某东h5st全流程#

免责声明#

本文仅供学习交流使用,严禁用于商业用途及非法用途!

  1. 本文所涉及的技术、代码、思路均来源于公开的网络资源及个人学习研究,仅用于技术交流与知识分享
  2. 本文中的爬虫示例代码、逆向分析方法,仅作为学习爬虫技术与反爬虫技术的参考,读者应在遵守相关法律法规及网站服务条款的前提下使用。
  3. 严禁使用本文中的技术手段进行任何形式的非法数据获取、商业竞争、侵犯隐私等行为。 任何因不当使用本文内容而产生的法律纠纷及后果,均由使用者自行承担,与本文作者无关。
  4. 如果相关网站或平台认为本文内容侵犯了您的权益,请联系作者,作者将在第一时间删除相关内容。
  5. 学技术是为了保护自己,而不是去伤害别人。 请尊重网站的数据所有权,合理合法地使用网络资源。
  6. 如有问题,请第一时间练习本人进行删除。

h5st采用控制流混淆,以及大量指纹与环境检测,颇为麻烦,这篇文章我们用补环境的方式来拿到h5st。

本篇针对最新版h5st,也就是5.3版本😭

由于我的账号被风控了,为了演示清楚,我在网上找了一些类似的浏览器截图✊

流程#

a

通过抓包我们可以发现,表单里有个h5st参数,这个参数就是我们的目标。

我们直接利用搜索,在所有和h5st相关的地方打上断点。

最后会断在这里

b

现在就清晰明了了,我们要拿到h5st,就要搞到d这个参数。d就在上面,没截进来。

我们在node里模拟出d,这里我取名为u,参数照着浏览器拿就行。

u = {
appid: "search-pc-java",
functionId: 'pc_search_searchWare',
client: "pc",
clientVersion: "1.0.0",
t: new Date().getTime(),
body: crypto.SHA256(JSON.stringify(params)).toString()
};

u的body又是一个sha256加密的params,我们接着去拿这个params

params = {
"enc": "utf-8",
"area": "52993_52994_146660_0",
"page": 1,
"mode": "",
"concise": false,
"hoverPictures": false,
"newAdvRepeat": false,
"mixerParam": false,
"new_interval": true,
"s": 1
}

这里只有pages是动态变化的,这个s生成逻辑如下,pf是个动态数组,需要请求才能拿到。

nction get_s(e) {
Pf = new Map([
])
try {
let t = 0
, n = 0;
return Pf.forEach(r => {
r.page < e && (t += +r.adNum,
n += +r.repeatNum)
}
),
1 + (e - 1) * 30 - t + n
} catch (t) {
return 1
}
}

但是我们通过重放,可以发现,对s校验不是很严格!(ps:这个有可能是个风控点)所以我们先不管他。

这样,我们就可以在node构造获取h5st的函数了。

function get_h5st() {
params = {
"enc": "utf-8",
"area": "52993_52994_146660_0",
"page": 1,
"mode": "",
"concise": false,
"hoverPictures": false,
"newAdvRepeat": false,
"mixerParam": false,
"new_interval": true,
"s": 1
}
u = {
appid: "search-pc-java",
functionId: 'pc_search_searchWare',
client: "pc",
clientVersion: "1.0.0",
t: new Date().getTime(),
body: crypto.SHA256(JSON.stringify(params)).toString()
};
window.PSign = new window.ParamsSign({
appId: 'f06cc',
preRequest: false,
onSign: (res) => {
// 签名可用率监控,业务方自行上报
if (res.code != 0) {
try {
window.dra &&
window.dra.sendCustomEvent &&
window.dra.sendCustomEvent({
name: 'main_search',
metrics: {
error_code: '751',
error_type_txt: '接口加密失败onSign非0',
},
context: {
error_code: res.code,
},
})
} catch (error) {
console.log(error)
}
}
},
onRequestTokenRemotely: (res) => {
// 算法接口可用率监控,业务方自行上报
if (res.code != 200) {
try {
window.dra &&
window.dra.sendCustomEvent &&
window.dra.sendCustomEvent({
name: 'main_search',
metrics: {
error_code: '751',
error_type_txt: '接口加密失败onRequestTokenRemotely',
},
context: {
error_msg: res && res.message ? res.message : '接口加密失败',
},
})
} catch (error) {
console.log(error)
}
}
},
})
h5st = window.PSign._$sdnmd(u)
return h5st
}

window.PSign通过搜索也能拿到,他是在文件开头被初始化,我们直接扣下来放在上面。

通过跟栈,我们可以发现,h5st生成就是在js_security这个文件里,我们把这个文件全部拿到node里并命名为js_code。

const crypto = require('crypto-js')
//生成逻辑
require('./js_code')
function get_h5st() {
params = {
"enc": "utf-8",
"area": "52993_52994_146660_0",
"page": 1,
"mode": "",
"concise": false,
"hoverPictures": false,
"newAdvRepeat": false,
"mixerParam": false,
"new_interval": true,
"s": 1
}
u = {
appid: "search-pc-java",
functionId: 'pc_search_searchWare',
client: "pc",
clientVersion: "1.0.0",
t: new Date().getTime(),
body: crypto.SHA256(JSON.stringify(params)).toString()
};
window.PSign = new window.ParamsSign({
appId: 'f06cc',
preRequest: false,
onSign: (res) => {
// 签名可用率监控,业务方自行上报
if (res.code != 0) {
try {
window.dra &&
window.dra.sendCustomEvent &&
window.dra.sendCustomEvent({
name: 'main_search',
metrics: {
error_code: '751',
error_type_txt: '接口加密失败onSign非0',
},
context: {
error_code: res.code,
},
})
} catch (error) {
console.log(error)
}
}
},
onRequestTokenRemotely: (res) => {
// 算法接口可用率监控,业务方自行上报
if (res.code != 200) {
try {
window.dra &&
window.dra.sendCustomEvent &&
window.dra.sendCustomEvent({
name: 'main_search',
metrics: {
error_code: '751',
error_type_txt: '接口加密失败onRequestTokenRemotely',
},
context: {
error_msg: res && res.message ? res.message : '接口加密失败',
},
})
} catch (error) {
console.log(error)
}
}
},
})
h5st = window.PSign._$sdnmd(u)
return h5st
}
console.log(get_h5st())

现在,我们直接挂上代理,补环境即可!这里耐心补就行,不过它检测了原型链!需要补原型链!

基本大量是document的环境。

接着是canvas指纹和webgl指纹。

这两个取巧可以直接去拿。

function _$RD(_$RO) {
_$RI(_$RO, 'pf', function(_$RY) {
return window.navigator.platform;
}, _$RW),
_$RI(_$RO, 'pr', function(_$RY) {
return window.devicePixelRatio;
}, _$RW),
_$RI(_$RO, 're', function(_$RY) {
return document.referrer;
}, _$RW),
_$I.AWTVk(_$RI, _$RO, Zn(0x26b), function(_$RY) {
return _$q8(0x2184 * -0x1 + 0x6dd + -0x86 * -0x33);
}, _$RW),
_$RI(_$RO, Zn(0x27e), function(_$RY) {
var Ze = Zn
, _$RQ = new RegExp(Ze(0x1f5))
, _$Rb = document.referrer.match(_$RQ);
return _$Rb && _$Rb[0xae0 + -0x21b8 + 0x2db * 0x8] ? _$Rb[-0x305 * 0x4 + 0x62 * -0x53 + 0x74f * 0x6] : '';
}, _$RW),
_$RI(_$RO, 'v', function(_$RY) {
return _$ql;
}, _$RW),
_$RI(_$RO, Zn(0x290), function(_$RY) {
var Za = Zn
, _$RQ = new Error(Za(0x2d8)).stack.toString()
, _$Rb = _$RQ.split('\x0a')
, _$Rw = _$Rb.length;
return _$RA.ZaihA(_$Rw, -0x1c4f + -0x228f + -0x22b * -0x1d) ? _$Rb[_$Rw - (-0x1 * -0x265 + 0x120c + -0x1470)] : _$RQ;
}, _$RW),
_$RI(_$RO, Zn(0x302), function(_$RY) {
return Window.toString() + '$' + Window.toString.toString.toString();
}, _$RW),
_$I.GxyBI(_$RI, _$RO, Zn(0x1d2), function(_$RY) {
return '0';
}, _$RW),
_$I.GxyBI(_$RI, _$RO, _$I.ELueU, function(_$RY) {
var _$RQ = _$qT.get(_$qk.CANVAS_FP)
, _$Rb = _$RA.CGaEl(_$q7, _$RQ) ? _$RQ.v : '';
return _$Rb || (navigator.userAgent && !/Mobi|Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) && (_$Rb = _$RA.ilAqh(_$qA)),
_$Rb && _$qT.set(_$qk.CANVAS_FP, {
'v': _$Rb,
't': Date.now(),
'e': 0x1e13380
})),
_$Rb;
}, _$RW),
_$RI(_$RO, _$I.xhXJd, function(_$RY) {
var _$RQ = _$qA();
return _$RQ && _$qT.set(_$qk.CANVAS_FP, {
'v': _$RQ,
't': Date.now(),
'e': 0x1e13380
}),
_$RQ;
}, _$RW),
_$I.AWTVk(_$RI, _$RO, Zn(0x227), function(_$RY) {
var _$RQ = _$qT.get(_$qk.WEBGL_FP);
return _$q7(_$RQ) && _$RQ.v ? _$RQ.v : '';
}, _$RW),
_$RI(_$RO, Zn(0x2ef), function(_$RY) {
return navigator.hardwareConcurrency;
}, _$RW),
_$RW;
}
}

我们可以看到,它检测了大量navigator,直接搜到这里,我们能发现_$RW对象包含了大量指纹信息。

直接把他拿到我们的node环境里赋值,注意里面有两个random随机数,这个我们可以通过追栈或者直接hook,最后能找到这个随机数的生成函数_q8,就能拿到random属性`"random": _q8(10)`。

python模拟请求#

由于检测了tls指纹,我们需要利用curl_cffi来模拟指纹,另外execjs这个狗屎编码问题也需要解决。方案如下

import time
from curl_cffi import requests
import subprocess
from functools import partial
# 解决 Windows 编码问题
subprocess.Popen = partial(subprocess.Popen, encoding='utf-8')
import execjs
print(execjs.get().name) # 确保能正常运行
with open("狗东h5st.js", "r", encoding="utf-8") as f:
js_code = f.read()
ctx = execjs.compile(js_code)
headers = {
"accept": "application/json, text/plain, */*",
"accept-language": "zh-CN,zh;q=0.9",
"cache-control": "no-cache",
"origin": "https://search.jd.com",
"pragma": "no-cache",
"priority": "u=1, i",
"referer": "https://search.jd.com/Search?keyword=jk&enc=utf-8&pvid=cd0e7cded0fd46e19f75c9ea87e29986&themeColor=&from=home&spmTag=YTAyMTkuYjAwMjM1Ni5jMDAwMDcxNjEuMSU0MDE3Nzg4OTI5MTA2MTQlMjMxNzc4NzY0NzE5NTE1MTY5OTk0OTU0MSUyMzE5Mjg0MTIyNzc",
"sec-ch-ua": "\"Chromium\";v=\"148\", \"Google Chrome\";v=\"148\", \"Not/A)Brand\";v=\"99\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-site",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36",
"x-referer-page": "https://search.jd.com/Search",
"x-rp-client": "h5_2.1.0"
}
cookies = {
"__jdv": "229668127|cn.bing.com|t_2037222536_0_0|adrealizable|f49c1979fb28d62d-p_0|1778760960019",
"jcap_dvzw_fp": "67G6EpotjV1J0eDMn-JsRuIHuU_J5iLXjyGeSV8Tmajx66RJzEiZ4WlUT1sDg04R6Es07eAs84QRfEuCdzxPlW3p1uA=",
"PCSYCityID": "CN_320000_320500_0",
"user-key": "1063871e-fef9-46fb-90f3-2f11fcc86806",
"shshshfpa": "7016242f-a138-659d-7491-5709d4550ab4-1778761184",
"shshshfpx": "7016242f-a138-659d-7491-5709d4550ab4-1778761184",
"__jdu": "17787647195151699949541",
"areaId": "52993",
"ipLoc-djd": "52993-52994-146660-0",
"TrackID": "1RZcfqjkwy76IJKCAe59oT1JBBKuBGlMrbE4NgcObczVnYG-e0UwFdLh7NeVnDUTohS1B4BCz2FDIzNSd36rexqAPv1GW9ae6HvVCJM56_S4",
"light_key": "AASBKE7rOxgWQziEhC_QY6ya2qL1uZOKJ26TLn-7vCoZWxnGUe6STkaVDa2a003PEPXUYde7",
"pinId": "OI66V8DdRcDvRP4X3ePfTg",
"pin": "jd_KlBTjtKcZjSM",
"unick": "2l652aq5b18189",
"_tp": "nRRl2Me9wmEDL1EY%2FCqfTg%3D%3D",
"_pst": "jd_KlBTjtKcZjSM",
"autoOpenApp_downCloseDate_autoOpenApp_handler": "1778839798571_1",
"thor": "43756E9B198DF241723079EF4BD85A79B835F3CDCF648F0BEB2B713C4B12EB320A368AA2F11FBD14F679272B9C59349767EBFA088D41BD415A033394A27983D0",
"3AB9D23F7A4B3CSS": "jdd034BPKMDD2RHGRUQV5KRSOZKORJMNUXX4XMCMHWQPUJ6S6H4YZVJ2TU4GFI7OULL45RNG7SPUP5HVZB7KFZA45ZHCP7EAAAAM6FZDYL2YAAAAADB6MGM3YYN2XO4X",
"mail_times": "4%2C1%2C1778892901084",
"umc_count": "1",
"__jda": "143920055.17787647195151699949541.1778764720.1778852393.1778892900.6",
"__jdc": "143920055",
"cid": "9",
"3AB9D23F7A4B3C9B": "4BPKMDD2RHGRUQV5KRSOZKORJMNUXX4XMCMHWQPUJ6S6H4YZVJ2TU4GFI7OULL45RNG7SPUP5HVZB7KFZA45ZHCP7E",
"flash": "3_3IVwA_AoKgPBjcEwGfJ2h_Q1FrVfx3ICqMi7CSFiFkp04BNsQQrKw538-hT1tH_HQnXHEg5uZN9r520I51HFpxb472wCxV29wpLR_ZtH9qMd2KmfhH4s0NzTv_6Yv2cc9tG7Zp5U1miT5yRbxY6snslW2-25fMjohNusHGdETDoW4TzxBZxp",
"sdtoken": "AAbEsBpEIOVjqTAKCQtvQu17ufJOKOnkyrGFeRRK4gqM-z1ZogKzU6BynGrK2MBnxOr6xKr65bBRTa9bDZ-dUEbF9g3zPVLJUutUZEdm3lGX1ZDNafLoH_YeoTDumsyCeqk7Q_NwHm_u1e45Ccsb",
"shshshfpb": "BApXWaiWLLftA4Io_U3-LKcZYPn5vk7tuBjo3Fqto9xJ1PdZfQq3asC7hmAD5CYxAXjWF06vnsaxgIblgv64M5Ip_Og3qp_RZiYI",
"__jdb": "143920055.40.17787647195151699949541|6.1778892900"
}
url = "https://api.m.jd.com/api"
res = ctx.call("get_h5st")
params = {
"appid": "search-pc-java",
"t": [
str(res['t']),
str(int(time.time()*1000)),
],
"client": "pc",
"clientVersion": "1.0.0",
"cthr": "1",
"uuid": "17787647195151699949541",
"loginType": "3",
"keyword": "jk",
"functionId": "pc_search_searchWare",
"body": "{\"enc\":\"utf-8\",\"pvid\":\"cd0e7cded0fd46e19f75c9ea87e29986\",\"from\":\"home\",\"area\":\"52993_52994_146660_0\",\"page\":1,\"mode\":\"\",\"concise\":false,\"hoverPictures\":false,\"newAdvRepeat\":false,\"mixerParam\":false,\"new_interval\":true,\"s\":1}",
"x-api-eid-token": "aldjaodaiodj",
"h5st":res["h5st"]
}
response = requests.get(url, headers=headers, cookies=cookies, params=params,impersonate="chrome110")
print(response.text)
print(response)

最后能拿到数据,但是注意风控!

裙+白色衬衫+蓝条纹领带","commentFuzzy":"32","d30ItemUvDesc":"100+","deliveryable":1,"disableCart":0,"finalPrice":{"count":1,"estimatedPrice":"461.64","isFinal":1,"subPriceType":null,"title":"到手价","type":1},"finalPriceJson":"{\"count\":1,\"estimatedPrice\":\"461.64\",\"isFinal\":1,\"title\":\"到手价\",\"type\":1}","followed":0,"govSubsidyBenefit":null,"hidePrice":0,"iconList1":null,"iconList2":null,"iconList3":null,"iconList4":null,"imageurl":"jfs/t1/327793/5/9733/112790/68a934a9F52c765ec/441fe048eb1734c9.jpg","isContrast":1,"jdPrice":"481.50","jdPriceDesc":"","jdPriceFuzzy":"","limitCompanyBuy":0,"longImageUrl":"","mockFinalPrice":null,"oriPrice":"","pingou":0,"plusGoodShop":0,"plusLimit":0,"presale":0,"productUrl":null,"promotionDiscount":"","realPrice":"481.50","sdx":"","seckill":0,"selfSupport":0,"showAddCart":1,"showPurchaseList":0,"skuId":null,"stock":1,"subsidyPrice":"","timeOrderType":0,"totalSales":"","wareId":"10175899914465","wholeSales":"","yushouInfo":null,"yuyueInfo":null,"yuyueYushouJson":""},{"addCartUrl":null,"advanceBooking":0,"cashFirst":"","clickUrl":"","color":"粉色百褶裙+白色衬衫+少女心事领","commentFuzzy":"32","d30ItemUvDesc":"1","deliveryable":1,"disableCart":0,"finalPrice":{"count":1,"estimatedPrice":"461.64","isFinal":1,"subPriceType":null,"title":"到手价","type":1},"finalPriceJson":"{\"count\":1,\"estimatedPrice\":\"461.64\",\"isFinal\":1,\"title\":\"到手价\",\"type\":1}","followed":0,"govSubsidyBenefit":null,"hidePrice":0,"iconList1":null,"iconList2":null,"iconList3":null,"iconList4":null,
分享

如果这篇文章对你有帮助,欢迎分享给更多人!

某东h5st逆向全流程
https://fatdog.20060113.xyz/posts/h5st/
作者
神秘大胖狗
发布于
2026-05-16
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

封面
Sample Song
Sample Artist
封面
Sample Song
Sample Artist
0:00 / 0:00