声明
本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!
目标
解决JD常用的验证码
前言
这里常用的就三种接口
- 登录接口的滑块
- cfe链接接口中的滑块
- cfe链接接口中的点选
还有两个验证码
- 旋转验证码
- 这个验证码我一直没有触发。听说触发了这个验证码 就离毁号不远了。且爬且珍惜吧。
- 手势验证码
三种验证
登录的滑块接口
接口链接
aHR0cHM6Ly9wYXNzcG9ydC5qZC5jb20vdWMvbG9naW4/bHR5cGU9bG9nb3V0
如上图所示。
这里就不多说废话了。直接刷新看接口
这里可以看到 有两个接口请求发出。分别为
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 < 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加密了
第三个api请求也是加密了 enbody。返回值没东西。应该也是个请求判定。
第四个fp请求。请求需要伪装si 和 ct。 而这个si 刚好和第二个api请求的si相同。 返回了 fp 和 st。
第五个请求: web_jcap_report 返回了 code为0 无用请求。
第六个请求:请求传参中带有上图fp参数的加密 还多了个tk加密。返回值 返回了 缺口图和背景图。
第七个请求:无用请求
第八个请求:即是第二个check请求。同第六个请求相似。但是返回值不一样。
那这样我们大概就能明白个具体流程了。
- api请求得到si
- 通过fp请求 请求出fp和st
- 通过上述这些返回值 以及其他参数获得check的请求参数加密。
- 通过第一次check请求得到图片。进行识别
- 然后通过识别到的信息再次请求check。完成滑块加密。
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) 里面传值的不是轨迹。而是 点选的坐标。
这里可以通过两种方式去解决
- 通过打码平台识别——亲测识别率很低
- 通过训练。这里可以导出几百张数据集。然后通过yolov5识别
这里还有一点需要注意。
这里点选的 验证码
第一次check返回的tp值是22 。
而滑块是30
可以通过这个去区分验证码。