Reverse-Engineering a Fingerprint Browser, Part 1: What an Anti-Detection Browser Actually Is

A closed-source Chromium fork, an encrypted profile format, and a subscription model

Posted by 叉叉敌 on November 10, 2026

0. 前言

早年自己喜欢做一些 Windows 相关的逆向,当时应该还是用中文开发,而且是易语言。如果在 Windows 上开发,不加壳的话,基本上都会被 360 杀毒软件报是病毒,因为它的特征码就是这样的。

当时就爱上了软件相关的东西,尤其是软件安全相关的。后来就慢慢了解了这些东西,包括 PE、解壳、砸壳,以及不同特征识别是什么样的壳。

再后来,就开始使用 OD 等逆向工具,了解了一些计算机指令,比如 mov、and、xor 等比较底层的指令集。了解这些之后,目前对我做编译器这块应该也是有一定的帮助,因为做编译器也是有一点相关的。但目前做编译器,主要是对它的性能进行 tuning,也就是 performance tuning。

回到这个主线上,我其实对这个感兴趣,所以最近找到了一些逆向相关的 niche business,就想尝试一下。因为这个市场,刚好前几天看了知识星球吴老板,他之前也是做安全相关的,他最近出了一本书叫《夹缝中生长》。

大概的意思是,任何宏观的商业背景下,都有一些细小的商业入口。

比如说我们把一个海岸线拉远一点看,它的边缘都是圆润的;但是放大之后我们都知道,其实每个地方都有很多纹路。

你可以理解成,任何平的东西都不是绝对 光滑的,都有摩擦力,所以说在这个蓝色星球上,没有相对光滑的东西。

再回到逆向上面,我对这个比较感兴趣,因为最开始的话,你需要自己去准备 debug、逆向加壳等各方面的软件。现在有了 AI 之后,这些东西其实都不需要你安装了,直接在 Python 里面有第三方库集成好了,你直接拿出来用就可以了。我觉得非常非常的 amazing,这个地方真的是 amazing,我觉得这是 AI 时代为数不多的其中一个 AHA moment。

下面就是我对一款指纹浏览器做的一些逆向分析。

它其实会提供一个 user profile 的文件,里面包含了很多信息。这个文件是通过 RSA 加密的,大家都知道 RSA 加密是很难破解的,基本上目前是没法破解的。如果破解了那就糟糕了,很多加密都会被攻破,这是很难的。

那怎么办呢?其实有个办法,就是你在 debug 的时候,把这个数据加载进去,然后在它出口的地方拿到原始数据,再把里面的字段梳理一下。拿到原始数据之后,如果是 RSA 密钥对加密,私钥没在这个软件上面,那么公钥肯定是在的。拿到公钥解密之后,就能拿到明文。

有没有可能这样呢?我直接生成一个密钥对,把公钥替换成我的公钥,私钥用我自己的。然后我拿到的明文,再通过我的私钥加密一遍,得到真正属于我的 user profile,那其他人也就解不了了。

再往下走的话,因为还涉及到一个 z level,类似哈夫曼编码压缩,这个压缩大家可以了解一下它的压缩思路。哈夫曼编码有很多分支,有很多很多压缩方法,你可以顺着这个方法去研究。

因为 RSA 本身的字符串就非常长了,再加上明文的 user profile 里面有很多东西,加起来这个文件大概就有几兆大,加密之后就更大了。那怎么办呢?其实通过哈夫编码这个加密算法,把它压缩之后,呈现出来的这个字应该就很小,我觉得大概在几十 KB 之内是可以完成的;解压的话应该会达到几兆。他们大概就是这么一个思路,学习学习。

主要我是想体验一下,在 AI 时代,你什么工具都不用准备,甚至环境都不用准备,直接就可以去做这些事情,amazing!

Let’s GOOO!!

Over the past few weeks I have been studying a class of software I will call an anti-detection browser (in this series, the “ant browser”). It is a closed-source, patched Chromium build that rewrites, at the C++ level, every device signal a page can read — user agent, screen, GPU, fonts, Canvas, WebGL, audio, timezone — according to an encrypted fingerprint profile file.

This is the first post in a series. Rather than dump one giant report, I am splitting the work into six parts so each one can stand on its own:

  1. What an anti-detection browser actually is (this post) — the product shape, the profile format, the tiering.
  2. Static recon of a closed-source binary — what you can learn without running it.
  3. The crypto layer — how the profile is protected, and why the obvious shortcuts do not work.
  4. Binary patching as a research exercise — crash traps and license guards.
  5. Offline analysis of the encrypted profile format — how far the data can be recovered.
  6. GeoIP — how an exit IP becomes a timezone, and why it so often goes wrong.

The whole series is a read-only study. I did not decompile anyone’s code to copy it, and I did not redistribute profile files. Everything below is either public documentation or measurements I made on a binary and profile set I already had access to. Where a claim is an inference rather than a measurement, I say so.

Scope and ethics. Anti-detection browsers have legitimate uses (multi-account management, automated testing, privacy research) and obvious abuse potential (fraud, ban evasion, scalping). The vendor’s own DISCLAIMER and RESPONSIBLE_USE documents forbid automated account creation, ticket scalping, and CAPTCHA defeat on production sites. Nothing here is a how-to for those. This series is about understanding a piece of engineering — how a vendor builds and protects a fingerprint product — and about being able to tell, from the outside, what such a browser is really doing.

A note on detail. This is a shareable write-up, not an internal engineering log. I deliberately describe what was found and how the investigation was framed, but not the exact addresses, bytes, constants, or step-by-step recipes that would let someone reproduce the work. If the engineering direction here is relevant to something you are building, that is exactly the conversation I would like to have.

1. One-sentence summary

An anti-detection browser is a closed-source, kernel-level Chromium fork. It replaces the device information a browser exposes to a page — UA, screen, GPU, fonts, Canvas, WebGL, audio, timezone — based on an encrypted fingerprint profile file. It is not a browser extension, not a CDP wrapper, and not a Playwright plugin. It is a modified Chromium binary plus an encrypted data file.

The public repository for the product I studied contains no browser source code. What it does contain:

  • Documentation: CLI flags, profile fields, feature descriptions.
  • Playwright / Puppeteer / Python usage examples.
  • A few Chromium patch samples, for illustration only — the core code is not published.
  • A handful of older public demo profiles.
  • Playwright test scripts and screen recordings against various anti-bot vendors.

The binary itself ships via releases. Profiles from a certain version onward require a paid subscription.

The product markets itself as a “privacy protection browser / fingerprint privacy research” tool. Look at the test suite, though — a long list of anti-bot vendors and major commerce sites — and the real intent is clear: it is a kernel-level anti-detection browser.

2. Product shape

What a buyer actually gets is a bundle:

Component Contents
Documentation Overview, feature matrix, version tiers, CLI flags, profile fields
Examples puppeteer / bot-script / mirror / storage-access samples
Container image A Docker image plus several vendor-specific examples
Test suite Anti-bot vendor tests and large-site tests
Tooling Canvas/audio lab tools, a multi-window mirror tool, a V8 log tool
Demo profiles A few older, public encrypted profiles
The binary A patched Chromium build, delivered via releases

There is also a companion GUI for picking profiles and managing multiple instances.

3. Core concepts

3.1 The fingerprint profile

The profile file’s outer layer is JSON. Some fields are plaintext; the core data is encrypted. A public demo profile looks roughly like this:

key:              <encrypted key block>
profile:          <encrypted fingerprint body>
version:          a build/date tag
userAgent:        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ..."
unmaskedVendor:   "Google Inc. (Intel)"
unmaskedRenderer: "ANGLE (Intel, Intel(R) Iris(R) Xe Graphics ... D3D11)"
userAgentData:    { platform, platformVersion, uaFullVersion, ... }
window:           { innerWidth, innerHeight, outerWidth, ... }
screen:           { width, height, availWidth, colorDepth, ... }

A profile hard-codes: browser version and Client Hints, screen and window geometry, DPR, CPU, memory, GPU, fonts, Canvas / WebGL / WebGPU / audio features, OS behavior, codecs, media devices, speech voices. In effect, one profile is a complete virtual device model.

Rules the vendor emphasizes:

  • One profile = one “device model”, not a one-time slot. The same profile can drive several processes at once, or several browser contexts.
  • The browser major version must match the profile major version.
  • Subscription profiles have an expiry; past it, the browser refuses to start.
  • You cannot edit the profile JSON directly. To change behavior you use CLI flags or a configs block, and editing configs needs a higher tier.
  • Priority: CLI flags > profile configs > profile defaults.

3.2 Startup

At the CLI level it behaves like a normal Chromium with a few extra flags: point it at a profile file (absolute path), give each process its own user-data directory, and route it through a proxy. Two operational notes matter:

  • Timezone and locale are inferred from the proxy exit IP by default. The IP-lookup service can be swapped out.
  • Proxy credentials are expected inline in the proxy URL, and the vendor advises against framework-level proxy handling, because that breaks automatic geo-inference.

3.3 Built-in random profile selection

The vendor offers a flag that picks a random profile from a directory on each start — the intended way to diversify fingerprints across instances. (The operation I was studying did not use it; it copied profiles into its own directory under randomized names. Same effect: uniform random, no dedup guarantee.)

4. Version tiers

The tier determines which features you get:

Tier Main capabilities
Free / public Core fingerprint protection, Playwright / Puppeteer, Docker, headed and headless consistency. Free profiles stop well behind the current version.
PRO More fields, more platform coverage.
MAX T1 Editable configs block, custom profile authoring.
MAX T2 Noise seeds, GPU simulation.
MAX T3 Per-context fingerprints (several profiles in one process).

Noise seeds deserve a note. On a higher tier, a noise-seed flag gives the same profile a fresh, restart-stable Canvas / WebGL / audio / text noise identity. One profile times an arbitrary seed times session parameters is effectively an unbounded identity space — without needing a new profile file. This becomes important in Part 5.

5. Relationship to a Python / Go automation stack

Externally, this class of browser is just a Chromium executable plus CDP. Python points Playwright’s executable_path at it; Go uses chromedp or rod with the same executable. There is no language constraint on integration.

The real gate is not code, it is licensing: recent profiles need a paid subscription with an expiry. The public demo profiles are old and do not support headless or automation frameworks. Some features (noise seeds, GPU simulation) need a higher tier. Profiles are encrypted — you cannot generate or modify them yourself; the pool size, diversity, and expiry are all decided by the vendor.

6. Licensing

The repository’s code license covers the docs and examples only. The browser core and the profiles are closed-source and subscription-gated. And the vendor’s responsible-use documents explicitly forbid:

  • Automated account creation against a site’s terms of service
  • Automated ticket purchasing or unauthorized queue jumping
  • Ticket scalping
  • Testing production ticketing flows without written authorization from the operator
  • Defeating CAPTCHA protections on production websites

In the US, the BOTS Act also makes it illegal to use software to circumvent ticketing-site access controls.

7. A concrete sample: a subscription profile pack

I had access to one subscription pack for study. Without going into anything that could be reassembled into a working profile, the shape of the pack was instructive:

  • A batch of encrypted profile files and no browser. Several platform folders (Windows, macOS, Android, Android tablet).
  • A filename convention that encodes the account, the tier, a per-file ID, the platform, the OS version, the major version, and the batch date.
  • The outer plaintext fields were readable; the encrypted core was not.

What the outer fields revealed:

Field class What it told us
Encrypted blobs the key block and the fingerprint body
Plaintext identity fields user agent, GPU vendor/renderer strings, window and screen geometry, Client Hints
Kernel / version / release tags which kernel line the profile targets, and the batch date
An expiry field a short, explicit validity window

Two observations worth keeping:

  • The validity window was very short (a few days), consistent with a trial or short-term pack rather than the monthly kind.
  • The device models were diverse but not unique. Across the batch there were dozens of GPU models and dozens of screen sizes, several platforms, and a spread of UA patch versions — and also some repeats. Notably, the UA version a profile reports can differ from the browser’s own version, which is a fingerprint-consistency detail worth understanding.

I will not publish the pack contents, the naming scheme, or the account details. The point of describing it is to show that these profiles are virtual device models, generated and curated on the vendor side, not user data.

8. What is next

This post established the surface: a closed-source Chromium fork, an encrypted profile format, a tier system, and a concrete subscription pack with a short expiry.

The interesting questions are all inside the binary and the file format:

  • Does it phone home? Does it check a license server?
  • What is actually inside the encrypted blocks?
  • Why does editing the plaintext expiry do nothing?
  • Can the binary be patched, and can the profile be analyzed offline?

The next five posts answer those, in order — at the level of mechanism and method, not of copyable recipe.


Series navigation:

Blog: chasays.github.io
WeChat: cdtfug — welcome to follow and discuss, or add xiaorik on WeChat.