指纹浏览器虚拟机检测技术分析
Fingerprint Playground 虚拟机检测原理分析
目标站点:
https://demo.fingerprint.com/playground
采集时间:2026-06-18
本报告基于本地 Firefox 内核domtrace / jscall / http落盘、Ruyipage 自动化网络记录和页面快照分析。结论用于技术研究与公众号解析,不等价于 Fingerprint 官方服务端模型。
一句话结论
Fingerprint Playground 的“Virtual Machine”不是前端 JS 里直接写死的一条规则,而是:
浏览器 SDK 采集环境指纹
-> 加密/压缩后提交到 Fingerprint 边缘接口
-> 服务端返回 visitor_id / event_id
-> Playground 再用 event_id 查询服务端事件详情
-> 服务端事件详情里给出 virtual_machine 与 virtual_machine_ml_score
本次样本最终结果:
virtual_machine: false
virtual_machine_ml_score: 0.155
suspect_score: 15
bot: not_detected
tampering: false
developer_tools: true
虽然 WebGL renderer 暴露了 Microsoft Basic Render Driver 这种软件渲染/回退渲染特征,但服务端没有直接判定为虚拟机。原因是它更像一个多信号评分模型:WebGL 是强信号之一,但不是唯一证据。
请求链复盘
核心链路如下:
1. GET /playground
加载 Next.js 页面和 playground chunk
2. GET /DBqbMN7zXxwl4Ei8/web/v4/ahNo3Idb3RiQg69bQglE?ci=jsl/4.0.0
加载 Fingerprint Pro JS agent
3. GET /DBqbMN7zXxwl4Ei8/tdSBLg/81tfzl/e?region=us&q=ahNo3Idb3RiQg69bQglE
获取一段短文本初始化材料
4. POST /DBqbMN7zXxwl4Ei8?region=us&ci=js/4.1.0&q=ahNo3Idb3RiQg69bQglE
SDK 提交加密后的环境采集 body,响应 event_id / visitor_id / suspect_score
5. POST /api/event/v4/1781762212213.gMJxBp
Playground 用 event_id 查询完整服务端事件,拿到 Smart Signals 与 raw_device_attributes
本地证据:
| 证据 | 文件 |
|---|---|
| SDK agent 源码 | tmp/static/37_demo.fingerprint.com_DBqbMN7zXxwl4Ei8_web_v4_ahNo3Idb3RiQg69bQglE_ci_jsl_4.0.0.js |
| SDK 首次识别响应 | tmp/trace/http_packet/000025_POST_200_demo_fingerprint_com_DBqbMN7zXxwl4Ei8_pid12988_380000057.http_packet.json |
| Playground 事件详情响应 | tmp/trace/http_packet/000028_POST_200_demo_fingerprint_com_api_event_v4_1781762212213_gMJxBp_pid12988_38000005a.http_packet.json |
| 页面入口 chunk | tmp/static/19_demo.fingerprint.com__next_static_chunks_2473-1eedb759263b52d2.js.js |
| DOM/JSCALL 运行轨迹 | tmp/trace/domtrace/trace_process_12492.jsonl、tmp/trace/jscall/trace_jscall_process_12492.jsonl |
前端只负责展示,判定来自服务端事件
页面 chunk 中可以看到 Playground 的逻辑:
getData({ linkedId, timeout, immediate: true })
-> 得到 agentResponse.event_id
-> fetch("/api/event/v4/" + event_id, { method: "POST" })
-> 渲染 server response 里的 virtual_machine
也就是说,virtual_machine 不是页面本地计算的字段。前端只是把服务端事件 JSON 中的字段展示为:
Virtual Machine: Not detected
服务端事件响应中的关键片段:
{
"event_id": "1781762212213.gMJxBp",
"sdk": {
"platform": "js",
"version": "4.1.0",
"integrations": [
{ "name": "fingerprintjs-pro-cloudfront", "version": "2.2.0" },
{ "name": "fingerprintjs-pro-react", "version": "3.0.0" }
]
},
"virtual_machine": false,
"virtual_machine_ml_score": 0.155,
"raw_device_attributes": {}
}
上面的 raw_device_attributes 在真实响应里是完整对象,README 中省略展开。
实际检测信号与证据链
这一节只列本次页面真实采到、服务端真实返回、trace 真实出现的信号。证据分四层:
页面展示值 -> HTTP 事件 JSON 字段 -> DOM trace 调用 -> agent 源码函数
页面侧可见证据在 tmp/capture/page_visible_text.txt:
Virtual Machine Not detected
JavaScript Agent Response:
event_id: "1781762212213.gMJxBp"
visitor_id: "orsF9S092M2X9NOJNLmr"
suspect_score: 15
Server API Response:
virtual_machine: false
virtual_machine_ml_score: 0.155
服务端事件响应在 tmp/trace/http_packet/000028_POST_200_demo_fingerprint_com_api_event_v4_1781762212213_gMJxBp_pid12988_38000005a.http_packet.json,核心字段路径都在 responseBody.text 里:
| 信号 | 服务端字段路径 | 本次实际值 |
|---|---|---|
| VM 最终结论 | virtual_machine / virtual_machine_ml_score |
false / 0.155 |
| WebGL renderer | raw_device_attributes.webgl_basics.renderer |
ANGLE (Microsoft, Microsoft Basic Render Driver Direct3D11 vs_5_0 ps_5_0), or similar |
| WebGL vendor/version | raw_device_attributes.webgl_basics.vendor/version |
Mozilla / WebGL 1.0 (OpenGL ES 2.0 Chromium) |
| WebGL 扩展摘要 | raw_device_attributes.webgl_extensions |
context_attributes、parameters、shader_precisions、extensions、extension_parameters 都是 hash |
| Canvas | raw_device_attributes.canvas |
winding=true,geometry=64eb3eab062851c8cbed87bca98b6f6c,text=c17750b6edad27927cb0401bca6ec2c6 |
| 字体 | raw_device_attributes.fonts/font_hash |
Calibri、Franklin Gothic、HELV、MS UI Gothic、Marlett、Segoe UI Light、SimHei、Small Fonts,hash=1affeb088cac7b5efc90e012a4cac857 |
| Audio | raw_device_attributes.audio |
35.749972093850374 |
| Math | raw_device_attributes.math |
5f030fa7d2e5f9f757bfaf81642eb1a6 |
| Screen | raw_device_attributes.screen_resolution/color_depth |
[920, 1961],24 |
| OS/平台 | user_agent、browser_details、raw_device_attributes.platform/oscpu |
Firefox 151 / Windows 10 / Win32 / Windows NT 10.0; Win64; x64 |
| 资源能力 | hardware_concurrency/plugins |
CPU 核心数 8,5 个 PDF 插件 |
| 存储能力 | session_storage/local_storage/indexed_db/cookies_enabled |
全部为 true |
| 时区/VPN | timezone/timezone_offset/vpn_methods |
本机时区 Asia/Shanghai,VPN 命中 timezone_mismatch 和 os_mismatch |
对应的 agent DOM trace 证据都来自 tmp/trace/domtrace/trace_process_12492.jsonl,并且 stack 命中 https://demo.fingerprint.com/DBqbMN7zXxwl4Ei8/web/v4/ahNo3Idb3RiQg69bQglE?ci=jsl/4.0.0:
| 信号 | 次数 | trace 位置 | 调用与返回 | agent 函数 |
|---|---|---|---|---|
| WebGL 基础参数 | 92 | trace_process_12492.jsonl:136349 |
getParameter(7938) -> WebGL 1.0 (OpenGL ES 2.0 Chromium) |
lX@5:42016 |
| WebGL 扩展列表 | 1 | trace_process_12492.jsonl:136357 |
getSupportedExtensions() -> 含 WEBGL_debug_renderer_info 等 27 项 |
EX@5:42654 |
| Shader 精度 | 12 | trace_process_12492.jsonl:136505 |
getShaderPrecisionFormat(35632, 36336) |
eG@5:7312 |
| Canvas 文本 | 4 | trace_process_12492.jsonl:133334 |
fillText("Cwm fjordbank gly ...", 2, 15) |
bG@5:14401 |
| Canvas 输出 | 4 | trace_process_12492.jsonl:133338 |
toDataURL() -> PNG data URL,length=7806,trace hash=d0383832c39a7dd8 |
lQ@5:95440 |
| Canvas 像素 | 2 | trace_process_12492.jsonl:133806 |
getImageData(1, 1, 958, 718) |
agent 内部 D@5:182853 |
| 插件 | 7 | trace_process_12492.jsonl:135696 |
navigator.plugins -> PluginArray length=5 |
aB@5:38510 |
| MIME | 6 | trace_process_12492.jsonl:135972 |
navigator.mimeTypes -> MimeTypeArray length=2 |
IF@5:84950 |
| CPU 核心 | 2 | trace_process_12492.jsonl:135657 |
navigator.hardwareConcurrency -> 8 |
tB@5:37599 |
| 平台 | 2 | trace_process_12492.jsonl:135690 |
navigator.platform -> Win32 |
TB@5:38229 |
| OSCPU | 2 | trace_process_12492.jsonl:135629 |
navigator.oscpu -> Windows NT 10.0; Win64; x64 |
nB@5:37003 |
| 语言 | 5 | trace_process_12492.jsonl:135635 |
navigator.languages -> ["en-US", "en"] |
rB@5:37171 |
| 触摸能力 | 3 | trace_process_12492.jsonl:135742 |
navigator.maxTouchPoints -> 0 |
cB@5:38825 |
| 屏幕宽高 | 4/4 | trace_process_12492.jsonl:132967、132970 |
screen.width=1961,screen.height=920 |
ud@5:133730 |
| 颜色深度 | 1 | trace_process_12492.jsonl:135642 |
screen.colorDepth -> 24 |
DB@5:37479 |
| Storage | 21/1/2 | trace_process_12492.jsonl:135672、135667、133280 |
localStorage、sessionStorage、indexedDB 均可读 |
qB/oB/YH |
| 随机源 | 113 | trace_process_12492.jsonl:132345 |
crypto.getRandomValues(Uint32Array) |
oU@5:168263 |
| 时区偏移 | 1 | trace_process_12492.jsonl:133857 |
Date.getTimezoneOffset() -> -480 |
y$1@5:5536 |
| Audio | 1/1/1 | trace_process_12492.jsonl:133096、133102、138877 |
createOscillator()、createDynamicsCompressor()、getChannelData(0) |
HZ@5:104381 |
| Worker 通信 | 4 | trace_process_12492.jsonl:138820 |
Worker.postMessage([0]) |
wG@5:13193 |
| Math 精度 | 1 | trace_process_12492.jsonl:135830 |
Math.pow(Math.PI, -100) -> 1.9275814160560206e-50 |
dX@5:41183 |
| 媒体能力 | 9/7 | trace_process_12492.jsonl:133555、133172 |
navigator.mediaCapabilities、requestMediaKeySystemAccess("com.microsoft.playready", ...) |
XK/EP |
静态源码对应关系在 tmp/static/37_demo.fingerprint.com_DBqbMN7zXxwl4Ei8_web_v4_ahNo3Idb3RiQg69bQglE_ci_jsl_4.0.0.js:
| 函数 | 源码位置 | 作用 |
|---|---|---|
bG |
column 13918 |
创建 canvas,绘制文字和几何图形,调用 isPointInPath、fillText、toDataURL,形成 canvas.winding/geometry/text |
lX |
column 41950 |
调 getParameter(VERSION/VENDOR/RENDERER/SHADING_LANGUAGE_VERSION),形成 webgl_basics |
EX |
column 42660 |
调 getSupportedExtensions、getContextAttributes、getExtension、批量 getParameter,形成 webgl_extensions |
eG |
column 7373 |
调 getShaderPrecisionFormat,形成 shader precision 指纹 |
HZ |
column 104318 |
创建 OfflineAudioContext,连接 oscillator/compressor,渲染后读 getChannelData,形成 audio 数值 |
dX |
column 40497 |
对 acos/acosh/asin/asinh/atanh/exp/expm1/log1p/pow 等做浮点精度采样,形成 math hash |
nB/rB/tB/TB/aB/cB |
columns 37078/37116/37661/38286/38577/38863 |
读取 oscpu/languages/hardwareConcurrency/platform/plugins/touch |
AB/y$1 |
columns 37728/5551 |
读取 Intl.DateTimeFormat().resolvedOptions().timeZone 与 Date.getTimezoneOffset() |
oB/qB |
columns 38063/38133 |
检测 sessionStorage/localStorage |
s(e) |
column 192211 |
配置 apiKey、modules、worker,URL.createObjectURL(new Blob(...)) 后 new Worker(...) |
首个识别请求在 tmp/trace/http_packet/000025_POST_200_demo_fingerprint_com_DBqbMN7zXxwl4Ei8_pid12988_380000057.http_packet.json:
POST /DBqbMN7zXxwl4Ei8?region=us&ci=js/4.1.0&q=ahNo3Idb3RiQg69bQglE
initiator: agent js line 5 column 90905
request body: 二进制/加密编码后的 text/plain
response: {"version":"4","event_id":"1781762212213.gMJxBp","visitor_id":"orsF9S092M2X9NOJNLmr","suspect_score":15,...}
Playground 的二次查询在页面 chunk tmp/static/19_demo.fingerprint.com__next_static_chunks_2473-1eedb759263b52d2.js.js:
fetch("/api/event/v4/".concat(event_id), { method: "POST" })
同一个 chunk 只负责渲染:
propertyName: "virtual_machine"
L.virtual_machine === true ? "Yes ..." : "Not detected"
这条链路证明:前端 agent 确实采集了 WebGL、Canvas、Audio、Navigator、Screen、Storage、Math、Worker 等环境信号;但是 virtual_machine=false 和 virtual_machine_ml_score=0.155 是服务端事件返回,不是页面本地 if/else 算出来的。
SDK 采集了什么
从 agent 静态代码和 DOM trace 可以确认,它采集的不只是 WebGL。主要包括:
| 类别 | 证据点 |
|---|---|
| WebGL | getParameter、getShaderPrecisionFormat、getContextAttributes |
| Canvas | createElement("canvas")、getContext("2d")、fillText、toDataURL、getImageData |
| Navigator | userAgent、platform、hardwareConcurrency、plugins、mimeTypes、languages、permissions |
| Screen/Window | screen.width/height/availWidth/availHeight/colorDepth、inner/outer |
| Storage | localStorage、sessionStorage、Storage.getItem/setItem |
| Audio | OfflineAudioContext、createOscillator、createDynamicsCompressor |
| Worker | 创建 Blob worker,采集 worker 侧 navigator / wasm 能力 |
| 时间与区域 | Intl.DateTimeFormat、Date.getTimezoneOffset |
DOM trace 中 agent 相关调用计数节选:
Document.createElement 238
HTMLElement.get offsetWidth 145
HTMLElement.get offsetHeight 137
Crypto.getRandomValues 113
WebGLRenderingContext.getParameter 92
Window.get crypto 36
Window.get localStorage 21
WebGLRenderingContext.getShaderPrecisionFormat 12
Navigator.get plugins 7
Navigator.get permissions 6
HTMLCanvasElement.getContext 5
Navigator.get mimeTypes 6
CanvasRenderingContext2D.fillText 4
HTMLCanvasElement.toDataURL 4
Screen.get width/height 4
Navigator.get hardwareConcurrency 2
Intl.DateTimeFormat 2
这些调用说明 Fingerprint 的虚拟机判断更像“环境一致性评分”,不是单点检测。
本次样本的原始设备属性
服务端事件里的关键 raw_device_attributes:
{
"webgl_basics": {
"version": "WebGL 1.0 (OpenGL ES 2.0 Chromium)",
"vendor": "Mozilla",
"renderer": "ANGLE (Microsoft, Microsoft Basic Render Driver Direct3D11 vs_5_0 ps_5_0), or similar",
"shading_language_version": "WebGL GLSL ES 1.0 (OpenGL ES GLSL ES 1.0 Chromium)"
},
"screen_resolution": [920, 1961],
"oscpu": "Windows NT 10.0; Win64; x64",
"hardware_concurrency": 8,
"platform": "Win32",
"plugins": [
{ "name": "PDF Viewer" },
{ "name": "Chrome PDF Viewer" },
{ "name": "Chromium PDF Viewer" },
{ "name": "Microsoft Edge PDF Viewer" },
{ "name": "WebKit built-in PDF" }
],
"timezone": "Asia/Shanghai",
"timezone_offset": "+08:00",
"audio": 35.749972093850374,
"math": "5f030fa7d2e5f9f757bfaf81642eb1a6",
"font_hash": "1affeb088cac7b5efc90e012a4cac857"
}
这里最值得注意的是 WebGL:
ANGLE (Microsoft, Microsoft Basic Render Driver Direct3D11 vs_5_0 ps_5_0), or similar
Microsoft Basic Render Driver 通常意味着没有走真实 GPU 厂商驱动,而是走了 Windows 的基础渲染/软件回退链路。这对虚拟机检测很敏感,因为很多虚拟化、远程桌面、无 GPU 环境都会表现出类似特征。
但它不是“必杀”。本样本还有几组正常信号:
platform: Win32
oscpu: Windows NT 10.0; Win64; x64
hardware_concurrency: 8
plugins: 5 个 PDF 类插件
storage: localStorage/sessionStorage/indexedDB 可用
bot: not_detected
tampering: false
所以服务端给出的是低 ML 分数 0.155,最终 virtual_machine=false。
虚拟机检测原理拆解
可把 VM 检测拆成 4 层:
1. 显式虚拟硬件标记
这是最直接的一层,主要看 WebGL renderer/vendor、系统能力和设备名中是否出现:
VirtualBox
VMware
Parallels
QEMU
SVGA
VirGL
llvmpipe
softpipe
SwiftShader
GDI Generic
Software Rasterizer
本次实测里,VMware, Inc. / SVGA3D / VMware SVGA 3D 是可以触发官方 virtual_machine=true 的强信号;VirtualBox Graphics Adapter 会触发篡改/异常侧信号,但没有单独触发 VM 最终结论。
2. 软件渲染与 GPU 回退
Microsoft Basic Render Driver、SwiftShader、llvmpipe 这类不是一定等于 VM,但说明渲染链路不正常。常见来源包括:
虚拟机未安装显卡增强驱动
云桌面或远程桌面
无硬件加速的浏览器环境
反检测浏览器刻意伪造失败
系统 GPU 驱动异常或被禁用
所以它通常是“加分项”,不是“一票否决”。
3. 跨信号一致性
服务端会把多组信号放在一起看:
UA/oscpu/platform 是否一致
WebGL backend 是否符合操作系统
屏幕尺寸与 DPR 是否像真实用户设备
CPU 核心数是否过低
插件、MIME、字体、语言是否像对应浏览器
storage/indexedDB/worker/audio/canvas 是否完整
canvas、font、audio、math hash 是否稳定
例如:
UA 显示 Windows,但 WebGL 是 Apple Metal
UA 显示 macOS,但 WebGL 是 Direct3D
平台是 Win32,但没有插件、没有 storage、只有 1 核 CPU
这些组合会比单一字段更有解释力。
4. 服务端 ML/规则融合
Playground 返回了 virtual_machine_ml_score,说明至少存在模型评分结果。前端只拿最终字段展示,具体模型权重和训练特征不在前端暴露。
因此可以确定:
前端 SDK = 采集器 + payload builder + 加密提交
服务端 = 规则/模型判定
Playground = event_id 二次查询 + 展示
不能把前端 JS 中某个 WebGL 函数直接等同为完整 VM 检测算法。
参数敏感性实验
用户关心的是“到底哪个参数决定虚拟机”。直接改 /api/event/v4/{event_id} 没意义,因为这个接口只是按 event_id 查询服务端已经计算好的事件。真正影响结果的是 Fingerprint agent 的 identify POST:
POST /DBqbMN7zXxwl4Ei8?region=us&ci=js/4.1.0&q=ahNo3Idb3RiQg69bQglE
Content-Type: text/plain
body: 二进制/加密编码后的采集 payload
所以实验方法是:导航前用 page.add_preload_script() 改浏览器环境源头,让官方 SDK 正常采集、正常打包、正常提交,再读取对应 /api/event/v4/{event_id} 的服务端结果。脚本在:
tmp/vm_signal_matrix.py
复现命令:
python tmp/vm_signal_matrix.py --cases baseline webgl_nvidia webgl_virtualbox webgl_vmware webgl_swiftshader low_cpu_only storage_disabled combined_vm webgl_vmware_vendor_only webgl_svga_renderer_only webgl_vmware_angle_renderer --wait 45
python tmp/vm_signal_matrix.py --summarize-existing --cases baseline webgl_nvidia webgl_virtualbox webgl_vmware webgl_swiftshader low_cpu_only storage_disabled combined_vm webgl_vmware_vendor_only webgl_svga_renderer_only webgl_vmware_angle_renderer
完整结果在:
tmp/vm_matrix/results.json
tmp/vm_matrix/results.md
本次 11 组结果如下:
| case | 改动 | 服务端采到的关键 WebGL | virtual_machine | VM ML | tampering | suspect |
|---|---|---|---|---|---|---|
| baseline | 不注入 | Mozilla / Microsoft Basic Render Driver |
false | 0.103 | false | 15 |
| webgl_nvidia | 物理 NVIDIA renderer | Google Inc. (NVIDIA) / NVIDIA GeForce RTX 3060 |
false | 0.104 | true | 23 |
| webgl_virtualbox | VirtualBox renderer | Oracle Corporation / VirtualBox Graphics Adapter |
false | 0.132 | true | 15 |
| webgl_vmware | VMware + SVGA3D | VMware, Inc. / SVGA3D; build: RELEASE; LLVM; |
true | 0.109 | true | 29 |
| webgl_swiftshader | SwiftShader | Google Inc. / Google SwiftShader |
false | 0.118 | true | 15 |
| low_cpu_only | CPU=2 | 原始 Basic Render Driver | false | 0.148 | false | 7 |
| storage_disabled | indexedDB 不可用 | 原始 Basic Render Driver | false | 0.070 | false | 7 |
| combined_vm | VirtualBox + CPU=2 + 空插件 + 小屏 + indexedDB 不可用 | Oracle Corporation / VirtualBox Graphics Adapter |
false | 0.195 | true | 15 |
| webgl_vmware_vendor_only | 只改 vendor=VMware, Inc. |
VMware, Inc. / 原始 Basic Render Driver |
true | 0.104 | true | 29 |
| webgl_svga_renderer_only | 只改 renderer=SVGA3D |
Mozilla / SVGA3D; build: RELEASE; LLVM; |
true | 0.164 | false | 21 |
| webgl_vmware_angle_renderer | ANGLE 形式 VMware SVGA 3D | Mozilla / ANGLE (VMware, VMware SVGA 3D Direct3D11...) |
true | 0.077 | false | 21 |
关键证据文件:
baseline:
tmp/vm_matrix/baseline/http_packet/000029_POST_200_demo_fingerprint_com_api_event_v4_1781765779900_oPqQNf_pid4172_40000005c.http_packet.json
VirtualBox:
tmp/vm_matrix/webgl_virtualbox/http_packet/000029_POST_200_demo_fingerprint_com_api_event_v4_1781765826280_hwq3pk_pid4936_40000005d.http_packet.json
VMware + SVGA3D:
tmp/vm_matrix/webgl_vmware/http_packet/000029_POST_200_demo_fingerprint_com_api_event_v4_1781765998121_ILLq9R_pid4952_40000005d.http_packet.json
只改 VMware vendor:
tmp/vm_matrix/webgl_vmware_vendor_only/http_packet/000029_POST_200_demo_fingerprint_com_api_event_v4_1781766299834_Ur96rm_pid13596_40000005d.http_packet.json
只改 SVGA3D renderer:
tmp/vm_matrix/webgl_svga_renderer_only/http_packet/000029_POST_200_demo_fingerprint_com_api_event_v4_1781766345049_n6NC0P_pid5328_40000005d.http_packet.json
ANGLE 形式 VMware renderer:
tmp/vm_matrix/webgl_vmware_angle_renderer/http_packet/000029_POST_200_demo_fingerprint_com_api_event_v4_1781766391556_CEMPGs_pid15880_40000005d.http_packet.json
可直接确认的结论:
1. 当前服务端 VM 最终布尔值最敏感的是 WebGL vendor/renderer 中的 VMware/SVGA 特征。
2. vendor=VMware, Inc. 单独足以触发 virtual_machine=true。
3. renderer=SVGA3D 单独足以触发 virtual_machine=true,而且这一组 tampering=false,说明它不是靠“补丁被发现”才判 VM。
4. renderer=ANGLE (VMware, VMware SVGA 3D Direct3D11...) 也触发 virtual_machine=true。
5. VirtualBox Graphics Adapter、SwiftShader、低 CPU、indexedDB 不可用、空插件组合,在本次实验里都没有触发 virtual_machine=true。
6. WebGL 字段被 JS preload 改写时,经常会触发 tampering=true;但 tampering 和 virtual_machine 是两套不同信号,不能混为一谈。
一个反直觉点:virtual_machine_ml_score 不是最终布尔值的简单阈值。比如 webgl_vmware_angle_renderer 的 ML 分数只有 0.077,但 virtual_machine=true;combined_vm 的 ML 分数 0.195 更高,却仍是 false。这说明服务端很可能是“规则命中 + ML 分数”融合:VMware/SVGA 这类字符串更像明确规则命中,Basic Render Driver、低 CPU、存储异常更像模型/异常分。
本地纯算实现
我在 src/vm_risk.py 写了一个可解释的本地纯算版本,用来复现“信号如何合成风险”的思路。
它不复刻 Fingerprint 私有模型,只做可解释规则:
明确 VM renderer: 高权重
软件/回退 renderer: 中低权重
ANGLE backend: 低权重上下文信号
低 CPU / 空插件 / 小屏 / webdriver / OS 渲染链不一致: 辅助权重
score >= 0.6 或命中明确 VM renderer 才判 vm_detected=true
运行真实服务端事件包:
$env:PYTHONIOENCODING='utf-8'
python src/vm_risk.py tmp/trace/http_packet/000028_POST_200_demo_fingerprint_com_api_event_v4_1781762212213_gMJxBp_pid12988_38000005a.http_packet.json
输出:
{
"vm_detected": false,
"score": 0.34,
"risk_level": "low",
"flag_ids": [
"webgl_software_renderer",
"webgl_angle_backend"
],
"facts": {
"webgl_vendor": "Mozilla",
"webgl_renderer": "ANGLE (Microsoft, Microsoft Basic Render Driver Direct3D11 vs_5_0 ps_5_0), or similar",
"hardware_concurrency": 8.0,
"platform": "Win32",
"oscpu": "Windows NT 10.0; Win64; x64",
"screen_resolution": [920, 1961],
"plugins_count": 5
}
}
这个结果和服务端趋势一致:有风险信号,但不够判虚拟机。
测试:
python -m unittest tests/test_vm_risk.py
测试覆盖了:
Microsoft Basic Render Driver: 有风险信号,但不单独判 VM
VirtualBox/SVGA3D/VMware renderer: 本地规则按明确 VM renderer 处理
普通 NVIDIA/Intel/AMD 实体 GPU: 低风险
http_packet responseBody.text 包装输入
[920,1961] 这种分辨率顺序不误判小屏
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)