fingerprint-js-Pro采集项分析
fingerprint.js Pro v3,近100多个采集项,只有压缩无混淆,直接开干
有几个stage2的条目挺奇怪的,里面的解密函数在chrome138上跑不起来,其余均分析完毕
总览
根据文档用这个脚本直接从cdn上把他的脚本文件拉下来,所有的代码都在fYicOuiT1WRGwuZgAESv.js这个文件里,小7000行还算ok,只有压缩无混淆,用prettier格式化后还算能看,大部分工作量在重命名压缩后损失的符号,先总体看下业务逻辑
1  | 
  | 
这个一个巨大的字典序列一看就是采集项,先看看这玩意怎么跑起来的
导出的函数是load,可以看到load里填充了一下配置项,然后就是跑服务
这里前面做了一大堆初始化,就不展示了,initSandbox是初始化了一个iframe,因为有些测试项需要添加DOM元素,所以需要在隔离的iframe里做
这里看到就是把module里的stage拆出来然后开始准备跑
这里就可以看到,stage里是一堆函数,然后并发的调用了这些函数,注意到这个context是传给stage[e]的第一个参数(用bind绑定),这点非常重要,因为这样后面分析每个采集函数时遇到的不知道什么类型还找不到引用的参数基本都可以确定是是这里的context,然后就可以去runService里找上下文里填充了什么对象,根据对象名判断到底调了浏览器里的什么东西
也就是说那个stage列里面是一堆worker函数,那一个个函数看过去就好了
总表
Stage1
| 项目ID | 函数名 | 实际功能 | 
|---|---|---|
| s94 | ia | tls支持检测,似乎tls指纹检测在服务端 | 
Stage2
| 项目ID | 函数名 | 实际功能 | 
|---|---|---|
| s52 | pc | voiceInfoHash 获取浏览器语音支持信息 | 
| s6 | Ki | ScreenSize 页面尺寸信息 | 
| s58 | Rc | PlatformInfoFromList 测试一些平台信息 | 
| s20 | lc | Font 测试字体支持 | 
| s36 | Ji | has_AdBlocker 测试有没有广告拦截插件 | 
| s51 | zi | test_font_width 测试字体宽度 | 
| s21 | qi | audio_data 采集浏览器音频数据指纹 | 
| s79 | Wc | |
| s69 | Sc | IframeUrl 逐层遍历iframe到顶层并获取各层url | 
| s23 | us | webkitRequestFileSystem 测试webkitRequestFileSystem是否可用 | 
| s29 | is | storageQuota 获取当前域名分配的储存空间 | 
| s84 | ss | Sandbox_Iframe_width_height 获取iframe沙箱的宽高 | 
| s85 | ds | test_indexDB 测试indexDB能否正常储存 | 
| s89 | Pc | storage.getDirectory 测试storag.getDirectory()能否调用 | 
| s17 | du | canvas_test 测试Canvas指纹和支持 | 
| s87 | Vs | system_color 测试系统颜色支持 | 
| s92 | Zs | mathML 测试mathML数学符号支持 | 
| s93 | Ds | emoji 测试emoji符号支持 | 
| s95 | Ea | |
| s97 | Aa | |
| s70 | Fa | 
Stage3
| 项目ID | 函数名 | 实际功能 | 
|---|---|---|
| s22 | Hc | wasm 测试wasm相关功能是否支持 | 
| s30 | Zc | doNotTrack 检查doNotTrack属性 | 
| s33 | ec | Brave Brave浏览器 | 
| s44 | oc | theme_color 颜色主题 | 
| s45 | uc | Time 获得用户本地时间与UTC时间的差值 | 
| s49 | dc | PerformanceIntervals 获取设备时间戳精度 | 
| s50 | vc | jsHeapSizeLimit 获取js堆大小 | 
| s57 | Uc | devicePixelRatio | 
| s59 | Yu | IE 检查IE浏览器相关属性是否存在 | 
| s60 | Xu | Edge 检查Edge浏览器相关属性是否存在 | 
| s61 | Ju | Chrome | 
| s62 | zu | Apple 是否为apple平台 | 
| s63 | qu | Safari | 
| s64 | Ku | Firefox | 
| s65 | Qu | mobile 是否为移动平台 | 
| s66 | Cu | attributionSourceId 测试<a></a>中的这个属性 | 
| s68 | nc | new_api 测试几个特定api | 
| s71 | Ac | origin 检测页面来源链接 | 
| s24 | Lc | get_eval_toString_length 对eval调用toString然后获取结果的长度 | 
| s72 | Cc | webdriver 典中典属性 | 
| s1 | Qi | oscpu 架构属性(现代多数弃用) | 
| s2 | nu | language 语言列表 | 
| s3 | tu | colorDepth 屏幕色深 | 
| s4 | eu | deviceMemory 获取设备内存近似值 | 
| s5 | ru | height_width 获取屏幕宽高 | 
| s7 | ou | hardwareConcurrency 获取设备线程数 | 
| s9 | iu | timezone 获取时区 | 
| s10 | uu | sessionStorage 测试相应api是否为null | 
| s11 | cu | localStorage 测试相应api是否为null | 
| s12 | ls | has_indexedDB 测试相应api是否为null | 
| s13 | su | openDatabase 测试相应api是否为null | 
| s14 | au | get_cpuClass_ | 
| s15 | lu | is_ipad_or_iphone_ | 
| s16 | fu | plugins | 
| s19 | mu | touch_event 测试touchEvent是否可用 | 
| s27 | vu | vendor 获取vendor属性 | 
| s28 | hu | vendor_keys 获取一些和平台相关的key | 
| s32 | pu | cookie 测试cookie是否开启 | 
| s37 | gu | color_gamut 测试色域 | 
| s41 | wu | invert_color 测试反色是否开启 | 
| s39 | yu | force_color 测试是否强制使用某颜色设置 | 
| s42 | bu | monochrome 获取色深 | 
| s38 | Eu | perfer_contrast 获取对比度偏好 | 
| s43 | Ru | motion_set 获取动画设置 | 
| s40 | Iu | dynamic_range_set 获取色彩范围 | 
| s46 | ku | Math_func_test_hash 测试三角函数和对数相关的函数返回值 | 
| s80 | Au | pdfViewerEnabled | 
| s81 | Lu | NaN_imply 获取NaN的内存表示 | 
| s82 | rs | language_ 又调了一次上面的获取语言的函数。。 | 
| s83 | os | language_ 连在一起还调用了一次。。 | 
| s86 | vs | no_storage 似乎是测试是否禁用本地储存 | 
| s91 | Su | transparency_set 获取透明度设置 | 
| s96 | Pu | AudioLatency 获取音频处理的AudioLatency属性 | 
| s98 | La | serviceWorker 检查属性 | 
| s99 | Ca | isSecureContext 检查属性 | 
| s200 | Za | timeStamp 获取一个时间戳 | 
| s201 | tc | new_web_api 测试几个api | 
| s202 | _u | locale 似乎是获取地区 | 
| s74 | Tu | webgl_context_info 获取webgl信息 | 
| s75 | Ou | webgl_infos 其他的webgl信息 | 
| s76 | hc | webgl_render_data 测试webgl渲染指纹 | 
其他参数
还有一些参数不在stage里面,看了看似乎也和采集关系不大
| 项目ID | 函数名 | 实际功能 | 
|---|---|---|
| s55 | nl | 获取本地存储和cookie中的_vid_t值 | 
| s48 | gc | 获取一段伪随机序列 | 
tls项似乎是自实现的tls传输函数,用来上传数据的
stage2
stage1只有一个项目,而且非常的长,不知道是什么,先看stage2
voiceInfoHash

这里是从window中获取浏览器语音序列,然后由r这个函数转换成json再转字符串后调murmurhash3转换成哈希值
注意到这里如果获取不到语音序列(speechSynthesis没有addEventListener成员,推测是为了防止一些指纹浏览器内置一个空的speechSynthesis)会调用两个属性检查函数,暂时没发现这两组属性有啥联系

ScreenSize
这个函数比较简单,就是测一下窗口尺寸和是否全屏

getPlatformInfoFromList
这个函数获取一些UA和平台信息
主要拿以下几个key
Font

这个函数对列表里预置的字体进行测试,他有一个CSS标准字体列表和测试字体列表,通过比对测试字体和标准字体渲染出的文字的实际宽高是否不同来对比浏览器是否支持对应字体
has_AdBlocker
这个函数测试用户是否安装了某几个广告拦截器
测试方法是先收集了一些广告拦截器默认会拦截的DOM元素属性,然后添加含这些属性的元素,再检查这几个元素的parentNode属性(如果被广告拦截器删掉的话parentNode会为null)

test_font_width
这个函数前面又把生成iframe沙箱的逻辑重写了一遍,直接看下面传给沙箱的函数
这里还是比较简单的,就是选择了几个字体,然后测试了他们的宽度
audio_data
这个函数生成了一段测试音频,然后对其采样值的前4500个数据的绝对值求和作为浏览器指纹的参考数据

IframeUrl
这个函数从这个页面所在的iframe开始尝试逐层向上遍历iframe嵌套结构并获取每一层iframe的url,应该是为了识别当前页是否被嵌套
webkitRequestFileSystem
这个函数通过在webkitRequestFileSystem申请1字节内存;来测试api是否可用

如果是检测到较新版本chrome则不测试,可能是因为新版本chrome弃用了这个api
storageQuota
尝试从webkitTemporaryStorage和storage两个成员中获取储存空间大小信息
Sandbox_Iframe_width_height
获取iframe的宽高,有点意味不明
test_indexDB
随机创建的一个indexDB并尝试储存一个数据(似乎前提是要先检测到为safari或firefox浏览器)

storage_getDirectory
测试storage.getDirectory方法能否调用
canvas_test
测试canvas指纹

system_color
在沙箱里生成一堆不同颜色的div测试系统颜色支持
mathML
生成一些数学公式并测试符号的渲染情况
emoji
测试浏览器emoji图案支持
stage3
wasm
5段测试demo测试不同的wasm功能,然后通过bitmask保存测试结果
doNotTrack
直接获取doNotTrack属性
Brave
测试是否为Brave浏览器(似乎是国外的一种隐私浏览器)并测试是否开启随机canvas指纹功能
theme_color
测试用户颜色主题
Time
计算用户本地时间和utc时间的差值
PerformanceIntervals
连续调用window.performance.now(),记录间隔时间中最小和次小的获取设备时钟精度
jsHeapSizeLimit
获取堆大小
devicePixelRatio
获取devicePixelRatio属性的值![]()
IE
检查了几个微软开头的key
Edge
这个应该是检查Edge,并且和IE区别开了
Chrome
检查chrome浏览器
Apple
测试apple平台的标签
Safari
测试safari浏览器
Firefox
测试firefox浏览器
mobile
测试是否为移动平台
attributionSourceId
测试attributionSourceId属性,但是chrome138上没找到这个属性
new_api
测试几个比较新的api(存疑),暂时没出来这个几个api有什么共性,有几个看着是跟隐私相关的(geolocation webRTC)
origin
获取当前页面来源及其父级来源,大概可以检测嵌入之类的
eval_toString_length
对eval调用toString然后获取结果的长度,据说不同js引擎这个返回值都是不一样的
下面是claude 4.0 给出的例子
1  | // Chrome/V8  | 
webdriver
简单直接,直接获取webdriver属性
oscpu
获取oscpu属性,似乎现代浏览器不少都弃用了这个api
language
获取浏览器语言列表
colorDepth
获取屏幕色深
deviceMemory
获取设备内存近似值
height_width
获取屏幕宽高(stage2还有个类似的函数,那个是返回屏幕边距的)
hardwareConcurrency
获取设备线程数
timezone
获取设备时区
sessionStorage,localStorage,indexedDB,openDatabase
这几个都是测试api是否为Null

cpuClass
获取cpuClass属性,chrome138上无
is_ipad_or_iphone
似乎是根据宽高比判断是ipad还是iphone
plugins
获取内置插件列表,也是典中典了
touch_event
获取最多触点数以及touchEvent是否可用
vendor
获取浏览器厂商标签
vendor_keys
匹配一些和厂商相关的标签
cookie
测试是否开启cookie
color_gamut,invert_color_,force_color_,monochrome_test,perfer_contrast_,motion_set_,dynamic_range_set_,transparency_set_
这些都是和画面设置相关的参数获取
Math_func_test_hash
测试三角函数和对数函数,并将结果hash保存

pdfViewerEnabled
如图
NaN_imply
获取NaN的具体内存表示(似乎不同的js引擎实现这个值是不同的)
no_storage
似乎是测试是否禁用本地储存,有一个属性在win上获取不到
AudioLatency
获取AudioLatency属性
serviceWorker
检查serviceWorker是否存在
isSecureContext
检查isSecureContext的值
timeStamp
获取一个时间戳
new_web_api
没看懂这几个api之间的关联
locale
获取语言地区
webgl_context_info
获取webgl信息,渲染驱动,厂商之类的
webgl_infos
获取一些别的webgl相关的信息用于生成指纹

webgl_render_data
运行一小段渲染程序获取当前webgl引擎的渲染指纹
注释版的采样代码
fingerprint-js-Pro采集项分析