快手指纹之十八罗汉

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

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

于是回想起验证时的额外参数,比如下面的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》一起交流和学习!

  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论
### 回答1: captchatoken是快手应用中使用的验证token。当用户在快手进行某些高级操作(如发布视频,点赞评论等)时,快手系统会要求用户进行身份验证,以确定用户是真正的人类而非机器人。而captchatoken就是快手用来验证用户身份的一种工具。 具体来说,captchatoken是由快手服务器生成的一串随机字符串,由系统随机分配给用户。在用户需要进行身份验证的时候,快手会提示用户输入captchatoken,用来验证用户的真实身份。如果用户输入的captchatoken与快手系统生成的相符合,则验证通过,用户便可以进行所需操作。否则,用户将被认定为机器人或非法行为者,可能会受到相应的惩罚。因此,captchatoken在快手应用中起到了非常重要的身份验证作用。 总之,captchatoken是快手应用中一种很重要的身份验证工具,用于确定用户的真实身份并防止机器人或非法行为者的入侵。 ### 回答2: captchatoken 是快手视频平台为了对抗恶意操作和机器人攻击所采用的一种验证码机制。该机制通过要求用户输入一组图形或文字验证码,来识别是否为真实用户,以保证快手的用户体验和内容质量。 对于快手这样的社交平台,其用户的活跃度和内容质量对于平台的发展至关重要。而遭受恶意攻击和机器人操作的平台,则会影响用户体验和平台形象。因此,快手采用了 captcha 机制,以识别是否为真实用户。 在快手平台上,用户在进行上传、评论等操作时,系统会随机产生一组验证码,并要求用户正确输入。当用户成功输入验证码后,才可以继续进行操作。而机器人等恶意攻击,则很难模拟正确输入验证码的过程,因为机器人不具备人类识别和反应的能力。 captchatoken 的采用,显然提高了快手的安全性和可信度,让平台能够更好地保护用户隐私和原创内容。当然,在一定程度上,这些验证码也给用户带来了一定的操作难度和不便,但这种牺牲是值得的,因为平台安全性和用户信任度的提升,终将使得快手的用户群体更加庞大和活跃。 总之,captchatoken 对于快手来说,具有重要的作用,保护了快手平台的合法用户,防止了恶意攻击和机器人入侵,保证了快手平台良好的用户体验和内容质量。 ### 回答3: captchatoken 是快手平台上为了防止机器人恶意攻击而推出的一种验证方式,类似于其他网站上的验证码,旨在确保用户的资料和数据安全。 快手是一款大众喜爱的视频社交应用,以其快速流畅的视频播放、热门内容的推荐和丰富的社交互动功能而闻名。但同时,快手平台也遭受了不少机器人或黑客等假用户的攻击,这些人使用自动化脚本等软件对用户账号进行非法操作甚至盗取用户数据、隐私等。因此,为了保护平台中的真实用户以及用户所上传的内容和数据,快手推出了 captchatoken 验证系统。 captchatoken 是一种由快手平台开发的一种人机验证系统,其原理是在用户进行登录、注册、修改密码或者特定流程操作时,要求用户输入一串字母或数字等组成的验证码,以证明该用户完全是人类而不是机器人。这种验证方式可有效防止机器人攻击或恶意注册,并确保快手平台上的多个功能和信息的真实性。 captchatoken 的使用,符合快手平台提高用户数据安全和信息保密的统筹战略。对企业而言,通过运用 captchatoken 进行注册、登录和验证等,可以更好地保障精准营销,为企业获取更精准且实际的用户数据。同时,这也提高了用户在平台的使用体验,降低了用户的投诉和退订率,使平台上的内容和使用环境更加安全可靠。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

考古学家lx(李玺)

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

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

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

打赏作者

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

抵扣说明:

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

余额充值