← 返回文章列表

深度解析:某盾文字点选验证码逆向分析(2.28.5版本)

前言:话说Ken大哥上回分享的某专利网站模拟登录逆向当中的滑块验证码过于简单了,今天分享个稍微有点难度的某盾的点选验证码,版本是最新的2.28.5。有什么问题欢迎大家留言或者私信。逆向分析:1、获取图片接口cb参数刷新后我们发现图片是通过g

前言:

话说Ken大哥上回分享的

某专利网站模拟登录逆向

当中的滑块验证码过于简单了,今天分享个稍微有点难度的某盾的点选验证码,版本是最新的2.28.5。

有什么问题欢迎大家留言或者私信。

逆向分析:

1、获取图片接口cb参数

刷新后我们发现图片是通过get接口生成的,请求载荷里一堆加密参数,看起来很吓人。实际上,dt、id和fp都是固定的可以写死,irToken、token注释掉也不影响结果生成,这里要解的只有cb一个变量。

由于get接口是个js请求,我们屡试不爽的XHR断点技术用不了。没关系,通过启动器一样可以定位到发包的位置。

由于页面加上了js脚本,此时需要耐心的一步一步往上跟栈,终于找到了cb参数生成的位置。这个方法的return值就是我们需要的数据。

考虑到脚本加上了js混淆,很怕自己一点一点扣代码会扣吐,干脆把整个文件的js代码全扣+补环境。

注意下,我们需要的方法在其他方法内部,必须得导出,建议保存在window对象中。否则我们没办法调用。

易盾需要补的环境不多,几个常用对象一补就搞定了。直接调用我们导出的cb参数即可得到参数值,使用python调用后成功获取图片信息。

window = global

window.addEventListener = function (args) {

console.log('window的addEventListener获取的参数:', args)

}

document = {

body: {},

createElement: function (args) {

console.log('document的createElement获取的参数:', args)

if (args == 'div') {

return {

addEventListener: function (args) {

console.log('document的addEventListener获取的参数:', args)

},

getAttribute: function (args) {

console.log('document的addEventListener的getAttribute获取的参数:', args)

},

}

}

},

addEventListener: function (args) {

console.log('document的addEventListener获取的参数:', args)

},

getElementById: function (args) {

console.log('document的addEventListener获取的参数:', args)

},

}

setTimeout = function(){}

setInterval = function(){}

navigator = {

userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36'

}

location = {

"ancestorOrigins": {},

"href": "https://dun.163.com/trial/picture-click",

"origin": "https://dun.163.com",

"protocol": "https:",

"host": "dun.163.com",

"hostname": "dun.163.com",

"port": "",

"pathname": "/trial/picture-click",

"search": "",

"hash": ""

}

...省略...

//生成cb参数

function getCbParam(){

return window._cbvalue()

}

2、图片文字及坐标识别

requests请求获取到接口返回url的图片数据、点选文字、图片token(token需要在后面的check接口里用到)后,接下来需要识别图片上文字的坐标。

点选文字的识别我试过一些第三方库,成功率都不高,一般推荐收费的打码平台。我个人习惯用云码,准确率还不错。

选择正确的图片验证类型(易盾对应30100类型,不确定的可以去咨询平台客服),参考接口格式直接将图片的content、点选文字、云码token传入,接口识别成功。

3、验证接口解析

界面上完成点选操作后会调用check接口验证点选的位置是否正确,这个验证接口也是最难的一个环节。

我们看到载荷里的大部分参数都是固定的,token取获取图片时拿到的token,需要解的只有data这一长串参数。暂时我们不知道这一长串是什么,不过验证码其实都大同小异,一般都是坐标+鼠标轨迹的加密值。

data参数当中有个空的d值,还有m、p、ext三个值需要解。一样采用启动器定位发包,发现还是断在同一行脚本处。仍然是耐心往前跟栈,幸好参数名称没有被混淆,轻松找到了赋值的位置。

虽然混淆后的代码看着恶心,幸好不多,一个个耐心的看过去。所有的方法都不用理会,直接从全扣下来的js脚本中导出就行了。

其中两个变量值引起了我们的注意:参数p里用到了一个3位的数组,m和ext里有个181位的数组(每次数组的长度都不一样)。查看上方的赋值逻辑,3位数组保存在pointsStack属性,181位保存在traceData属性。于是我们可以大胆猜测,这个3位数组记录的是点选的3个文字坐标,181位数组是鼠标移动的轨迹。

在页面上搜索关键词pointsStack、traceData,在赋值的地方都打上断点。刷新后重新点选,点了一下就成功断住,印证了我们的猜测。

传递的几个参数分别是图片token、坐标XY、时间差值,并通过_0x22ad00方法加密。

然后,我们鼠标每次移动到图片上都会在traceData赋值处断住,这实际上是触发了鼠标轨迹的记录。鼠标轨迹的加密一样是传了token、坐标XY、时间差值、并通过_0x22ad00方法加密。

至此,我们的js分析全部完成,剩下的就是要模拟鼠标的轨迹了。

function get_encryValue(token, tr_list){

var gj_list = []

for (i = 0; i < tr_list.length; i++){

gj_data = window._0x3ea30f(token, [tr_list[i][0], tr_list[i][1], tr_list[i][2], 0] + '')

gj_list.push(gj_data)

}

return gj_list

}

function getTrackData(token, tr_list, zb_list){

I= get_encryValue(token, tr_list)

T= get_encryValue(token, zb_list)

return JSON['stringify']({

'd': '',

'm': window._0x44d665(window._0x5255f1["sample"](I, 50)['join'](':')),

'p': window._0x44d665(T['join'](':')),

'ext': window._0x44d665(window._0x22ad00(token, 3 + ',' + I['length']))

})

}

4、生成鼠标轨迹验证通过

鼠标轨迹的生成要尽量模拟真人的操作,避免被服务器判断为脚本生成的。大致逻辑就是,在相邻的两个点坐标间,取随机的轨迹生成数量、以及随机的拖动时间,两次轨迹坐标加上少量的偏差避免成一条直线。不多说,直接上代码。

# 生成轨迹及点选

def get_trackData(self, xy):

xy = xy.split('|')

xy_list = [i.split(',') for i in xy]

xy_list = [[int(x), int(y)]for x, y in xy_list]

tr = [] #轨迹列表

dx = [] #点选列表

for i in range(len(xy_list) - 1):

# 循环获取到坐标点

s, e = xy_list[i], xy_list[i + 1] # 取出相邻的两个点

if not tr:

tr.append([*s, 3])

dx.append([*s, 3])

np = random.randint(30, 40) # 两个点直接生成的轨迹数量

bt = random.randint(30, 60) # 随机时间差值

for j in range(np):

# 生成两个点之间的 轨迹

p = (j + 1) / (np + 1) # 进度比例

x = int(s[0] + (e[0] - s[0]) * p)

y = int(s[1] + (e[1] - s[1]) * p)

tr.append([x, y, tr[-1][2] + bt])

tr.append([*e, tr[-1][2] + bt])

dx.append([*e, tr[-1][2] + bt])

return tr, dx

将轨迹列表、点选列表和token传给js后即可获得加密后的data参数。调用check验证接口返回验证成功的结果。

Ken大哥是爬虫逆向行业的新人,分享的文章都是开放的,很乐意跟大家共同探讨提高。

感谢大家的留言及关注!有合作意向的朋友们欢迎私聊。。。