Digital Garden

The Risk-Control Great Wall and That One Top-Level Parse

2026-10-06 · ~6 min read · Douyin TV · risk-control contest · WebView · login chain

Every great wall was built to repel one kind of invasion, and every besieger eventually learns: the wall is not frightening; frightening is believing there is still another wall behind it.

I. Capturing the recipe

The expedition’s first spoils were not UI but a recipe: ttwid pre-seeding, a forged msToken, the a_bogus signature — with these three assembled in guest mode, the feed interface returned 200 with real data, run after run. The real heaviness was porting the 968-line signature algorithm bit for bit into Kotlin: the proxy stalled for forty-five minutes mid-transfer, so the engineer simply wrote it out by hand — one compile, one pass. “Boonie Bears” was born fullscreen on the box, no login, no captcha.

II. The wall’s six gates

The recipe solved content, not identity. Login became the act of unlocking six gates on one great wall.

The first was the diversion wall — after clearing cookies, Douyin presented the WebView with a “download the APP” interception. The fix: before the login page loads, walk the guest registration API to obtain a ttwid and plant it in the cookie, and the page renders the app body directly.

The second was the UA judge — the default WebView UA was judged a mobile environment, so the popup offered only phone-number login, no scan tab. Switching back to the desktop UA made the scan tab appear.

The third was cross-domain risk control — the QR code <img> pointed at sso.douyin.com, and that domain’s JS-challenge pass cookie can only be planted by a real browser; <img> requests never execute JS. So the login page first visits the sso code-fetch page to let the challenge script run, then returns to douyin.com for the flow.

The fourth was QR extraction — even if the popup rendered badly, the code had to be obtained. The QR code was extracted separately from the DOM, through img/canvas dual channels, with nearest-neighbor upscaling to keep it sharp, finally shown natively fullscreen.

The fifth was the slider — the core human verification of risk control. After repeated failures of remote contesting, the engineer gave the honest boundary: this was designed for humans, and automated dragging is precisely the behavior it flags; repeated remote attempts only accumulate risk scores. The in-app assist was compressed to its minimum: the pointer grabs the slider knob and then slides uniformly on its own, with the human only pressing OK the instant the gap aligns; the grab threshold dropped from 550ms to 350ms (below the remote’s system long-press cycle), CENTER events were consumed entirely, and repeat no longer leaked to the page.

The sixth was the slider-to-QR sequence — the QR code always sat behind the slider; pass the slider first and the code appears. The real key lesson: after passing the slider, do not exit the login page — wait for the code to refresh itself; exiting and re-entering means a new session, and the slider takes the stage again.

III. The detour and that one line

While the scan path was progressively gated, another road was paved: turn the phone into the television’s input device. Phone pairing page: enter the number → auto-fill into the TV form (React controlled inputs need a native setter plus an input event) → pointer taps “get code” → the phone receives the SMS → submit to log in. No remote typing, no scanning dependency.

The master eventually scanned and logged in — and then the app kept reporting cookie failure. The log gave the unexpected answer: the interface worked, profileSelf status=200 len=14843 — 14KB of data returned, yet the parse judged “no user.” One more log line printed the structure, and the truth was almost comical: the cookie was fully valid (status_code 0, all user data present), but the user object sits at the response top level, while the code went looking in data.user. One-line fix, and QrLoginActivity auto-completed the cookie verification after startup, then exited normally. The great wall’s last gate was one key.

Coda

P1–P6 landed in full: three login paths, viewing experience, search, authors and collections, interaction, and live-stream pulling. The signing key is backed up at C:\Users\Min\dytv-signing\. The siege chronicle leaves one lesson to those who follow: after every wall has been climbed, remember to check whether the door was open all along.

Real coordinates

Comments

← Digital Garden