每次回家坐火车都需要使用软件来抢票,而前段时间爆出了“12306网站密码大规模泄露”的事件。泄露的罪魁祸首就是那些第三方抢票软件!因此,自己写一个抢票软件来抢票,无毒无害,效率更高。
1. 破解登录表单
12306在登录账户时,使用了验证码、加载动态js、查找抢票软件关键词、加密表单内容的反抢票软件措施,因此需要搞清登录表单的每一个值是如何得到的。
登录时,需要向 https://kyfw.12306.cn/otn/login/loginAysnSuggest 这个url发送一个POST,内容如下:
oginUserDTO.user_name 和 userDTO.password 为自己账户的用户名和密码。
randCode为验证码图片中的字符。
randCode_validate 和 myversion 始终为一个定值。
第五行的表单值为动态生成的,每次都不固定。
因此,目前还得不到的就是验证码和动态生成的表单值。
1.1 验证码
验证码比较简单,经过抓包分析,获取验证码的图片的 url 为 https://kyfw.12306.cn/otn/passcodeNew/getPassCodeNew?module=login&rand=sjrand&0.7544988989830017 最后的这一串数字,随机生成就好。
验证码验证 url 为 https://kyfw.12306.cn/otn/passcodeNew/checkRandCodeAnsyn ,POST内容为 :
randCode 为验证码,rand 和 randCode_validate 始终为定值。
需要注意的是,经过试验,验证码的验证请求需要重复发送两次
1.2 动态表单值
这个表单值是成功的关键,需要生成正确的值,不然提交表单后会被视为抢票软件。同时,在之后的订单提交的表单中会多次用到这个方法来生成一个动态值。
经过对html的分析和抓包分析,刚才那个动态的表单值是在https://kyfw.12306.cn/otn/dynamicJs/lusnqex 这个 js 脚本中产生的,而这个脚本的
url 每次不固定,需要在登录界面的html 中提取:
分析这个 js 脚本,我们可以看到这个动态值是在这个位置产生的:
表单的 key 为 keyValues[0] , value 为
encode32(bin216(Base32.encrypt(keyVlues[1], keyVlues[0]))) 这个语句的结果,其中 encode32、bin216这些函数为这个 js 脚本内的几个加密函数。
分析来看这几个加密函数并不是采用标准的加密算法,而是对标准的加密算法经过了一些修改。
keyValues 是由gc()函数产生:
这个函数是分析当前html是否含有 js 抢票插件所产生的一些 html 元素,看来12306 在防范抢票插件里也下了不少功夫。经过试验,如果没有抢票插件这个函数将返回“NzU5MTI2:1111” 这个字符串,其中 NzU5MTI2 是gc函数中的 key 变量,每次都不同,是动态生成的。
得到了gc函数的值,下面就是得到那几个加密函数的算法。由于这段 js 是经过变量混淆过的代码,纯看代码来还原加密算法所需要的时间将会很多。于是,我采取了一个曲线救国的方式,将这个几个加密函数原封不动的放到 Java 程序里,然后使用 Java 内的 Javascript 引擎去运行它,最后得到加密后的值。(其它语言类似,需要用一个 Javascript 引擎来运行这段 js 代码)
到这里,我们就将登陆所需要的表单数据都得到了。但是此时向登录 url 发送请求,依然无法正常登录。经过与浏览器生成的请求进行一步步比对,发现我缺少一个 html 头—— “Referer” ,即浏览器的引用url,将这个值设为登录界面的url ——https://kyfw.12306.cn/otn/login/init
即可。
总结来讲就是以下几步:
1、发送 https://kyfw.12306.cn/otn/passcodeNew/getPassCodeNew?module=login&rand=sjrand&0.7544988989830017 请求获取验证码
2、发送 https://kyfw.12306.cn/otn/passcodeNew/checkRandCodeAnsyn 请求验证输入的验证码是否正确,发送两次
3、在 登录 html 中得到 https://kyfw.12306.cn/otn/dynamicJs/lusnqex 这个动态js的url,在这个 js 中得到key变量的值,然后使用 Java 的 js 引擎调用加密函数对 key:1111进行加密。
4、向 https://kyfw.12306.cn/otn/login/loginAysnSuggest 发送登录 POST请求,注意Http 头 信息的正确。
下一节,将介绍如何获得常用乘车人和余票信息