吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 8111|回复: 66
收起左侧

[Web逆向] [验证码逆向]某手势验证码逆向分析

  [复制链接]
s1lencee 发表于 2024-2-8 01:00
本帖最后由 s1lencee 于 2024-2-8 15:56 编辑

[验证码逆向]某手势验证码逆向分析

本文章所有内容仅供学习和研究使用,本人不提供具体模型和源码。若有侵权,请联系我立即删除!维护网络安全,人人有责。

前言

手势验证码是一种绘制轨迹进行人机的验证码,我曾经遇到过几次。

查了查网上没有较好的教程,便打算自己写一篇文章。

目标网址:

aHR0cHM6Ly93d3cudmFwdGNoYS5jb20vI2RlbW8=

手势验证码长这样

1

1

我将该验证码的协议逆向分析和验证码识别分为2篇文章,本文主要研究该验证码的逆向分析。

目录

  • 请求分析

    • 准备工作
    • 4种请求
    • config请求
    • get请求
    • validate请求
  • 获取验证码

    • en参数分析
    • fp指纹分析
    • fp指纹分析2
    • 构造en参数
    • 获取数据
  • 图片还原

    • 还原代码分析
    • 获取图片还原顺序
    • python还原图片
  • 图片识别

    • 关于图片识别
  • 构造请求

    • 请求分析
    • 轨迹分析
    • 轨迹加密
    • 请求验证
  • 总结


请求分析

准备工作

首先,每个验证码平台都有唯一的业务标识,用于区分不同网站。我们应该养成看文档的好习惯,一般情况下直接在该验证码的官网找到Web端开发文档

2

2

可以看到可以看到验证码初始化的代码,vid就是唯一标识,然后在验证码出现的页面全局搜索vaptcha(

3

3

然后记住该id59b252ed57f5a2111xxxxxxx(为了脱敏,部分真实数据我将用xxx代替)

4种请求

该验证码没有debugger反爬措施,直接打开浏览器开发者工具抓包即可。

4

4

可以看到一共有4种请求,其中59b252ed57f5a2111xxxxxxx接口就是用户的vid,用于获取验证服务器。

5

5

返回的api就是下面3个请求的域名。

config请求

该请求是获取验证码配置的,即一些sdk地址和版本。

请求数据

6

6

响应数据

7

7

vi参数就是vid

其中我们只需要响应数据的knock

那么u值是怎么生成的呢?

我们开始跟栈

8

8

getConfig这个函数名那么明显,我们进去看看

9

9

可以明显看到,u值的来源是localStorage中的vaptchanu,那么意味着可以是空值。

当把该页面的缓存清空后,可以发现u值为空,这里我就不放图了,有兴趣大家试试。

get请求

顾名思义,该请求是获取验证码数据的。

请求数据

10

10

响应数据

12

12

可以看到k参数就是config接口返回的knock

en参数包含一些环境检测,我将在下文分析

validate请求

该请求用于验证是否正确。

请求数据

13

13

正确的响应数据

14

14

返回的token值就是我们的目标,我们携带该值请求需要验证的接口就可以返回数据了。

获取验证码

en参数分析

我们进入post的前面的栈

15

15

16

16

由于switch的存在,代码是分步执行的,我们在switch打上记录点,记录执行顺序

11

11

可以看到,获取了一些localStorage的内容,

hex_md5其实就是md5加密 (严格来说不算加密,因为md5无法解密)

我们清空缓存后重新请求可以发现执行顺序。

6
0
1
2
5

我们只需要把执行过的函数扣下来即可。

我只说几个重点

  1. 获取了localStorage的变量都是三元运算符,当localStorage里的值为空时就会设置默认值

  2. _0x502684['GenerateFP']_0x5d0164['GenerateFP']都是浏览器指纹,但是后者是Promise异步任务,我接下来会分析这两个的区别。

  3. _0x4f54b3['secretC']是一个固定值。

17

17

  1. _0x4f54b3['globalMd5']config返回内容处理后的md5加密值。

18

18

19

19

将返回值的值相加即可得到该字符串。由于js的相加和python不太一样,我们可以使用以下代码在python中实现

def sort_dict_by_key(input_dict):
    """字典按照键排序"""
    sorted_keys = sorted(input_dict.keys())
    sorted_dict = {key: input_dict[key] for key in sorted_keys}
    return sorted_dict

def splicing_obj(obj: dict):
    obj = sort_dict_by_key(obj)
    result = ""
    for k, v in obj.items():
        if isinstance(v, str):
            result += v
        elif isinstance(v, int):
            result += str(v)
        elif isinstance(v, float):
            result += str(v)
        elif isinstance(v, bool):
            result += "true" if v else "false"
        elif isinstance(v, list):
            result += ",".join(v)
        else:
            result += "[object Object]"
    return result

fp指纹分析

_0x502684['GenerateFP']函数主要通过canvas绘制特定字符再转换为base64来计算浏览器指纹。

传入一个参数,该参数将会绘制在canvas上。

20

20

该方法在不同浏览器上的结果不相同,这就意味着我们可以不用canvas来计算指纹,但是要求同一参数的计算值相同。

我们可以使用加盐的哈希算法来计算,在同一次请求中盐值应当相同(要注意CRC32校验,否则验证不通过)。

由于本文是教程,所以我就直接使用canvas来计算指纹(nodejs安装canvas库有些麻烦)

21

21

其他就是一些转换了,包括CRC32校验,直接扣下来即可。

fp指纹分析2

_0x5d0164['GenerateFP']是个异步函数

22

22

我们跟进函数内查看,在返回部分打个断点重新请求。

23

23

可以看到,_0x33ff5b是浏览器环境,经过hashComponents后返回一个哈希值(我没见过这种加密,有懂的大佬可以说说)

查看代码得知该GenerateFP函数可以传入两个参数:

  • 第一个参数将被设置进_0x33ff5b中,和浏览器环境一起加密
  • 第二个参数是个布尔值,当为true时返回全部哈希值,false时截取前面8为返回
// _0x41154d存在时将加入浏览器环境
if (_0x41154d) {
  _0x33ff5b = _0x3633c3(_0x3633c3({}, _0x2d65f5['components']), {
    'param': {
      'value': _0x41154d,
      'duration': 0x0
    }
  });
} else {
  _0x33ff5b = _0x3633c3({}, _0x2d65f5['components']);
}

// 计算哈希值
_0x4c267d = _0x5bf32f['hashComponents'](_0x33ff5b);

// 是否截取前八位,_0x596253是回调函数
if (_0x5799b7)
  _0x596253(_0x4c267d);
else
  _0x596253(_0x4c267d['slice'](0x0, 0x8));

_0x596253是回调函数,在下一步代码的_0x4f1db8['sent']()可以获取该值

24

24

我们直接将hashComponents全部扣下来即可。

关于浏览器环境

就是检测一些字体、插件、设置等,完全可以弄成定值,同样的相同的参数返回的值应该相同。

构造en参数

encryFuncselectFrom没有特殊含义,直接扣下来即可

最后,把扣下来的代码整合可以得到en

25

25

获取数据

最后把en携带参数去请求服务器即可获得验证码数据

26

26

图片还原

还原代码分析

27

27

可以看到,接口返回的图片是乱序的。

我们可以在事件侦听器中勾选canvas创建,因为浏览器还原乱序图片肯定会用到canvas

28

28

跳过几个检测指纹的canvas,我们成功断住了还原图片的地方

29

29

我们只需要注意传入的_0x8782d1即可,该变量就是图片顺序。

获取图片还原顺序

向前跟栈找到生成顺序的地方

30

30

大体就是一个计算得到的整数_0x19be99和接口返回的img_order经过一个_0x2f94f6['Decrypt']方法得到

en一样,找到switch的执行顺序以后可以得到0x19be99的值

_0x19be99 = hex2int(GenerateFP(ha)) + hex2int(hashComponents(hb)) + parseInt(secretC) + pow(r);

rhahb就是get接口返回的内容

当有一项为空值时,就直接设为0.

powwork2.js返回的内容,直接扣下来即可

31

31

整合后

function get_order(img_order, r, hb, ha){
    let hb_en = 0;
    let ha_en = 0;
    if (hb !== "" && hb) {
        hb_en = hex2int(hashComponents(hb))
    }
    if (ha !== "" && ha) {
        ha_en = hex2int(GenerateFP(ha))
    }

    let _0x28c598 = ha_en + hb_en + 8549731620 + pow(r);
    return Decrypt(img_order, _0x28c598)
}

python还原图片

from PIL import Image
from io import BytesIO

def restore(img, order):
    """还原图片"""
    img = Image.open(BytesIO(img))
    new_img = Image.new('RGB', (400, 230))
    width = 80
    height = 115

    def drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight):
        """
        param image:
        param sx:       开始剪切的 x 坐标位置。
        param sy:       开始剪切的 y 坐标位置。
        param sWidth:   被剪切图像的宽度。
        param sHeight:  被剪切图像的高度。
        param dx:       在画布上放置图像的 x 坐标位置。
        param dy:       在画布上放置图像的 y 坐标位置。
        param dWidth:   要使用的图像的宽度
        param dHeight:  要使用的图像的高度
        """
        split_img = image.crop((sx, sy, sx + sWidth, sy + sHeight))
        new_img.paste(split_img, (dx, dy))

    for i in range(10):
        if i < 5:
            j = i
            if int(order[i]) < 5:
                o = int(order[i])
                drawImage(img, j * width, 0, width, height, o * width, 0, width, height)
            else:
                o = int(order[i]) - 5
                drawImage(img, j * width, 0, width, height, o * width, height, width, height)
        else:
            j = i - 5
            if int(order[i]) < 5:
                o = int(order[i])
                drawImage(img, j * width, height, width, height, o * width, 0, width, height)
            else:
                o = int(order[i]) - 5
                drawImage(img, j * width, height, width, height, o * width, height, width, height)

    return new_img

传入2个参数即可还原,img是图片二进制数据,order为图片还原顺序

图片识别

关于图片识别

图片识别我将在另一个专题详细讲

大家可以稍等

构造请求

请求分析

get请求一样,在post前面断住

32

32

我们可以看到,和get请求的en加密基本相同,只不过多了几个参数。

其中_0x13cb8b变量包含验证信息:

  • dt: 用时,不到4位数用0补上
  • ch: canvas的高
  • cw: canvas的宽
  • v:  加密后的轨迹值

33

33

轨迹分析

_0x491ed2就是轨迹信息。

34

34

可以看到这个轨迹有点迷糊,有很多小数。我们找到生成轨迹的地方。

35

35

可以看到,就是鼠标位置减去绘制的起始位置。

这里有个坑。

36

36

这里区域实际上比事件位置多出30px,(验证图片原高230px,宽400px)

所以我们在原来的位置加上30px即可。

其他注意事项:

当轨迹x或y间隔小于5时,不会加入轨迹列表

37

37

38

38

轨迹加密

assemblyCoordData就是轨迹加密函数

39

39

我们直接扣下来即可

40

40

验证请求

这样,我们使用识别图片的轨迹加密生成en后去请求。

41

41

总结

在这个验证码飞速发展的时代,可能我今天的文章,明天就过期了。

所以,不要只一昧的CV,我们要学习新的思路,这样知识才是自己的。

如果文章有什么不足的欢迎各位大佬们补充。

免费评分

参与人数 26吾爱币 +29 热心值 +25 收起 理由
alienhe + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
dxiaolong + 1 + 1 热心回复!
铭焱 + 1 + 1 谢谢@Thanks!
ponypony + 1 + 1 谢谢@Thanks!
958wkk + 1 + 1 打算学习一下,刚好有文章,谢谢大佬
千小秋 + 1 + 1 我很赞同!
fengbolee + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
kkyi + 1 + 1 谢谢@Thanks!
Yangzaipython + 1 鼓励转贴优秀软件安全工具和文档!
风动鸣 + 1 + 1 用心讨论,共获提升!
都同学 + 1 + 1 用心讨论,共获提升!
qq1481 + 1 + 1 谢谢@Thanks!
lookerJ + 1 + 1 用心讨论,共获提升!
没有细胞的人 + 2 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
eijop252023 + 1 + 1 我很赞同!
xvsc + 1 + 1 谢谢@Thanks!
liuxuming3303 + 1 + 1 谢谢@Thanks!
T4DNA + 1 + 1 用心讨论,共获提升!
fengoto + 1 + 1 热心回复!
ytfh1131 + 1 + 1 谢谢@Thanks!
timeni + 1 + 1 用心讨论,共获提升!
ABCblock + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
Issacclark1 + 1 谢谢@Thanks!
prince_cool + 2 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
dooppojie + 1 + 1 我很赞同!
howsk + 2 + 1 鼓励转贴优秀软件安全工具和文档!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| s1lencee 发表于 2024-2-14 18:41
443566434 发表于 2024-2-14 17:15
请教一下,有些APP的比如某宝的滑动验证码,手动右滑就可以验证成功,但是用js代码的swipe滑 能右滑过去了 ...

大概率检测轨迹了,你的轨迹不像人滑的

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
443566434 + 1 + 1 我很赞同!

查看全部评分

黄色土豆 发表于 2024-3-4 11:29
最近想写一个程序,访问一个网页,但是卡在第一步
1、正常手动访问的话,需要经过滑动验证码进行验证;
2、但是我直接使用代码进行http请求的话,会返回错误状态码,信息提示需要经过验证码验证,请问有没有绕过的方案,或者是其他方案
 楼主| s1lencee 发表于 2024-2-8 01:02
海是倒过来的天 发表于 2024-2-8 08:39
凌晨发帖,分析得这么详细,学习了
次谐波 发表于 2024-2-8 08:40
进来学习下。
ztqddj007 发表于 2024-2-8 08:47
牛逼大佬支持一波
lhfcsm 发表于 2024-2-8 10:01
呵呵,正在学习中
序列号001 发表于 2024-2-8 10:48

进来学习下
xuelinghua 发表于 2024-2-10 16:03
进来学习下
Zrzy123456 发表于 2024-2-10 16:11
前排膜拜一下大佬
jflmao 发表于 2024-2-11 07:58
这个帖子怎么消失了几天,8号上午还有,中午就没了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-22 19:15

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表