当前位置:   article > 正文

ks滑块逆向及流程分析_快手滑块

快手滑块

声明

本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!

介绍

想做快手的数据采集需要解决一个 did,这个 did 会由请求页面直接返回,但是却不能直接使用,需要后续发包激活。而只要滑过验证码,携带返回的 captchaToken 去请求数据,不仅能够拿到正确结果,还能激活 did 进行后续使用。所以,做ks数据采集,过验证码还是很有必要的,且验证码逆向难度不是很大,也可以当做练手项目来学习。

逆向目标

拿到一个视频链接,打开后清缓存,刷新就会出现滑块。抓包随便滑一下,可以看到验证接口为 captcha/sliding/kSecretApiVerify

此接口只有一个参数 verifyParam ,那么本次目标就是分析此参数是如何生成的。

分析

通过搜索 verifyParam 可以快速定位到加密点。

在这里插入图片描述

可以看到加密点在一个平坦流中,那么我们的断点可以打在 switch 语句上,一步步进行调试。

分支 0

首先进入分支 0,分支 0 中执行了一个语句 i[Jt("0x34")](Tt["a"], c);

其中 Tt["a"] 是一个函数,c 则是一个对象,里面有许多明文数据。我们先分析 c 的内容

c 内容分析
{
    "captchaSn": "Cgp6dC5jYXB0Y2hhEtgCyWw9CbLiBYsQuiKZDriENd0wgRuOcpjp18c0mLiC-KGoQc4cg8PMrLW7UnOEVg1xHeCTP6m6R2PreatN3qSRwN3OF2u50S0b4WdZjTHjP6zRH6fJSEuif21DnFRelxFCKqEosJLc1bCykcCrp2dEAm6oMofzkY8hSHhlNHmObaey7pz9bf6OELLjWYTwsEXo4SwX7oRxYqAEldaioef8sf9RIRcvEaBOy8I_Jr2Cug91PkogroYtHG5SQBk_kbjH4w8vSnJ06Lj3k0zTjbPYmVwk5hsbpzpARp7o7vjWf_ef1t9eisjPreKz0x2LZVMogVcFXwIDcnRr4tKyTZgu7SkHjKHEioot-J6F2mnWkR7eo7bqGVen4sYA7nCWL_B0EWrGxVbPMQ_RDwKql4113uC6mCEr-STsJcuXlbTYFO1rgTc327bsqvhADEKW48a1p7QEGOZ-OCcaEleIHYY8IfeWArTHzyF818xZgigFMAI",
    "bgDisWidth": 316,
    "bgDisHeight": 184,
    "cutDisWidth": 56,
    "cutDisHeight": 56,
    "relativeX": 165,
    "relativeY": 58,
    "trajectory": "3|24|0,7|24|14,10|24|15,14|24|17,18|24|22,21|24|23,25|24|26,28|24|31,32|24|32,225|112|35,39|24|39,43|24|41,47|24|44,50|24|51,54|24|52,54|23|55,57|23|55,61|23|62,341|111|64,68|23|69,72|23|78,76|23|80,79|23|82,83|23|88,86|23|90,90|23|92,94|23|98,469|111|100,101|23|103,105|23|111,108|23|112,112|23|113,115|23|123,119|23|127,123|23|134,126|23|177,601|111|178,134|23|180,137|23|185,141|23|187,144|23|188,144|24|192,148|24|192,152|24|194,155|24|195,717|112|197,163|24|198,166|24|200,170|24|202,173|24|203,177|24|204,181|24|207,188|24|208,192|24|210,861|112|212,199|24|213,202|24|215,206|24|216,210|24|217,213|24|218,217|25|221,221|25|222,224|25|224,993|113|224,231|25|226,235|25|228,239|25|230,242|25|231,246|25|234,250|25|234,253|25|235,257|25|237,1121|113|239,264|25|239,268|25|242,271|25|242,275|25|244,278|25|246,282|25|248,286|25|248,289|25|250,1253|113|250,297|25|252,300|25|254,304|26|254,307|26|256,311|26|258,318|26|259,322|26|261,326|26|262,1397|114|263,333|26|264,340|26|264,347|26|267,351|26|267,355|26|269,362|26|270,369|26|272,373|26|272,1585|114|274,384|26|274,387|26|276,391|26|277,398|26|278,402|26|279,409|26|280,413|26|281,420|26|282,1773|114|282,431|26|284,434|26|285,442|26|286,445|26|287,452|26|287,456|26|288,460|26|290,467|26|291,1965|114|291,474|26|294,485|26|295,489|26|296,496|26|298,500|26|298,503|26|299,507|26|300,510|26|301,2153|114|303,521|26|304,525|26|304,528|26|305,532|25|306,539|25|309,543|25|310,550|25|313,554|25|315,2309|113|318,561|25|320,565|25|323",
    "gpuInfo": "{\"glRenderer\":\"WebKit WebGL\",\"glVendor\":\"WebKit\",\"unmaskRenderer\":\"ANGLE (NVIDIA, NVIDIA GeForce GTX 1050 Ti Direct3D11 vs_5_0 ps_5_0, D3D11)\",\"unmaskVendor\":\"Google Inc. (NVIDIA)\"}",
    "captchaExtraParam": "{\"ua\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36\"xxx"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

c 的值比较重要,我们逐个讲解。

captchaSn

captchaSn 的值是由接口 captcha/sliding/config 返回的,该接口包含了验证码图片信息。

在这里插入图片描述

而请求该接口也需要提交一个参数 captchaSession。不用担心,此参数也是在该请求之前的接口中返回的。不过此参数包含一个小坑,很有迷惑性,我们下文再讲。

在这里插入图片描述

bgDisWidth|bgDisHeight|cutDisWidth|cutDisHeight

这个值分别为验证码背景图片长宽以及缺口的长宽,为固定值。

relativeX|relativeY

relativeX 为滑动距离,relativeY 为 captcha/sliding/config 接口中 disY 的值乘以 0.46 得来。

trajectory

这个很明显就是滑动轨迹了,由 x轴|y轴|时间 组成。滑动轨迹的生成也是比较棘手的,下文再讲。

gpuInfo|captchaExtraParam

这两个为设备指纹,可以直接固定。

我们接着分析加密流程。

分支 0 中会进入到 return e.Ueadz(n, t) 中,我们接着往下跟,会进入另一个函数中。i[Jt("0x34")](Tt["a"], c); 实际为 Tt["a"](c);

继续往下跟,会进到一个新的平坦流中。

在这里插入图片描述

这部分主要是对 c 的值进行序列化、转 Uint8Array。

继续往下跟会进到一个 Promise 中,也是本次逆向最关键的地方。

Jose 分析
new Promise((function(t, c) {
    Jose[r("0x2a")](n[r("0x28")], [e, l, {
        suc: t,
        err: c
    }])
}
))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这段代码的作用是创建一个 Promise 对象,并通过调用 Jose 对象的方法,将传递给 Promise 构造函数的成功和失败处理函数 tc 作为参数传递给该方法。

其中 n[r("0x28")]l 的值为固定的,e 则是上面对 c 处理后的值。

我们继续往下走的话会进到另一个文件中。到这里后,跟值就变得比较麻烦了。我们观察代码结构可以发现这是一个经过 webpack 打包后的代码,那么我们就可以使用最简单的方法,全局导出来解决这个问题。

在这里插入图片描述

我们知道最终加密参数的值由 Jose[r("0x2a")] 生成,所以我们需要拿到 Jose。通过搜索关键字我们可以找到

在这里插入图片描述

观察代码结构,这是一个自执行函数。结构如下

!function(r, o) {
                "object" == ("undefined" === typeof exports ? "undefined" : n(exports)) && "object" == n(t) ? t.exports = o() : "function" == typeof define && e("3c35") ? define([], o) : "object" == ("undefined" === typeof exports ? "undefined" : n(exports)) ? exports.Jose = o() : r.Jose = o()
            }(window, (function() {xxx})
  • 1
  • 2
  • 3

exports.Jose = o()Jose 的值为参数 o ,而 o 为一个方法。通过执行这段代码,就可以得到 Jose 。这里我们将代码修改一下,为:

window = {}
!function(r, o) {
                window.Jose = o()
            }(window, (function() {xxx})
  • 1
  • 2
  • 3
  • 4

​ 这样我们执行完后就可以直接通过 window.Jose 来调用加密方法。当我们执行时会报一个错误 。ReferenceError: n is not defined

我们缺了一个 n方法,将它补上即可。

在这里插入图片描述

最后调整一下调用代码:

async function encrypt(e) {
    var l = "c7b645db-65e8-401f-b38c-4c07c5fff247";
    var n = {};
    console.log(e)
    return n.uNXrD = "$encrypt",
        new Promise((function (t, c) {
                window.Jose.call(n.uNXrD, [e, l, {
                    suc: t,
                    err: c
                }])
            }
        ))
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
'
运行

到这里,最关键的部分就基本结束了,这段异步代码会返回一个长度与 e 相近的数组。

然后就是新平坦流剩余的部分。

在这里插入图片描述

这部分是对生成的数组进行处理,将数组转为 Uint8Array,然后对每个元素进行按位与操作。

var sent_ = new Uint8Array(sent_length)
for (var c = 0; c < sent_length; c += 1)
    sent_[c] = 255 & sent[c];
  • 1
  • 2
  • 3

最后将新数组进行 base64 编码,到这里整个加密流程就结束了。

细节讲解

验证码图片信息获取

上文中我们提到了获取 captchaSession 时有一个小坑,就是这个参数值的获取有两种情况。

一般 captchaSession 可以从评论数据等接口中拿到,/graphql

但是这个接口会有两种响应信息,如下:

{‘errors’: [{‘message’: ‘Need captcha’…
{‘data’: {‘result’: 400002,

两种响应内容中都包含 captchaSession ,但是只有第一种的 captchaSession 是有效的。如果使用的是 400002 的 captchaSession ,即使过了滑块,拿到的 captchaToken 也是无法使用的。

轨迹

轨迹应该是ks最难处理的问题了,代码模拟随机生成的轨迹基本无法使用,重放轨迹验证,同一坐标相同的轨迹也无法使用,也就是一个轨迹只能用一次。
350014 基本上就是轨迹问题。
轨迹生成我尝试过多种方法:

随机生成:普通生成相似轨迹,基本无法使用,通过率10%以下。
缩放法:取一个真实轨迹,再根据当前缺口位置进行缩放,通过率30%左右。
缓动函数:通过率90%左右
自建轨迹库:手动录入真实轨迹,再对真实轨迹进行随机偏移,通过率80%左右,也是我在使用的方法。

关于轨迹可用率很低这种情况,我也怀疑是自己遗漏了某些细节导致。

响应信息

成功:

{'result': 1, 'desc': 'ok', 'unifiedType': 2, 'captchaToken': 'HEADCgp6dC5jYXB0Y2hhEskC0WTsg4gi79naGkKCVH985uiGGP-nkHVb8r3mqAFKXFntcP42UDrKFfe2QlEOWjDJAUXDPU4MO960Yi1joyLWHT14972aSw_lbfYnr3NbY7HmYPLzwnsMg95Q5NASCXHSaTpzyyNt_MJLmIbkzg_8Wm3dvia82kNiUjbacF8gXthNo1XzmTlxcSDbT-2vsEmtC1sIP9PKx6OL105PVHCwELO8-Ax2RP3kfHfH3uBXg3P0CmFlXiLuSRpM0xB41RXuuznO6bw6Dox4LZkPGvijL5YtPZOBCgAlykW8J7Y1fh-JNt79WKIN4A16oS89sLr8jt6tIdnmFNncnmjGKya1MkCtEQ7qHcP_uyrwaV3k5Is9Cra3gnslM1huvA2x8GD521Sn-dXNsjRkb2dG6cPbre2lk0N8JURgItzl2bjjKKqCimS0-VjszscaElTx5qdOA_0N2qfb3EUSeB2MrygFMAITAIL'}
  • 1

轨迹问题:

{'result': 350014, 'desc': 'anti check err, try to get a new captchaSN to verify', 'unifiedType': 2}
  • 1

加密参数生成错误:

{'result': 350005, 'desc': 'captchaSN err', 'unifiedType': 2}
  • 1

缺口识别问题:

{'result': 350002, 'desc': 'verify err.try again', 'unifiedType': 2}
  • 1

常见响应信息就是以上几种。

流程

基本流程如下:

1、请求内容页获取 did

2、graphql 通过该接口获取验证码相关信息,可以从评论中获取。

3、请求验证接口,获取 captchaToken

4、携带 captchaToken,请求一次内容接口

完成这几步后 did 就处于激活状态了,可以重复使用一段时间。

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号