Chameleon时区伪装深度解析:完整替换Date API的逆向指南
【免费下载链接】chameleonWebExtension port of Random Agent Spoofer项目地址: https://gitcode.com/gh_mirrors/chamel/chameleon
Chameleon 是一款浏览器 WebExtension 插件,其核心能力之一是时区伪装——它通过完整替换Date对象及其全部原型方法,让网页代码"以为"用户身处任意时区。本文带你逆向这套机制,搞懂它是如何用不到 350 行代码把 20 多个 API 全部劫持的。
网站是怎么探测你真实时区的?
任何指纹检测库只需几行代码就能拿到你的时区:
new Date().getTimezoneOffset() // 返回 UTC 偏移分钟数 Intl.DateTimeFormat().resolvedOptions().timeZone // 返回时区 ID这两个值来自浏览器底层,普通脚本无法修改。Chameleon 的思路不是"欺骗返回",而是直接把整个Date类替换成自己写的版本,让所有依赖 Date 的代码都走它的逻辑。
整体架构:三个模块协作
时区伪装涉及三个核心文件:
| 模块 | 文件 | 职责 |
|---|---|---|
| 时区伪装代码 | src/lib/spoof/timezone.ts | 生成注入页面的 Date 替换脚本 |
| 时区数据源 | src/lib/tz.ts | 提供 560+ 个 IANA 时区 ID |
| 注入引擎 | src/lib/inject.ts | 组装最终注入脚本并执行 |
流程简述:用户在下拉框选择一个时区 → 引擎查询 IANA 时区信息 → 将伪装代码拼接为自执行脚本 → 注入当前页面及所有 iframe。
Date API 是如何被完整替换的?
构造函数劫持
替换的核心是一个自定义函数,它不再继承原生Date,而是内部调用原Date后重新包装:
- 调用
new ORIGINAL_DATE(...arguments)拿到真实时间 - 通过
Intl.DateTimeFormat把真实时间换算到目标时区 - 把换算结果缓存到
WeakMap中,后续 getter 都从缓存读取 - 若
new被省略(当函数调用),返回字符串而非对象
原型方法逐体重写
Date.prototype上的 20 个方法全部被替换,分为两组:
Getter 组(getDate、getDay、getFullYear、getHours、getMinutes、getMonth、getTimezoneOffset):
每个方法内部先检查缓存,若无缓存则调用modifyDate(this)重新计算,然后从目标时区的 Date 对象上取值。
Setter 组(setDate、setFullYear、setHours、setMilliseconds、setMonth、setSeconds、setTime):
先执行原始 setter 修改时间戳,再重新触发modifyDate更新缓存,保证后续读取一致。
字符串方法组(toDateString、toString、toTimeString、toJSON、toLocaleString、toLocaleDateString、toLocaleTimeString):
这组最关键——指纹库最爱看字符串输出。处理方式:
- 用目标时区 Date 调用原方法生成字符串
replaceName()函数把字符串中出现的真实时区缩写(长名/短名各 2 份)替换为目标时区缩写toString和toTimeString额外拼接GMT+偏移量和时区全称
所有 setter 方法都做了 NaN 兜底,防止无效日期导致崩溃。
Intl.DateTimeFormat 与 window.open 的同步伪装
只替换Date还不够——Intl.DateTimeFormat能绕过 Date 直接读取系统时区。Chameleon 在 src/lib/inject.ts 中做了双重拦截:
- 替换
Intl.DateTimeFormat构造函数:无论页面传什么参数,都在内部强制注入timeZone为目标时区 ID,再调用原始实现 - 拦截
window.open:新窗口继承同一个伪装过的Date和Intl.DateTimeFormat,防止跨窗口时区不一致
此外,所有 DOM 节点操作方法(appendChild、innerHTML等)被 patch,检测到<iframe>插入时自动向子 frame 注入同一套伪装代码。
反检测技巧:伪装成"原生代码"
指纹检测常通过以下方式识别 hook:
Date.toString() // 期望 "function Date() { [native code] }"Chameleon 的对策:
- 每个被替换函数都通过
Object.defineProperty重写toString,返回"function 方法名() { [native code] }" - 重写
name属性,让fn.name返回正确的方法名 - 将原始
Date引用重命名为随机标识符(随机字母+随机 base36 字符串),避免字符串搜索找到ORIGINAL_DATE Date.length被锁定为7,与原生构造函数参数数量一致Date.UTC、Date.now、Date.parse保持指向原生实现,防止typeof检查异常
时区数据从哪来?
560 个时区 ID 硬编码在 src/lib/tz.ts 中,格式为 IANA 标准(Asia/Shanghai、America/New_York等),同时携带 UTC 偏移字符串用于 UI 展示。实际偏移计算由moment-timezone库完成,它内置了完整的夏令时规则表,确保冬夏切换时偏移量自动正确。
配置时区伪装:默认值、IP 联动与白名单
在选项页 src/options/App.vue 中,时区下拉框提供三种模式:
- default:使用系统真实时区
- 具体时区 ID:伪装到指定时区
- ip:根据当前出口 IP 自动推断时区(通过后台请求 IP 定位服务获取)
白名单规则可以按站点粒度独立配置时区,与主设置互不干扰。设置变更在 src/lib/chameleon.ts 中通过tempStore.ipInfo.tz缓存 IP 推断结果,避免每次切换页面都重新请求。
快速上手:启用时区伪装
- 打开扩展选项页,找到Timezone下拉框
- 选择目标时区(如
Europe/London、Asia/Tokyo)或ip自动匹配 - 保存后刷新已打开的标签页,伪装即生效
- 在控制台执行
new Date().toString(),应看到目标时区的 GMT 偏移和时区名称
如果某些网站行为异常,可将该站点加入白名单并设为real(真实环境)或spoofed(使用伪装),避免全局规则影响登录验证等敏感场景。
总结
Chameleon 的时区伪装本质上是一套运行时 API 劫持框架:用Intl.DateTimeFormat做时区数学运算,用WeakMap缓存伪造结果,用Object.defineProperty抹平检测痕迹,最终实现Date全家桶 +Intl+ 跨窗口/跨 frame 三层一致的时区欺骗。理解这套机制后,你就能把它当作"浏览器 API 逆向工程"的教科书案例来参考。
【免费下载链接】chameleonWebExtension port of Random Agent Spoofer项目地址: https://gitcode.com/gh_mirrors/chamel/chameleon
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考