# 总纲的全流程
## 基本说明
- **同一个网站两个页面隔离加密**
- **基于 `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)
}
}
自动化要点
如果是自动化情况下,只需要把 screenX
和 screenY
修改成不要和 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 也检测这个。
处理方法举例:
- 去源码中把
debugger
删除,不要断下来。 - 让
console.debug
不打印数据。 - 其他的可能性就看实际遇到情况了。
补环境要点
下面说的就是 补环境,只说困难的部分。
- 24 年才 10 多个加密层,现在已经 30 多个了 哭死,可能以后更多。
- 也说明了 自动化 是王道。
具体难点:
- 以下对象必须补全:
window
、document
、navigator
、screen
、screen.orientation
- 异常处理必须要优化:
Error
- 页面上面的
style
- 需要提取,因为里面会用到,然后再做一个
sha256
加密 - 这个加密后的值必须一致
styleSheets
- 需要提取,因为里面会用到,然后再做一个
- 表情的 HTML 文件
- 需要计算它们的长度
😀 getComputedTextLength
- 另一个 HTML 文件
- 用来计算当前网页大小
Ssss tttt getBoundingClientRect
- 其他散项:
Worker
中的postMessage
需要记录并手动指定时间触发addEventListener
图片加载也要触发,且要得到图片的width
和height
addEventListener
点击按钮也要触发,拿到许多参数console
打印检测,需重写 hook 或者编译 Node 底层代码iframe
流程处理好即可- 请求发送处理,因为有时间或其他校验,不能太快也不能太慢
- 所有 DOM 流程都必须完整运行,因为里面做了大量的 DOM 流程
- 大量异步回调需手动触发,因为 Node 不会自动帮你调用
结论
- 在 Node 或 V8 里补环境,难点多在异步。
- 其他的都还算简单,JS 实现不了就用 C 也行。
当前项目实现思路
- 环境:基于 Node + VM2 + Cheerio
- 当前项目借鉴了 qxVm 框架的思路,虽然有不少 bug,但它的隔离、调度、设计思路很棒
- 所有 DOM 都封装成基于 Cheerio 去处理
- 还加了一个基于 Puppeteer 打开的浏览器接口,主要处理计算长度的逻辑
- 因为没找到任何工具来做
getComputedTextLength
或类似计算,自己实现又太复杂 - 可能需要考虑速度问题,但整体运行会非常快
- 因为没找到任何工具来做
动态调试方案
- 用 Babel 还原算法
- 不能改里面的逻辑代码,只做还原
- 之后可以成功运行就行
- 因为后面需要直接从网站返回的数据到环境里面了
- 本地 hosts 修改指向对应网站名
- 用 Caddy 做反向代理到 Python 接口
- 记录一份成功的所有请求和响应
- 之后一直用这份记录比对
- 直到调试过程达到预期为止
我这里说的可能有点乱,还需要手动去实现才可以的知道里面的运行加密的轨迹。
但理解思路就好。
下面是案例图片的展示
基本成功了
-
这里就是
dom
的记录
-
图片的响应 状态码为401
-
这里好像是时间的 中间有用到回调
-
表情的
html
得到了数据getComputedTextLength
-
这两个是dom的检测加上的数据
-
相关的流程
收集流程数据的地方图解
- 主要是好久没有写博客啦啦啦
- 可能里面用词不怎么精准 谅解 谢谢啦~~~~
- 结束啦 拜!