闲来无事了看了下之前搞得datadome,更新频率很快,废话不多说,直接撸。
先ast还原一版本,跟调用栈找到函数入口,箭头所指就是captchaEncodePayload生成的地方。
继续a函数往上跟跳到这个位置,发现这个a函数在_a函数的返回数组里,我们直接把他全干下来。
var _a = function () { var t = 1789537805, l = !0; function h(e) { if (!e) { return t; } for (var I = 0, f = 0; f < e["length"]; f++) I = (I << 5) - I + e["charCodeAt"](f) | 0; return (0 == I ? true : false) ? t : I; } function D(e) { return e > 37 ? 59 + e : e > 11 ? 53 + e : e > 1 ? 46 + e : 50 * e + 45; } function I(e) { e ^= e << 13; e ^= e >> 17; return e ^ e << 5; } function f(e, t) { var a = e , n = -1 , c = t , i = l; l = !1; var o = null; return [function(e) { var t; return null !== o ? (t = o, o = null) : (++n > 2 && (a = I(a), n = 0), t = a >> 16 - 8 * n, t ^= i ? --c : 0, t &= 255, e && (o = t)), t; } ]; } return ka = function (e, t) { var o = 9959949970 ^ h(e) ^ t, s = I(I(Date["now"]() >> 3 ^ 11027890091) * 9959949970), u = f(o, s)[0], M = [], g = !0, l = 0, N = function (e) { for (var t = [], a = 0, n = 0; n < e["length"]; n++) { var c = e["charCodeAt"](n); c < 128 ? t[a++] = c : c < 2048 ? (t[a++] = c >> 6 | 192, t[a++] = 63 & c | 128) : 55296 == (64512 & c) && n + 1 < e["length"] && 56320 == (64512 & e["charCodeAt"](n + 1)) ? (c = 65536 + ((1023 & c) << 10) + (1023 & e["charCodeAt"](++n)), t[a++] = c >> 18 | 240, t[a++] = c >> 12 & 63 | 128, t[a++] = c >> 6 & 63 | 128, t[a++] = 63 & c | 128) : (t[a++] = c >> 12 | 224, t[a++] = c >> 6 & 63 | 128, t[a++] = 63 & c | 128); } for (var o = 0; o < t["length"]; o++) t[o] ^= u(); return t; }, j = function (e) { return JSON["stringify"](e); }, p = function (e) { for (var t = 0, a = [], o = s; t < e["length"];) { var u = (255 & --o ^ e[t++]) << 16 | (255 & --o ^ e[t++]) << 8 | 255 & --o ^ e[t++]; a["push"](String["fromCharCode"](D(u >> 18 & 63)), String["fromCharCode"](D(u >> 12 & 63)), String["fromCharCode"](D(u >> 6 & 63)), String["fromCharCode"](D(63 & u))); } var M = e["length"] % 3; return M && (a["length"] -= 3 - M), a["join"](""); }; function z(e, t) { if ("string" == typeof e && 0 != e["length"] && (!t || -1 != ["number", "string", "boolean"]["indexOf"](typeof t))) { var a, n = j(e), o = j(t); if (e && void 0 !== o && e !== "xt1") { if (M["push"](u() ^ (M["length"] ? 44 : 123)), Array["prototype"]["push"]["apply"](M, N(n)), M["push"](58 ^ u()), Array["prototype"]["push"]["apply"](M, N(o)), g) { g = !1, ("string" == typeof _hsv && _hsv["length"] > 0 || "number" == typeof _hsv && !isNaN(_hsv)) && (a = _hsv), z("l019SX", a || 33); } } } } var A = new Set(); return [z, function (e, t) { A["has"](e) || (A["add"](e), z(e, t)); }, function (e) { var t = f(1809053797 ^ h(e), s)[0]; z("IYH4Id", ++l); for (var a = [], n = 0; n < M["length"]; n++) a["push"](M[n] ^ t()); return a["push"](125 ^ u(!0) ^ t()), p(a); }]; }; }();
a函数的实际操作就是把M数组里的值就行编码得到我们要的结果,M数组就是return返回数组里的z函数每次往里面添加的,就是采集环境,类似如下方式:
我给它这个_a函数重写了,方便后面写解码函数(文章最下面)。
const createHashCalculator = () => { const BASE_HASH = 1789537805; let stateFlag = true; const calculateStringHash = (inputStr) => { if (!inputStr) return BASE_HASH; return Array.from(inputStr).reduce((hash, char) => (hash << 5) - hash + char.charCodeAt(0), 0) || BASE_HASH; }; const encodeCharCode = (num) => num > 37 ? 59 + num : num > 11 ? 53 + num : num > 1 ? 46 + num : 50 * num + 45; const bitScrambler = (num) => { num ^= num << 13; num ^= num >> 17; return num ^ (num << 5); }; const createNumberGenerator = (seed, sequence) => { let currentSeed = seed; let counter = -1; let storedValue = null; stateFlag = false; return () => { if (storedValue !== null) { const val = storedValue; storedValue = null; return val; } if (++counter > 2) { currentSeed = bitScrambler(currentSeed); counter = 0; } const result = (currentSeed >> (16 - 8 * counter)) & 255; const finalValue = result ^ (stateFlag ? --sequence : 0); return finalValue; }; }; const hashFunctions = (hashInput, modifier) => { const HASH_CONSTANT = 9959949970; const TIME_CONSTANT = 11027890091; const combinedHash = HASH_CONSTANT ^ calculateStringHash(hashInput) ^ modifier; const timeValue = bitScrambler(bitScrambler(Date.now() >> 3 ^ TIME_CONSTANT)) * HASH_CONSTANT; const numberGenerator = createNumberGenerator(combinedHash, timeValue); const buffer = []; let isFirstEntry = true; let callCount = 0; const trackingSet = new Set(); const utf8Encoder = (str) => { const bytes = []; for (let char of str) { let code = char.charCodeAt(0); if (code < 128) { bytes.push(code); } else if (code < 2048) { bytes.push(0xC0 | (code >> 6), 0x80 | (code & 0x3F)); } else if (code >= 0xD800 && code <= 0xDBFF) { const nextCode = char.charCodeAt(++i); code = 0x10000 + ((code & 0x3FF) << 10) | (nextCode & 0x3FF); bytes.push(0xF0 | (code >> 18), 0x80 | ((code >> 12) & 0x3F), 0x80 | ((code >> 6) & 0x3F), 0x80 | (code & 0x3F)); } else { bytes.push(0xE0 | (code >> 12), 0x80 | ((code >> 6) & 0x3F), 0x80 | (code & 0x3F)); } } return bytes.map(byte => byte ^ numberGenerator()); }; const serializeData = (data) => JSON.stringify(data); const bufferEncoder = (dataBytes) => { let output = []; let sequence = timeValue; for (let i = 0; i < dataBytes.length;) { const byte1 = (--sequence ^ dataBytes[i++]) & 255; const byte2 = (--sequence ^ dataBytes[i++]) & 255; const byte3 = (--sequence ^ dataBytes[i++]) & 255; const combined = (byte1 << 16) | (byte2 << 8) | byte3; output.push( String.fromCharCode(encodeCharCode(combined >> 18 & 63)), String.fromCharCode(encodeCharCode(combined >> 12 & 63)), String.fromCharCode(encodeCharCode(combined >> 6 & 63)), String.fromCharCode(encodeCharCode(combined & 63)) ); } return output.join('').slice(0, dataBytes.length % 3 ? -(3 - dataBytes.length % 3) : undefined); }; const dataRecorder = (key, value) => { if ("string" == typeof key && 0 != key["length"] && (!value || -1 !== ["number", "string", "boolean"]["indexOf"](typeof value))) { const keyStr = serializeData(key); const valueStr = serializeData(value); if (key && valueStr !== undefined && key !== "xt1") { buffer.push(numberGenerator() ^ (buffer.length ? 44 : 123)); buffer.push(...utf8Encoder(keyStr)); buffer.push(58 ^ numberGenerator()); buffer.push(...utf8Encoder(valueStr)); } } }; const trackingRecorder = (key, value) => { if (!trackingSet.has(key)) { trackingSet.add(key); dataRecorder(key, value); } }; const finalEncoder = (identifier) => { const finalGenerator = createNumberGenerator(1809053797 ^ calculateStringHash(identifier), timeValue); dataRecorder("IYH4Id", ++callCount); const finalBuffer = buffer.map(byte => byte ^ finalGenerator()); finalBuffer.push(125 ^ numberGenerator(true) ^ finalGenerator()); return bufferEncoder(finalBuffer); }; return [dataRecorder, trackingRecorder, finalEncoder]; }; return hashFunctions; };
网页的调用方式等同于如下:
知道它怎么采集和编码的,接下来就看它收集了哪些环境,因为点很多就不一一说明了,不知道的过程可以直接在还原的代码里面搜索到,下面放个所有集合,一共224个值:
{ rlGN1e: '1.15.8', //版本 l019SX: 'B5E135E79384', //hsv mMjOSt: '128812fd950325b13452a31bc799247ea975e83bb426a7c5f5088fdaa0d1eb84', //js里固定值 XlxhlR: 'bgra8unorm', //Canvas 纹理格式 navigator["gpu"]["getPreferredCanvasFormat"]() //navigator["gpu"]["wgslLanguageFeatures"] 浏览器支持的 WGSL 语言扩展特性集合 BA1loz: 'packed_4x8_integer_dot_product,unrestricted_pointer_parameters,pointer_composite_access,readonly_and_readwrite_storage_textures', //GPU 硬件厂商信息 UNMASKED_VENDOR_WEBGL hs9u1y: 'Google Inc. (AMD)', //GPU GPU 具体型号 UNMASKED_RENDERER_WEBGL OXYMeX: 'ANGLE (AMD, Radeon RX550/550 Series (0x0000699F) Direct3D11 vs_5_0 ps_5_0, D3D11)', //js里固定值 MeFAWm: 'e99e3b6cfbe52d07f077e1ebdae686a6f4253e0f0044957819129a69822144a7', //检测 Ogg Vorbis 支持性 'audio/ogg; codecs="vorbis"' k4OsZE: 'probably', //检测浏览器是否支持通过 MSE 动态流式传输 Ogg 容器 + Vorbis 音频编码 的媒体内容 JnPm5j: false, //检测mpeg支持特性 "audio/mpeg;" '2TeLsE': 'probably', //检测浏览器是否支持通过 MSE 动态流式传输 mpeg 音频编码 的媒体内容 LUxZZQ: true, //检测 wav 支持性 "audio/wav; codecs=\"1\"" QAOCQT: 'probably', //检测浏览器是否支持通过 MSE 动态流式传输 wav 音频编码 的媒体内容 cxs9vr: false, //检测 x-m4a 支持性 "audio/x-m4a;" IeJHrC: 'maybe', //检测浏览器是否支持通过 MSE 动态流式传输x-m4a 音频编码 的媒体内容 '0acNh5': false, //检测 aac 支持性 "audio/aac;" '6HzvCR': 'probably', //检测浏览器是否支持通过 MSE 动态流式传输 aac 音频编码 的媒体内容 hLYpnB: true, //检测 3gpp 支持性 "audio/3gpp;" Jf0ijG: 'maybe', //检测浏览器是否支持通过 MSE 动态流式传输 3gpp 音频编码 的媒体内容 gYzoYb: false, //检测 flac 支持性 "audio/flac;" y7PLqG: 'probably', //检测浏览器是否支持通过 MSE 动态流式传输 flac 音频编码 的媒体内容 Ww5pEt: false, //检测 mp4 支持性 "audio/mp4;" eBVvVF: 'maybe', //检测浏览器是否支持通过 MSE 动态流式传输 mp4 音频编码 的媒体内容 VtZZDx: false, //检测 mp3 支持性 "audio/mp3;" Rloxhj: 'probably', //检测浏览器是否支持通过 MSE 动态流式传输 mp3 音频编码 的媒体内容 D3w5ij: false, //检测 webm 支持性 "audio/webm;" x4pROF: 'maybe', //检测浏览器是否支持通过 MSE 动态流式传输 webm 音频编码 的媒体内容 '0kdSH5': false, //检测audio.canPlayType方法是否被hook '1XDIcn': false, //网站hook了atan2和cos函数进行一系列操作,检测你是否hook Math.random() HdRxLA: 1.0442171073339803, //本地语音服务(localService: true)的名称(name)组成的数组的长度 '9Dzln2': 6, //所有非本地语音服务(localService: false)的名称(name)组成的数组的长度 Cjqovy: 322, // '4PYckD': 142, //用户系统默认的本地语音 e["default"] && e["localService"], 返回的对象哈希,由于对象无法哈希,固定就行 kegall: 2147483648, //navigator.mimeTypes下的所有type集合 lpChUz: 'application/pdf,text/pdf', //时区 WtKAY3: 'Asia/Shanghai', //dd_testcookie 未开启测试cookie EnXqIM: false, //检测存在的字体在列表中对应的索引(也就是存在哪些字体) L8Z1PY: ',1,2,3,12,16,17,19,22,', //检测列表中的api,window是存在 CyqYRP: '3223aeb6721e0d0917e7928181193ac88dcd62fad5cadfbe7a2b2b473ecf88ee70f018dbdb1a1832e8dc6528387b0745971dbcd82384261e9a4e3f', //navigator.language A6ZXDR: 'zh-CN', //检测是否为Brave 浏览器 SpD7WL: false, //检测IdleDetector api npEQ8d: true, //检测rtt值 L8v7Td: 50, //navigator.vendor 浏览器供应商 wI3WpY: 'Google Inc.', //navigator.buildID firefox才有,其他默认NA B1Ey4A: 'NA', //navigator.mediaDevices 访问用户媒体设备的api 存在defined否则NA Gqzjef: 'defined', //Object["getOwnPropertyDescriptor"](navigator, "platform") //获取描述符 eVvWDW: false, //MutationEvent api废弃的api MF3J16: false, //检测Promise是否存在 '6AO257': true, //WebGLObject api is1NuQ: true, //PressureObserver api EaGuxl: true, //WebSocketStream api CkNdVJ: true, //PermissionStatus api JdL0JL: true, //EyeDropper api rFv6xG: true, //AudioData api c9Qz2q: true, //WritableStreamDefaultController api '3UxElq': true, //CSSCounterStyleRule api iIxjsY: true, //NavigatorUAData api C2RtlB: true, //BarcodeDetector api Bnzrmd: false, //Intl.DisplayNames api Wr5krK: true, //navigator.ContactsManager api ZSJZqe: false, //SVGDiscardElement api iyiEzK: false, //HTMLVideoElement api y1TLKr: true, //navigator.platform rr75L5: 'Win32', //navigator.hardwareConcurrency TOYwLK: 24, //outerHeight k421Xy: 1143, //outerWidth audH6j: 1214, //navigator.userAgent AO1tev: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 Edg/134.0.0.0', //navigator.webdriver '9g1vhI': false, //navigator.maxTouchPoints giKYb8: 0, //navigator.userAgentData.mobile B6zbaa: false, //navigator.languages GyE7Fc: '["zh-CN","en","en-GB","en-US"]', //navigator.deviceMemory z4QBr5: 8, //contentWindow === windoow tIEE41: false, //检测postMessage ox0U7q: false, //检测contentWindow.navigator.webdriver R1zWXA: false, //所有navigator.plugins name iA7AJU: 'PDF Viewer,Chrome PDF Viewer,Chromium PDF Viewer,Microsoft Edge PDF Viewer,WebKit built-in PDF', //!!d["Object"]["getOwnPropertyDescriptor"](d["navigator"], "plugins") sTOcHW: false, //navigator.plugins.length '9aYmiH': 5, //d["navigator"]["plugins"][0]["name"] === d["navigator"]["plugins"][0][0]["enabledPlugin"]["name"] '35KQZ1': true, //d["navigator"]["plugins"][0][0]["enabledPlugin"] === d["navigator"]["plugins"][0] uMROG5: true, //d["navigator"]["plugins"]["item"](859523698994125) === d["navigator"]["plugins"][0] Wyfe6p: false, //d["Object"]["getOwnPropertyDescriptor"](d["navigator"]["__proto__"], "plugins")["get"]["toString"]()["indexOf"]("return") > -1 DlB4do: false, //检测css属性 qXCwSi: '', //spawn api b1VX6H: false, //emit api HyPNnb: false, //Buffer api '1vXVZs': false, //暂时不理解 qMobs2: false, //awesomium api 属于 Awesomium 框架 phCBVr: false, //callPhantom api 属于PhantomJS 无头浏览器 UGnsnN: false, //domAutomation api 仅在通过 --enable-dom-automation 命令行标志启动的 Chrome 实例中可用。 QtMxlt: false, //"__nightmare" ZFcvWk: false, //geb api zCXdQS: false, //window["external"]["toString"]()["indexOf"]("Sequentum")检测测试工具 KF2toD: false, //Date()["getTimezoneOffset"]() hIw8m0: -480, //document.hidden 当前页是否隐藏 OzJEeD: false, //navigator.pdfViewerEnabled 使用内置pdf查看器 '9wGjEk': true, //document.hasFocus() ** C278pj: false, //d["XMLDocument"]["toString"]()["length"] QjRUhc: 40, //window.chrome下的api ZApJbm: ',loadTimes,csi,app', //检测node.process l67t8v: false, //css检测 ** TSkrb0: '10.26,11.58,12.14,12.41,1.52,11.41,10.89,1.28,10.03', //css相关 q9ljsS: '20, 255, 112', //css相关 pKynlM: '12.4077, 0.172083, -0.16224, 0.0148981, 0.00781994, 1.46811, 2.15523, -0.197909, 0.152232, -0.414377, 11.2028, -1.02873, 1.65781, -4.51257, 121.999, -10.2028', //css相关 cfIUDf: '0px', //d["Math"]["max"](d["document"]["documentElement"]["clientWidth"], d["innerWidth"] || 0) e4AaKA: 1190, //d["Math"]["max"](d["document"]["documentElement"]["clientHeight"], d["innerHeight"] || 0) yQ4g2r: 973, //innerWidth omBIRB: 1190, //innerHeight tMY4oa: 973, //screen.availWidth vyIvCD: 2560, //screen.availHeight Sbn9Db: 1400, //screen.width i2S5Mf: 2560, //screen.height p0YxR0: 1440, //screen.colorDepth Rrc9eU: 24, //screen.devicePixelRatio xK0WpT: 1, //screen.orientation.type 屏幕方向 DPlHM8: 'landscape-primary', //检测设备类型 aptr:fine, ahvr:hover // 桌面鼠标设备 // aptr:coarse, ahvr:none // 手机触摸屏 // aptr:none, ahvr:none // 无指针设备(如电视) NSwGn4: 'aptr:fine, ahvr:hover', //网页自生成的ErrorStack 取slice.(0,150)范围 OmKAXf: 'Error\n' + 'at zt (https://geo.captcha-delivery.com/captcha/?initialCid=AHrlqAAAAAMAkt8LC7yeqbUAozUSGQ==&cid=mzP4Psqman0iymNOS~nDH23Z6TJdeq_Wcde2uF9KB33B2qa', //网页自生成的ErrorStack 取slice.(-150)范围 O4m2Jd: 'cale%3Dus_en&hash=2211F522B61E269B869FA6EAFFB5E1&t=fe&s=8945&e=e291a9e1c246f1955a29fbe9fb71cacf53d983f1a0fec5299baf690c6fe9c602&ir=47&dm=dc_ir:547:728', //网页自生成的ErrorStack的hash agryCv: 2930808676, //ErrorStack中所有getScriptHash()拼接的字符串 vAEqKQ: '86fafa3af9c996903dc602b14d69c3aaa1e0103a743494d6265f29a54abc6046', //hardwareConcurrency相关描述符信息 bXMpgJ: false, //platform相关描述符信息 ivxsxT: false, //d["Function"]["prototype"]["toString"] UNeDtu: false, //检测这些函数toString[[d["Math"], "random"], [d["console"], "debug"], [d["String"]["prototype"], "toString"], [d["Object"], "defineProperty"], // [d["String"]["prototype"], "indexOf"], [d["String"]["prototype"], "split"]] np9mF4: '', //vedio["canPlayType"]("video/ogg; codecs=\"theora\"") M8HTSW: '', //["isTypeSupported"]("video/ogg; codecs=\"theora\"") SViURr: false, //["canPlayType"]("video/mp4; codecs=\"avc1.42E01E\"") pb5Hcv: 'probably', //["isTypeSupported"]("video/mp4; codecs=\"avc1.42E01E\"") TdxWyM: true, //["canPlayType"]("video/webm; codecs=\"vp8, vorbis\"") PsRJxw: 'probably', //["isTypeSupported"]("video/webm; codecs=\"vp8, vorbis\"") rlIlRD: true, //["canPlayType"]("video/3gpp;") tClXXs: 'maybe', //["isTypeSupported"]("video/3gpp;") l5C919: false, //["canPlayType"]("video/mpeg;") '8n9MaA': '', //["isTypeSupported"]("video/mpeg") '4MG0Qu': false, //["canPlayType"]("video/quicktime;") NWYHY3: '', //MediaSource["isTypeSupported"]("video/quicktime;") oux4re: false, //["canPlayType"]("video/mp4; codecs=\"av01.0.08M.08\"") ZxxoNA: 'probably', //["isTypeSupported"]("video/mp4; codecs=\"av01.0.08M.08\"") r6cKBn: true, //performance相关内容 cqRf6V: 0, l0qHoe: 0, '9sRwEh': 0, yS56gw: -3.2999999998137355, XNZ3cQ: 90.3999999994412, UoBz5c: 1.5, fitGnW: 172.70000000018626, xyj8lt: 1.7999999998137355, SwXJKd: 400614, TptJsI: 'http/1.1', nauNim: 0, DmNHxl: 'navigation', mPd0Oi: 1.5, VAz4nk: 0, VzXZPs: null, '4VGVmN': 0, '1tKsBH': 0, '0m18lQ': 0, kkgoyM: 0, //自生成ErrorStack的hash JtlE5A: 2150886373, //ErrorStack中所有getScriptHash()拼接的字符串 KdJUrf: '86fafa3af9c996903dc602b14d69c3aaa1e0103a743494d6265f29a54abc6046', //固定 l8UcNo: 7, //通过captchaChallengeSeed计算出来 '3FXRl0': 9, //所有js字符串的拼接后的MD5 XYnvWJ: 'c7c0af505f99f5b3dc5573ed2286ef16', //arguments["callee"]["caller"]["toString"]()上一层调用者["substring"](0, 150)的btoa lzepR4: 'ZnVuY3Rpb24gc2MoYSxiLGMsZCxlKXt2YXIgZj1BLmNyZWF0ZUVsZW1lbnQoInNjcmlwdCIpO3JjKGYsZCxwYyk7Zi50eXBlPSJ0ZXh0L2phdmFzY3JpcHQiO2YuYXN5bmM9ZCYmZC5hc3luYz09PSExPyExOiEwO3ZhciBnO2c9SmIoZWMoYSkpO2Yuc3JjPUtiKGcp', //网页自生成ErroorStack信息的["substring"](d["length"] - 150)的btoa '70IV9F': 'OUIyUDo3MDQ6MjczKQphdCB1QyAoaHR0cHM6Ly93d3cuZ29vZ2xldGFnbWFuYWdlci5jb20vZ3RtLmpzP2lkPUdUTS1XMzlCMlA6NzA3OjkpCmF0IHdDIChodHRwczovL3d3dy5nb29nbGV0YWdtYW5hZ2VyLmNvbS9ndG0uanM/aWQ9R1RNLVczOUIyUDo3MDg6NDQp', //检测navigator下的相关权限 zbTqfc: 'denied', //User-Agent Client Hints API 收集高熵值的设备信息 Aof5bd: 'x86,64,false,,Windows,10.0.0,134.0.3124.93,false', //getLayoutMap QtTjaj: 'Err: Z2V0TGF5b3V0TWFwKCkgbXVzdCBiZSBjYWxsZWQgZnJvbSBhIHRvcC1sZXZlbCBicm93c2luZyBjb250ZXh0IG9yIGFsbG93ZWQgYnkgdGhlIHBlcm1pc3Npb24gcG9saWN5Lg==', //电池信息charging gNAgnZ: true, //level '1ALbuP': 1, //chargingTime PyMRmr: 0, //dischargingTime KHlZXr: null, //webGPU api a5tudI: 'float32-blendable,depth32float-stencil8,rg11b10ufloat-renderable,bgra8unorm-storage,depth-clip-control,texture-compression-bc,dual-source-blending,timestamp-query,clip-distances,float32-filterable,indirect-first-instance,subgroups','3KvNTK': '{"vendor":"amd","architecture":"gcn-4","device":"","description":"","subgroupMinSize":64,"subgroupMaxSize":128}', wvJ9nf: 'mbs: 2147483648, msbbs: 2147483644', jAL1eW: 'getAd: 421.3999999994412, adFt: 423.59999999962747', //onmessage HFjGFC: false, //canvas sha256指纹 LNuBa6: '0767924f186f5ae9ffbdbb0d1090ed699a3bc55da5378e0de2e8879cd6d18290', //结合渲染时序差异和Canvas 图像特征生成唯一指纹 Z9lxa6: '482ad6b6d6b1091b277aa7427ace822c', //document["hasFocus"]() JlMyqP: true, //下面这些是鼠标移动的一些信息 '7hzUcq': 0, LDQmmh: 209, '4jeuLi': 1, bWDPfF: 17, DPvvWX: 570.0648148148148, LE7z5h: 6.949917596298248, Ot1PTl: 306.14684999710676, '8Ieh1m': 146.86259647144698, dkcgOc: 303.29445304302504, kDKMrv: 145.85277630974446, AEGcWc: 28.02335134347105, aZc9Et: 35.3362445802932, Z6jV0h: 0.9805052117240357, '73j8GS': 1.2213067720551554, ucUPVC: 7.790601401429956, tEwSJY: 12.302058018610222, Jsyj9U: 631.2383415435096, SEz4Km: 108, rbU3Ta: 0, zL3bAO: 225, Ppv1kt: 2.0833333333333335, '6fKI1g': 84, iAhQPL: 33898917948, WvONf4: 1, tN7BSR: 0.20886451932689534, IrY7O3: 0.4057791551146873, fZDvYp: 2.1154032584098563, HCKikW: 3.774758283725532e-15, EO4LOC: 368.9967511543552, EEXEPq: 0, tZYpL8: 0, dOni5N: false, eLNlqE: '28,14', Z4lWhe: false, dSXU9E: 'puzzle', w95AAt: 190, FxMDfj: false, AnKOM2: false, XPt5Jo: false, WqpV1h: false, srLx6X: 1611733337, adAmm9: 0, nHA4Vm: 0, //几次提交验证 IYH4Id: 1 }
先分析下恶心的devtool检测,看下面这段代码你会想到什么,当执行
console.debug(l)
时,控制台输出Error
对象,默认会格式化显示完整的错误堆栈信息,为了实现这一点,控制台内部会隐式调用stack
属性的 getter 来获取堆栈字符串。起始的M = t,当调用console.debug(l)如果没有打开控制台他就不会执行M += a,所有最后返回false;如果打开控制台执行M += a;那么就会执行完t + a != M && (g = true),最终返回true,被检测出打开devtool.var d = window; function Aa(e) { if (!(void 0 !== e)) { throw new d["ReferenceError"]("this hasn't been initialised - super() hasn't been called"); } return e; } function Da(e) { return Da = d["Object"]["setPrototypeOf"] ? d["Object"]["getPrototypeOf"]["bind"]() : function (e) { return e["__proto__"] || d["Object"]["getPrototypeOf"](e); }, Da(e); } function Oa(e, t, a) { return t = Da(t), function (e, t) { if (!(!t || "object" != typeof t && "function" != typeof t)) { return t; } if (void 0 !== t) { throw new d["TypeError"]("Derived constructors may only return object or undefined"); } return Aa(e); }(e, function () { if ("undefined" == typeof d["Reflect"] || !d["Reflect"]["construct"]) { return !1; } if (d["Reflect"]["construct"]["sham"]) { return !1; } if ( !("function" != typeof d["Proxy"])) { return !0; } try { return !d["Boolean"]["prototype"]["valueOf"]["call"](d["Reflect"]["construct"](d["Boolean"], [], function () { })); } catch (e) { return !1; } }() ? d["Reflect"]["construct"](t, a || [], Da(e)["constructor"]) : t["apply"](e, a)); } function Ia(e, t) { return Ia = !d["Object"]["setPrototypeOf"] ? function (e, t) { e["__proto__"] = t; return e; } : d["Object"]["setPrototypeOf"]["bind"](), Ia(e, t); } (function (e) { if (!d["chrome"]) { return !1; } var t = 1000 * e["Math"]["random"]() | 0, a = 1000 * e["Math"]["random"]() | 0, M = t, g = !1; var l = new (function (e) { function t(e) { var a; return function (e, t) { if (!(true && e instanceof t)) { throw new d["TypeError"]("Cannot call a class as a function"); } }(this, t), (a = Oa(this, t, [e]))["name"] = " ", a; } return function (e, t) { if ("function" != typeof t && null !== t) { throw new d["TypeError"]("Super expression must either be null or a function"); } e["prototype"] = d["Object"]["create"](t && t["prototype"], { constructor: { value: e, writable: !0, configurable: !0 } }), d["Object"]["defineProperty"](e, "prototype", { writable: !1 }), t && Ia(e, t); }(t, e), function (e) { d["Object"]["defineProperty"](e, "prototype", { writable: !1 }); return e; }(t); }(e["Error"]))(); e["Object"]["defineProperty"](l, "stack", { configurable: !1, enumerable: !1, get: function () { M += a; return ""; } }), e["console"]["debug"](l), l["stack"], 0, t + a != M && (g = true); return g; })(window);
补一个解码函数,要注意每个网站的tParam参数不一样。
function reverseD(charCode) { if (charCode >= 97 && charCode <= 122) { // a-z (处理D(e) >37的情况) return charCode - 59; } else if (charCode >= 65 && charCode <= 90) { // A-Z (处理D(e) >11的情况) return charCode - 53; } else if (charCode >= 48 && charCode <= 57) { // 0-9 (处理D(e) >1的情况) return charCode - 46; } else if (charCode === 45) { // '-' (对应e=0) return 0; } else if (charCode === 95) { // '_' (对应e=1) return 1; } else if (charCode === 0) { // 处理填充空字符 return 0; // 直接返回0避免报错 } else { throw new Error(`Invalid character code: ${charCode}`); } } function decodeP(encodedStr) { let byteArray = []; let len = encodedStr.length; // 计算有效字符组数(去掉可能的填充) const fullGroups = Math.floor(len / 4); const totalProcessed = fullGroups * 4; for (let i = 0; i < totalProcessed; i += 4) { let chars = encodedStr.slice(i, i + 4); let e1 = reverseD(chars[0].charCodeAt(0)); let e2 = reverseD(chars[1].charCodeAt(0)); let e3 = reverseD(chars[2].charCodeAt(0)); let e4 = reverseD(chars[3].charCodeAt(0)); let combined = (e1 << 18) | (e2 << 12) | (e3 << 6) | e4; byteArray.push((combined >> 16) & 0xFF); byteArray.push((combined >> 8) & 0xFF); byteArray.push(combined & 0xFF); } // 处理剩余字符(不进行填充) if (len > totalProcessed) { const remaining = encodedStr.slice(totalProcessed); let combined = 0; for (let i = 0; i < remaining.length; i++) { const val = reverseD(remaining[i].charCodeAt(0)); combined |= val << (18 - 6 * i); } // 根据剩余字符数量生成字节 if (remaining.length >= 2) { byteArray.push((combined >> 16) & 0xFF); if (remaining.length >= 3) { byteArray.push((combined >> 8) & 0xFF); if (remaining.length >= 4) { byteArray.push(combined & 0xFF); } } } } return byteArray; } function h(e) { let hash = 0; for (let i = 0; i < e.length; i++) { hash = (hash << 5) - hash + e.charCodeAt(i); hash |= 0; } return hash === 0 ? 1789537805 : hash; } function I(e) { e ^= e << 13; e ^= e >> 17; e ^= e << 5; return e; } function createF(seedO, seedS) { let a = seedO; let n = -1; let c = seedS; let isFirst = true; let hold = null; return function () { if (hold !== null) { let t = hold; hold = null; return t; } else { n++; if (n > 2) { a = I(a); n = 0; } let t = (a >> 16 - 8 * n) & 0xFF; if (isFirst) { c--; t ^= c; isFirst = false; } t &= 0xFF; return t; } }; } function decode(encrypted, eArg, ddmHash, tParam) { const a = decodeP(encrypted); // Calculate s const shiftedTimestamp = Math.floor(Date.now() / 8); let sValue = shiftedTimestamp ^ 11027890091; sValue = I(sValue); sValue = I(sValue); sValue *= 9959949970; // Generate t generator const hEArg = h(eArg); const tSeedO = 1809053797 ^ hEArg; const tGen = createF(tSeedO, sValue); // Recover M array const M = a.map(byte => byte ^ tGen()); // Generate u generator const hDdmHash = h(ddmHash); const uSeedO = 9959949970 ^ hDdmHash ^ tParam; const uGen = createF(uSeedO, sValue); // Recover JSON bytes const jsonBytes = M.map(byte => byte ^ uGen()); // Convert bytes to string let jsonStr = ''; for (let byte of jsonBytes) { jsonStr += String.fromCharCode(byte); } try { return JSON.parse(jsonStr); } catch (e) { return jsonStr; } } // 示例用法 const ddm = { "cid": "Syi4QUflgxTCR0AMUXuxCGU70ubyOisCUZe4P~DFFiqYJcsgkg7~mrXMCENpiTdvMhfXvBT5s4PygrHIL~J5IWd9tqKaYnKYMV2B49_er1e7MyZpuoP7fcBI0oyoOFMT", "hash": "2211F522B61E269B869FA6EAFFB5E1", "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 Edg/134.0.0.0", "s": "8945", "noPuzzle": false, "sdkMsgFormat": "", "userEnv": "9ac8d833cb3ef0913478bbb1f34dbdf554e942cdffe79d65743cc45bdc878253", "referer": "http://bck.hermes.com/homepage?locale=us_en" }; const encrypted = 'y4jVgVdgCxDQiQJ6qS0OeeGgZlxfC7fV-pu2JDZfxYyu9B6vZltDAN6sVLv-3A_QKNUYfGOyulQo4eTqn9JdRjLTlpqFxXXqk5TyOumvXcaVmKBOecgTq7pa8IaKpV4_Sm33EmXJPe1-vIzyGp3t6xCxsXKWHzHB5i3x4VgYG9GLL67t1sOl5ZABPNSgVX00I-Qo_reXAWn3SYb1ZEIosi05oOfgBpZ626T0rs5Wb9OFRJD21CqEcUH0SogtczKjGr3gwZpygqfmVZlUBFJOiItKqIz7GKgkiDsgvgp88bjgXnsyMP21SSBL_7FrrCApl-0-G6e4Y3eqF77VCJkdETcfBEwvaAcFtFhlkduP_4GGYJyyH_xkuvS-StDvvwcot1Vvk6kLZXGkWwWLhBOGjXbMqC54CNLNRYkP57fkWwySF_UAsZVBxRDVFNQg6N6YIm_jArwJwhfWGhkfSyc4Px62lF9HvzrjbqBTglhCVN47LLe0JK8U9iVkC5AHPgAZ5tPJv1NhMVHi9lMXdst5WHTQzXtucV7qvD7eHHVFep6U1qLsHOQbHOgUJymMgI2putGdEgBdAN3ecCLg606gjzYII7aJHYH0h2vb6UZ19RsQB_CuUiqlzDdomZ6fMOatAfaVtsdaVK7LpjB_ZOcxkXvOJZyIT-T16Q_qqlNbXW-s_B_kRqs5e3lbAB86-uyLuaIkfPzSirE60SHkVp23M63fJBjmuqVRUTfsqpwuyFjV0_KdRE6Y2nq3Kv4lavaWTvGqR9_2hlEGUhYb7OclCqhzO2OmGvZTpXbmcfu1_o6EbpzD5IBJLQa5Tf4gteUFlSx1DzgeuCORqY_6MUbb0PvXcd32xtE2eegZc_q-s3nweM3bEXJb7jazgedFrRs--_oQ_KHlOugedt2hdYLr07tRxGY4rcbYTt_NyqZJzvaC9qFJYJHk7wSfINP7Z0GzaZ4610HSOTmBeyFkklW4A7S7Oj_qld3xu9cUC_ZBvMDpLH1yRCCS23MRQ-lO328DwYAJMnvYzBBSLn0TwH5VteEsXoxzIWBkWgsSGc5ZzXcJ3qu7eSOJJE8sHvQD6CycEtWXeqwOnTeIhGxKpdzVNp6eLLfDjU8ojOkE0RO3TCtECI1dngAijfeMiBNl0gwKiO1p63jobSK3cpd2UihKOSJAYu2l7UEwY5xG2ICpls2e6UvJQgd-Z5fM5C9shH86N-8ZdwC-6fAEEGHDj7Ybvfd-BQJxqrzvrHMybKZOmh8MyNbrqzh9of1FEWrd7gMSSWNBKdBUrtGSzdl_pLHCfxvHZ0mcbqixyp5sCam4KBVsjGrcOVRLRC06VIXgOezit-7hEKwhROgKTTMBWj8O2ApK32ogA7abYNTidB4DANR7D6dj6utJskeUh7xU11CGnsdiIVIsxtysTY26-Qc16d9wuwXq2XFVBmo2ZOsDFe38uRV4OTu3rDv0k09zIT346CU8ocA3OF0DtYWvv410j3OipoRX-p0qfJVOLaQ5OtoGTDDl-874CO52e1pLOUyCAkfRvwvkqD5eD2-ypoAUgP3VWJQEX2uKZrQ0UXrOlARne-oYxt32lnk6qLW-i0wq0uIePkDO3EbNVj_TzNovP6lk9Fs8JYY8HS242ZpidzEWhk4IJn8F4f24VzApFUYiKLrvmbaXB0wzJtnugBZs97cfQ7yf3CpthfuK1zEi2uwdrAj4-TSukuiejsA8qTBW2YGQbmOeyk96IG9jir2toxoaek2FW2xojw9XJ6_OvXKC2DgzXqTWNPEy9NGcC6n3HQdRulKsokSBxtE23q7I27uuW7q9Brpi4pB6mybYXDBPz2SfBvq8OeAlPWBqQwF7kRrkN9DEfP7t6ShfqAayrtWra0J4BkmJ9XZISNk4KxIKHH-CDAgjLXWzMovj-GYjsScHYFJ58h7f_t_qRDQYsBi3t8_mDvV2siUYz0kpFeCilUWwn_qIN4fI3dCs3e76Ph07AKTAjwmAk3Nq5W35Nt56NkKE3iUUDdW2Hq20m1TEKNVvNiNTvbK2ipvP4sJBh3h7bnwe8jU5J0wqcoNGZiLEOFDiTuiuUkM-Q1-_O08mNCN0aQ8d6vUNnY5HshprbtOe65NuJ4IL4Wrc1HBHWjH9fs1hkREsZf5ePVdTtl1uDxXq8xFFLYO0T97mbFaTl0QCyETzK8Ozl4g3z8MaYjFUY1SvoKc-9PsbAY7NFpNnhi9iAvx5bvc6nWiefJ7AIUzObW9yHWwW8O_yx6G8FdfGjKd-8UzyXecmdVGVzA93kMyWQC_xTMNUgeTwC-3n-SZAwLZa920H2OxMxpQp486RcQ7pK8TlKefGiYUfd3KfTfN_WNF-gJuADOIut8scSUnft3kQ881bHNpkL54TqvhZFgIkFwQvwM8Naff-guc1Z2ttS4YM95J6qjXUneDoX_dDMjyObAjsQbfpkdvfr55dZjtoUZ611Nu_OlytGbJevVl8KW65cMmyCNk25X0Bub0M0TsFer4KxTb9Tn0WgWfTj4YtTJJvC3Pc15yCWUOvFUf04y6Uk3f8VGNlws-EvK13XnEF2s8vUDlPTebTFkH9Ib3tmaRfmzIUr2yEU-4DAehVucjIiaNLlJs6uZgcJVfGNFs2udNFbUzsy-GSQcpbXt5I75q6LjFkyepVmsN3oCzXll0n6AnwBJQQ5F3g5LYYlC-RHlgLmwmmYdp8DQNZ-lkBODP4bhcTNidoeWNg96tdxql1HLNg8EvzFDvemoqnCs27x07g8G8rf3oedIIgTPxXPckISaJDmLg2OCAFTprquiSLc9jb27D3hpNo9Tow2HFUCQrj0w1kRD8HOqG97GgvEXR8aev4yKUxxJu-t1HocFIsjyYz7gA7PlZcPaNDzVJXDsYn3qw2qmmRBhOzEdl93PxFKd1iT4JjXiR_9ch86Z7gccqRsESrVwIOAROf9YrIaghjlGaKM9JfzSdbEIy5PtlLh9BmPGRwxYK5Iflan6As6-mkkcm7mO-XJx7zrdBFzkZu6TtuPOidn10a56yD2SZn57_P7Wmq7DAk_HhxhIMqhFiejemAQcPwLglG9QnaEVNpsl6UaY7ZCe_5HpPoq7D-tVxYruw6kWXiCS2DKOEaKTOWn_u8qciimuLPVZPSu04fQ84_qUFac2X7a3rGi3U4IMle_9S-Wiqz5Nf_yO17EMDkXrB80rDOR8l2BnnT5YaCbBzw7UBxDZT88UAvGW56JmQI98ZiDtnmILynzjGAk7uSIekFsNKtS--Na673YO56Yd-01KAGx6gxNi0XsTpT76snrqBDFvQjctt3v5PYzVry2dmsecNf2uWUze5eMKGpsOIxMNCN775bH_Bbm2e0c4CCdo-W9rGT70Plxx5gI8XCddQj-5D505zKyd0jEyMfl1ON6JjEL9BvtMJTjNaXgxLHhdyYK82YaBLkowgA8EfrsLxeAW8a8C7HhtzVhXTLX4xNIHRv7itZRBqGScrrrXtGAFkl7HmwOwJKqu3ZArT4hr1xNMg9xycDyXCDFQ3ExhmP1h1LSqxC6vEUAfiqondV5bJIr94smMVGWLjP11eg90RGzyLew5jTQ8umOMA5HJqr5UgyTtjxENKMXKcItEGes2vsW52qjqN2dxEKthN7XLiM3hRJ3z4IKM92G3xWgVh1LUS8naGhs6z6F2QyyEo6OjJRzVQEVNnBnPGQfpjXtj5gJK9ZFtPovEQ5UtiLah_gqo5J0oZmFwS6ODzr6uL0QJzinaEw1KQAE1qBvGZI9HYYL7lRLeaH59OZ5zNPfCPi-Yzf4FWjNjaMGhQykXN6SIk7ILiubtfswOZxTRnU4UNfy1HzmSZkAho87a7SX6YmBBeB4DcGxF3XDYWYZ6rH8YKanJErjLfmL_GSfEZMhNjMN0Wca0tPdVviFms-jicg8ddw1iP3z4kgaCBmdwlL1iV_Z0YHoiZvWW25wC_ibsDfSm8ZWH7CwEk7I1ZK9R9noFQ8-VcM6De3p82DOH1M9ccakJOLV92RqLsBNKh4TI7zS6AaqTSII5DhztCh8ER-limcvYxaP-FkzvQBrSPBx1PXveJZzoAUGWVP3ztgJZ0fr2lQedTwCzzbPHSAJVowoEd2fr_9Y1CgxSfodYJrpt6kpD70GEqh6Q6mK6eLDV_NEU1gAI8yKI4mf5DpfP2KlhjrpiLrtwR1YBPuO3tzDwI8v_aYNaPGQ7mEM1D1S1W7owFUmlo8Yb6yhhLKtGtqo4p-AJjeBDpOsEGaw6dsIIkDnHXd-4MX8bR58gosIk1ThyLCFHGCNYAPw21VnaDjSByp-TOqrFR7tSGFDCrPjoXRDk0ZdyZ5Ex6WhBrD6osCbwyrSSD2y88B_TGUvaJgos94F6nZVwoEQFAKP3upG0cAnGmlJWNaHm-gAiYfM0zzaAmG9QHH__aAOdtuH_O6cp5SczlZkOXsWFKfLmEalJD8gJ7nA2uQ9RTgYkHIwtwhUvZNXUA9993u7OkwrEjDTsqtAFP1MvFwUCo5oruyoB9tL9OFm1pHvlWzLuLDO_rt4DejOi6ze_7iUFRF0J2i3Ppcpw7qRgYnqeVsXdezRGqip-IihZ_A_o_DLzL7m66nr0qCBZUbwBhhhcjhXjUC9VBbM33Y_ol2AXqV3HOuL1lRqp48cQtMKPKYnBrmSGDcpvaXYRE8gKPQUWEkUiDNMJy58ZFc7zdagN3DmESS9FZ4dZWHV9AqjmHh96MeMaG6vXW7xl7nrpm6XmfcpF_Yle9rAiOzWqRP0FYsEx6yQINmPj-KgiRgdpp84EfjgP59D7EplBtkwOQg5sm_I56owqPYmqsO5Ri7Lxi1pzk6W_hOWEsTz6wYUtCrvVEHDGTDDTisJMKP2AzLmDVU30ltG5E7cYgMZdvazlj5OIwMauxoC8L8IW-rWSWgDLouWv6c8yg29QfcE9jNoWn7lk6xNEBoViusJsgsN5c8wS0aqyi-3PtMbGuph0G4nJHxZuo4io36XTct-xP7NKDjtq3w7AkclRoKw7l61I6PkvAs6v1l_Efr_UUf8Tvvp7_M0uMdP03tzmjz2-A-q-YetBqyeQC5G4MS32us4XRF8Vsl5rD-GpS7upShEHIAcZkuNTRGAFqs4GSsyDsMj4EjIa96mKEd_HH9e3wLgtw0YPqbWb3T2IT7uG9PR6hzibgTTLCUSFKvCLs4713SD26x449YjxQ-Q-Z-psyNrv4eT3sAp3uJJA79DChZrUitiqRTIUTrFVY2DRmod8Hu6JDhVFabw1jh6DEyDahOtdaEkcxiy24yBFfNHarCpCcoA6-S0xCeVak5z816GSHFvGUaHoVTAophkF90FaiYPncxcLLZjazbDsF9GdP2Ugrx2CSnn7HYDS_KM7M7Hjlmjsvo2xO20dJGkiv9xqB2DRSMl9xOJpFmV2xGfMNZNKDS9uFUcZLFKz-7IslJXVLTz2-QUiZqxkWzt0ujYv2BlPGEdoDO6DPs6ugkUNAktZHwG5Zuplw1YO8HQXRhyXjAf7f75LrVmvzxXxLegFUWT9998U1bTQxp3ds-bmOJJg68nSHUOK1byqIO9DHo6SYueXZ4ZHopzJGXThSi_TTAJP9Hf-y_JJc9yrNdOZC6STA6yzIScmH4chbo-QjNwbVp1mmcUH3oKketF7ur0Ev55nGh6SBfCWJgPT9Dz4NV0Wyl5Z3iQK--gmDwhxdIhuXbN0c3EnUrTQmEeWsXnBEWhal0CaIPhB2Fvv5pvP6Es4f-q5Pe6ByfA1fNvbE64MVsK5uc3NFDurnpQY5cqqPfbrqWp-Ym_YN_JcMdFpdJKAZ7nAxFNy-dX6-JxhBvsawsJsc6cn47FGhH0IWkBseesaMBwyyT1ue8rhLQzs_rC1b9SUNiJLaWSyPzTO-8nO0XLQMJMBEOH_-x2L1G8G04D1dKrTakb0FO2I9fZ6G-ajGyyADuh1Ov-Ue87TcwfQSta1U66PmHk7HjMBV60_IjwU4WiuMwluIejUuAu2oDeJ87U2C64T8l9luZDDzbe1nFPEo_x5JmGPD6JqSn8Vv_eS6OkoOGwIEjURnyfnUeNCqN0YgqR7kd-rTPetyJtBNbeyFzmQgmzXFD4wIiheuWjYCJnsY_v7azfqZwUwtSvFaGIH7ya5-_QayxEqg-N1xVW6ypU2186kQ6Hv2LDkPcZbAPPI0Wm8_mC9yP65o4Mny-xBrDVCFCypRcUCWJCWunO1whTG5S4TR2QWrtZp46MHHeCCD5eYIQtu7-yaD-gS0vCxQVRl-4qQK7K-f_yjXwjw1K8IXM8IBQ6cE217MxtS1yT1W1iq3qPI7Lw679Jhi3JKxRxBYKWijnnOVzPT_ycT7EujninapCRIfdhB5xgnQ04JbpFxVGZxfBPaAfPEjvGuUHT7_JfMMX5eBrnrV5SOjRUwPayDVmqBSu0c1SjMX0t51fgwORlcmosOaJ7u6hK6I9eL1leFF9Ih1JAw6c3kKnyMVdaSXfFncqbBavpoDakvD6MFu0BOrEE3jGbAx-KInwbkufTtTUdBVR_CRGjAEA-MAYSG0RW7InMBJeABQ1FRTsWCAdbHuWFb-lL-srKvSsfOYdJy8mwrEByf9LPC75QB_h2VsFNbc_rI6dWVjFBJjmujf90rnSYzz7tFnyUfbQGCK0UmwMalnTN7QGS2zhxMR4jBxVU2ofqHKVYk2-l9kAuDEESRevZTW6fAvppsUogQVmEJRQOr78-TwHk3mR4Or-xgTS-XE-J6Dld00OecX7YNbCYM_EIB9rDLjuLiCpjglb_27qzWdQhLPNPnRoc_lIG9e9V-dPjLcT2J8rjQ9tg_hYICIpQK0pUDVxG-FTIvcrmm0hkmQ2Y2Blk_w1ibRpZcsNStp_6yZQX5vAFVCQ8tBvEPW5p_z6ewwdkOEgyWdB-g7RT9IBpInXEAtJElgf8LsgBvi9h0YrOfDZZLEKPsqcOWZEiBswEuwBpmwAiDG1oydejz30VprAbmZGDAPXt9Fk1K5IdBX0x3ecWYyuTzx-m7TKrvv0BG35N47u6pwb7tkJGTzvN58KAS8pF-6tBNx2IlyEmBAL8bKSFhEgcF0xRa6cafQZyX7CrUDdE7HkuSHo_cHXNXZex3Vo6AtlIhREAB61WSNpOQwKH6UQJJY5YiBcAdabA98EahX8gw0INsFjursJLsjZmt2XNUYvQ3qqvAxGk72uBr4atdEUYb9ZfmVhrvAKFoifO3gxDEo8EMAo5I9zccBZh423_to3drw5pcX29-dmdoGqh-emO37iGQXY7gSYMuaRroD0yPCJrQ_iB33VL89nek0IuyiznluQC_tQkjoOhT-ZSwmGB0nwyJ5Tyj8tzT_WbAKhEoz2EcKqVbX4Uy48V4zynk-yWFKPdQA7z6HT4I8T9ktC8KzLJQy5Gh2HhjQcxagqz3lEwc31GzKp4WgKYmZ7e1xvv4rh3PAhRV6R2oF0sjWQoLakMz1UYyty7Es5ZoFUSO8XmAZeMMhOw6YjZ9B2n_Owx0sV3_cuSjOq_awx-a3mUlT_Fty4h4ibjqql_UGaM-2URyXcsTVzEeSiwlXzGS1GKvw2-IumQxSdJhjKYqNW8zH2ehglc9dyOup6Kvm1FEhbk3SF3_wiM36lsogoHqYANA9NeJ0Uch5QRgVJzSF6Dtukhi1W-rLxhEN-jx2QmdSY6NHZou54X8H6e1Go6k7KWpz0FI6Y0GHNaOjnqFxvowUoYtP8U7xjvjsEw-fXy8IU2Kk7K2zDW2ravLffmgm'; // 示例中的encrypted变量值 const tParam = -1748112727; const decoded = decode(encrypted, ddm.cid, ddm.hash, tParam); //224 console.log(decoded); console.log(Object.keys(decoded).length)
总结一下里面涉及不少hook检测,devtool检测和自动化检测,在新建的iframe创建contetnWindow拿来做环境检测是一个很不错的思路,以及生成两次指纹对比,看是不是随机了指纹,还有部分不理解的地方,有兴趣可以交流交流。