一直想让路由器也用上srun3000,但是不知道协议,我们这又关闭了web认证方式。后来google到了一些文章,通过实验,基本可行,现在我来总结、简析一下。
srun3000这个东西有个网页认证方式,就是连上网后,随便打开一个网页,就会自动跳转到一个网页,像cmcc那样。这个网页有可能是个登录界面,那么登录即可。可惜我们这不是的,而是一个信息页面,可以下载客户端。但是如果用手机打开,就会跳转到mobile.html,这个是个登录界面,但是输入正确的用户名密码之后,提示不允许web登录。
web登录是向认证服务器post一段数据,后来抓包发现,客户端也是post这段数据,只不过有些值不同,密码也经过了处理:
username=%s&password=%s&drop=%d&type=2&n=%d&mac=%s
数据中username是用户名,password是密码,drop是是否只访问免费资源(1是,0否),type代表认证方式(1为web,2为客户端),n代表客户端版本(我这是8),mac当然是mac地址了
web方式password是明文,而客户端方式是加密了的。加密方法是这样:
有两个字符串,一个password,一个key,设password 为 12345678,key为 20678439。
逆序循环取key的字符,也就是 9,3,4,8,7,6,0,2,9,3,…
分别与password 的 [0], [1], [2] … 直到password结束 进行异或运算
对于结果,一个字符[0],[2],….[2n]的字符,译码之后:(低4位 加上 0x36 )连上 (高4位 加上 0x63)
[1],[3],….[2n+1]的字符,译码之后:(高4位 加上 0x63)连上 (低4位 加上 0x36 )然后连接之后的结果就是提交的密码的密文
提交的时候,用 urlencode或类似的函数处理这段数据。其中key与服务器时间戳有关系,详情可参考此处
搞定密码加密了,就可以实现登录了。向认证服务器的3333端口post上述数据即可,服务器会返回数据。php代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
<?php //假设该文件命名为encrypt.php function encrypt($password,$time) { $key = strval(floor(($time )/60)); //此处有修改,用原作者的算法无法登录 return _encrypt($password, $key); } function _encrypt($pass, $key) { $pass = substr($pass, 0, 16); $len = strlen($pass); $ret = ''; for ($i=0; $i<$len; $i++) { $_pass = ord($pass[$i]); $_key = _get_keychar($key, $i); $_key = $_key ^ $_pass; $ret .= _build_key($_key, $i%2); } return $ret; } function _get_keychar($str_in, $num) { $len = strlen($str_in); return ord($str_in[ $len - $num % $len - 1 ]); } function _build_key($num, $reverse=0) { $ret = ''; $_low = $num & 0x0f; $_high = $num >> 4; $_high = $_high & 0x0f; if (!$reverse) { $ret .= chr($_low + 0x36); $ret .= chr($_high + 0x63); } else { $ret .= chr($_high + 0x63); $ret .= chr($_low + 0x36); } return $ret; } |
下面是登录了,请参考注释。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<?php include ('encrypt.php'); $username='';//此处填写你的用户名 $pass='';//此处填写你的密码 $serverurl='http://10.16.16.2:3333/cgi-bin/do_login';//此处修改为你的srun3000认证服务器 $password = encrypt($pass,12321321);//为了获取正确的时间戳,我们直接post一个错误的时间戳,服务器会返回密码错误的信息,还会给出服务器的时间戳 $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $serverurl ); curl_setopt($ch, CURLOPT_TIMEOUT, 5); $url = 'username='.$username.'&password=' . urlencode($password) . '&drop=0&type=2&n=8&mac='; curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $data = curl_exec($ch); curl_close($ch); preg_match("/[0-9]+/",$data,$a); //获取服务器时间戳 $righttime=$a[0]; settype($righttime,'float'); $password = encrypt($pass,$righttime);//加密密码 $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $serverurl); curl_setopt($ch, CURLOPT_TIMEOUT, 5); $url = 'username=' .$user.'&password=' . urlencode($password) . '&drop=0&type=2&n=8&mac='; curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_USERAGENT, 'my session'); curl_setopt($ch, CURLOPT_POSTFIELDS, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $data = curl_exec($ch); curl_close($ch); //echo $data; //去掉注释可查看服务器返回的结果 ?> |
登录成功后服务器会返回几个数字,有一个是用户uid,一个是服务截止日期的时间戳。至此,网络上相关srun3000的研究就这些,还有一个比较完整的php类可以使用,上面还有注销,查询用户信息等协议,点击查看。
登录之后,我发现有个问题,20多分钟后就会掉线。有一个向服务器的/cgi-bin/keeplive路径不断post uid的方法,但针对的是web认证方式,客户端登录还是会掉线。通过对抓包发现,客户端每格3分钟会向服务器的3335端口发送一个udp包,我用php模拟之后确认是用来保持在线的,但是这段数据每个用户名不一样,也是加密的,加密算法还有待研究。下面这段php是用来模拟udp的,需要socket库支持。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<?php function SendUDP($host, $port, $buff, $waitAckSec=0) //参数分别为目标ip,端口,数据,等待响应时间 { $result = FALSE; $socket = ($result=@socket_create(AF_INET, SOCK_DGRAM, SOL_UDP)); if ($socket) { $result = @socket_sendto($socket, $buff, strlen($buff), 0, $host, $port); if ( $waitAckSec>0 ) { $result = FALSE; $read = array($socket); $write = NULL; $except = NULL; if ( @socket_select($read, $write, $except, $waitAckSec) > 0 ) { $fromHost = ''; $fromPort = 0; @socket_recvfrom($socket, $result, 3, 0, $fromHost, $fromPort); } } @socket_close($socket); } return $result; } $keepstr="\x64\x22\x00\x00\x0B\x1A\x00\x00"; //这段目前没有找到算法,只能自行抓包,改成对应的(十六进制) SendUDP('10.16.16.2','3335',$keepstr,$udp); ?> |
通过上面的分析,基本可以自己编写客户端了,什么web,exe,android,只要会都能做出个客户端了。我使用的是openwrt的路由器,安装php环境,这样路由器就可以自动登录srun3000,共享网络了。
update:
已经找出保持在线的算法,简单说明下。向服务器3335端口发送的udp包是根据uid算出来的:
把uid转换成16进制,然后从末尾开始每两位分割,把整个数字倒过来,然后在末尾加上0000(最后是16位的),最后的结果是以每两位作为一个ascii字符的字符串。
比如uid为10153302696068,转换成16进制是93C00002084,最后的结果是 $keepstr=’\x84\x20\x00\x00\x3C\x09\x00\x00′
代码我就只写个字符串的算法,其他代码自己写,uid也自己获取吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?php $s1 = base_convert($uid, 10, 16); //uid自己先获取哦 $len = strlen($s1); $add = 16 - $len; $s2 = str_repeat('0', $add) . $s1; $s3 = str_split($s2, 2); $s4 = array_reverse($s3); foreach ($s4 as $s) { $str = $str.chr(dexhex($s)); } //echo $str; 这个str就是最后的结果了 ?> |
可惜了,虽然我这个编程的门外汉也基本了解了srun3000的登录机制,可惜因为不会写代码,没办法让android平台的手机、平板也能用上自己的windows平台上使用的帐号。
很厉害,加密方式都被你搞定了!参考的文章非常有价值,早知道有这个PHP类,我还写啥呢,囧!
p.s. 我们学校有一个type=3的登陆方式,是特别给linux登陆的,我利用的是这个type,我们这边web登陆的type=1,你对参数的了解也很详细,非常强大。
p.s.2 能不能求解一下如何给openwrt设置crontab?我设置了crontab总是不来事…好郁闷…
crontab和linux的完全一样。我是在luci里直接设置的,在服务-计划任务里。比如我保持srun3000在线用的命令是:
*/5 * * * * /usr/bin/php-cgi /www/wwwroot/keep.php > /dev/null 2>&1
也就是每5分钟运行一次。执行后在系统日志里有信息。
嗯,回去试试去。
你好,看了你的文章,确实有了很多思路,但是没学过php,现在去学估计也够呛,能不能给个完全一点的php登陆页面,或者做一下具体点的介绍,感激不尽
我已经在路由器上写好了,不过路由器上还需要一些配置,下次有时间在写到博客上。
已经开源了,下载和详细可参考此文章:
http://blog.5istar.net/?p=560
嗯,那就麻烦了