快手指纹之十八罗汉

举报
冬晨夕阳 发表于 2022/06/16 23:43:48 2022/06/16
【摘要】 这里的十八罗汉是笔者给快手网页端指纹起的名字,用以记录和感叹。 起因在尝试解决风控时屡调不通,修改了各种参数,也对埋点日志进行了追踪,模拟后依旧无法完美解决。 于是回想起验证时的额外参数,比如下面的1...

这里的十八罗汉是笔者给快手网页端指纹起的名字,用以记录和感叹。

起因在尝试解决风控时屡调不通,修改了各种参数,也对埋点日志进行了追踪,模拟后依旧无法完美解决。

于是回想起验证时的额外参数,比如下面的18个指纹参数,尽管有一半是重复的

在这里插入图片描述

指纹的重要性相信大家都明白,一套指纹用于一个单独的用户,如果某个参数和IP有关系,那切换代理也无用。

比如我当前环境中会出现验证码的重复校验,导致生成的did可用性很差。

除了上述18个指纹ID,还有时区、语言、字体、系统、驱动、内核、分辨率等检测。


指纹生成分析

由于偶尔通过校验并不能用于量级业务,所以有待进一步分析。

需要注意该JS仅在验证时可进入,并且该JS是webpack打包的。

在这里插入图片描述

这里有十八罗汉的生成方法。
在这里插入图片描述

现在还未形成33位的字符。

在这里插入图片描述

继续断点调试就能找到最终的值。
在这里插入图片描述

且在此处进行了赋值操作。
在这里插入图片描述


经过一阵子分析,找到对象中的关键词 info,然后通过搜素找到加密转换的位置。

在这里插入图片描述
其通过ec进行转换。

在这里插入图片描述
可在控制台调试。
在这里插入图片描述


本地指纹加密

把ec拿出来,以及ec中所调用的方法。

一些info的值太长了,我只截取了开头。

function ec(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"))
}
function Pr(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]]
}
function Lr(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]]
}
function Kr(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])
}
function qr(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]]
}
function nc(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
}
function tc(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) {
    case 15:
        u = $r(u, qr([0, n.charCodeAt(e + 14)], 48));
    case 14:
        u = $r(u, qr([0, n.charCodeAt(e + 13)], 40));
    case 13:
        u = $r(u, qr([0, n.charCodeAt(e + 12)], 32));
    case 12:
        u = $r(u, qr([0, n.charCodeAt(e + 11)], 24));
    case 11:
        u = $r(u, qr([0, n.charCodeAt(e + 10)], 16));
    case 10:
        u = $r(u, qr([0, n.charCodeAt(e + 9)], 8));
    case 9:
        u = $r(u, [0, n.charCodeAt(e + 8)]),
        u = Lr(u, s),
        u = Kr(u, 33),
        u = Lr(u, x),
        a = $r(a, u);
    case 8:
        o = $r(o, qr([0, n.charCodeAt(e + 7)], 56));
    case 7:
        o = $r(o, qr([0, n.charCodeAt(e + 6)], 48));
    case 6:
        o = $r(o, qr([0, n.charCodeAt(e + 5)], 40));
    case 5:
        o = $r(o, qr([0, n.charCodeAt(e + 4)], 32));
    case 4:
        o = $r(o, qr([0, n.charCodeAt(e + 3)], 24));
    case 3:
        o = $r(o, qr([0, n.charCodeAt(e + 2)], 16));
    case 2:
        o = $r(o, qr([0, n.charCodeAt(e + 1)], 8));
    case 1:
        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))

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166

运行后可以和开头的指纹对比一下,结果是相同的。

在这里插入图片描述


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()

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

备注

特征一共有这些参数: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》一起交流和学习!

文章来源: blog.csdn.net,作者:考古学家lx(李玺),版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/weixin_43582101/article/details/125299845

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。