Files
live-hub-py/core/huya/fingerprint/runner.js
T
yml2213 4f099cc196 fix(huya): 一号一设备补完 — hydevice 设备参数跟随账号画像 + 画像一致性字段下沉
用户实弹测试暴露两个缺口:
1. Hebe/GUID32 全空: 登录路径走 get_profile, 未经过 account_env 补字段
   → _enrich_profile 下沉到 get_profile (幂等, 存量账号自动补齐)
2. sdid 前缀跨账号同源 (0UnHUgv0_qmfD4KAKlwzh...): env.js 是静态设备仿真
   (写死 M2102J2SC), 状态目录隔离只改了'存储', 没改'采集输入'
   → env.js makeEnv(overrides) 支持按画像覆盖 UA/屏幕/机型/Android版本;
     runner 接收 device.json; get_huya_sdid(device_hint=画像);
     下次登录每账号 hydevice 采集输入=各自绑定机型 → sdid 彻底分家
2026-08-29 16:51:41 +08:00

88 lines
3.9 KiB
JavaScript

// hydevice 指纹运行器: 仿真 Android WebView 环境 -> 真实请求 df/token + df/collect -> 输出 sdid
// argv: [stateDir, deviceJson] — deviceJson 为账号画像派生的设备覆盖参数 (一号一设备)
process.on('uncaughtException', e => { console.error('[uncaught]', String(e && e.message).slice(0, 120)); });
const fs = require('fs');
const path = require('path');
const https = require('https');
globalThis.window = globalThis;
let _deviceOverrides = null;
try { _deviceOverrides = JSON.parse(fs.readFileSync(process.argv[3] || '', 'utf8')); } catch (e) {}
require(path.join(__dirname, 'env.js')).makeEnv(_deviceOverrides);
// ---- localStorage 持久化(设备稳定性) ----
const stateDir = process.argv[2] || '.';
try { fs.mkdirSync(stateDir, {recursive: true}); } catch (e) {}
const lsFile = path.join(stateDir, 'localstorage.json');
let _ls = {};
try { _ls = JSON.parse(fs.readFileSync(lsFile, 'utf8')); } catch (e) {}
globalThis.localStorage = {
getItem: k => (k in _ls ? _ls[k] : null),
setItem: (k, v) => { _ls[k] = String(v); try { fs.writeFileSync(lsFile, JSON.stringify(_ls)); } catch (e) {} },
removeItem: k => { delete _ls[k]; try { fs.writeFileSync(lsFile, JSON.stringify(_ls)); } catch (e) {} },
clear: () => { _ls = {}; try { fs.writeFileSync(lsFile, JSON.stringify(_ls)); } catch (e) {} },
key: i => Object.keys(_ls)[i] || null,
get length() { return Object.keys(_ls).length; },
};
globalThis.sessionStorage = {getItem: () => null, setItem(){}, removeItem(){}, clear(){}, key: () => null, get length(){ return 0; }};
// ---- 真实 XHR ----
function xhr(url, body, headers) {
return new Promise((resolve) => {
const u = new URL(url);
const data = body == null ? null : Buffer.from(String(body), 'utf8');
const req = https.request({
hostname: u.hostname, port: 443, path: u.pathname + u.search, method: 'POST',
headers: Object.assign({
'accept': 'application/json, text/javascript, */*; q=0.01',
'content-type': 'application/json',
'origin': 'https://aq.huya.com',
'referer': 'https://aq.huya.com/',
'user-agent': globalThis.navigator.userAgent,
'accept-language': 'zh-CN,zh;q=0.9',
}, headers || {}),
timeout: 15000,
}, res => {
let chunks = [];
res.on('data', c => chunks.push(c));
res.on('end', () => resolve({status: res.statusCode, text: Buffer.concat(chunks).toString('utf8')}));
});
req.on('timeout', () => { req.destroy(); resolve({status: 0, text: ''}); });
req.on('error', () => resolve({status: 0, text: ''}));
if (data) req.write(data);
req.end();
});
}
globalThis.XMLHttpRequest = class {
open(m, u){ this._m = m; this._u = u; this._h = {}; }
setRequestHeader(k, v){ this._h[k] = v; }
getAllResponseHeaders(){ return ''; }
addEventListener(t, f){ if (t === 'load' || t === 'readystatechange') this._on = f; }
send(d){
xhr(this._u, d, this._h).then(r => {
this.status = r.status; this.readyState = 4;
this.responseText = r.text; this.response = r.text;
const done = () => {
if (String(this._u).includes('/collect')) {
try {
const j = JSON.parse(r.text);
if (j && j.data) {
if (j.data.sdid) console.log('__SDID__' + j.data.sdid);
if (j.data.hdid) console.log('__HDID__' + j.data.hdid);
setTimeout(() => process.exit(0), 200);
}
} catch (e) {}
}
try { this.onreadystatechange && this.onreadystatechange(); } catch (e) {}
try { this._on && this._on(); } catch (e) {}
try { this.onload && this.onload(); } catch (e) {}
};
setTimeout(done, 30);
});
}
};
eval(fs.readFileSync(path.join(__dirname, 'hydevice-prod-1.2.45-min.js'), 'utf8'));
try { globalThis.initFingerprint(process.argv[3] || '5008'); } catch (e) { console.error('[init]', e.message.slice(0, 120)); }
setTimeout(() => { console.error('__TIMEOUT__'); process.exit(2); }, 25000);