您的位置:首页 > 运维架构 > Linux

自己动手写H3C校园网登录客户端(Linux平台版)

2012-04-11 20:21 656 查看
自己动手写H3C校园网登录客户端(Linux平台版)

By 马冬亮(凝霜  Loki)

一个人的战争(http://blog.csdn.net/MDL13412)
        周一晚上的时候,和实验室的ZL同学提聊到了Android手机使用Wifi连接学校的无线网掉线的问题。由于我们学校的上网登录客户端仅支持Windows平台,在其他平台无法使用,所以,一直以来大家的解决方案就是使用浏览器进行登录。在Linux PC上使用网页登录还是很稳定的,但是一旦使用我的小平板(Android系统)登录的时候,就会在10分钟以内掉线。其实,早在一年前就想给我的Linux PC写一个登录的客户端,但是一直懒得去分析协议,这次经ZL同学一说,决定动手分析一下登录协议,写一个客户端给大家用。

        首先交代一下我的开发环境:

                操作系统:Fedora 16(Verne)

                内核版本:3.3.1-3

                浏览器:Google Chrome 17.0.963.79

                开发语言:Python(3.2)

                开发工具:Eclipse 3.7.1 (PyDev 2.2.4.2011110216)

                网络分析工具:WireShark 1.6.5

        下面开始分析登录协议,在gnome-terminal终端下使用root权限运行wireshark(注意:在Fedora上,必须要用root权限去运行wireshark才能设置网卡到混杂模式),如下图所示:



        接下来设置过滤器,点击菜单栏上的“Capture”->“Options”,弹出如下图所示的"Capture Options"对话框:



        单击“Capture Filter”按钮,弹出过滤器选择对话框,如下图所示:



        由于我是要分析网络客户端登录的数据包,所以用IP进行过滤是最佳选择,我们校园网验证的服务器地址为192.168.252.251,将其填入上图的"Filter string"选项中就可以进行过滤了。然后点击“确定”,在单击“Start”开始抓包。

        打开浏览器,输入http://192.168.252.251:8080/portal/index_default.jsp,这个地址是我们进行网页登录的首页。此时抓到的数据并不是我们想要的数据,填入帐号和密码,如下图所示:



        点击“上线”,开始查看数据包,经过分析,我找到的关键数据包如下图所示:



        我们在Line-based text data: application/x-www-form-urlencoded这项中看到了如下字段,userName=mdl_&userPwd=V1hYWVlESkowMDA%3D&isQuickAuth=false&language=Chinese&browserFinalUrl=&userip=null,很明显userName没有进行加密,而userPwd被加密了,为了得知加密算法,我修改了几次密码,并对其映射关系进行了分析,映射关系如下:

        AAA000                  QUFBMDAw

        000AAA                  MDAwQUFB

        000000A                MDAwMDAwQQ%3D%3D

        000000AA              MDAwMDAwQUE%3D

        000000AAA            MDAwMDAwQUFB

        000BBB                  MDAwQkJC

        000000B                MDAwMDAwQg%3D%3D

        000000BB              MDAwMDAwQkI%3D

        000000BBB           MDAwMDAwQkJC

        经过分析,这个加密算法是每3个字节加密一次,并且将其映射为4位ASCII字符,对于不足三位的用%3D填充,很明显了,这个是Base64加密。有了加密算法,还要分析Cookie字段Cookie: JSESSIONID=3D4B4FBA9E201DFEF973138DF52B5161; hello1=mdl_; hello2=false; hello3=; hello4=\r\n,对于记住密码和不记住密码,这个字段的内容是不同的,经过分析HTTP的流程,我发现Cookie的JSESSIONID是客户端Notify给服务器的,那么就给我们伪造Cookie提供了可能。

        经过分析,hello1字段是用户名,hello2字段是是否记住密码,hello3字段在记住密码的时候是一段经过加密的字符串,不记住密码的时候为空,hello4字段是登录的资费类型,我们学校没有使用到这个字段,所以始终为空。

        分析完这个数据包,我发现,帐号和密码验证成功后客户端又向服务器发送了如下的数据包:



        其中Cookie字段和上一个数据包一致,Line-based text data: application/x-www-form-urlencoded中language=Chinese&heartbeatCyc=240000&heartBeatTimeoutMaxTime=3&userDevPort=IAG_5000-vlan-02-0000%40vlan&userStatus=99&userip=null&serialNo=-19730&basip=这段后来经过分析,是同时post给本地和服务器的,客户端的在线页面也要接收一份此字段中的参数。

        接下来,我找到了心跳检测的数据包,这个是客户端主动发送给服务器的验证包,如下图:


           开始的时候我认为只要每隔一段时间向服务器GET这个数据包的内容,并且保持TCP长连接就可以不掉线了,但是经过验证,这是不可行的。后来又尝试模拟浏览器的所有行为,但是也没有成功。最后,想到在一台机器上如果帐号已经在线,再次发送的登录请求,服务器会返回用户已经在线信息,并重新设置掉线时间,于是突破点找到了,我每隔1分钟,向服务器发送一次登录请求,终于,可以保证稳定在线了。

        下面将我用Python写的客户端代码贴出来,给大家做一个参考,为了能最小负担的移植到其他平台,我将最初的PyQt4做的界面去掉了,还是使用了纯终端的程序:

        这个项目总共分为3个文件,config.ini保存用户的账户和密码,NsINodeLogin.py负责登录并维持在线,NsINodeLogout.py负责下线。

config.ini

[Account]
username:mdl_
password:MYPASSWORD
NsINodeLogout.py
# -*- coding: utf-8 -*-

welcomeInfo = '''
作者:马冬亮
单位:内蒙古科技大学信息工程学院ACM程序设计协会
博客:http://blog.csdn.net/MDL13412
邮箱:mdl2009@vip.qq.com
Q Q:401074567
版权所有 (C) 2012 凝霜.保留所有权利.

使用方法:
修改当前路径下的config.ini文件,将用户名和密码填写至相应字段
登录使用NsINodeLogin.py
注销使用NsINodeLogout.py
在线时请不要关闭本程序
'''

import http.client
import base64
import os
from configparser import ConfigParser

requesteaders = {
'Connection':'keep-alive',
'Cache-Control':'max-age=0',
'User-Agent':'Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.79 Safari/535.11',
'Content-Type':'application/x-www-form-urlencoded',
'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Encoding':'gzip,deflate,sdch',
'Accept-Language':'zh-CN,zh;q=0.8',
'Accept-Charset':'GBK,utf-8;q=0.7,*;q=0.3',
'Cookie':''
}

if __name__ == '__main__':
try:
print(welcomeInfo)

try:
configFile = ConfigParser()
configFile.read(filenames=os.getcwd() + '/config.ini', encoding='utf-8')
username = configFile.get('Account', 'username')
pwd = configFile.get('Account', 'password')
except:
print('加载用户信息错误')

logoutBody = 'userName={0}&userPwd={1}&isQuickAuth=false&language=Chinese&browserFinalUrl=&userip=null'
requesteaders['Cookie'] = 'JSESSIONID=F447CB1C348B7D7AA6C02CBA3ECBF7AF; hello1={0}; hello2=flase; hello3=; hello4='.format(username)

pwd = base64.encodebytes(pwd.encode(encoding='utf_8', errors='strict'))
pwd = pwd.replace('='.encode(encoding='utf_8', errors='strict'), '%3D'.encode(encoding='utf_8', errors='strict'))
logoutBody = logoutBody.format(username, pwd)
logoutBody = logoutBody.replace("b'", "")
logoutBody = logoutBody.replace("\\n'", "")

while True:
conn = http.client.HTTPConnection('192.168.252.251:8080')
conn.request('POST', '/portal/logout.jsp', logoutBody, headers=requesteaders)
res = conn.getresponse()

if res.status == 200:
print('下线成功')
else:
print('下线失败')

break
except Exception as e:
print('出错啦...请检查网络连接...')


NsINodeLogin.py

# -*- coding: utf-8 -*-

import http.client
import time
import base64
import os
from configparser import ConfigParser

requesteaders = {
'Connection':'keep-alive',
'Cache-Control':'max-age=0',
'User-Agent':'Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.79 Safari/535.11',
'Content-Type':'application/x-www-form-urlencoded',
'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Encoding':'gzip,deflate,sdch',
'Accept-Language':'zh-CN,zh;q=0.8',
'Accept-Charset':'GBK,utf-8;q=0.7,*;q=0.3',
'Cookie':''
}

if __name__ == '__main__':
try:
os.system('python3 ./NsINodeLogout.py')

try:
configFile = ConfigParser()
configFile.read(filenames=os.getcwd() + '/config.ini', encoding='utf-8')
username = configFile.get('Account', 'username')
pwd = configFile.get('Account', 'password')
except:
print('加载用户信息错误')

loginBody = 'userName={0}&userPwd={1}&isQuickAuth=false&language=Chinese&browserFinalUrl=&userip=null'
onlineBody = 'language=Chinese&heartbeatCyc=240000&heartBeatTimeoutMaxTime=3&userDevPort=IAG_5000-vlan-02-0000%40vlan&userStatus=99&userip=null&serialNo=15500&basip='
requesteaders['Cookie'] = 'JSESSIONID=F447CB1C348B7D7AA6C02CBA3ECBF7AF; hello1={0}; hello2=flase; hello3=; hello4='.format(username)

pwd = base64.encodebytes(pwd.encode(encoding='utf_8', errors='strict'))
pwd = pwd.replace('='.encode(encoding='utf_8', errors='strict'), '%3D'.encode(encoding='utf_8', errors='strict'))
loginBody = loginBody.format(username, pwd)
loginBody = loginBody.replace("b'", "")
loginBody = loginBody.replace("\\n'", "")

while True:
conn = http.client.HTTPConnection('192.168.252.251:8080')
conn.request('POST', '/portal/login.jsp', loginBody, headers=requesteaders)
res = conn.getresponse()

if res.status == 200:
print('发送验证信息成功')
data = res.read()
if -1 == data.find(b'3032'):
print('登录信息正确')
else:
print('请检查登录信息')
break
else:
print('发送验证信息失败')
continue

conn = http.client.HTTPConnection('192.168.252.251:8080')
conn.request('POST', '/portal/online.jsp', onlineBody, headers=requesteaders)
res = conn.getresponse()

if res.status == 200:
print('发送在线信息成功')
else:
print('发送在线信息失败')
continue

time.sleep(60)
except Exception as e:
print('出错啦...请检查网络连接...')
        这个程序因为属于实验性的代码,没有进行详细的错误校验,不过对于校园网的登录来说,已经足够用了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: