反爬(八)

反爬八

网站介绍

本次破解目标网站,该网站是一个搜索式的网站,支持模糊搜索,我们输入“公司”进行搜索,发现有滑动验证码。

分析其网络请求,我们可以看到这样几个关键请求:

获取验证码的请求:https://neris.csrc.gov.cn/shixinchaxun/sxcx/captcha/get(实际为一个POST请求)

post参数

1
2
3
captchaType: "blockPuzzle"
clientUid: "slider-d063f7fa-f53c-4868-8fed-12d68cfe932b"
ts: 1647072841626

请求返回json数据,内容如下

很显然,jigsawImageBase64originalImageBase64应该是图片的base64编码,推测应该分别是背景图片和目标图片(小的滑块)。

验证码是否正确通过的验证请求:https://neris.csrc.gov.cn/shixinchaxun/sxcx/captcha/check(实际为一个POST请求)

post参数

1
2
3
captchaType: "blockPuzzle"
pointJson: "JC4f4zowe7H03O8Qxh0kwtkf8Vvm1CS8N0T2Y1A/+mc="
token: "82a6a87861574bf4a0b12a3a89f0f9bb"

请求返回json数据,内容如下

看起来这个请求就是单纯做一次check,check成功的话,success返回true,其他并没有返回什么额外的信息。

真实的数据查询请求:https://neris.csrc.gov.cn/shixinchaxun/sxcx/PublicData/query(实际为一个POST请求)

post参数

1
2
3
4
obj_name: "公司"
page: {pageNo: 1, pageSize: 10}
real_cardnumber: ""
verification: "aIOk/lh5bEYvzgpaCRwNzCKJbClYHL615v3+YEzJMXVFAQhRU47fIccxwmEcsr0gVnDRpfWt181XQZEwe3DJCw=="

返回的内容就是实际查询的数据了,没啥特别要说的。最主要的就是这里的post参数,verification的值怎么得到的。

开始破解

综合观察上面的三个请求,其中获取验证码的请求,参数分别为captchaTypeclientUidts,前两个基本是一个比较固定的值,ts就是时间戳。所以这个请求比较简单,没啥问题。

接着check请求的参数分别为captchaTypepointJsontoken,token是get请求返回的,最主要的就是pointJson参数是如何得到的。推测是滑块的坐标数据,经过加密得到的。同样的,query请求的verification参数,也是目前需要破解的。

这个时候,我通过网络请求调用顺序,往前找了几层,都没有找到什么结果。然后直接利用搜索pointJson关键字,直接定位到相关的js文件,打个断点调试看一下。

PointJson参数

我们把这段核心的代码拿出来看看

1
2
3
4
5
6
7
8
9
10
11
12
13
var i = parseInt((this.moveBlockLeft || "0").replace("px", ""));
i = 310 * i / parseInt(this.setSize.imgWidth);
var c = {
captchaType: this.captchaType,
pointJson: this.secretKey ? it(JSON.stringify({
x: i,
y: 5
}), this.secretKey) : JSON.stringify({
x: i,
y: 5
}),
token: this.backToken
};

很容易想到,this.moveBlockLeft是滑块滑动的距离(一般是滑块的左边缘距离最左边的距离),this.setSize.imgWidth的值是400pxthis.secretKey就是get请求返回的secretKey,理论上肯定是有值的,所以针对这个三目条件运算符,实际的pointJson是通过以下代码运行得到的。

1
2
3
4
it(JSON.stringify({
x: i,
y: 5
}), this.secretKey)

这里有一点需要说明,假设滑块距离为left,这里的x(也就是i)应该为

1
i = 310 * i / 400

这是因为,验证码图片的宽度实际为310px,但是在浏览器中被放大成了400px,所以要进行等比例缩小。而我们实际下载下来验证码图片就是310px,我们识别得到的滑块距离就是实际的值,无需再进行上述的缩小操作。

接着,很容易找到it方法

1
2
3
4
5
6
7
8
9
10
function it(t) {
var e = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : "XwKsGlMcdPMEhR1B"
, i = et.a.enc.Utf8.parse(e)
, c = et.a.enc.Utf8.parse(t)
, o = et.a.AES.encrypt(c, i, {
mode: et.a.mode.ECB,
padding: et.a.pad.Pkcs7
});
return o.toString()
}

这里如果你对jscrypto-js包比较熟悉,很容易就可以将上面的代码更换为

1
2
3
4
5
6
7
8
9
10
11
12
const CryptoJS = require("crypto-js")

function it(t) {
var e = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : "XwKsGlMcdPMEhR1B"
, i = CryptoJS.enc.Utf8.parse(e)
, c = CryptoJS.enc.Utf8.parse(t)
, o = CryptoJS.AES.encrypt(c, i, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return o.toString()
}

这样pointJson参数基本就很容易的解决了

1
2
3
4
5
6
7
8
9
10
// 生成pointJson参数,需要传入x坐标和secretKey
function get_point_json(x, secretKey){
// var secretKey = "1J4w40BxPRW3m7V7";
// x = 310 * parseInt(x) / 400;
x = parseInt(x); // 要保证x为整型
var t = JSON.stringify({
"x":x,"y":5
});
return it(t, secretKey);
}

下面你只要使用特定的x和secretKey,看看生成的pointJson是否和浏览器中的一致即可。

Verification参数

使用相同的方法,定位到Verification参数,发现也是很简单的代码

然后使用的也是相同的it方法,只不过传入的参数略有不同,直接改写

1
2
3
4
5
6
7
8
9
// 生成verification参数,需要传入token、secretKey以及x坐标
function get_verification(x, secretKey, token){
// x = 310 * parseInt(x) / 400;
x = parseInt(x);
var t = JSON.stringify({
"x":x,"y":5
});
return it(token+"---"+t, secretKey);
}

很简单的,我们就把pointJsonverification参数解决了,然后主要的问题就是如何得到滑块的距离,这里可以采用两种方案,一种是openCV的模板匹配,另一种是使用深度学习的目标检测。

关于模板匹配的文章:https://forchenxi.github.io/2020/09/28/ip-slider-captcha/

关于深度学习的目标检测的文章:https://forchenxi.github.io/2020/11/22/dl-slider-captcha/

pytorch版本:https://forchenxi.github.io/2021/07/23/dl-pytorch-yolov3/