工具下载页面
开源地址
Github:https://github.com/Doone-skser/WFU
写在前面
校园网助手是从今年的5月份开始计划写的,刚开始是用的Python来实现的,主要的功能就是实现断网时能够自动认证。刚开始的界面就是依靠Windows的命令提示符窗口来展现信息,对于我来说没有啥,可是如果让别人来使用这个脚本确实还是不太友好的。于是学习了PyQt,封装成一个单独的窗体程序,稳定性也比之前好很多了。再往后也就是手机端APP,恶补了一手JavaScript及其三姐妹,写了个还算是成模样的APP,功能和电脑端几乎一样,因为电脑端可以用多线程,而安卓,不,是uniapp对多线程支持不是很友好,但是有非常优秀的延时函数,所以功能上稳定性其实比多线程还可以,但唯一不足的是没有保活(在后台容易被杀),手机端可以在安卓上运行,苹果上需要费用(一年688元)家境贫寒的我只能放弃在苹果设备上的试用了。不过Mac是可以使用的,我将在后面放上源码地址,可以自行封装哦~如有大佬有开发者账号,或者能给予我一些保活的支持,非常希望您能在关于页与我交流哦~
Python版本的校园网的编写历程
第一步:JavaScript 逆向
JavaScript 逆向是什么
JavaScript 逆向是什么?我简单解释一下:在连上校园网无论是WiFi还是有线的方式都需要通过认证界面是提交我们的账户信息来确认我们有使用网络的许可。当我们输入完账户信息在点击提交或者登录时会在浏览器或者内置的Webview(内置的浏览器)上完成对账号的信息加密再将信息发送要验证的API接口。在上述过程中对账号信息加密的过程进行反向分析就是逆向,又因为使用的加密语句是用JavaScript写的,所以说其是JavaScript逆向。
JavaScript 逆向分析过程
这个逆向还是挺简单的,因为在其html源码里面就有体现。不难看出正是用的AES加密,模式为CBC来进行的。加密所需要的参数有三个:iv,key,data。iv是偏移量,key是前后端需要同步的秘钥,data就是要加密的数据了,其详细以及AES的其他的加密方式可以见https://www.cnblogs.com/starwolf/p/3365834.html
第二步:Python实现AES加密
Python实现AES加密的包Crypto包的坑
实现AES加密需要使用到crypto包,但是这个包已经很久没更新了,现在已经不能用啦。我们可以下载pycryptodome,我们可以在终端输入一下代码来完成包的安装,需要在安装包的目录把crypto文件的文件名改成Crypto!
pip install pycryptodome
Python实现AES_CBC加密示例
梨子很简单,我就直接上代码了。
crypto = AES.new(self._key, self.AES_MODE, iv=iv.encode('utf-8')) // 实例化类
result = base64.encodebytes(crypto.encrypt(data)).decode('ascii') // 使用类的方法
需要注意的是需要导入base64的包包哦~
第三步:获取用户的电脑网络状态
检查WiFi是否连接
python可以用过pywifi库来实现获取wifi的连接状态。我的思路是当电脑连上wifi的时候再执行一系列操作(比如认证网络,检查网络等等)所以我们不需要知道更多的信息,只需要知道wifi是否连接就可以了。具体的实现方法如下:
class EXMAPLE(PyWiFi):
def __init__(self, ):
super().__init__()
self.wifi = PyWiFi()
self.ifaces = self.wifi.interfaces()[0]
# 检查wifi是否连接
def check_wifi(self):
if self.ifaces.status() == const.IFACE_CONNECTED:
return True
else:
return False
if __name__ == '__main__':
new = EXMAPLE()
new.check_wifi() // 有WiFi连接返回True 否则False
如果是有线怎么办呢?为了让有线用户也可以使用,我在窗体里增加了有线模式的选项,这样就可以跳过WiFi连接的检测啦~
校园网登录的实现
准备加密的数据
请求加密信息的url
self.url_login_get = 'http://210.44.64.60/gportal/web/login'
因为key已经在html源代码里面说明了我直接贴过来
self._key = '1234567887654321'.encode("utf-8") # AES16位自定义秘钥
iv也在html的源代码里,但是需要发送GET请求后才可以得到(requests库的使用比较简单,这里不赘述。),除此之外加密还需要一个sign的字符串、WiFi的名字、当前的ip也需要拼接在一起加密才可以,具体代码实现如下(附带了AES加密):
def _get_info(self):
response = get(url=self.url_login_get, headers=self.headers)
self.headers.update(response.cookies.get_dict())
selector = html.fromstring(response.text)
ul = selector.xpath('//*[@id="frmLogin"]/ul/input')
wifi_name = ul[0].xpath("@value")[0]
ip = ul[2].xpath("@value")[0]
login_sign = ul[8].xpath("@value")[0]
self.login_iv = ul[9].xpath("@value")[0]
url = "nasName=" + wifi_name + "&nasIp=&userIp=" + ip + "&userMac=&ssid=&apMac=&pid=20&vlan=&sign=" + \
login_sign + "&iv=" + self.login_iv + "&name=" + self._accout + "&password=" + self._psw
data = self.complete_to_16(url) // 十六位补齐,具体原因请见上方的在介绍AES加密的链接里。
cry = AES.new(self._key, self.AES_MODE, iv=self.login_iv.encode('utf-8'))
self.login_sign = base64.encodebytes(cry.encrypt(data)).decode('ascii')
十六位补齐的方法如下:
# 处理加密缺位问题
@classmethod
def complete_to_16(cls, text):
if len(text.encode('utf-8')) % 16:
add = 16 - (len(text.encode('utf-8')) % 16)
else:
add = 0
text = text + ('\0' * add)
return text.encode('utf-8')
发送POST请求登录
请求的地址
self.url_login_post = 'http://210.44.64.60/gportal/web/authLogin'
直接上代码:
# 登陆方法
def _login(self):
self._get_info()
post_data = {
"data": self.login_sign,
"iv": self.login_iv
}
response = post(self.url_login_post, data=post_data, headers=self.headers).json()
print(response['info'])
if "密码错误" in response['info']:
return "密码错误"
elif "账号不存在" in response['info']:
return "账号错误"
elif "认证拒绝" in response['info']:
return "认证拒绝"
elif "已认证" in response['info']:
return "重复认证"
elif "频繁" in response['info']:
return "操作频繁"
else:
return "认证成功"
我这里把一些常用的返回的结果都放在上面了,保真不保漏哦~
一些其他的功能
检查当前的在线时长
主要是请求在线时长的API,请看:
# 获取在线时长
@classmethod
def get_online_time(cls):
url = 'http://210.44.64.60/gportal/web/queryAuthState'
response = get(url).json()
secs = int(response['data']['data']['onlineTime'])
m, s = divmod(secs, 60)
h, m = divmod(m, 60)
res = str(h) + "时" + str(m) + "分" + str(s) + "秒"
return res
网络连接的判断
方法有很多我上一个我自己用的:
# 检查网络连接
@classmethod
def check_net(cls, url=None):
try:
if url:
res = get(url, timeout=1)
if res.status_code == 200:
return True
else:
return False
else:
res = get("http://www.baidu.com", timeout=1)
if res.status_code == 200:
return True
else:
return False
except:
return False
正常就True,有问题就False
用户以及密码的管理
其实就是记住用户的登陆信息。输入完账户信息并验证其真确性后会在D盘和注册表生成一个文件和键值,文件方法网络上一堆,这里放上注册表的生成以及读取的操作方法:
// 生成用户的信息
def writeConfi(accout, password):
"""
:param accout: 账户
:param password: 密码
:return: 成功则返True,失败返回False
"""
location = r"Software"
# 获取注册表该位置的所有键值
try:
key_w = winreg.OpenKey(winreg.HKEY_CURRENT_USER, location)
new_key = winreg.CreateKey(key_w, "WFU_CNA")
winreg.SetValue(new_key, "accout", winreg.REG_SZ, accout)
winreg.SetValue(new_key, "password", winreg.REG_SZ, password)
winreg.CloseKey(key_w)
winreg.CloseKey(new_key)
return True
except:
return False
// 读取用户的信息
def readConfi():
"""
:return: 成功则返数据,失败返回False
"""
pwd_location = r"Software\WFU_CNA\password"
act_location = r"Software\WFU_CNA\accout"
# 获取注册表该位置的所有键值
try:
key_pwd = winreg.OpenKey(winreg.HKEY_CURRENT_USER, pwd_location)
key_act = winreg.OpenKey(winreg.HKEY_CURRENT_USER, act_location)
try:
accout = winreg.EnumValue(key_act, 0)
password = winreg.EnumValue(key_pwd, 0)
data = {
"accout": accout[1],
"password": password[1]
}
return data
except:
winreg.CloseKey(key_act)
winreg.CloseKey(key_pwd)
return False
except:
return False
APP校园网的编写历程
日后在更吧。。。2021年10月1日21:56:06
文档最后编辑于4年前
评论已关闭