分析 Datadome 的最新变化
Datadome 偶尔才会真正上点新东西,我每次都盼着能碰上点刺激的局面,但这次(又一次)好像并非如此。

Datadome 偶尔才会真正上点新东西,我每次都盼着能碰上点刺激的局面,但这次(又一次)好像并非如此。

没有拼图了!
先来聊聊 noPuzzle 验证码这次看起来是全局启用了。
这其实是一段很久以前就埋下的"沉睡代码",但直到几周前我才第一次真正看到它被激活。
目前看来,他们经典的"滑块验证码"已经被全局停用了(所以这大概不是按网站配置的)。
好处
其实从解决的角度看什么都没变,甚至可能还更好了,因为你以前得计算"滑块到达"的位置(用于生成移动轨迹和信号),而现在它永远是一个固定值。解决耗时也缩短了,因为不再需要去解算和分析挑战图片了。
坏处
我猜这意味着他们快要上一个新的、基于图片的验证码了(我倒是希望他们能上)。
虚拟机 —— plv3
这次更新之后,验证码和拦截页上都跟着上了 plv3 这个参数。

它的生成完全由他们的 vm-obj.js 文件负责。

接下来这部分我会尽量用最简单的方式讲清楚,好让任何读者都能明白我们在说什么。
什么是 JavaScript VM(虚拟机)?
简单说,JavaScript 平时能在你的浏览器里运行,靠的是浏览器的引擎,它专门能看懂 JavaScript。
而 VM 让你能运行别的代码,几乎就像是在你的浏览器窗口里塞了一台小电脑。这主要用于代码保护和混淆。
现在我们大致理解了 VM,接下来看看 Datadome 具体是怎么实现他们那套的。
Datadome 的 VM
它的逻辑遵循这 3 个步骤:
┌─────────────────────────────────────────────────────────────┐
│ BYTECODE │
│ (A long string of numbers - the encoded instructions) │
│ Example: [45, 128, 67, 23, 199, 54, 12, 88, ...] │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ THE VM ENGINE │
│ - Reads bytecode one instruction at a time │
│ - Has a "stack" to store temporary values │
│ - Has a "program counter" to track current position │
│ - Executes ~100 different operation types │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ OUTPUT │
│ A "fingerprint" - a unique signature of your browser │
│ Example: {"r": "abc123...", "i": 340000, "t": 25} │
└─────────────────────────────────────────────────────────────┘
这些字节码指令来自我们能在他们的 vm-obj.js 文件里找到的那个超长字符串。
栈(Stack)
VM 使用一个"栈",你只能从顶部压入(push)或弹出(pop)。
PUSH 5 → [5]
PUSH 3 → [5, 3]
ADD → [8] (pops 5 and 3, pushes their sum)
PUSH 2 → [8, 2]
MULTIPLY → [16]
**程序计数器(PC)
**程序计数器告诉 VM 下一条指令从哪里读。
Bytecode: [PUSH, 5, PUSH, 3, ADD, PUSH, 2, MULTIPLY]
↑
PC = 0 (start here)
After PUSH 5:
Bytecode: [PUSH, 5, PUSH, 3, ADD, PUSH, 2, MULTIPLY]
↑
PC = 2 (moved forward)
这是因为字节码里的每个数字都代表一个操作,在 Datadome 这套里我们大约能找到 100 种不同的操作:
| Opcode | Name | What it does |
|--------|----------|--------------------------------|
| 45 | PUSH | Put a value on the stack |
| 67 | ADD | Add two numbers |
| 89 | JUMP | Go to a different position |
| 92 | JUMP_IF | Jump only if condition is true |
| 23 | GET_PROP | Get a property from an object |
| 54 | CALL | Call a function |
| ... | ... | ... |
假设 VM 想检查你的 navigator 是否具备某个特性,它会这样进行:
Bytecode: [GET_WINDOW, GET_PROP, "navigator", GET_PROP, "userAgent", STORE]
Step 1: GET_WINDOW
Stack: [window]
(Gets the browser's window object)
Step 2: GET_PROP "navigator"
Stack: [navigator]
(Gets window.navigator)
Step 3: GET_PROP "userAgent"
Stack: ["Mozilla/5.0 Chrome/120..."]
(Gets the browser's user agent string)
Step 4: STORE
Stack: []
(Saves this value to use in the fingerprint)
输出数据
VM 成功执行完之后(验证码大约是 36 万条指令,拦截页挑战大约少 10 万条),输出会长成这样:
{
"r": "aGVsbG...", # output fingerprint value
"i": 340580, # interactions done
"t": 25 # elapsed time
}
有了这份输出数据,我们现在就有了 r,它会被用作我们的 plv3,而另外两个则会用于 signals payload。
怎么解决?
目前这些字节码指令会在指纹提取之前/期间做若干次检查,它们不仅需要匹配你所解出的 payload 的细节,还要匹配你正在解决的那个挑战的细节。
这意味着用一个浏览器标签页去解它,不仅是最糟糕、最没法扩展的方案,而且根本就跑不通。所以祝那些基于 selenium 的现成方案好运了。
真正最好的做法,是搞懂这些指令、弄清 VM 在找什么、在分析什么,然后创建一个基于脚本的环境,能用与构造 signals payload 时相同的细节正确地运行这个文件。
逆向反机器人最核心的一点永远是:"对正在发生的事有个数",否则以后你会为此付出代价!
我猜这只是 Datadome 的第一次试水,让我们看看接下来的 VM 变化到底会不会硬核吧,哈哈。
需要一个稳定的 Datadome 方案?
去联系那些真正懂这项技术的专家吧,你总不想每次 Datadome 推个改动,你的项目就跟着趴窝。

在 TakionAPI 我们就提供这个,一定去看看,开始免费试用,然后去看看我们的文档,一次 API 调用,Datadome 就不再是你需要操心的问题了。
与我联系
如果你喜欢这篇文章,在 GitHub 和 Medium 上关注我,每当我发帖或开源什么东西时你都会收到通知。