FOR GETTING LAST VERSION OF DATADOME (CHECK/interstitial/INVISIBLE) SOLVER DM ME @yallc
A Go library and CLI tool for generating DataDome browser fingerprints and obtaining valid cookies. Uses Chrome TLS fingerprinting via uTLS to bypass TLS-based bot detection.
Author: yallc
Tracks
tags.jsversion 5.9.0 (ddv=5.9.0). Verified end-to-end: a two-phase solve clears a DataDome-protected/scrapingendpoint with HTTP 200 while a bogus cookie on the same path returns 403.
- Chrome 149 fingerprint generation (~180 signals)
- 5.9.0 behavioral / biometric
eventCounters(mouse-stroke medians, coalesced/predicted pointer stats, session flags) - Encrypted jspl payload construction (XOR + dual PRNG keystreams + custom base64)
- Chrome TLS fingerprint impersonation via uTLS (JA3/JA4 match)
- HTTP/2 transport with proxy support (HTTP CONNECT / SOCKS)
- Single-phase and two-phase solve modes
- Cookie verification against target site (uses same Chrome TLS)
- XOR integrity checksums (sgb/sgd/sgc) computed correctly inline
The jump from 5.7.0 to 5.9.0 is behavioral, not cryptographic. The jspl encryption
codec, the TLS transport, and the Chrome-149 fingerprint builder are unchanged; what
changed is the eventCounters field that rides alongside the encrypted jspl.
| v5.7.0 | v5.9.0 | |
|---|---|---|
tags.js version |
5.7.0 |
5.9.0 |
ddv POST field |
5.7.0 |
5.9.0 |
In 5.7.0 the behavioral phase sent a flat count of DOM events:
{"mousemove":87,"pointermove":87,"click":4,"scroll":1,
"touchstart":0,"touchend":0,"touchmove":0,"keydown":22,"keyup":21}In 5.9.0 tags.js runs a behavioral analyzer over the real pointer/mouse stream and sends
derived statistics instead. Build590EventCounters() reproduces the object with
internally-consistent values (ratios derived from the counts) in the ranges a short human
session produces:
{"m_s_c":1,"m_m_c":98,"m_c_c":5,"m_cm_r":0.05102,"m_ms_r":98,"m_fmi":895,
"es_sigmdn":0.0137,"es_mumdn":10.16,"es_distmdn":278.98,"es_angsmdn":0.85,"es_angemdn":1.39,
"p_fc":97,"m_clsdcnt":355,"p_cf":71,"p_cmx":11,"p_ps":162,"p_pf":54,
"jset":1785319984,"nddc":1,"exp8":0,"nowd":false,"sfex":false}| Group | Fields | Meaning |
|---|---|---|
| Mouse counts / ratios | m_s_c, m_m_c, m_c_c, m_cm_r, m_ms_r, m_fmi |
scroll/mousemove/click counts, click÷mousemove and mousemove÷scroll ratios (-1 when the denominator is 0), first-mouse-interaction delay in ms |
Mouse-stroke medians (es_*) |
es_sigmdn, es_mumdn, es_distmdn, es_angsmdn, es_angemdn |
per-stroke medians: std & mean of log(timeStamp), euclidean stroke distance (px), start & end angle (rad) |
Pointer coalesced / predicted (p_*) |
p_fc, m_clsdcnt, p_cf, p_cmx, p_ps, p_pf |
stats from getCoalescedEvents() / getPredictedEvents(): frame count, coalesced-event count, coalesced frames, max coalesced per frame, predicted samples & frames |
| Session flags | jset, nddc, exp8, nowd, sfex |
session start (unix s), navigator flags, no-webdriver / same-frame-exec booleans |
The library sends this object automatically in the two-phase behavioral (jsType=le)
request; pass -event-counters '{...}' to override it.
- jspl codec — the PRNG seeds are byte-identical (
seedO=1789537805,seedE=9959949970,encryptSeed=1809053797,ivXorConst=11027890091,defaultV=741130091). No re-keying, no format change. - TLS transport —
utls.HelloChrome_Autofor both POST and GET (see the 5.7.0 TLS fix below). - Fingerprint builder — the ~180-signal Chrome-149/Win10 payload is carried forward unchanged.
tags.jsemits the jspl signal object under computed (minified) property names, so no signal-level fingerprint delta is claimed for 5.9.0.
| v5.6.6 | v5.7.0 | |
|---|---|---|
| Chrome | 148 | 149 |
| UA | Chrome/148.0.7778.217 |
Chrome/149.0.0.0 |
| Full version | 148.0.7778.217 |
149.0.7827.53 |
v5.6.6: "Not/A)Brand";v="8"
v5.7.0: "Not)A;Brand";v="24"
This affects sec-ch-ua, sec-ch-ua-full-version-list, and the nhi signal.
| Signal | Description |
|---|---|
cfpjs |
Canvas fingerprint JS hash |
tbce |
Tab close event listener |
rce |
Reduced color encoding |
dfps |
Device font pixel size |
htmlcs |
HTML charset detection |
| Signal | Description |
|---|---|
wwlrv |
WebWorker language revision |
exp8 |
Experimental flag 8 |
cdhf |
CDH fingerprint flag |
bbs3 |
Built-in browser storage v3 |
| Old Name | New Name | Description |
|---|---|---|
vcmku |
vcmkuts |
VP9/MKV codec TS flag |
| Signal | Description |
|---|---|
nt_unt |
Unload timing |
nt_ddi |
DOM domain interactive |
nt_dcl |
DOM content loaded |
nt_rsd |
Response start delta |
nt_dce |
DOM content end |
| Signal | Description |
|---|---|
mmapi |
MediaMetadata API |
jset2 |
JS execution time v2 |
wcsm |
WebCrypto subtle methods |
New APIs checked in bchk fingerprint:
| API | Present in Chrome 149 |
|---|---|
DetachedViewControlEvent |
No |
SiteBoundCredential |
No |
WebSocketStream |
Yes |
DisplayNames |
No |
SVGDiscardElement |
Yes |
| Header | v5.6.6 | v5.7.0 |
|---|---|---|
sec-fetch-site (POST) |
same-origin |
cross-site |
v5.7.0 solver uses utls.HelloChrome_Auto for both the POST (cookie creation) and GET (cookie verification) requests. This ensures the TLS fingerprint (JA3/JA4) matches Chrome consistently. Previously, using Go's default net/http for GET requests caused cookie rejection (403) because DataDome binds cookie trust to the TLS fingerprint used during creation.
go install github.com/L0ed0/datadome-solver/cmd/solver@latestOr build from source:
git clone https://github.com/L0ed0/datadome-solver.git
cd datadome-solver
go build -o solver ./cmd/solver/solver -site "https://example.com" -key "YOUR_DDK_KEY" -solvesolver -site "https://example.com" -key "YOUR_DDK_KEY" -verifyThe behavioral phase automatically ships the 5.9.0 biometric eventCounters:
solver -site "https://example.com" -key "YOUR_DDK_KEY" -two-phase -delay 5solver -site "https://example.com" -key "YOUR_DDK_KEY" -verify -proxy "http://user:pass@proxy:8080"solver -site "https://example.com" -key "YOUR_DDK_KEY" -two-phase \
-event-counters '{"m_s_c":1,"m_m_c":98,"m_c_c":5,"m_cm_r":0.051, ...}'solver -site "https://example.com" -output payload.jsonsolver -site "https://example.com" -key "YOUR_DDK_KEY" -encryptpackage main
import (
"context"
"fmt"
"time"
"github.com/L0ed0/datadome-solver/pkg/solver"
)
func main() {
client, err := solver.New("https://example.com",
solver.WithDDJSKey("YOUR_DDK_KEY"),
solver.WithProxy("http://proxy:8080"), // optional
)
if err != nil {
panic(err)
}
// Two-phase solve: phase 1 (jsType "ch") mints the CID, phase 2 (jsType "le")
// ships the 5.9.0 behavioral eventCounters. Pass "" to auto-generate them.
result, err := client.SolveTwoPhase(context.Background(), 5*time.Second, "")
if err != nil {
panic(err)
}
fmt.Println("Cookie:", result.Cookie)
// Verify cookie works
status, body, err := client.Verify(context.Background(), result.Cookie)
fmt.Printf("Status: %d, Body length: %d\n", status, len(body))
}Generate the 5.9.0 behavioral counters directly:
counters := solver.Build590EventCounters()| Flag | Default | Description |
|---|---|---|
-site |
Target site URL (required) | |
-key |
DataDome JS key (ddk) | |
-solve |
false |
Solve and print cookie |
-verify |
false |
Solve then verify with GET |
-two-phase |
false |
Two-phase solve (initial + 5.9.0 behavioral) |
-delay |
10 |
Delay between phases (seconds) |
-proxy |
HTTP/SOCKS proxy URL | |
-profile |
chrome_win10 |
Browser profile |
-cid |
Existing CID value | |
-bpc |
1 |
BPC value |
-jstype |
ch |
jsType field |
-event-counters |
(empty) | Behavioral counters JSON; empty = generate the 5.9.0 biometric set |
-encrypt |
false |
Print encrypted jspl only |
-output |
Write payload JSON to file | |
-seed |
0 |
PRNG seed (0 = random) |
cmd/solver/ CLI entrypoint
pkg/solver/
client.go Client, Solve, SolveTwoPhase, Verify, Build590EventCounters
transport.go Chrome TLS transport (uTLS + HTTP/2)
internal/
builder/
builder.go Signal payload assembly
generators.go Fingerprint generators (bchk, nav timing, checksums)
profiles.go Browser profiles (Chrome 149 / Win10)
crypto/
crypto.go jspl encryption (XOR + PRNG + custom base64)
- Build fingerprint — Generates ~180 browser signals matching Chrome 149 on Windows 10 (screen, WebGL, plugins, codecs, nav timing, error stacks, etc.)
- Compute checksums — Calculates XOR integrity checksums (sgb/sgd/sgc) inline during payload construction, ensuring internal consistency
- Encrypt — Encodes signals as JSON, XORs with two PRNG keystreams (seeded by DDK + CID + timestamp), encodes with custom base64
- Phase 1 (
jsType=ch) — POSTs the encrypted jspl toapi-js.datadome.co/js/over a Chrome-impersonated TLS connection (uTLS); the response cookie carries the fresh CID - Phase 2 (
jsType=le) — Re-encrypts withbpc=2under the minted CID and POSTs alongside the 5.9.0 behavioral/biometriceventCounters; the server returns the trusteddatadome=...cookie - Verify — Cookie is used for requests to the target site over the same Chrome TLS transport
MIT