这里的十八罗汉是笔者给快手网页端指纹起的名字,用以记录和感叹。
起因在尝试解决风控时屡调不通,修改了各种参数,也对埋点日志进行了追踪,模拟后依旧无法完美解决。
于是回想起验证时的额外参数,比如下面的18个指纹参数,尽管有一半是重复的。
指纹的重要性相信大家都明白,一套指纹用于一个单独的用户,如果某个参数和IP有关系,那切换代理也无用。
比如我当前环境中会出现验证码的重复校验,导致生成的did可用性很差。
除了上述18个指纹ID,还有时区、语言、字体、系统、驱动、内核、分辨率等检测。
指纹生成分析
由于偶尔通过校验并不能用于量级业务,所以有待进一步分析。
需要注意该JS仅在验证时可进入,并且该JS是webpack打包的。
这里有十八罗汉的生成方法。
现在还未形成33位的字符。
继续断点调试就能找到最终的值。
且在此处进行了赋值操作。
经过一阵子分析,找到对象中的关键词 info,然后通过搜素找到加密转换的位置。
其通过ec进行转换。
可在控制台调试。
本地指纹加密
把ec拿出来,以及ec中所调用的方法。
一些info的值太长了,我只截取了开头。
functionec(n){var t = n.error
, e = n.version
, r = n.info;return n.info ?"".concat(e).concat(tc(r)):"E".concat(e).concat(tc(t ||"UNKNOWN"))}functionPr(n, t){
n =[n[0]>>>16,65535& n[0], n[1]>>>16,65535& n[1]],
t =[t[0]>>>16,65535& t[0], t[1]>>>16,65535& t[1]];var e =[0,0,0,0];return e[3]+= n[3]+ t[3],
e[2]+= e[3]>>>16,
e[3]&=65535,
e[2]+= n[2]+ t[2],
e[1]+= e[2]>>>16,
e[2]&=65535,
e[1]+= n[1]+ t[1],
e[0]+= e[1]>>>16,
e[1]&=65535,
e[0]+= n[0]+ t[0],
e[0]&=65535,[e[0]<<16| e[1], e[2]<<16| e[3]]}functionLr(n, t){
n =[n[0]>>>16,65535& n[0], n[1]>>>16,65535& n[1]],
t =[t[0]>>>16,65535& t[0], t[1]>>>16,65535& t[1]];var e =[0,0,0,0];return e[3]+= n[3]* t[3],
e[2]+= e[3]>>>16,
e[3]&=65535,
e[2]+= n[2]* t[3],
e[1]+= e[2]>>>16,
e[2]&=65535,
e[2]+= n[3]* t[2],
e[1]+= e[2]>>>16,
e[2]&=65535,
e[1]+= n[1]* t[3],
e[0]+= e[1]>>>16,
e[1]&=65535,
e[1]+= n[2]* t[2],
e[0]+= e[1]>>>16,
e[1]&=65535,
e[1]+= n[3]* t[1],
e[0]+= e[1]>>>16,
e[1]&=65535,
e[0]+= n[0]* t[3]+ n[1]* t[2]+ n[2]* t[1]+ n[3]* t[0],
e[0]&=65535,[e[0]<<16| e[1], e[2]<<16| e[3]]}functionKr(n, t){return t %=64,32=== t ?[n[1], n[0]]: t <32?[n[0]<< t | n[1]>>>32- t, n[1]<< t | n[0]>>>32- t]:(t -=32,[n[1]<< t | n[0]>>>32- t, n[0]<< t | n[1]>>>32- t])}functionqr(n, t){return t %=64,0=== t ? n : t <32?[n[0]<< t | n[1]>>>32- t, n[1]<< t]:[n[1]<< t -32,0]}function$r(n, t){return[n[0]^ t[0], n[1]^ t[1]]}functionnc(n){return n =$r(n,[0, n[0]>>>1]),
n =Lr(n,[4283543511,3981806797]),
n =$r(n,[0, n[0]>>>1]),
n =Lr(n,[3301882366,444984403]),
n =$r(n,[0, n[0]>>>1]),
n
}functiontc(n, t){
n = n ||"",
t = t ||0;var e, r = n.length %16, c = n.length - r, i =[0, t], a =[0, t], o =[0,0], u =[0,0], x =[2277735313,289559509], s =[1291169091,658871167];for(e =0; e < c; e +=16)
o =[255& n.charCodeAt(e +4)|(255& n.charCodeAt(e +5))<<8|(255& n.charCodeAt(e +6))<<16|(255& n.charCodeAt(e +7))<<24,255& n.charCodeAt(e)|(255& n.charCodeAt(e +1))<<8|(255& n.charCodeAt(e +2))<<16|(255& n.charCodeAt(e +3))<<24],
u =[255& n.charCodeAt(e +12)|(255& n.charCodeAt(e +13))<<8|(255& n.charCodeAt(e +14))<<16|(255& n.charCodeAt(e +15))<<24,255& n.charCodeAt(e +8)|(255& n.charCodeAt(e +9))<<8|(255& n.charCodeAt(e +10))<<16|(255& n.charCodeAt(e +11))<<24],
o =Lr(o, x),
o =Kr(o,31),
o =Lr(o, s),
i =$r(i, o),
i =Kr(i,27),
i =Pr(i, a),
i =Pr(Lr(i,[0,5]),[0,1390208809]),
u =Lr(u, s),
u =Kr(u,33),
u =Lr(u, x),
a =$r(a, u),
a =Kr(a,31),
a =Pr(a, i),
a =Pr(Lr(a,[0,5]),[0,944331445]);switch(o =[0,0],
u =[0,0],
r){case15:
u =$r(u,qr([0, n.charCodeAt(e +14)],48));case14:
u =$r(u,qr([0, n.charCodeAt(e +13)],40));case13:
u =$r(u,qr([0, n.charCodeAt(e +12)],32));case12:
u =$r(u,qr([0, n.charCodeAt(e +11)],24));case11:
u =$r(u,qr([0, n.charCodeAt(e +10)],16));case10:
u =$r(u,qr([0, n.charCodeAt(e +9)],8));case9:
u =$r(u,[0, n.charCodeAt(e +8)]),
u =Lr(u, s),
u =Kr(u,33),
u =Lr(u, x),
a =$r(a, u);case8:
o =$r(o,qr([0, n.charCodeAt(e +7)],56));case7:
o =$r(o,qr([0, n.charCodeAt(e +6)],48));case6:
o =$r(o,qr([0, n.charCodeAt(e +5)],40));case5:
o =$r(o,qr([0, n.charCodeAt(e +4)],32));case4:
o =$r(o,qr([0, n.charCodeAt(e +3)],24));case3:
o =$r(o,qr([0, n.charCodeAt(e +2)],16));case2:
o =$r(o,qr([0, n.charCodeAt(e +1)],8));case1:
o =$r(o,[0, n.charCodeAt(e)]),
o =Lr(o, x),
o =Kr(o,31),
o =Lr(o, s),
i =$r(i, o)}return i =$r(i,[0, n.length]),
a =$r(a,[0, n.length]),
i =Pr(i, a),
a =Pr(a, i),
i =nc(i),
a =nc(a),
i =Pr(i, a),
a =Pr(a, i),("00000000"+(i[0]>>>0).toString(16)).slice(-8)+("00000000"+(i[1]>>>0).toString(16)).slice(-8)+("00000000"+(a[0]>>>0).toString(16)).slice(-8)+("00000000"+(a[1]>>>0).toString(16)).slice(-8)}var canvasGraph ={error:"",info:"data:image/png;base64,iVBORw0KGgoAAA",name:"canvasGraph",version:1}
console.log("canvasGraph:",ec(canvasGraph))var canvasTextZh ={error:"",info:"data:image/png;base64,iVBORw0KGgoAA",name:"canvasTextZh",version:1}
console.log("canvasTextZh:",ec(canvasTextZh))var webglGpu ={error:"",info:"\"{\"glRenderer\":\"WebKit WebGL\",\"glVendor\":\"WebKit\",\"unmaskRenderer\":\"ANGLE (NVIDIA, NVIDIA GeForce GT 710 Direct3D11 vs_5_0 ps_5_0, D3D11)\",\"unmaskVendor\":\"Google Inc. (NVIDIA)\"}\"",name:"webglGpu",version:1}
console.log("webglGpu:",ec(webglGpu))
运行后可以和开头的指纹对比一下,结果是相同的。
canvasGraph
另外说一下 canvas 这种图片内容的生成,给大家扣了一个。
该部分执行后会返回一段字符串,就是canvasGraph对应的 n.info,再用ec进行加密就是canvasGraph指纹了。
本地node生成的话可以看之前的文章《浏览器指纹解读》。
function Ur(n){
var t = document.createElement("canvas"), e = t.getContext(n);return{
canvas: t,
context: e
}}
function _r(n){return n.toDataURL()}
function canvasGraph2(){
var n =Ur("2d"), t = n.canvas
, e = n.context;
var r = e;
r.globalCompositeOperation ="multiply";for(var c =0, i =[["#f2f",40,40],["#2ff",80,40],["#ff2",60,80]]; c < i.length; c++){
var a = i[c], o = a[0], u = a[1], x = a[2];
r.fillStyle = o,
r.beginPath(),
r.arc(u, x,40,0,2* Math.PI,!0),
r.closePath(),
r.fill()}return r.fillStyle ="#f9c",
r.arc(60,60,60,0,2* Math.PI,!0),
r.arc(60,60,20,0,2* Math.PI,!0),
r.fill("evenodd"),_r(t)}canvasGraph2()
备注
特征一共有这些参数:userAgent、timeZone、language、cpuCoreCnt、platform、riskBrowser、webDriver、exactRiskBrowser、webDriverDeep、exactRiskBrowser2、webDriverDeep2、resolution、pixelDepth、colorDepth、plugins、canvasGraphFingerPrint、canvasTextEn、canvasTextFingerPrintEn、canvasTextZh、canvasTextFingerPrintZh、webglGraphFingerPrint、webglGpu、webglGPUFingerPrint、fontListEn、cssFontFingerPrintEn、fontListZh、cssFontFingerPrintZh、voiceFingerPrint、audioTriangle、nativeFunc。
所以我们可以根据生成规则去创建一些指纹用于校验。
关注公众号《Pythonlx》一起交流和学习!
版权归原作者 考古学家lx(李玺) 所有, 如有侵权,请联系我们删除。