一直想写个订票软件,一是学习学习抓包,分析报文,HTTP协议,二是锻炼一下码代码能力。我将实现订票软件分为了几步,第一步是实现登录功能,第二部是实现查询功能,第三部是下订单功能。其中第二步实现略简单抓个包分析分析就行了,第三步可能以后还会细分为几步。参考了几篇博文,完成了现在的登录功能。在此只和大家分享一下登录的思路。
1. 12306登录流程分析
作者采用fiddler进行抓包,fiddler的使用教程见http://www.cnblogs.com/TankXiao/archive/2012/02/06/2337728.html
1.1 初始化登录页面
执行GET请求,请求URL为https://kyfw.12306.cn/otn/login/init,响应正文为登录页面的html源码,将改源码记录,在1.4中会用到
1.2 获取验证码
1.3 验证码检验
执行POST请求,请求URL为https://kyfw.12306.cn/otn/passcodeNew/checkRandCodeAnsyn
请求正文截图如下:
参数分析:
randCode:验证码参数,前些天12306对验证码进行了升级,升级之后分为八张图片供选择,该参数的几个值就是你点击点的横纵坐标,以左上角为(0,0),右下角为(300,300),一张图片的长宽是75。假如我点击的是第二张和第五张,那么该参数包含四个值ABCD,以英文逗号隔开,其中75<A<150,0<B<75,0<C<75,75<D<150。
rand:写死为"sjrand"即可。
验证码认证成功返回的消息体为:{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"result":"1","msg":"TRUE"},"messages":[],"validateMessages":{}}
1.4 登录信息提交
POST请求,请求URL为https://kyfw.12306.cn/otn/login/loginAysnSuggest
请求正文截图如下:
参数分析:
loginUserDTO.user_name:用户名
userDTO.password:密码
randCode:验证码参数
ODk2MzM2:这个等一下重点说明
myversion:写死为"undefined"即可
对于第四个参数,参数名和值都是动态生成的,生成思路是这样的:在1.1初始化登录页面得到的源码中,存在一个动态js,<script src="/otn/dynamicJs/lwvawai" type="text/javascript" xml:space="preserve"></script>,这个动态js的src是随机生成的,初始化一次登录页面生成一次,并且对其执行GET请求只能执行一次,我们需要的参数名就是其中的function gc()里的第一个变量值。value是这样通过encode32(bin216(Base32.encrypt(keyVlues[1], keyVlues[0])))得到的,其中通过阅读js代码得知keyVlues[0]为该参数名,keyVlues[1]是对抢票软件的检测,初始化为"",每次检测通过则+1,共四次检测,最后得到1111,也就是说keyVlues[1]写死为"1111"即可。将提取到的密码脚本保存在本地,代码如下:
function bin216(s){
var i,l,o = "",n;
s += "";
b = "";
for(i = 0,l = s.length;i < l;i ++ ){
b = s.charCodeAt(i);
n = b.toString(16);
o += n.length < 2 ? "0" + n : n;
}
return o;
};
var Base32 = new function(){
var delta = 0x9E3779B8;
function longArrayToString(data,includeLength){
var length = data.length;
var n = (length - 1) << 2;
if (includeLength){
var m = data[length - 1];
if((m < n - 3) || (m > n))return null;
n = m;
}
for(var i = 0;i < length;i ++ ){
data[i] = String.fromCharCode(data[i] & 0xff,data[i] >>> 8 & 0xff,data[i] >>> 16 & 0xff,data[i] >>> 24 & 0xff);
}
if (includeLength){
return data.join('').substring(0, n);
}
else{
return data.join('');
}
};
function stringToLongArray(string, includeLength){
var length = string.length;
var result = [];
for (var i = 0;i < length;i += 4){
result[i >> 2] = string.charCodeAt(i) | string.charCodeAt(i + 1) << 8 | string.charCodeAt(i + 2) << 16 | string.charCodeAt(i + 3) << 24;
}
if (includeLength){
result[result.length] = length;
}
return result;
};
this.encrypt = function(string, key){
if (string == ""){
return "";
}
var v = stringToLongArray(string, true);
var k = stringToLongArray(key, false);
if (k.length < 4){
k.length = 4;
}
var n = v.length - 1;
var z = v[n], y = v[0];
var mx, e, p, q = Math.floor(6 + 52 / (n + 1)), sum = 0;
while (0 < q -- ){
sum = sum + delta & 0xffffffff;
e = sum >>> 2 & 3;
for (p = 0;p < n;p ++ ){
y = v[p + 1];
mx = (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
z = v[p] = v[p] + mx & 0xffffffff;
}
y = v[0];
mx = (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
z = v[n] = v[n] + mx & 0xffffffff;
}
return longArrayToString(v, false);
};
};
var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
function encode32(input){
input = escape(input);
var output = "";
var chr1, chr2, chr3 = "";
var enc1, enc2, enc3, enc4 = "";
var i = 0;
do{
chr1 = input.charCodeAt(i ++ );
chr2 = input.charCodeAt(i ++ );
chr3 = input.charCodeAt(i ++ );
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)){
enc3 = enc4 = 64;
}
else if (isNaN(chr3)){
enc4 = 64;
}
output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4);
chr1 = chr2 = chr3 = "";
enc1 = enc2 = enc3 = enc4 = "";
}
while (i < input.length);
return output;
};
然后调用密码脚本js计算得到value。如果前面步骤争取且该步骤参数正确,将得到回应:{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"loginCheck":"Y"},"messages":[],"validateMessages":{}}
1.5 用户登录
暂且称之为用户登录吧,因为是对https://kyfw.12306.cn/otn/login/userLogin进行一个GET请求,请求正文写死为:_json_att=
1.6 初始化用户首页
对https://kyfw.12306.cn/otn/index/initMy12306执行GET请求,即可得到登录之后的用户首页。
2.参考博文
(1) Fiddler教程
(2) 玩转12306之系统登录
(3) C# winForm中调用javascript文件中的方法
感谢这些博文的博主!