某东常用验证码逆向流程分析

查看 206|回复 10
作者:hybpjx   
声明
本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!
目标
解决JD常用的验证码
前言
这里常用的就三种接口
[ol]
  • 登录接口的滑块
  • cfe链接接口中的滑块
  • cfe链接接口中的点选
    [/ol]
    还有两个验证码
  • 旋转验证码
  • 这个验证码我一直没有触发。听说触发了这个验证码 就离毁号不远了。且爬且珍惜吧。
  • 手势验证码
  • 只有固定接口才有。


    三种验证
    登录的滑块接口
    接口链接

    aHR0cHM6Ly9wYXNzcG9ydC5qZC5jb20vdWMvbG9naW4/bHR5cGU9bG9nb3V0


    如上图所示。
    这里就不多说废话了。直接刷新看接口

    这里可以看到 有两个接口请求发出。分别为
  • s: 负责验证请求
  • g:负责获取图片和参数

    g请求
    这里看g请求。直接进第二个栈。

    这里看到 很多值已经给了我们值了。这里我们重点观察两个值。分别是e 和 j

    这里打断点进栈

    好了 然后再进去。然后发现到一个虚拟机里了 这里包含了  e的生成。

    然后就是j

    j的生成不必多说。看下图就知道了。

    其他的值。
  • appId:写死就行
  • scene: 同上
  • product: 同上
  • lang: 同上
  • callback: 一个callback随机数算法 写死就行

    然后看返回值

    上述请求有几个值需要提出来。方便后续s调用
    S请求
    这里继续看s请求。

    这里和上图结果图作为比较。可以发现。
    c好像就是challenge。
    这里话不多说了。其他的值都不重要 。
    最主要是这个d参数。
    我们重点来看这个d

    如下图所示。很多值的生成都能直接看到。
    这里d 值的生成 则是由一个方法传参加密了一堆数组。

    那这个数组是啥东西。其实我们不难发现这就是个轨迹。通过getCoordinate这个方法 才变成了请求接口中的d这个样子。
    d方法如下:
    'getCoordinate': function(a) {
            var b = this;
            var c = new Array();
            for (var d = 0x0; d
    这个方法很简单。缺什么扣什么。
    然后传值轨迹。
    至于轨迹的生成。可以通过某些算法去实现这个列表数组的生成。
    这里是通过举例然后偏移出数组变量
    这里贴个方法用于生成数组。
    def offer(distance):
        index = 0
        slide = []
        indexTime = str(int(time.time()))[:9]
        for item in self.base_slide:
            index += 1
            item[2] = int(indexTime + str(item[2])[-4:])
            if int(item[0]) >= (distance + int(self.base_slide[0][0])):
                slide = self.base_slide[:index]
                slide.append(
                    [str(distance + int(self.base_slide[0][0])), item[1], item[2] + 700 + int(random.random() * 1000)])
                break
        last = int(slide[-1][0].split('.')[0])
        pIndex = 0
        for item in self.push_slide:
            if pIndex == 0 or pIndex == len(self.push_slide) - 1:
                times = slide[-1][2]
            else:
                times = slide[-1][2] + (self.push_slide[pIndex + 1][2] - self.push_slide[pIndex][2])
            slide.append([str(item[0] + last), '369', times])
            pIndex += 1
        # print(json.dumps(slide))
        return slide
    最后通过getCoordinate 生成加密值。
    最后伪装成功参数 然后请求就行了 返回下图代表成功。

    cfe接口滑块
    下面接口网站如下

    aHR0cHM6Ly9jZmUubS5qZC5jb20vcHJpdmF0ZWRvbWFpbi9yaXNrX2hhbmRsZXIvMDMxMDE5MDAvP3JldHVybnVybD1odHRwcyUzQSUyRiUyRml0ZW0uamQuY29tJTJGMTAwMDQ4MjcyNzYyLmh0bWwmcnFob3N0PWh0dHBzJTNBJTJGJTJGYXBpLm0uamQuY29tJnJwaWQ9cnAtMTg2NTQ5NDkwLTEwMDU2LTE3MDk3MTMyMDgzNzEmZXZ0eXBlPTImZXZhcGk9Y29sb3JfcGNfZGV0YWlscGFnZV93YXJlQnVzaW5lc3Mmc291cmNlPTEmZm9yY2VDdXJyZW50Vmlldz0x

    如下图页面。这就到了另一个滑块的接口。

    这里我们点击快速验证。然后完整的走一遍流程看看。具体的走向是如何的
    如下图所示。

    这里首先筛选xhr请求。请求太多
    如下图所示。图中经过了这么多的请求。

    我们一个一个看。
    第一个m?std 请求 返回值是0 目测只是判断状态的请求。
    第二个api请求。如下图 返回了一个值。请求参数中enbody加密了


    第三个api请求也是加密了 enbody。返回值没东西。应该也是个请求判定。
    第四个fp请求。请求需要伪装si 和 ct。 而这个si 刚好和第二个api请求的si相同。 返回了 fp 和 st。


    第五个请求: web_jcap_report 返回了 code为0 无用请求。
    第六个请求:请求传参中带有上图fp参数的加密 还多了个tk加密。返回值 返回了 缺口图和背景图。


    第七个请求:无用请求
    第八个请求:即是第二个check请求。同第六个请求相似。但是返回值不一样。
    那这样我们大概就能明白个具体流程了。
    [ol]
  • api请求得到si
  • 通过fp请求 请求出fp和st
  • 通过上述这些返回值 以及其他参数获得check的请求参数加密。
  • 通过第一次check请求得到图片。进行识别
  • 然后通过识别到的信息再次请求check。完成滑块加密。
    [/ol]
    api请求
    这里我们首先去看一下api请求。

    如上图。所示 只有这个enbody值是通过加密获得的。那我们去搜索一下 或者走栈。

    如下图所示 直接获取到了 enbody加密的地方。

    这里简化一下代码
    result = encrypt(JSON.stringify(param), 'rhiasnkdhandrisk', 'r-s-h-n_r_isnkdk')
    然后过一下断点。这里可以看到 加密的值分别为
    eid: 可以为空
    evType: 应该是验证码类型 2
    requestId: 由另一个接口605返回的requestID
    shshshfpx:  是一个随机值的算法。包含随机生成以及时间戳的拼接。

    第二次api请求。这里第二次请求加密的值是 第一次的返回值 已经第一次的那个请求参数。其实可以不需要。这里也展示一下

    fp请求
    这里fp请求。不太好搜索。我们直接进栈

    进栈然后搜索 .si 这里可以发现。ct的算法 已经出来了。


    这里可以看到 。这个值应该就是加密的值。
    然后把几个函数互相扣一下就行。
    这个x函数的算法如下

    function s(t, e, n) {
                    var r = t
                      , a = o;
                    e && (i = e);
                    return c(r, a)
                }
    这里层层扣即可。
    然后把第一次api返回的那个data和这个新生成的ct
    去请求 得到fp和 st
    两次check
    第一次
    断点继续走 走到下图所在位置

    如上图所示。
  • si即是 第一次请求的返回的data
  • lang: 写死。
  • tk:上述乱七八糟的值拼接加密的值
  • ct: 字符串拼接 包含环境代码。
  • version: 写死
  • client: 写死

    这里需要注意的是 这里的t 暂时是不传值的。这里t是什么先留个悬念。

    然后进行加密就得到了tk和ct。
    然后请求。得到两张以base64为编码的图片。
    第二次check请求
    这里其他值加密都是一样的。
    唯一不同就是这个 JSON.stringify(t)  如下图所示

    这里传值是轨迹。通过两张图片识别出来的距离 最终通过算法生成轨迹。
    这里轨迹简单看看 第一个是移动的距离值。

    第二个和第三个 如下图js所示的位置

    这里可以通过算法去实现 也可以通过自建库来实现。
    最后通过这些值加密 得到请求参数即可完成滑块。整体流程如下

    cfe 点选验证
    下面接口网站如下

    aHR0cHM6Ly9jZmUubS5qZC5jb20vcHJpdmF0ZWRvbWFpbi9yaXNrX2hhbmRsZXIvMDMxMDE5MDAvP3JldHVybnVybD1odHRwcyUzQSUyRiUyRml0ZW0uamQuY29tJTJGMTAwMDQ4MjcyNzYyLmh0bWwmcnFob3N0PWh0dHBzJTNBJTJGJTJGYXBpLm0uamQuY29tJnJwaWQ9cnAtMTg2NTQ5NDkwLTEwMDU2LTE3MDk3MTMyMDgzNzEmZXZ0eXBlPTImZXZhcGk9Y29sb3JfcGNfZGV0YWlscGFnZV93YXJlQnVzaW5lc3Mmc291cmNlPTEmZm9yY2VDdXJyZW50Vmlldz0x

    和滑块一个连接 不同的是 验证码不一样。
    触发方式应该是与爬取的等级而定。

    如上图所示。
    其实上图大体流程都是一样的。这里唯一有一点区别。
    如下图所示。这里JSON.stringify(t) 里面传值的不是轨迹。而是 点选的坐标。

    这里可以通过两种方式去解决
    [ol]
  • 通过打码平台识别——亲测识别率很低
  • 通过训练。这里可以导出几百张数据集。然后通过yolov5识别
    [/ol]
    这里还有一点需要注意。
    这里点选的 验证码
    第一次check返回的tp值是22 。
    而滑块是30

    可以通过这个去区分验证码。

    接口, 验证码

  • Jaime666   

    学习了,原来是这样做的                                                           
    DawnXi   

    请问第一个验证码   轨迹数组生成完成后  执行getCoordinate  都能理解   伪装成功参数 和验证部分是怎么成功的,如何保证轨迹对应的加密串是正确的呢
    xiaozitwo   

    这个好啊,通过该分析挺多网站的验证码都能搞了
    xqdyan   

    这个好啊,通过该分析挺多网站的验证码都能搞了
    Voccoo   

    好东西,研究研究
    ctiger   

    非常详细,很有启发
    roothalo   

    mark  多谢分享
    lzf880   

    分析的很详细,好好学习下
    Lake0570   

    感谢师傅的分享,可以举一反三了,打算找个其他网站测试一下
    您需要登录后才可以回帖 登录 | 立即注册

    返回顶部