本篇主要介绍树美滑块的提交参数分析和demo本地调用
以下是获取滑块
以下是获取滑块返回信息
以下是提交验证
以下是提交参数
参数差异对比
会变化的参数是以下几个
以下是提交参数返回状态
这里提前说一下,树美官网的滑块demo,在逆向参数的时候,captcha-sdk.min.js文件做了格式化检测,当你 抓包替换 格式化的之后,再去滑动提交是不会成功的,当你把格式化之后的代码再压缩回去,他又可以了。关于检测函数,已经找到了,后面会讲。
首先拖动滑块,进到这个请求堆栈
这里是发请求的一个函数,需要的URL拼接参数是已经生成完毕了
可以看到,函数里面, _0x5490b4 这个参数是来自这个位置
这一部分又是以下生成的,先看一下这个函数的内容
_0x5490b4 = _0x350cf6[_0x133ab2(0x54f)][_0x133ab2(0x18d)]((_0x777e0f = {
'organization': _0x2dc6db
}
查看以下其他的参数,可以看到,函数里面, _0x3e991e 这个参数是来自这个位置
进到函数里面,这里有这些参数的生成方式,到了这里,我们基本上完成了80%,把关键函数找的差不多,这里我们就需要细致的去分析和验证我们的猜想以及校验数据的对错,用Fiddler替换 js 文件改为本地调试。
前面说过他的一个坑,对于本地替换调试的代码,当你保存本地,只要格式化保存,然后你再去滑动验证,即便滑块位置对了,你是验证不过去的,通过调试发现,他的检测函数放在了 this['getEncryptContent'] 这个函数里面,也就是下面这行,只需要把他删除或者格式化即可
_0x350cf6[_0x5ec861(0x54f)][_0x5ec861(0x385)]() && (_0x1814e2 = _0x157b97);
下面他用的是标准DES计算的。然后转换为base64,我这里呢,使用一个全局函数将其导出到外部使用,这里就不扣代码了,想扣的自己扣,不扣呢,是因为万一这货改了标准加密,我又得麻烦
所有参数都会经过this['getEncryptContent'] 这个函数得出结果,几个重要参数说一下
th 参数
第一个参数是滑块轨迹,第二个参数是一个key值,key值可以暂时写死
gk 参数
第一个参数是轨迹最后的结束时间,也就是时间差,第二个参数是一个key值,key值可以暂时写死
ud 参数
第一个参数是滑块的距离,第二个参数是一个key值,key值可以暂时写死
根据他的滑块创建规律,我自己写的滑块轨迹算法
// 随机数
function randomNum(minNum, maxNum) {
return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
}
// 创建路径
function createPath(mouseX) {
let length = randomNum(7, 9)
let path = [[0, -2, 0]]
let m = mouseX % length
let e = Math.floor(mouseX / length)
for (let i = 0; i < length; i++) {
let y = -2 + (-randomNum(2, 4))
let time = (i + 1) * 100 + randomNum(1, 3)
let x = (i + 1) * e + m + randomNum(20, 40)
if (i == length - 1) {
x = mouseX
path.push([x, y, time])
} else {
path.push([x, y, time])
}
}
return path
}
下面为识别算法
import requests
import re
import cv2
import sys
import json
# 链接正则
def urlRegular(text):
reg = re.compile(r"\w{4,5}://(\w|\.|:)+")
res = re.match(reg, text)
if res:
return webImage(text)
else:
return text
# 保存网络图片
def webImage(webImg):
r = requests.get(webImg)
picName = webImg.split('/')[-1]
with open(picName, "wb") as f:
f.write(r.content)
f.close()
return picName
# 简单滑块识别(极验、易盾、数美等)
def simpleSliderDistinguish(bgImgGray, sliderImgGray):
bgImgGray = urlRegular(bgImgGray)
sliderImgGray = urlRegular(sliderImgGray)
originImg = cv2.imread(bgImgGray)
bgImg = cv2.imread(bgImgGray, 0)
sliderImg = cv2.imread(sliderImgGray, 0)
# 高斯滤波
imgGaussianBlur1 = cv2.GaussianBlur(sliderImg, (7, 7), 0)
imgGaussianBlur2 = cv2.GaussianBlur(bgImg, (7, 7), 0)
# 边缘计算
cannySlider = cv2.Canny(imgGaussianBlur1, 100, 200)
cannyBgImg = cv2.Canny(imgGaussianBlur2, 100, 200)
# 获取模板图像的高和宽
th, tw = cannySlider.shape[:2]
# 使用标准相关系数匹配,1表示完美匹配,-1表示糟糕的匹配,0表示没有任何相关性
result = cv2.matchTemplate(cannyBgImg, cannySlider, cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
tl = max_loc
br = (tl[0] + tw, tl[1] + th)
resObj = [tl[0], tl[1], br[0], br[1]]
obj = {}
obj['topX'] = tl[0]/2
obj['topY'] = tl[1]/2
# obj['br[0]'] = br[0]/2
# obj['br[1]'] = br[1]/2
j = json.dumps(obj)
print(j)
# 绘制画框
im = cv2.rectangle(originImg, tl, br, (0, 0, 255), 2)
cv2.imwrite("result.jpg", im)
return resObj
simpleSliderDistinguish(sys.argv[1], sys.argv[2])
识别服务我是用的 koa 写成了一个接口调用python代码来处理
以下为前端本地调用测试,还是喜欢可视化一点,主要是方便输出调试,也可以将他改为py或其他代码,方案很多,仅供参考
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0" />
<title>Document</title>
<script src="/jquery.min.js"></script>
<script src="/cap.js"></script>
</head>
<body>
<button id="getpdf">获取数美</button>
<button id="getMouseX">getMouseX</button>
<script>
function randomNum(minNum, maxNum) {
return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
}
// 创建路径
function createPath(mouseX) {
let length = randomNum(7, 9)
let path = [[0, -2, 0]]
let m = mouseX % length
let e = Math.floor(mouseX / length)
for (let i = 0; i < length; i++) {
let y = -2 + (-randomNum(2, 4))
let time = (i + 1) * 100 + randomNum(1, 3)
let x = (i + 1) * e + m + randomNum(20, 40)
if (i == length - 1) {
x = mouseX
path.push([x, y, time])
} else {
path.push([x, y, time])
}
}
return path
}
// 测试用
$('#getMouseX').click(function () {
$.ajax({
type: "POST", // 推荐POST请求
url: "http://127.0.0.1:9876/list",
data: {
bg: 'https://castatic.fengkongcloud.cn/crb/set-000006/v2/028a2775fde334e864be5de82a570fce_bg.jpg',
slider: 'https://castatic.fengkongcloud.cn/crb/set-000006/v2/028a2775fde334e864be5de82a570fce_fg.png'
}, //格式{key:value}
dataType: "json",
beforeSend: function () { },
success: function (res) {
console.log(Math.floor(res.data['topX']));
},
error: function (e) {
},
complete: function () { },
})
})
$('#getpdf').on('click', function () {
let ridurl = "https://captcha.fengkongcloud.cn/ca/v1/register";
let url = "https://captcha.fengkongcloud.cn/ca/v2/fverify"
let imgHost = 'https://castatic.fengkongcloud.cn'
let params = {
"act.os": "web_pc",
"organization": "RlokQwRlVjUrTUlkIqOg",
"xz": "17v7DhZlX/U=",
"rversion": "1.0.3",
"lf": "LZ5lj7Zsx6o=",
"xd": "GGbuRtaOhEw=",
"mt": "/uUI72Au/8s=",
"yv": "xO8GIkawPMM=",
"hx": "yxz8/gOo4Go=",
"sdkver": "1.1.3",
"protocol": "164",
"rt": "x0EplGM6ZEE=",
"st": "qD7xdbARjYA=",
"yn": "mGZ3zDovaWQ=",
"ostype": "web",
"callback": "sm_" + (+new Date()),
}
let paramsrid = {
"sdkver": "1.1.3",
"organization": "RlokQwRlVjUrTUlkIqOg",
"callback": "sm_" + (+new Date()),
"data": "{}",
"model": "slide",
"rversion": "1.0.3",
"appId": "default",
"channel": "DEFAULT",
"lang": "zh-cn"
}
let mouseX = 0
$.ajax({
type: "GET",
url: ridurl,
data: paramsrid,
dataType: "jsonp",
beforeSend: function () { },
success: function (res) {
params.rid = res.detail.rid
let bg = 'https://castatic.fengkongcloud.cn' + res.detail.bg
let slider = 'https://castatic.fengkongcloud.cn' + res.detail.fg
$.ajax({
type: "POST",
url: "http://127.0.0.1:9876/list", // 获取滑块距离
data: { bg, slider },
dataType: "json",
beforeSend: function () { },
success: function (res) {
let mouseX = Math.floor(res.data['topX'])
let p = createPath(mouseX)
console.log(mouseX, p);
let th = win_getEncryptContent(p, "72015d49")
let gk = win_getEncryptContent(p[p.length - 1][p[p.length - 1].length - 1], "2ece114c")
let ud = win_getEncryptContent(mouseX / 300, "3494d909")
let object = Object.assign({ th, gk, ud }, params)
let html = ''
let icon = ''
for (const key in object) {
if (html != '') {
icon = '&'
}
if (Object.hasOwnProperty.call(object, key)) {
const item = object[key];
html += `${icon}${key}=${item}`
}
}
html = 'https://captcha.fengkongcloud.cn/ca/v2/fverify?' + html
var oScript = document.createElement('script');
oScript.type = 'text/javascript';
oScript.async = true;
oScript.src = html;
document.body.appendChild(oScript);
},
error: function (e) {
},
complete: function () { },
})
},
error: function (e) { },
complete: function () { }
})
})
</script>
</body>
</html>
验证演示:
文件地址
链接: https://pan.baidu.com/s/1VCYrb3IsEKnffld3uhRXUA?pwd=kfr5
提取码: kfr5