油猴脚本过ICP的验证码,手动输入太麻烦了
目标网站:
https://m-beian.miit.gov.cn/#/
注意: 当前移动站的服务已经挂掉,此脚本虽然还能工作但目标网站的接口已经无法查询,仅剩一些参考价值故而保留。
未开启插件时:
开启插件后:
目标网站上有个验证码:
让我们来提交一个请求看一下:
请求虽然没有成功,但是它的请求上没有携带验证码参数,这说明什么,让我们再来尝试输入一个错误的验证码来试一试,可以看到当输入错误的验证码的时候会弹出一个提示“错误的验证码”:
虽然有个错误弹窗,但是观察开发者工具发现并没有捕获到有请求发出,这说明什么,这个验证这他妈应该就是前端验证的啊,接下来我们来探究一下它是如何生成的,又是如何校验的。
我们看到这个验证码是使用一个canvas绘制的,这个canvas的id是s-canvas
:
如果要用JS
来在canvas
上绘制验证码,第一件事可能就是先根据canvas
的id
获取到它,所以让我们来全局搜索一下,ok
很顺利的就找到了绘制验证码的代码:
让我们在这个绘制函数里打一个断点,然后刷新验证码触发断点,从drawPic
这个函数能够看到,验证码文本没有使用函数参数传递,而是放在this.identifyCode
上的,所以现在的关键点就是通过调用栈追溯到this.identifyCode
是怎么生成的:
或者我们选择全局搜索给this.identifyCode
赋值的地方,用什么方法都可以,总之定位到了一个名为refreshCode
的方法里,而这个方法又调用了makeCode
方法:
验证码生成的核心逻辑:
// e = 6
for (var n = 0; n < e; n++) {
this.identifyCode += this.identifyCodes[this.randomNum(0, this.identifyCodes.length)]
}
关于this.identifyCodes
的值,能够在初始化的时候看到,这个就是验证码允许出现的字符都有哪些:
而this.randomNum
就很简单了,就是在给定的[t, e)
的区间内随机选择一个整数:
randomNum: function(t, e) {
return Math.floor(Math.random() * (e - t) + t)
},
到这里验证码生成的逻辑就很简单了,就是每次在字符集"1234567890abcdefjhijklmnopqrstuvwxyz"
中随机选择一个字符,一共选择六次(是可以重复的),将这六个字符按顺序拼接起来作为最终的验证码。
而让我们再来看一下验证码验证的逻辑:
至此就有大概的思路了,我们只需要操控生成的验证码的值,然后再给验证码的input
输入框自动填充上我们操控生成的验证码的值,就能实现自动识别验证码,虽然这个自动识别没啥技术含量....
让我们再来观察一下选择验证码字符的函数,我们选择hook
Math.floor
,操作其返回值,比如我们返回一个固定的返回值5
,就相当于是在字符集"1234567890abcdefjhijklmnopqrstuvwxyz"
中每次都选择下标为5的字符:
randomNum: function(t, e) {
return Math.floor(Math.random() * (e - t) + t)
},
这样的效果大概就是这样子的:
插件的核心代码也只有几行:
Math.floor = function () {
return 5;
}
当然我们能够这么简单粗暴的做的原因是因为页面上并没有其它地方用到了这个函数,所以我们可以大胆的修改接管这个函数,如果页面逻辑很复杂并且不确定要hook
的函数会被哪些地方用到的话则不能这么粗暴了,当然具体情况需要具体分析给出不同的应对方案,找准关键点,轻轻一拨达到想要的效果,这也是逆向的魅力所在,如果是纯体力活无脑跟跟跟的话反而没啥意思了。
完整代码请查看项目根目录下的main.js文件。
PC站也加入bypass