hybpjx 发表于 2024-3-20 09:46

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

# 声明

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



# 目标

解决JD常用的验证码



# 前言

这里常用的就三种接口

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

还有两个验证码

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

# 三种验证

## 登录的滑块接口

接口链接

> 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方法如下:

```js
'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 < 0x3ffff ? a : 0x3ffff, 0x3, !![]));
                c['push'](b['pretreatment'](a < 0xffffff ? a : 0xffffff, 0x4, !![]));
                c['push'](b['pretreatment'](a < 0x3ffffffffff ? a : 0x3ffffffffff, 0x7, !![]));
            } else {
                var e = a - a;
                var f = a - a;
                var g = a - a;
                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']('');
    }
```

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

然后传值轨迹。

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

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

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

```python
def offer(distance):
    index = 0
    slide = []
    indexTime = str(int(time.time()))[:9]
    for item in self.base_slide:
      index += 1
      item = int(indexTime + str(item)[-4:])
      if int(item) >= (distance + int(self.base_slide)):
            slide = self.base_slide[:index]
            slide.append(
                )), item, item + 700 + int(random.random() * 1000)])
            break
    last = int(slide[-1].split('.'))
    pIndex = 0
    for item in self.push_slide:
      if pIndex == 0 or pIndex == len(self.push_slide) - 1:
            times = slide[-1]
      else:
            times = slide[-1] + (self.push_slide - self.push_slide)

      slide.append( + 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请求。同第六个请求相似。但是返回值不一样。



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

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



### api请求

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



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



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



这里简化一下代码

```js
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函数的算法如下



```js
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) 里面传值的不是轨迹。而是 点选的坐标。



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

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



这里还有一点需要注意。

这里点选的 验证码

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

而滑块是30



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

我想登上月球 发表于 2024-5-21 16:22

第二次check返回的数据,验证失败有两种情况:
s_code":16130和s_code":16190
貌似16130是距离有问题,16190是轨迹数据有问题
我缺口距离用的是ddddocr,貌似有点不准。轨迹是自己大概地写了个函数,前几次验证可以,提交几次后就一直返回16190

xiaozitwo 发表于 2024-3-20 10:02

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

xqdyan 发表于 2024-3-20 10:07


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

Voccoo 发表于 2024-3-20 10:29

好东西,研究研究

ctiger 发表于 2024-3-20 10:32

非常详细,很有启发

roothalo 发表于 2024-3-20 10:33

mark多谢分享

lzf880 发表于 2024-3-20 10:41

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

Yukin0shita 发表于 2024-3-20 11:00

学到了,感谢楼主

stopf578 发表于 2024-3-20 11:05

好东西啊

不是9527 发表于 2024-3-20 11:05

学习学习,谢谢分析@!
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 某东常用验证码逆向流程分析