此文章时效性已过期。
又是美好的一天。我习惯性地打开「WHUers」预约自习座位,一个猝不及防的提示却让我差点再睡过去——“登录失败,系统异常,请稍后重试。”这一瞬间我的心里只有三种想法:
- 学校的图书馆系统崩了;
- 阿里云宕机了;
- 世界末日了。
我绝不相信自己昨晚新写的代码又出了问题,这是作为开发者该有的骨气。
于是我尝试打开图书馆网站——

原来在抢座脚本肆无忌惮的猖獗之下,图书馆预约系统上线了滑块验证。
这让我想起彼时为了优雅地解决验证码的问题,标识了大量的图片验证码样本,以供机器学习训练。没想到一夜之间,曾经眼睛胀痛、手指麻木换来的成果都付之东流。

我甚至可以想象,这背后的程序员在寒冷的冬夜里是怎样点着蜡烛(也许他们并不断电)、喝着咖啡,绞尽脑汁去向投机取巧的脚本家们反击——只为了给普通同学提供公平的自习机会。也许他手腕会酸,也许他颈椎会疼,甚至连脱落的银丝都给了他寒舍飘雪的幻象——思绪至此,不禁热泪盈眶。收拾一下心情,按下 F12。
在点击“请点此进行验证”按钮时,监测到浏览器发起了一个名为 verifyCode 的请求,它为我们返回了一个 HTML 页面。我们将其在新的页面打开——

一目了然,这是一个纯粹的滑块验证页面,它被嵌套在了原来的登录页面里。
查看这个页面的源代码,可以找到几个有用的 JavaScript 脚本文件:verify-code.js、dragdealer.js、bigdecimal.min.js。经验使然,提取出最重要的 verify-code.js,另外两个分别是 jQuery 的滑块组件和 JS 的高精度计算库。
与此同时,监测到在加载此验证页面时浏览器发起了一个名为 verifycode(这次是小写 c)的请求。它为我们返回了一些 JSON 数据——

顾名思义,在 data 键值里它告诉了我们完整的图片数据(wholeImg)、缺少的图片块(repairImg)、图片块的大小(repairWidth/Height)、身份标识码(verifyCode)以及目前不知道什么用的 point 值。
回到验证页面里,我们去体验一下这个滑块。
当拖动滑块完成验证时,浏览器又发起了一个请求,名为 verify——

这次,它向服务器转达了一些信息。一共有四个字段。显然,xvalue 为滑块向右滑动的水平偏移量,yvalue 和 appId 在多次请求中始终为固定值。而 code 的值又与刚刚的身份标识码(verifyCode)一样。
再看看它给我们返回了什么信息——

仍然是一些 JSON 格式的数据。在 data 键值里它告诉了我们当前的 IP 地址(ip)、完成滑块验证的用时(authStart/StopTime)以及最重要的认证标识码(authId)。
这意味着,服务器认为当前访问的用户已经完成了滑块验证,你可以畅通无阻地输入学号和密码登录进系统了。
事情至此似乎一切都变得简单起来:只要我们找出滑块所需的向右的水平偏移量 x,就可以伪造请求告诉服务器当前用户完成了滑块验证。可是问题来了,怎么去计算出需要的水平偏移量呢?
我想起了开头提到的那个最重要的 JS 脚本文件 verify-code.js,让我们去一探究竟。
打开这个 JS 文件,并不大,只有 8KB,然而——

它被混淆并加密了。
我们有一些盲目乐观了。挠一挠头发,我们尝试去解除混淆并人工解密。
第一步,先将 UTF-8 编码的字符解码,得到一个包含有多个字符串的字符串数组,里面包含一些常见的 js 指令。
第二步,将字符串数组的索引值由十六进制转换为十进制,并替换原字符串,得到其中一个较为清晰的函数——
function 这是发起名为 verifycode 请求的命令,我们前文已经解析过了。
第三步,通过字符串替换等等,对剩下的代码进行分析。只可意会,难以言传,幸在代码并不长。我来把解出的代码进行重点分析(所有的变量名均为自定义的)。
var 这两句是用之前名为 verifycode 的请求获取到的完整的图片数据(wholeImg)构建一个 Image 对象。
我们不妨去看一看这个完整的图片到底是什么样的。直接将 Base64 编码的图片数据放在浏览器的地址栏,按下回车。

……
我想这当中可能有些许误会。为什么我们获取到的图片是一张“马赛克”,而浏览器里显示的却是一张完好的图片?这其中必定有某种未知的转换方式。
耐着性子继续往下分析。
var 这段代码很神奇。通过最后一句我发现,只有经过这番处理之后,完整的图片才被显示出来。而上面的一系列循环操作又是什么意思呢?
我们找回通过 verifycode 请求获取的到的当时不知什么用的 point 数据。
{
经过 JSON 解析,point 数据的 point 键值是一个对象数组,每一个对象都有 x 和 y 两个坐标元素。
答案似乎将要浮出水面了。
原本完整的图片被分成了 30*15 个碎片,这些碎片被打乱排布以使得我们看到了一张“马赛克”。而 point 数组里共有 450 个坐标元素,这些坐标数据正是这些碎片的正确排列方式。我们可以按照代码里既有的算法,将这些碎片重新排列。
下面我们开始写代码。人生苦短,我用 Python。
运行环境 Windows 10,Python 版本 3.6.6,用到的库:PIL、BytesIO、OpenCV、AirCV、Requests、Base64、JSON。
国际惯例,先构造请求头。模拟之前名为 verifycode 的请求,获取返回的 JSON 数据并解析。将 Base64 编码的完整图片数据解码并保存。
url 接下来,按照 point 数组里的坐标数据,对原图片进行切割和拼凑,生成新图片并保存。
img 得到结果。

我们要找什么来着?滑块的偏移量 x。
我们先截取出图片中的补偿部分,保存为“tile.jpg”。

最后,利用 AirCV 库提供的 find_template 方法,找出这一小块在原图中的位置。
father 运行一下,大功告成。

本文讲述了作者在遇到图书馆预约系统采用滑块验证后,如何通过分析JavaScript代码和利用Python进行图像处理,尝试破解这一验证机制的过程。在解析混淆加密的JS文件、理解图片碎片重组算法后,最终成功定位滑块验证的偏移量。文章揭示了验证码技术与自动化工具之间的博弈,同时也展示了技术在维护公平性方面的应用。

被折叠的 条评论
为什么被折叠?



