完整代码可以在
Github:https://github.com/hausa-han/HAUST_auto_get_score Gitee(访问较快):https://gitee.com/hausa/HAUST_auto_get_score
找到。
分析与开发过程
从对教务系统的第一次访问开始抓包分析:
请求方式是GET,获得了一个SessionId,此后每一次请求都需要SessionId,因此需要模拟这一过程,将cookie中的SessionId保存下来:
import requests
def getcookie():
print('正在进行第一次请求,以获取Cookies')
url = 'http://jxglxt2.haust.edu.cn/'
cookies = requests.get(url).cookies
#由于这时的Cookies是RequestsCookieJar类型,所以要进行转换:
cookies = requests.utils.dict_from_cookiejar(cookies)
print('Cookies获取成功:' + cookies['ASP.NET_SessionId'] + '\n')
return cookies['ASP.NET_SessionId']
getcookie()
运行:
之后在网页上进行登录操作,并对登录时的包进行抓包,可以看到:
所有的POST参数中,__VIEWSTATE
和__VIEWSTATEGENERATOR
参数是固定的;Sel_Type
参数是用户类型,即STU;txt_sdsdfdsfryuiighgdf
参数是学号;很明显sdfdfdhgwerewt
参数即是加密后的密码,其他参数均为空。接下来来分析密码的加密过程。
进行JS调试:
在Sources栏,展开直到发现一个叫md5.js
的文件,md5是一种加密方式。看名字,猜测是加密密码的脚本文件,在md5函数处打断点,开始动态分析。
在输入密码时中断,不断跟进函数调用过程(F10),发现这样一段代码:
var s=md5(yhm+md5(obj.value).substring(0,30).toUpperCase()+schoolcode).substring(0,30).toUpperCase();
document.all.sdfdfdhgwerewt.value=s;
可以看到,下面一行对sdfdfdhgwerewt
参数的值进行了设置,值为上面加密过程得出的结果。分析可知,上面一句对密码的加密过程为:第一次MD5加密:用户输入的密码的md5的32位大写,取其前30位,然后将:学号、第一次加密结果、学校编码拼接到一起进行第二次md5加密,并取结果的32位大写的前30位。
我们用python模拟这样的加密过程,并将结果与这里的结果对照:
import hashlib
def encodepasswd(stuID, passwd):
print("正在加密密码")
a = hashlib.md5()
a.update(passwd.encode(encoding='utf-8'))
str = a.hexdigest()
str = stuID + str[:-2] + '10464'
b = hashlib.md5()
str = str.upper()
b.update(str.encode(encoding='utf-8'))
str = b.hexdigest()
str = str.upper()
print("加密完成:" + str[:-2] + '\n')
return str[:-2]
encodepasswd('191404050107', 'hausa')
是一致的,所以加密过程解决。
之后将加密后的参数与其他所有参数拼接到一起,进行一次login尝试:
(在这里卡了好久,最后发现这里有一个小坑,这个包之前有两个完全一样的请求发出,但是返回的__VIEWSTATE
不一样,就这里,耗了至少5个小时)
P.S.这里是部分代码,完整代码参考上面的仓库地址。登录后,即可用相同的方法对获取成绩的过程进行分析和模拟,并最终调整,写出最终程序。
程序运行效果:
第一次使用需要输入学号和密码,若教务系统正常运行,则会在当前目录生成一个score.jfif图片文件,其中即为成绩:
从第二次使用开始,会自动读取加密存储的账号密码,2~4秒内实现查成绩操作:
因为教务系统过一段时间后就会清除已登录的cookie(主要是我懒。。),所以没有实现Logout,有兴趣的小伙伴可以来实现一下,加强程序的安全性。有问题欢迎在下方评论嗷~~