闲来无事了看了下之前搞得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拿来做环境检测是一个很不错的思路,以及生成两次指纹对比,看是不是随机了指纹,还有部分不理解的地方,有兴趣可以交流交流。