cloudflare-turnstile学习及了解

# 总纲的全流程

## 基本说明
- **同一个网站两个页面隔离加密**  
- **基于 `postMessage` 信息互通**

---

## 页面与链接说明
```text
x => 第一个url `https://${host}/cdn-cgi/challenge-platform/h/${cFPWv}/orchestrate/chl_page/v1?ray=${cRay}`

y => 第二个url `https://challenges.cloudflare.com/cdn-cgi/challenge-platform/h/${cFPWv}/turnstile/if/ov2/av0/rcv/${[a-z0-9]{5}}/${sitekey}/[light|dark]/fbE/new/normal/auto/`

工具网站地址

- cheerio
https://github.com/cheeriojs/cheerio

- qxVm
https://github.com/ylw00/qxVm

- jsbin
https://jsbin.com

- spidertools
https://www.spidertools.cn

详细流程

x1 基本流程 主要获得一个关键数据 chlApiChlPageData
y1 一个生成点击的挑战页面 几乎所有算法都在这个 现在为止好像有 30多个环境加密
y2 这个主要用于监控了点击按钮控件 点击后又会在上面y1的基础上面加一个环境加密
y3 这个里面监控点击是不是通过了 通过了就会用 postMessage发送成功的token数据 否则失败重试
x2 接收了y3的token 然后加上x1的数据一起发送出去了

里面用到的加密有三个

// - 1 标准的sha256
// - 2 魔改的LZW压缩算法
// - 3 TextEncoder

// 25/03/14
// 这里提供一个解TextEncoder的算法
/*
- str 通过了TextEncoder加密的参数
- cRay 自带的参数
- salt 额外的 需要找到的参数 现在是这个`CehQMPOSTvpkdJAMYyuyZdDdYBUjqVRu`会改动的哦
*/
function decode(str, cRay, salt) {
    try {
        const key = cRay + salt; // 之前这个调换了位置
        const keyLen = key.length;
        const decodedStr = atob(str);
        const decryptedArr = [];

        for (let idx = 0; idx < decodedStr.length; idx++) {
            decryptedArr.push(decodedStr.charCodeAt(idx) ^ key.charCodeAt(idx % keyLen));
        }

        const textDecoder = new TextDecoder();
        const payloadStr = textDecoder.decode(new Uint8Array(decryptedArr));
        const payloadArr = JSON.parse(payloadStr);

        return payloadArr;
    } catch (error) {
        console.error(error.message)
    }
}

自动化要点

如果是自动化情况下,只需要把 screenXscreenY 修改成不要和 x 和 y 一样的就可以了。

  • 不推荐修改浏览器的 navigator.userAgent
    因为里面还有一个可以检测版本的,这是一个异步的 navigator.userAgentData.getHighEntropyValues

  • 其实它肯定也检测了开发工具,但也能让过。因为里面有时间检测、其他检测,不需要特别管这个的。

  • 在网页上动态调试非常困难,也非常不现实,检测点通过了 console.debug 检测及 debugger,也称 cdp 检测


调试相关与检测逻辑

常见 CDP 检测示例

// 代码 www.browserscan.net/bot-detection 从这个网站的扣过来的
function testCDP() {
    const error = new window["Error"];
    let res = false;
    return window["Object"]["defineProperty"](error, "stack", {
        configurable: false,
        enumerable: false,
        get: function(){
            res = true
            return "";
        }
    }),
    window["console"]["debug"](error),
    res
}

CF CDP检测代码示例

// 这个代码是cf里面的检测 但有点不一致 但是他的用意就是如此
let index = 0;
function callback(number) {
    index++;
    if(number === 1){
        return "yuyu"
    }
    if(number === 2){
        return "kkgg"
    }
    if(number === 3){
        return "qwqw"
    }
    return "3333333333333"
}

self = {
    g: 99999,
    h: [...[]]
}

// window = globalThis;
function run(key, error, ...args) {
    let p1 = "Object";
    let p2 = window[p1];
    let p3 = "defineProperty";
    let p4 = key;
    let p5 = {};
    let p6 = "get";
    let p7 = callback.bind(self, ...args);
    let p8 = "configurable";
    let p9 = ![];
    let p10 = "enumerable";
    let p11 = ![];
    let p12 = { [p6]: p7, [p8]: p9, [p10]: p11 }; // <= p5
    let p13 = p2[p3]["apply"](p2, [error, p4, p12]);
    return p13;
}

let str = "Error";
let obj = window[str];
let error = new obj();

run("name", error, 1);
run("message", error, 2);
run("stack", error, 3);

console.log("------");
console.log(index);
//console.log(String(error));
console.debug.apply(console, ["%c%d", "font-size:0;color:transparent", error])
//console.log(error)
console.log(index);
console.log("------");

说明
它不只是这个 debug 检测,基本把 console 里所有方法都执行了一遍。
现在只是记录了,但没有拒绝访问,也可能会让 cf_clearance 提前过期。


Debugger 检测

如果 CF 以后检测更严格,不让过了,个人觉得在开启开发者工具时想通过只能编译源码。因为里面还检测了 debugger

// 原理是
// 他里面同时调用了这两个 他们的含义就是分路走
// 如果没有打开开发者工具 那么他就会走第一个
// 如果打开了开发者工具那么他就会走第二个
eval("debugger"); postMessage({ bxPc6:"SLDXK7" });
setTimeout(function(){ self.postMessage({ oyMo4:"1"}) },1500)
  • 后端会看 console.debug 是否打印。
    • 没有打印 -> 走第一个流程
    • 打印了 -> 走第二个流程
    • 如果对不上就不让过

发现的原因
是因为我用 Chromium 测试的,之前就把 debugger 在源码层去掉了,本来是为了过其他网站的检验,正好发现了 CF 也检测这个。

处理方法举例

  1. 去源码中把 debugger 删除,不要断下来。
  2. console.debug 不打印数据。
  3. 其他的可能性就看实际遇到情况了。

补环境要点

下面说的就是 补环境,只说困难的部分。

  • 24 年才 10 多个加密层,现在已经 30 多个了 哭死,可能以后更多。
  • 也说明了 自动化 是王道。

具体难点:

  1. 以下对象必须补全:
    windowdocumentnavigatorscreenscreen.orientation
  2. 异常处理必须要优化:
    Error
  3. 页面上面的 style
    • 需要提取,因为里面会用到,然后再做一个 sha256 加密
    • 这个加密后的值必须一致
    • styleSheets
  4. 表情的 HTML 文件
    • 需要计算它们的长度
    • 😀 getComputedTextLength
  5. 另一个 HTML 文件
    • 用来计算当前网页大小
    • Ssss tttt getBoundingClientRect
  6. 其他散项:
    • Worker 中的 postMessage 需要记录并手动指定时间触发
    • addEventListener 图片加载也要触发,且要得到图片的 widthheight
    • addEventListener 点击按钮也要触发,拿到许多参数
    • console 打印检测,需重写 hook 或者编译 Node 底层代码
    • iframe 流程处理好即可
    • 请求发送处理,因为有时间或其他校验,不能太快也不能太慢
    • 所有 DOM 流程都必须完整运行,因为里面做了大量的 DOM 流程
    • 大量异步回调需手动触发,因为 Node 不会自动帮你调用

结论

  • 在 Node 或 V8 里补环境,难点多在异步。
  • 其他的都还算简单,JS 实现不了就用 C 也行。

当前项目实现思路

  • 环境:基于 Node + VM2 + Cheerio
  • 当前项目借鉴了 qxVm 框架的思路,虽然有不少 bug,但它的隔离、调度、设计思路很棒
  • 所有 DOM 都封装成基于 Cheerio 去处理
  • 还加了一个基于 Puppeteer 打开的浏览器接口,主要处理计算长度的逻辑
    • 因为没找到任何工具来做 getComputedTextLength 或类似计算,自己实现又太复杂
    • 可能需要考虑速度问题,但整体运行会非常快

动态调试方案

  1. 用 Babel 还原算法
    • 不能改里面的逻辑代码,只做还原
    • 之后可以成功运行就行
    • 因为后面需要直接从网站返回的数据到环境里面了
  2. 本地 hosts 修改指向对应网站名
    • 用 Caddy 做反向代理到 Python 接口
  3. 记录一份成功的所有请求和响应
    • 之后一直用这份记录比对
    • 直到调试过程达到预期为止

我这里说的可能有点乱,还需要手动去实现才可以的知道里面的运行加密的轨迹。
但理解思路就好。

下面是案例图片的展示

基本成功了
在这里插入图片描述

  • 这里就是dom的记录
    在这里插入图片描述

  • 图片的响应 状态码为401
    在这里插入图片描述

  • 这里好像是时间的 中间有用到回调
    在这里插入图片描述

  • 表情的html得到了数据 getComputedTextLength
    在这里插入图片描述

  • 这两个是dom的检测加上的数据
    在这里插入图片描述
    在这里插入图片描述

  • 相关的流程
    在这里插入图片描述

收集流程数据的地方图解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 主要是好久没有写博客啦啦啦
  • 可能里面用词不怎么精准 谅解 谢谢啦~~~~
  • 结束啦 拜!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值