快手指纹之十八罗汉

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

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

于是回想起验证时的额外参数,比如下面的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))

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

在这里插入图片描述


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》一起交流和学习!

评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

考古学家lx(李玺)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值