指纹浏览器开发教程五:浏览器内存信息相关能力该怎么改
[TOC]TOC导语浏览器内存信息是指纹识别中常用的一个维度。网站可以通过 navigator.deviceMemory 和 performance.memory 等接口读取设备的大致内存容量和当前内存使用情况。对于指纹浏览器来说这个信息需要能够按环境单独配置让每个环境呈现出不同的内存特征。这篇教程会讲解如何在 Chromium 源码中修改这些接口的返回值。内存信息暴露的两个主要接口接口一navigator.deviceMemory这是一个只读属性返回设备的大致内存容量以 GB 为单位取值为 0.25、0.5、1、2、4、8 等离散值。javascript// 网页中读取console.log(navigator.deviceMemory); // 输出: 8接口二performance.memory这是 Chrome 特有的扩展接口返回更详细的内存信息javascript// 网页中读取console.log(performance.memory);// 输出:// {// usedJSHeapSize: 12345678,// totalJSHeapSize: 23456789,// jsHeapSizeLimit: 2197815296// }这两个接口的返回值都可以被指纹检测脚本用来判断这个浏览器是否在虚拟机里或者多个账号是否来自同一台物理机。修改 navigator.deviceMemory定位源码位置navigator.deviceMemory 的实现位于 Blink 渲染引擎中thirdparty/blink/renderer/core/frame/navigatordevicememory.ccthirdparty/blink/renderer/core/frame/navigatordevicememory.h理解现有实现打开 navigatordevicememory.cc你会看到类似这样的代码cppfloat NavigatorDeviceMemory::deviceMemory() const {// 从系统获取实际内存容量int64t physicalmemory base::SysInfo::AmountOfPhysicalMemory();// 转换为 GB 并取离散值return GetDeviceMemoryInGB(physicalmemory);}修改思路不是从系统读取真实内存而是从环境配置中读取预设值cppfloat NavigatorDeviceMemory::deviceMemory() const {// 尝试从环境配置读取std::string configvalue GetEnvironmentConfig(“devicememory”);if (!configvalue.empty()) {return std::stof(configvalue);}// 兜底返回系统真实值int64t physicalmemory base::SysInfo::AmountOfPhysicalMemory();return GetDeviceMemoryInGB(physicalmemory);}配置传递这个修改依赖于参数传递链路参考教程四。控制程序在启动浏览器时需要把 devicememory 参数通过配置文件或命令行传入。编译验证修改后重新编译 Blink 层bashautoninja C out/Default blink启动浏览器在控制台输入 navigator.deviceMemory验证返回值是否与配置一致。修改 performance.memory定位源码位置performance.memory 的实现位于thirdparty/blink/renderer/core/timing/memoryinfo.ccthirdparty/blink/renderer/core/timing/memoryinfo.hthirdparty/blink/renderer/core/timing/performance.cc理解现有实现MemoryInfo 类封装了内存信息的返回cppclass MemoryInfo : public ScriptWrappable {public:unsigned long long usedJSHeapSize() const;unsigned long long totalJSHeapSize() const;unsigned long long jsHeapSizeLimit() const;private:// 内部通过 V8 获取实际内存数据v8::HeapStatistics heapstatistics;};修改思路performance.memory 的数据来自 V8 引擎的真实堆统计不建议直接篡改因为会影响 JS 引擎的内存管理。更合理的做法是1. 保持 usedJSHeapSize 和 totalJSHeapSize 为真实值它们反映的是当前运行状态2. 修改 jsHeapSizeLimit 的返回值让它与环境配置的内存容量对应cppunsigned long long MemoryInfo::jsHeapSizeLimit() const {// 从环境配置读取目标内存容量std::string configmemory GetEnvironmentConfig(“devicememory”);if (!configmemory.empty()) {float memorygb std::stof(configmemory);// V8 堆限制通常为物理内存的某个比例return staticcastunsigned long long(memorygb 1024 1024 1024 0.8);}// 兜底返回 V8 原始值return heapstatistics.heapsizelimit();}为什么只改 jsHeapSizeLimitusedJSHeapSize 和 totalJSHeapSize 反映的是实时状态篡改后可能导致 JS 引擎行为异常jsHeapSizeLimit 是一个上限值修改后不会影响实际运行但能让指纹检测脚本读到合理的内存容量很多指纹检测脚本主要关注 jsHeapSizeLimit 和 deviceMemory 的对应关系验证修改效果本地验证1. 启动修改后的浏览器2. 打开 DevTools 控制台3. 输入以下命令javascript// 验证 deviceMemoryconsole.log(‘deviceMemory:’, navigator.deviceMemory);// 验证 performance.memoryconsole.log(‘memory:’, performance.memory);// 验证两者是否对应console.log(‘ratio:’, performance.memory.jsHeapSizeLimit / (navigator.deviceMemory 1024 1024 1024));自动化测试建议写一个 Playwright 脚本自动验证内存信息的返回值javascriptconst { chromium } require(‘playwright’);(async () {const browser await chromium.launch({executablePath: ‘/path/to/easybr/chrome’});const page await browser.newPage();await page.goto(‘about:blank’);const deviceMemory await page.evaluate(() navigator.deviceMemory);const memoryInfo await page.evaluate(() performance.memory);console.log(‘Device Memory:’, deviceMemory);console.log(‘JS Heap Limit:’, memoryInfo.jsHeapSizeLimit);await browser.close();})();常见问题问题一修改后编译报错检查是否正确引入了 GetEnvironmentConfig 的头文件确认 std::stof 的输入字符串格式正确如 “8” 而不是 “8GB”清理编译缓存autoninja C out/Default clean然后重新编译问题二deviceMemory 返回值不是离散值Chromium 的规范要求 deviceMemory 返回离散值0.25、0.5、1、2、4、8。如果配置值是 6需要映射到最接近的离散值cppfloat GetDiscreteMemoryValue(float rawvalue) {const float kDiscreteValues[] {0.25, 0.5, 1, 2, 4, 8};// 找到最接近的离散值float closest kDiscreteValues[0];for (float v : kDiscreteValues) {if (std::abs(v rawvalue) std::abs(closest rawvalue)) {closest v;}}return closest;}问题三修改对某些网站不生效检查是否在正确的渲染进程中修改主进程修改不会影响已存在的渲染进程确认网站没有使用 Service Worker 缓存旧的 JS 执行结果尝试无痕模式或清除缓存后重新测试EasyBR 的内存指纹实践EasyBR 在内存指纹的实现上有几个经验配置粒度不提供任意数值输入而是提供几个预设选项2GB、4GB、8GB、16GB。这样既满足大多数场景又避免用户输入不合理的值。一致性检查在控制程序中当用户选择 8GB 内存时同时检查分辨率、CPU 核心数等参数是否匹配。避免出现8GB 内存 1366x768 分辨率 双核 CPU这种不合理的组合。动态调整对于 performance.memory 的 usedJSHeapSizeEasyBR 选择不改。原因是1. 篡改实时内存数据可能导致 JS 引擎的 GC 策略异常2. 大多数指纹检测只关注 deviceMemory 和 jsHeapSizeLimit 的对应关系3. 保持 usedJSHeapSize 真实反而更自然结语内存信息指纹的修改是指纹浏览器开发中相对简单但很重要的一环。它涉及 Blink 层的 JS 接口修改是理解如何在 Chromium 中定制浏览器行为的一个好起点。完成这个修改后你就掌握了从配置传递到内核生效的完整链路。后续修改其他指纹参数如 CPU 核心数、UA、时区等思路都是类似的。下一篇教程我们会进入 CPU 指纹信息的修改。