浏览器硬件参数欺骗:CPU核心数、内存大小、设备像素比的精准伪造
2026/6/11 4:29:48 网站建设 项目流程

在指纹浏览器的对抗中,当我们解决了 Canvas、WebGL、Audio 等高维度的渲染指纹后,往往会栽在几个最基础的硬件参数上:navigator.hardwareConcurrency(CPU 核心数)、navigator.deviceMemory(设备内存)和window.devicePixelRatio(设备像素比)。

风控系统对这三个参数的检测逻辑极其简单粗暴:逻辑一致性校验。如果你声称自己是顶配的 MacBook Pro,但hardwareConcurrency却返回2deviceMemory返回2,风控不需要懂任何底层渲染原理,一秒钟就能用规则引擎将你封杀。更致命的是,这三个参数不仅暴露了硬件本身,它们还是多维度指纹关联的锚点。一个真实的用户,其 ClientRects 的渲染精度必然受到 DPR 的制约,其 WebGL 的渲染耗时必然与 CPU 核心数相关。

劣质指纹浏览器往往在 JS 层覆写这几个属性,却不知现代风控通过iframe隔离、Worker线程探测,能轻易撕开 JS Hook 的伪装。

本文将摒弃水话,直插 Chromium 的 C++ 内核,在 Blink 引擎的数据源头,实现这三个硬件参数的精准、无痕、全上下文伪造。

一、 核心痛点:为什么 JS 层覆写必死?

1. Web Worker 的幽灵

navigator.hardwareConcurrency最常见的探测手段不是在主线程读取,而是在 Web Worker 中读取。
主线程的 JS 环境你可以随意 Hook,但 Worker 运行在独立的线程,拥有独立的 V8 上下文。你无法通过主线程的代码去污染 Worker 内部的NavigatorPrototype。风控只需一行代码:

letworker=newWorker('data:text/javascript,self.postMessage(navigator.hardwareConcurrency)');

你的伪装瞬间土崩瓦解。

2. 设备像素比的渲染脱节

如果你在 JS 层拦截window.devicePixelRatio返回2.0,但底层渲染引擎(Blink)依然认为当前设备的 DPR 是1.0,会导致灾难性的后果:

  • 媒体查询穿透window.matchMedia('(min-resolution: 2dppx)')返回false
  • Canvas 模糊异常:Canvas 按照 1x 渲染,你却告诉 JS 它是 2x,风控通过比对 Canvas 的物理像素和逻辑像素,直接判定环境伪造。

3. 内存探测的时序陷阱

deviceMemory是一个只读属性,且风控会测量其读取耗时。JS 层的Object.defineProperty会引入微秒级的延迟,而真实的底层 C++ 属性读取是纳秒级的。
唯一的解法:斩断 JS 层,深入 Blink 引擎的 C++ 实现,从源头上替换操作系统返回的真实数据。

二、 斩断 CPU 核心数:全上下文物理级伪造

navigator.hardwareConcurrency反映的是操作系统的逻辑 CPU 核心数。Chromium 获取这个值的逻辑是:在主进程调用系统 API(如 Windows 的GetSystemInfo,Mac 的sysctlbyname),然后通过 Mojo IPC 传递给渲染进程缓存。
我们要做的,是在渲染进程(Blink)收到这个缓存值之前,将其替换。

1. 主线程 Navigator 层拦截

精准坐标third_party/blink/renderer/core/frame/navigator.cc
找到hardwareConcurrency的 C++ 实现:

unsignedNavigator::hardwareConcurrency()const{// 原始逻辑:返回从 Browser 进程 IPC 传来的真实核心数// return GetFrame()->Loader().GetServiceWorkerContainerHost()->hardware_concurrency();// 【指纹浏览器拦截点】constauto&fp_config=FingerprintConfig::GetInstance();if(fp_config->HasOverride("hardwareConcurrency")){returnfp_config->GetUnsignedInt("hardwareConcurrency");// 返回预设的核心数}returnbase::SysInfo::NumberOfProcessors();// 兜底}

2. Web Worker 上下文拦截(致命一击)

这是区分工业级指纹浏览器和玩具的分水岭。Worker 中也有Navigator对象,但它不是blink::Navigator类,而是blink::WorkerNavigator类。
精准坐标third_party/blink/renderer/core/workers/worker_navigator.cc

unsignedWorkerNavigator::hardwareConcurrency()const{// 必须在这里做同样的拦截!constauto&fp_config=FingerprintConfig::GetInstance();if(fp_config->HasOverride("hardwareConcurrency")){returnfp_config->GetUnsignedInt("hardwareConcurrency");}returnbase::SysInfo::NumberOfProcessors();}

避坑实录:如果你只改了主线程的Navigator,没改WorkerNavigator,风控只要对比主线程和 Worker 线程返回的核心数是否一致,就能立刻识破你的双轨制伪装。

三、 斩断设备像素比:渲染管线的深度联动

DPR(Device Pixel Ratio)是物理像素与逻辑像素的比值。它不仅是一个 JS 变量,更是整个 Chromium 渲染管线的基石。
如果你只改了window.devicePixelRatio的返回值,而没有改底层排版引擎的缩放因子,你的浏览器在高清屏上会呈现出一种诡异的“逻辑自洽但视觉错乱”的状态。

1. JS 属性与媒体查询的双重堵截

精准坐标third_party/blink/renderer/core/frame/screen.cc(或window.cc视 Chromium 版本而定)

doubleWindow::devicePixelRatio()const{// 原始逻辑:返回 Frame 的缩放因子// return GetFrame()->DevicePixelRatio();// 【指纹浏览器拦截点】constauto&fp_config=FingerprintConfig::GetInstance();if(fp_config->HasOverride("devicePixelRatio")){returnfp_config->GetDouble("devicePixelRatio");}return1.0;}

仅仅拦截 JS 属性是不够的,风控常用 CSS 媒体查询探测 DPR:
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { ... }
媒体查询的求值不经过 JS,而是在 Blink 的 CSS 解析器中直接计算的。
精准坐标third_party/blink/renderer/core/css/media_query_evaluator.cc
在评估device-pixel-ratio特征时,Chromium 会调用GetFrame()->DevicePixelRatio()。因此,最优雅的做法是直接在 Frame 层面修改 DevicePixelRatio 的源头
终极拦截点third_party/blink/renderer/core/frame/local_frame.cc

floatLocalFrame::DevicePixelRatio()const{// 【源头拦截】constauto&fp_config=FingerprintConfig::GetInstance();if(fp_config->HasOverride("devicePixelRatio")){returnstatic_cast<float>(fp_config->GetDouble("devicePixelRatio"));}// 兜底:真实计算returnGetPage()->DeviceScaleFactorDeferred();}

效果:一旦在这里修改,从 JS 的window.devicePixelRatio,到 CSS 媒体查询,再到 Blink 排版引擎的布局计算,所有模块获取的 DPR 都将强制对齐为你预设的值,实现了全局逻辑自洽。

2. Canvas 渲染分辨率的联动(防模糊关键)

如果你将 DPR 改为 2.0,Canvas 必须以 2 倍物理分辨率渲染,再缩放回逻辑尺寸,否则 Canvas 画面会模糊,风控通过像素比对即可发现物理分辨率与声明 DPR 不符。
这不需要我们在 C++ 层额外处理,因为 Blink 的 Canvas 绑定层在分配缓冲区时,会自动调用DevicePixelRatio()来乘以宽高。只要我们在LocalFrame层面成功伪造了 DPR,Canvas 的渲染分辨率也会自动适配,这是 Chromium 架构设计的精妙之处。

四、 斩断设备内存:跨进程的数据篡改

navigator.deviceMemory是一个粗粒度的值(0.25, 0.5, 1, 2, 4, 8),它反映的是系统可用内存。
这个值的获取流程极度危险:它只能在 Browser 进程中通过系统 API 获取,然后通过 Mojo 发送给 Renderer 进程。Renderer 进程处于沙箱中,根本无权查询系统内存。
如果在 JS 层 Hook,风控在 Worker 中读取就会穿帮。如果在 Blink 层拦截,我们需要处理繁琐的 Mojo 回调。

最暴力也最稳妥的解法:直接修改 Browser 进程的系统 API 返回值

我们不需要修改复杂的 IPC 逻辑,只需要在 Browser 进程获取真实内存的源头动刀。
精准坐标base/system/sys_info.cc
Chromium 获取系统内存的核心函数是AmountOfPhysicalMemoryImpl

// staticint64_tSysInfo::AmountOfPhysicalMemoryImpl(){// 【指纹浏览器拦截点 - Browser 进程】constauto&fp_config=FingerprintConfig::GetInstance();if(fp_config->HasOverride("deviceMemoryGB")){// 将 GB 转换为 Bytes 返回returnstatic_cast<int64_t>(fp_config->GetDouble("deviceMemoryGB"))*1024*1024*1024;}// 兜底:调用真实 OS API#ifdefined(OS_WIN)...#elifdefined(OS_MACOSX)...#endif}

底层逻辑剖析

  1. Browser 进程启动时,会调用AmountOfPhysicalMemoryImpl()获取真实内存。
  2. 我们在这里拦截,返回预设的内存大小(如 8GB 对应的字节数)。
  3. Browser 进程将这个假数据通过正常的 Mojo 通道发给 Renderer 进程。
  4. Renderer 进程的 Blink 引擎和 Worker 线程拿到这个假数据,按照标准算法截断(如 8GB 截断为 8),返回给 JS 的navigator.deviceMemory
    这种做法的威力
    它不仅骗过了 JS 层,甚至骗过了 Chromium 自身的内存管理模块。浏览器内部的缓存分配策略、OOM 杀手机制,都会基于这个伪造的内存值进行计算,使得整个运行时的内存压力特征完全符合你声明的配置,彻底杜绝了行为特征与声明参数不一致的风险。

五、 避坑实录:硬件伪造的三个致命悖论

在实战中,单点伪造很容易,难的是不产生逻辑悖论

1. 核心数与并发能力的悖论

你声称hardwareConcurrency为 16,但风控 JS 启动了 16 个密集计算的 Web Worker,结果由于宿主机真实核心只有 4 个,导致执行时间比真实的 16 核机器慢了 3 倍。
破局:指纹配置必须与宿主机性能相匹配。如果宿主机是 4 核,你可以伪装成 4 核或 8 核,绝不能伪装成 32 核。高级的风控会通过Performance.now()测量多线程并发任务的耗时,反推真实的硬件算力。

2. 内存大小与 OOM 崩溃的悖论

你声称deviceMemory为 8,但风控 JS 尝试分配 6GB 的 ArrayBuffer(真实机器只有 4GB 内存)。此时由于底层 OS 内存不足,浏览器会直接崩溃。
破局:不要过分夸大内存容量。将伪装值设定在宿主机真实内存的 50% - 100% 之间是最安全的策略。

3. DPR 与屏幕分辨率的悖论

你声称devicePixelRatio为 2,但screen.width返回 1920,window.innerWidth也返回 1920。在真实的 2x 设备上,如果物理分辨率是 3840,逻辑宽度应该是 1920。如果你的逻辑宽度占满了物理宽度,说明你根本不是 2x 设备。
破局:DPR、Screen 分辨率、Viewport 宽高必须作为一组强关联的配置同时下发。修改 DPR 时,必须同步调整Screen类的宽高返回值,确保物理像素 = 逻辑像素 * DPR的绝对数学等式成立。

六、 结语:微观世界的宏观法则

硬件参数的伪造,看似只是修改几个数字,实则是在重构浏览器运行时的物理法则。

从 CPU 的算力分配,到内存的边界,再到像素的密度,这些参数构成了浏览器存在的物质基础。当我们深入 Chromium 的 C++ 内核,在LocalFrameSysInfoWorkerNavigator这些底层枢纽中偷天换日时,我们创造的不只是一个骗过风控的脚本,而是一个逻辑自洽、行为一致的赛博硬件幽灵。

至此,浏览器本地的环境伪装已至化境。然而,浏览器从来不是一座孤岛,当它向服务器发出第一个请求时,网络层的较量才刚刚开始。TLS 指纹、HTTP/2 帧特征、QUIC 协议,这些更底层的网络协议级指纹,将是是更严格的风控。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询