The last part of this series steps away from the binary internals and looks at a runtime behavior: how an anti-detection browser turns a proxy exit IP into a timezone, and why that is a common source of fingerprint inconsistency.
This post answers three questions:
- How does the browser use its local GeoIP database?
- What is the timezone consistency rate across a few hundred measured exit IPs?
- Where did the bundled database come from, and what data generated it?
Method: read-only static analysis plus a hand-written pure-Python mmdb reader, cross-validated against the official MaxMind reader on a per-IP basis. No project file was modified.
A note on detail. This post keeps the measurements and conclusions — they are the interesting, shareable part — while omitting the specific operational parameters that would make it a step-by-step guide.
1. Summary
| Question | Conclusion |
|---|---|
| How the DB is used | exit IP -> local lookup for country/timezone/coordinates -> used as timezone/language/geo defaults; explicit flags can override |
| Exit-IP measurement | 53.1% consistent with the configured timezone; 42.7% US non-Eastern mismatch; 4.2% resolved to an unexpected country (gateway leak); 46.9% mismatch total |
| What the file is | standard MaxMind DB format, with a custom database type string, built on a specific date |
| Where the data came from | no vendor attribution / license fields; mixed-granularity data fingerprint + partial agreement with a public IP-geo endpoint -> a dated snapshot packaged with standard tooling; the upstream cannot be uniquely determined from the file |
| Risk | the snapshot ages; newly allocated / re-announced IPs resolve incorrectly; all bundled copies are byte-identical (same build artifact) |
2. The data path: how an exit IP becomes a timezone
At a structural level:
- Exit IP discovery: reach an embedded IP-echo service through the proxy to get the exit IP. A flag lets you swap in a custom service, and the response is parsed in a well-known public-IP-API shape. If the exit is already known, another flag skips discovery.
- Local lookup: parse the bundled GeoIP database next to the executable, using an embedded MaxMind reader library. The GeoIP decision sends no external request — this is an explicit offline design.
- Apply defaults: country / timezone / coordinates become the timezone, language, and geo-coordinate defaults. When the profile value is
auto, it follows the IP; explicit timezone/locale/language/location flags have the highest priority. - Failure fallback: on no match, it resets to a neutral, region-less UTC fallback.
The vendor’s own stance: country-level is reliable, city-level approximate, coordinates are not GPS-accurate.
3. A few hundred exits measured: 46.9% timezone mismatch
Querying the actual bundled database per exit:
| Timezone decision | Share | Verdict |
|---|---|---|
| Consistent with the configured (Eastern) identity | 53.1% | consistent |
| US non-Eastern | 42.7% | mismatch |
| Resolved to an unexpected country | 4.2% | severe mismatch |
The unexpected-country rows all clustered in one small IP range, and in each case “gateway host == exit public IP” — the tunnel was not established, so the gateway address was looked up directly. The external source used when the CSV was generated labeled these rows as US, while the browser’s bundled database said otherwise — when two data sources disagree, the browser only trusts its own bundled database.
Root causes (by impact):
- The configuration hard-codes a fixed timezone/locale identity, injected on every start, overriding the profile’s
auto-> non-matching exits inevitably mismatch. - The mobile proxy pool rotates across the whole country on a short interval (the code’s own comment names it as a trigger for a “browsing activity paused” response).
- Snapshot freshness (section 6).
4. Database forensics
The bundled file is a large MaxMind-format database. Key physical evidence, without publishing the file itself:
- Metadata: standard binary format version, a custom database type string, a self-written description, a specific build date, IPv6-first storage, and empty language metadata (where an official city database would list supported languages).
- Records have only four keys: country code, timezone, latitude, longitude. No city/asn/org/subdivision/postal.
- Coordinates are stored as float32 (an official GeoLite2 city database uses double) -> the data was repackaged, not a renamed official database.
- No attribution / license fields: the whole file contains only the format’s own binary marker; no vendor strings, no license strings, no custom source key. The vendor’s public docs never mention the data source.
5. Data fingerprint: source judgment
Comparing the database’s values against a public IP-geo endpoint for a set of well-known anchor IPs:
- Mixed granularity: some IPs have city-level coordinates; those without fall back to a repeated country centroid (the US default centroid is exactly the well-known GeoLite2 US country centroid).
- The four-field schema is identical in shape to a public IP-API endpoint, consistent with the parse template embedded in the binary.
- Some IPs match the live endpoint to several decimals, others do not -> it is not a live export; it is one or more dated snapshots, or a mix.
Conclusion (strong inference): the developer used standard MaxMind tooling on a specific date to package one or more geo data sources into a purpose-built four-field database shipped with the app. The upstream supplier cannot be uniquely determined from the file itself — no attribution, no license, repackaged float32 coordinates. This is consistent with the vendor’s “offline, no city-level guarantee” stance.
6. Freshness risk and alignment advice
- The snapshot ages. IPs newly allocated or re-announced in the interval will resolve incorrectly; an offline pre-check can catch this before deployment.
- Alignment advice (by priority): drop the hard-coded timezone from long-lived identities and let the profile’s
autofollow the exit; or fix on a static ISP proxy geographically matched to the profile; restrict the mobile pool to the matching region or lengthen the sticky duration; pass the known exit directly to save a discovery round-trip. - All bundled copies are identical -> the database can only be updated by redistributing the whole application; there is no sign of a local hot-update mechanism.
7. An offline verification tool
I wrote a pure-Python MMDB reader (no third-party dependencies), matching the official reader per-IP on the anchor set. It implements the record layout, data-pointer base, IPv4-in-IPv6 prefix tree, float decoding, and CSV batch timezone-consistency statistics. It is the kind of small tool that turns “the vendor says it is offline and approximate” into “here is the exact mismatch rate on my traffic”.
8. Open questions
| # | Question | Status |
|---|---|---|
| 1 | Unique determination of the upstream supplier (GeoLite2 base vs IP-API export vs mix) | no attribution in the file; cannot be determined from the file alone |
| 2 | Whether / how the vendor updates the database | no update-mechanism evidence found |
| 3 | Whether some country-level differences are due to carrier re-announcement | needs historical routing data |
9. Safety boundary for this stage
- Read-only throughout: database and binary read-only; all scripts in a scratch area.
- Redaction maintained: the embedded third-party key value does not appear in the doc.
- Minimal external contact: only a small set of public anchor IPs was queried against a public endpoint (no key, no private IPs) for data fingerprinting; no configured API key was touched.
10. Series wrap-up
Six parts, one arc:
- Part 1 — what the thing is: a closed-source Chromium fork, an encrypted profile format, a tiered subscription.
- Part 2 — static recon: it does not phone home; the expiry is local; the outer plaintext expiry is a dead field.
- Part 3 — the crypto layer: a runtime gate, a signature lock, no fallback load.
- Part 4 — binary patching: crash traps, two symmetric guards, both-paths verification, re-signing.
- Part 5 — the payoff: the full client-side recovery chain, a deterministic field layout, and the hard wall at the signature.
- Part 6 — GeoIP: exit IP -> timezone, a 46.9% mismatch, and database forensics.
What I hope a reader takes away is not “how to defeat this one product” but a method: scan strings and URLs before disassembling; distinguish what code can read from what it appears to read; treat a signature as the lock and encryption as the data protector; verify both code paths; keep an errata list. Those habits transfer to any closed-source binary you need to understand.
If this direction is relevant to something you are building — a fingerprint system, a browser-level anti-detection stack, or the reverse-engineering work behind it — I would genuinely like to hear about it. The whole point of writing this down is to study and discuss it with people who work on the same problems.
Series navigation:
- Part 1: What an Anti-Detection Browser Actually Is
- Part 2: Static Recon Without Running It
- Part 3: The Crypto Layer and the Runtime Gate
- Part 4: Binary Patching as a Research Exercise
- Part 5: Analyzing the Encrypted Profile Offline
- Part 6: GeoIP, Timezone Mismatch, and Database Forensics (this post)
Blog: chasays.github.io
WeChat: cdtfug — welcome to follow and discuss, or addxiaorikon WeChat.