以GeeTest为例的滑动验证码破解 - 总体分析

以GeeTest为例的滑动验证码破解 - 总体分析

接下来便正式开始这个系列的教程了。虽然我本身已经完成破解的绝大部分,但是我仍然会在这个教程里以第一次接触的视角进行讲解,以避免出现太过跳跃的情况。

要想完成破解,必须先了解整个系统的运作原理。这里,乃至这往后的几篇文章里,的整个过程无需写任何一句代码。你只需要一个浏览器就够了。

至于浏览器的选择,我并不推荐Edge。因为未知的原因,在捕捉该页面的包的时候,其中一个包的Response Body在Edge的F12 Dev Tool中显示为空,实际上它包含重要的内容。而经过测试,在Chrome中该包内容正确显示。除了这一个包之外,Edge没有任何问题。

如果你实在不想用Chrome,可以选择Edge+Fiddler的组合(我就是这样解决的?)。

选择好你的浏览器之后,访问中山大学教务系统,待首次加载完成后,再开启F12,并从地址栏重新进入网页抓包(非刷新)。这样做可以避免一大堆CSS和图片之类的无用包干扰视线,最后拉动滑块完成验证。于是大概会得到如下的结果:

全程是清一色的GET Request,而且可以注意到,除了被高亮了的四个package,其他的包都是没有带Query String的,所以分析的重点便落在了这四个包上。

------------------------------------------------------------------------------------------

首先看1号包,也就是我前面提到的Edge无法抓到的包。这个包只带有一个参数ts:

如果重复多次抓包,可以看到这个ts值每次都不一样,初步猜测是随机数,防止浏览器从缓存中直接返回结果。至于Response Body,从Fiddler中看到如下:

{"success":1,"gt":"80a8ed78d1d22e16f5b6c478f03b9212","challenge":"c7f5d6e24c34b500221aed87feca10f8"}

这是一段包含了三个值的JSON,其中的gt和challenge显然会用在后面的请求中。

------------------------------------------------------------------------------------------

接着是2号包,这个包发送的参数如图:

其中gt便是我们刚才从1号包的回复里找到的值,至于callback,从名字推测是本地生成了名为geetest_xxxx的函数,待请求完成后会回调这个函数,至于后面的xxxx是怎么来的暂时不清楚。猜测是随机数。

至于Response Body,则意外地是一段没有被混淆的明文JavaScript。浏览后发现这段JavaScript是在执行一些初始化工作,并发出后面的请求。代码比较长这里不贴出来了,这也是这次破解里能见到的最后一段没有被混淆的JavaScript代码了?。

------------------------------------------------------------------------------------------

然后是3号包。刚才看的2号包实际上没有太多作用,返回的JavaScript中除了每次会更改要调用的回调函数名之外没有改变,并没有包含有效信息。3号包则不一样,其参数为:

2016-12-24更新:现在3号包已经新增参数"type",其值设置为"slide"即可。

callback应该也是回调函数的名称。challenge和gt是之前1号包得到的值。offline并不知道是什么?,不过看上去并不重要,应该设置为false就好了吧。至于product,从官网上可以了解到它的验证码有多种嵌入模式,而中山大学教务系统采用了float的模式。所以product设置为float就错不了了。

至于返回值,则包含了重要的信息:

geetest_1473597983192({"fullbg": "pictures/gt/6121c49a0/6121c49a0.jpg", "fullpage": false, "hide_delay": 800, "staticservers": ["uems.sysu.edu.cn/jwxt/geetest/"], "clean": false, "theme_version": "3.0.21", "https": false, "slice": "pictures/gt/6121c49a0/slice/88d04351e.png", "feedback": "http://www.geetest.com/contact#report", "product": "float", "link": "", "ypos": 31, "challenge": "c7f5d6e24c34b500221aed87feca10f8g6", "type": "slide", "id": "ac7f5d6e24c34b500221aed87feca10f8", "apiserver": "http://api.geetest.com/", "gt": "80a8ed78d1d22e16f5b6c478f03b9212", "height": 116, "xpos": 0, "version": "5.5.9", "show_delay": 250, "benchmark": false, "theme": "golden", "mobile": false, "logo": true, "bg": "pictures/gt/6121c49a0/bg/88d04351e.jpg"})

这是在以这段JSON为参数,调用本地的回调函数。这段JSON里包含了三个图片地址:fullbg、bg和slice。分表访问其URL,得到的图片如下:

看得出来,slice并不在我们的兴趣范围内。fullbg和bg则分别是完整的背景和缺块的背景。这两张图片被打乱的方法是相同的,可见可以通过同一个算法还原

另外,返回值里的staticservers和https可以帮我们还原出完整的图片地址。而剩余的对我们来说并没有太大意义。

------------------------------------------------------------------------------------------

最后是4号包,4号包明显是我们最后拖动滑块后触发的一个包,先看返回值:

geetest_1473598011335({"success": 1,"message":"success","validate":"8e9256b7a4857018ca8c247e3c4a1ee1","score":1})

总共4个返回值,其中score是用以显示“您击败了xx%的用户”的分数(这风格好像360?)。而validate相信是在网页的实际登录过程中的一个参数。

接下来看参数:

这里总共有多达7个参数。其中callback、challenge和gt的含义我们都已经了解。

a是一段看上去毫无意义的乱码,但是想必是用以防破解的Hash之类。

imgload,这个参数的含义暂时不清楚。

passtime,从名字便可以看出是指用户拉动滑块后用了多少时间完成,这也是返回值里score的来源。

userresponse,这个参数里包含了用户拉动滑块的数据。估计是用滑动距离加密得出。

------------------------------------------------------------------------------------------

至此,我们的第一步就大致完成了。我们知道了这些:

  • 总共需要模拟4个(甚至3个)包来完成验证码自动化
  • gt和challenge是服务器用以识别用户的参数
  • fullbg和bg两张图片被打乱了,但是能用同一个算法还原

然而更多的是未知:

  • 1号包的参数ts和后面的callback的后缀是否真的是随机数?
  • fullbg和bg通过什么算法还原完整图象?
  • imgload是什么参数,它是否服务器防破解的一环?
  • userresponse是如何算出的,其算法的参数中的移动距离使用px作单位吗?
  • a包含了什么信息,为什么这么长?

在接下来的文章里,我们将一一探索这些问题。通过一个个解决这些问题,我们便可以了解GeeTest系统运作的原理。在探索的过程中,我们会逐步开始写代码模拟。

这篇文章就到这里,从下一篇文章开始,我们便要开始阅读那些乱七八糟的经过混淆的JavaScript代码。我强烈建议读者将本教程里的内容自己动手试一遍,只有真的自己动手(get your hands dirty),才可能学到新的知识。

如果你喜欢我的文章,不妨关注我的专栏并点赞。本人编程水平不高,欢迎大家投稿让更多人学到新技术。谢谢!

编辑于 2016-12-24

文章被以下专栏收录