Skip to content
On this page

Instrumenting a browser means adding observation points so you can watch exactly which APIs a page calls -- which is how researchers study fingerprinting and anti-bot scripts. The goal is to record calls like canvas.toDataURL(), navigator property reads, and WebGL queries so you can understand what the script collects. The hard part is observing without changing what the script does: every hook you add in JavaScript leaves a trace the script can notice, and a script that notices it is being studied can take a different path.

Quick facts

GoalRecord which browser APIs a page calls, for analysis
Naive approachMonkey-patch functions in page JavaScript
Why it failsPatched functions are observable via toString, descriptors, frames
EscalationContent script -> CDP -> OOPIF hooks -> engine fork
Faithful layerObservation below JavaScript, inside the engine (Blink)

The observation problem

To understand how a site fingerprints visitors you need to see the script run from the inside: which properties it reads, which canvas operations it performs, what it sends back. The obvious move is to replace the real functions with wrappers that log and then call the original. This is monkey-patching, and it works -- until the script checks whether it is being watched. Anti-bot collectors do exactly that, so a reverse engineer ends up in an escalation ladder where each rung is harder to detect than the last.

The escalation ladder

The rungs, from easiest to least intrusive:

Each JS-level rung is fragile because of one fact: a hooked function never looks exactly like a native one. The page can ask, and toString gives it away.

Why this mirrors the coherence problem

The same logic that exposes a naive fingerprint spoof exposes a naive hook. A fabricated value has to stay coherent with everything else the real machine reports, or lie detection catches the mismatch. A hooked function has to stay coherent with how a real native function presents itself -- same source string, same descriptor, same prototype -- or analysis catches that mismatch instead. In both cases the only durable answer is to work below the layer the script can inspect: at the engine level, not in JavaScript. Patching the engine itself avoids the entire leak class, which is why faithful instrumentation lives there rather than in page JavaScript.

Code example

javascript
// The naive hook everyone starts with -- and why it loses.
const real = HTMLCanvasElement.prototype.toDataURL;
HTMLCanvasElement.prototype.toDataURL = function (...args) {
  console.log('canvas fingerprint attempt', args);
  return real.apply(this, args);
};

// The page checks back, and the wrapper confesses:
HTMLCanvasElement.prototype.toDataURL.toString();
// -> "function (...args) { ... }"   (native would be "function toDataURL() { [native code] }")
HTMLCanvasElement.prototype.toDataURL.name;          // -> ""   (native: "toDataURL")
// Detected. The fix is to stop hooking in JavaScript at all.

Next in Instrumenting a browser · 2 of 6

The first wall: extensions cannot reach page scripts.

Why Can't a Browser Extension Hook Page JavaScript?

Related terms

Concept map

Concept map

How How Do You Instrument a Browser to Study Anti-Bot Scripts connects

The terms most directly tied to this one. Hover a node to see its neighbours, click to preview, drag to rearrange.

0 terms · 0 connections
You are here · Reverse Engineering
Building map…

Frequently asked questions

Why not just monkey-patch the functions you care about?

You can, and it captures the calls -- but a patched function does not look native. Its toString output, its name property, and its property descriptor all differ from the original, so any script that checks can tell it has been replaced and change its behaviour.

What is the most faithful way to observe a fingerprinting script without altering it?

Instrument the rendering engine itself. By adding observation points in Chromium's Blink layer -- below JavaScript -- you record calls like toDataURL without any JS-reachable wrapper existing, so your observation does not change what the script does.

Do I need to fork Chromium to study fingerprinting?

Not to start. CDP injection is enough for most observation. You only need an engine-level fork when the script you are studying actively checks for instrumentation and changes its behaviour if it finds any -- common in commercial anti-bot collectors.

Last updated: 2026-06-09