Reverse Engineering Dolphin{anty}: How Blink C++ Voice Hooks Expose Commercial Anti-Detect Browsers
A deep dive into how Dolphin{anty}'s Zubre Chromium engine hooks Blink's BuildVoiceList, creating a deterministic 22-voice signature and Unicode leaks.
- ✔ Dolphin{anty} patches Blink C++ BuildVoiceList in components/zubre/overrides/voices.cc.
- ✔ Generates a static 22-voice array on Windows/Mac and 19 voices on Linux.
- ✔ Leaks Unicode non-breaking space characters in Google Cloud TTS strings.
- ✔ Can be reliably detected in under 5ms via client-side JavaScript.
Reverse Engineering Dolphin{anty}: How Blink C++ Voice Hooks Expose Commercial Anti-Detect Browsers
Commercial anti-detect browsers market themselves as bulletproof cloaking devices for web automation and multi-accounting. They claim to spoof your hardware, operating system, and browser fingerprint so completely that advanced bot detection systems cannot tell you apart from an everyday user.
In reality, many commercial anti-detect browsers make crude modifications to Chromium source code that leave glaring, deterministic signatures.
In this post, we will walk through how Dolphin{anty} modifies Chromium's speech synthesis engine, how its proprietary C++ overrides work under the hood, and how any webpage can detect Dolphin{anty} using basic JavaScript in under 5 milliseconds.
# The Illusion: Spoofing the SpeechSynthesis API
The Web Speech API (window.speechSynthesis) allows web applications to read text aloud using voices installed on the host operating system or provided by browser cloud services.
Because the list of installed voices differs across Windows, macOS, and Linux, anti-bot systems (like Verisoul, Cloudflare, and DataDome) query speechSynthesis.getVoices() to verify if your declared operating system matches your available voice engine.
To fake this, Dolphin{anty} forks Chromium into a custom build known internally as the Zubre engine. Inside Zubre, Dolphin intercepts the speech synthesis pipeline to inject a synthetic list of voices matching the user's selected profile OS.
Specifically, Dolphin patches Chromium in components/zubre/overrides/voices.cc by hooking Blink's BuildVoiceList function.
# Invariant 1: The Fixed 22-Voice Array
When you call speechSynthesis.getVoices() in a normal browser:
- ◆On Windows, Chrome queries the Windows SAPI and OneCore voice registries. A fresh Windows 10/11 install typically returns between 2 and 6 local voices, plus 19 Google Cloud TTS voices.
- ◆On macOS, Chrome queries the macOS system speech synthesizers (like Alex, Samantha, Fred, Victoria).
- ◆On Linux, Chrome has zero local voices and only loads Google Cloud TTS voices (or whatever speech-dispatcher daemon provides).
Dolphin does not query the host OS or emulate dynamic voice loading. Instead, Dolphin's C++ hook hardcodes an exact static array:
- ◆On Windows profiles: Exactly 22 voices
- ◆On macOS profiles: Exactly 22 voices
- ◆On Linux profiles: Exactly 19 voices
If you run Dolphin on a macOS machine while emulating Windows, or on a Linux server while emulating macOS, Dolphin always returns these exact counts.
# Invariant 2: Deterministic OS Voice Ordering
Dolphin hardcodes the first three slots of its 22-voice array with local voices (localService: true) in a strict, unvarying sequence:
Windows Profile Order
- Slot 0:
Microsoft David - English (United States) - Slot 1:
Microsoft Zira - English (United States) - Slot 2:
Microsoft Mark - English (United States)
macOS Profile Order
- Slot 0:
Samantha - Slot 1:
Fred - Slot 2:
Tom
On genuine Windows installations, Microsoft Mark is not installed by default on many Windows builds, and voice ordering varies based on locale priority and registry enumeration. Dolphin's C++ code populates them in the exact same array indices every single time.
On Linux profiles, Dolphin drops the 3 local voices and provides 19 Google Cloud voices, strictly starting with Google Deutsch at index 0 and Google US English at index 1.
# Invariant 3: The Non-Breaking Space Signature (\u00a0)
The clearest fingerprint in Dolphin is how they defined their Google Cloud TTS voice strings.
In Google Cloud TTS, certain localized language labels contain standard spaces. When Dolphin's developers copy-pasted or serialized the localized voice names into components/zubre/overrides/voices.cc, they hardcoded non-breaking spaces (Unicode character \u00a0) inside the string literals:
- ◆
"Google\u00a0\u666e\u901a\u8bdd\uff08\u4e2d\u56fd\u5927\u9646\uff09" - ◆
"Google\u00a0\u7ca4\u8a9e\uff08\u9999\u6e2f\uff09" - ◆
"Google \u570b\u8a9e\uff08\u81fa\u7063\uff09"
In standard Google Chrome, these voice names use regular space characters (\u0020), not Unicode non-breaking spaces.
# The Detector Code
Combining these invariants gives us a deterministic detector. There are no fuzzy probabilities or heuristic scores. If these exact conditions hit, the browser is Dolphin{anty}:
window.detect_dolphin_anty = async function () {
try {
if (typeof window === 'undefined' || !window.speechSynthesis) {
return false;
}
let voices = window.speechSynthesis.getVoices();
if (!voices || !voices.length) {
await new Promise(resolve => {
let timer = null;
const handler = () => {
try {
if (timer) clearTimeout(timer);
} finally {
try {
window.speechSynthesis.removeEventListener('voiceschanged', handler);
} catch (_) {}
resolve();
}
};
try {
window.speechSynthesis.addEventListener('voiceschanged', handler);
timer = setTimeout(handler, 100);
} catch (_) {
handler();
}
});
voices = window.speechSynthesis.getVoices();
}
// Dolphin only generates 22 voices on Windows/Mac, or 19 voices on Linux
if (!Array.isArray(voices) || (voices.length !== 22 && voices.length !== 19)) {
return false;
}
// Check Windows and Mac C++ voice template ordering
if (voices.length === 22) {
const isDolphinWin = voices[0]?.name?.startsWith('Microsoft David') &&
voices[1]?.name?.startsWith('Microsoft Zira') &&
voices[2]?.name?.startsWith('Microsoft Mark') &&
voices[0]?.localService === true;
const isDolphinMac = voices[0]?.name === 'Samantha' &&
voices[1]?.name === 'Fred' &&
voices[2]?.name === 'Tom' &&
voices[0]?.localService === true;
if (!isDolphinWin && !isDolphinMac) {
return false;
}
}
// Check Linux C++ voice template ordering
if (voices.length === 19) {
const isDolphinLinux = voices[0]?.name === 'Google Deutsch' &&
voices[1]?.name === 'Google US English';
if (!isDolphinLinux) {
return false;
}
}
// Check for Dolphin's hardcoded non-breaking space (\u00a0) strings
const cloudVoices = voices.length === 22 ? voices.slice(3) : voices;
const hasDolphinGoogleTTS = cloudVoices.some(v =>
v && typeof v.name === 'string' &&
(v.name.includes('Google\u00a0\u666e\u901a\u8bdd') || v.name.includes('Google\u00a0\u7ca4\u8a9e')) &&
v.localService === false
);
return hasDolphinGoogleTTS;
} catch (_) {
return false;
}
};# Why This Matters for Bot Bypass Engineering
Commercial anti-detect vendors sell convenience. They add a slick Electron UI, a profile manager, and an automated proxy rotator, but their core browser modifications are often sloppy.
Anti-bot systems like Verisoul, DataDome, and Kasada track these exact C++ overrides. When you run automated tasks through Dolphin, you are not bypassing bot detection; you are presenting a known signature that screams automation.
To bypass modern bot detection systems consistently, you cannot rely on shared off-the-shelf tools. You need private, source-level Chromium patches that generate mathematically sound, dynamic hardware profiles without leaving static C++ fingerprints.