吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4731|回复: 140
上一主题 下一主题
收起左侧

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

    [复制链接]
跳转到指定楼层
楼主
hybpjx 发表于 2024-3-20 09:46 回帖奖励
本帖最后由 hybpjx 于 2024-3-20 09:48 编辑

声明

本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!

目标

解决JD常用的验证码

前言

这里常用的就三种接口

  1. 登录接口的滑块
  2. cfe链接接口中的滑块
  3. cfe链接接口中的点选

还有两个验证码

  • 旋转验证码
    • 这个验证码我一直没有触发。听说触发了这个验证码 就离毁号不远了。且爬且珍惜吧。
  • 手势验证码
    • 只有固定接口才有。

三种验证

登录的滑块接口

接口链接

aHR0cHM6Ly9wYXNzcG9ydC5qZC5jb20vdWMvbG9naW4/bHR5cGU9bG9nb3V0

login接口

如上图所示。

这里就不多说废话了。直接刷新看接口

登录接口

这里可以看到 有两个接口请求发出。分别为

  • s: 负责验证请求
  • g:负责获取图片和参数

g请求

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

g请求

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

参数

这里打断点进栈

image-20240315143031247

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

vm5

然后就是j

j

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

j的生成

其他的值。

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

然后看返回值

返回值

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

S请求

这里继续看s请求。

s请求参数

这里和上图结果图作为比较。可以发现。

c好像就是challenge。

这里话不多说了。其他的值都不重要 。

最主要是这个d参数。

我们重点来看这个d

g栈

如下图所示。很多值的生成都能直接看到。

这里d 值的生成 则是由一个方法传参加密了一堆数组。

那这个数组是啥东西。其实我们不难发现这就是个轨迹。通过getCoordinate这个方法 才变成了请求接口中的d这个样子。

d方法如下:

'getCoordinate': function(a) {
        var b = this;
        var c = new Array();
        for (var d = 0x0; d < a['length']; d++) {
            if (d == 0x0) {
                c['push'](b['pretreatment'](a[d][0x0] < 0x3ffff ? a[d][0x0] : 0x3ffff, 0x3, !![]));
                c['push'](b['pretreatment'](a[d][0x1] < 0xffffff ? a[d][0x1] : 0xffffff, 0x4, !![]));
                c['push'](b['pretreatment'](a[d][0x2] < 0x3ffffffffff ? a[d][0x2] : 0x3ffffffffff, 0x7, !![]));
            } else {
                var e = a[d][0x0] - a[d - 0x1][0x0];
                var f = a[d][0x1] - a[d - 0x1][0x1];
                var g = a[d][0x2] - a[d - 0x1][0x2];
                c['push'](b['pretreatment'](e < 0xfff ? e : 0xfff, 0x2, ![]));
                c['push'](b['pretreatment'](f < 0xfff ? f : 0xfff, 0x2, ![]));
                c['push'](b['pretreatment'](g < 0xffffff ? g : 0xffffff, 0x4, !![]));
            }
        }
        return c['join']('');
    }

这个方法很简单。缺什么扣什么。

然后传值轨迹。

至于轨迹的生成。可以通过某些算法去实现这个列表数组的生成。

这里是通过举例然后偏移出数组变量

这里贴个方法用于生成数组。

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加密了

image-20240315145753650

第三个api请求也是加密了 enbody。返回值没东西。应该也是个请求判定。

第四个fp请求。请求需要伪装si 和 ct。 而这个si 刚好和第二个api请求的si相同。 返回了 fp 和 st。

第五个请求: web_jcap_report 返回了 code为0 无用请求。

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

第七个请求:无用请求

第八个请求:即是第二个check请求。同第六个请求相似。但是返回值不一样。

那这样我们大概就能明白个具体流程了。

  1. api请求得到si
  2. 通过fp请求 请求出fp和st
  3. 通过上述这些返回值 以及其他参数获得check的请求参数加密。
  4. 通过第一次check请求得到图片。进行识别
  5. 然后通过识别到的信息再次请求check。完成滑块加密。

api请求

这里我们首先去看一下api请求。

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

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

这里简化一下代码

result = encrypt(JSON.stringify(param), 'rhiasnkdhandrisk', 'r-s-h-n_r_isnkdk')

然后过一下断点。这里可以看到 加密的值分别为

eid: 可以为空

evType: 应该是验证码类型 2

requestId: 由另一个接口605返回的requestID

shshshfpx:  是一个随机值的算法。包含随机生成以及时间戳的拼接。

image-20240315150702706

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

fp请求

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

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

这里可以看到 。这个值应该就是加密的值。

然后把几个函数互相扣一下就行。

这个x函数的算法如下

image-20240315151752045

function s(t, e, n) {
                var r = t
                  , a = o;
                e && (i = e);
                return c(r, a)
            }

这里层层扣即可。

然后把第一次api返回的那个data和这个新生成的ct

去请求 得到fpst

两次check

第一次

断点继续走 走到下图所在位置

如上图所示。

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

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

然后进行加密就得到了tk和ct。

然后请求。得到两张以base64为编码的图片。

第二次check请求

这里其他值加密都是一样的。

唯一不同就是这个 JSON.stringify(t)  如下图所示

这里传值是轨迹。通过两张图片识别出来的距离 最终通过算法生成轨迹。

这里轨迹简单看看 第一个是移动的距离值。

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

这里可以通过算法去实现 也可以通过自建库来实现。

最后通过这些值加密 得到请求参数即可完成滑块。整体流程如下

cfe 点选验证

下面接口网站如下

aHR0cHM6Ly9jZmUubS5qZC5jb20vcHJpdmF0ZWRvbWFpbi9yaXNrX2hhbmRsZXIvMDMxMDE5MDAvP3JldHVybnVybD1odHRwcyUzQSUyRiUyRml0ZW0uamQuY29tJTJGMTAwMDQ4MjcyNzYyLmh0bWwmcnFob3N0PWh0dHBzJTNBJTJGJTJGYXBpLm0uamQuY29tJnJwaWQ9cnAtMTg2NTQ5NDkwLTEwMDU2LTE3MDk3MTMyMDgzNzEmZXZ0eXBlPTImZXZhcGk9Y29sb3JfcGNfZGV0YWlscGFnZV93YXJlQnVzaW5lc3Mmc291cmNlPTEmZm9yY2VDdXJyZW50Vmlldz0x

和滑块一个连接 不同的是 验证码不一样。

触发方式应该是与爬取的等级而定。

如上图所示。

其实上图大体流程都是一样的。这里唯一有一点区别。

如下图所示。这里JSON.stringify(t) 里面传值的不是轨迹。而是 点选的坐标。

这里可以通过两种方式去解决

  1. 通过打码平台识别——亲测识别率很低
  2. 通过训练。这里可以导出几百张数据集。然后通过yolov5识别

这里还有一点需要注意。

这里点选的 验证码

第一次check返回的tp值是22 。

而滑块是30

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

免费评分

参与人数 38吾爱币 +35 热心值 +34 收起 理由
zzyzy + 1 + 1 谢谢@Thanks!
sohowtodo + 1 + 1 讲的非常好
Mortalie + 1 热心回复!
kitty_too + 1 + 1 谢谢@Thanks!
Twenty2k + 1 + 1 谢谢@Thanks!
skycom + 1 + 1 谢谢@Thanks!
外酥内嫩 + 1 + 1 后面两种验证码我都没见过
YQYuan + 1 + 1 谢谢@Thanks!
lemonrains + 1 + 1 用心讨论,共获提升!
XianNianGao123 + 1 用心讨论,共获提升!
嗯,是我he + 1 + 1 用心讨论,共获提升!
lrhdabb + 1 + 1 用心讨论,共获提升!
XMQ + 1 我很赞同!
m2kar + 1 + 1 用心讨论,共获提升!
3bbdyg + 1 + 1 谢谢@Thanks!
ccwav + 1 + 1 谢谢@Thanks!
rootcup + 1 + 1 用心讨论,共获提升!
jesssy + 1 + 1 用心讨论,共获提升!
wolfstudio + 1 + 1 我很赞同!
ssdlh + 1 + 1 我很赞同!
kilkilo502 + 1 我很赞同!
丶峰宇 + 1 + 1 我很赞同!
xhonker + 1 谢谢@Thanks!
crizquan + 1 + 1 谢谢@Thanks!
b714572258 + 1 + 1 谢谢@Thanks!
Hc0133 + 1 + 1 热心回复!
axy + 1 + 1 谢谢@Thanks!
laozhang4201 + 1 + 1 谢谢@Thanks!
JaniQuiz + 1 用心讨论,共获提升!
我是一颗哈哈哒 + 1 我很赞同!
kittylang + 1 + 1 谢谢@Thanks!
notproblem + 1 + 1 谢谢@Thanks!
Courser + 1 + 1 用心讨论,共获提升!
evelinexue + 1 + 1 用心讨论,共获提升!
kylee + 1 + 1 我很赞同!
stopf578 + 1 + 1 热心回复!
0Fly + 1 + 1 谢谢@Thanks!
Voccoo + 1 + 1 用心讨论,共获提升!

查看全部评分

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

推荐
Jaime666 发表于 2024-3-20 10:36
学习了,原来是这样做的                                                           
推荐
DawnXi 发表于 2024-4-1 22:54
请问第一个验证码   轨迹数组生成完成后  执行getCoordinate  都能理解   伪装成功参数 和验证部分是怎么成功的,如何保证轨迹对应的加密串是正确的呢
3#
xiaozitwo 发表于 2024-3-20 10:02
这个好啊,通过该分析挺多网站的验证码都能搞了
4#
xqdyan 发表于 2024-3-20 10:07

这个好啊,通过该分析挺多网站的验证码都能搞了
5#
Voccoo 发表于 2024-3-20 10:29
好东西,研究研究
6#
ctiger 发表于 2024-3-20 10:32
非常详细,很有启发
7#
roothalo 发表于 2024-3-20 10:33
mark  多谢分享
8#
lzf880 发表于 2024-3-20 10:41
分析的很详细,好好学习下
9#
Lake0570 发表于 2024-3-20 10:59
感谢师傅的分享,可以举一反三了,打算找个其他网站测试一下
10#
Yukin0shita 发表于 2024-3-20 11:00
学到了,感谢楼主
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

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

GMT+8, 2024-4-27 19:23

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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