前言
SEIG的校园网分为两种客户端,分别是未登录时会弹出的网页登陆界面,以及安装程序版的登录客户端。网页端只能登录教师账号,学生账号需要在后者登录。
本文将探索网页端登录过程,分析登录数据包。由于每次登录都要 *重新输入* 账号密码,以及只能拦截人类的 *验证码* ,所以实现自动登录也是本次的目标。
分析
访问登录界面
登录界面需要输入三个信息:
1、账号
2、密码
3、验证码
此时打开浏览器开发者工具(按下F12)切换到网络选项卡,然后正常输入账号密码,尝试抓取登录数据包。
分析登录数据包
当点击“提交登入”按钮后,会发送POST请求到服务器,发现界面中出现了Login字样的数据包,右键复制cURL(cmd),分析数据。
curl ^"http://125.88.59.131:10001/ajax/login^" ^
-H ^"Accept: application/json, text/javascript, */*; q=0.01^" ^
-H ^"Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6^" ^
-H ^"Cache-Control: no-cache^" ^
-H ^"Connection: keep-alive^" ^
-H ^"Content-Type: application/x-www-form-urlencoded; charset=UTF-8^" ^
-H ^"Cookie: JSESSIONID=a_LXXXXXXXXXXXXXXXXXXXXXXXXXXXZ6o9JNK2yG-efkG^!-42XXXXXXXX98^" ^
-H ^"Origin: http://125.88.59.131:10001^" ^
-H ^"Pragma: no-cache^" ^
-H ^"Referer: http://125.88.59.131:10001/qs/index_gz.jsp?wlanacip=183.3.151.148^&wlanuserip=172.16.XX.XXX^" ^
-H ^"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0^" ^
-H ^"X-Requested-With: XMLHttpRequest^" ^
--data-raw ^"loginKey=2897b82c1142XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXf8fef6ecae2362da468b76912171fff439532383dfd656c15c6127f8711bf55de381bb4f66138ca86acd5c309b971514daf2cdef5df07c7492c052dcced9d6b0b790b3e78447ecbXXXXXXXXXXXXXXXXXXXXXXXXXXXXXf2e6aa9da5778b5f^&wlanuserip=172.18.42.102^&wlanacip=183.3.151.148^" ^
--insecure
可以发现,数据包中包含的重要参数:
1、登录的URL:http://125.88.59.131:10001/ajax/login
2、Referer中的wlanacip=183.3.151.148^&wlanuserip=172.16.XX.XXX (登录服务器ip和本机ip)
3、Cookie中的JESSIONID
4、loginKey (账号密码大概就在里面)
尝试直接将curl复制进cmd中执行,返回以下结果:
{"resultCode":"11063000","resultInfo":"验证码错误"}
尝试将loginkey打乱,再次发送,返回结果如下:
{"resultCode":"11000000","resultInfo":"账号或密码错误(11000000)"}
由此可知,登录的关键数据就是loginkey以及wlanacip、wlanuserip。其中loginkey里包含了账号密码以及验证码,但loginkey是一段加密数据,还需寻找加密过程。
分析Login.js
切换浏览器开发者工具的选项卡至“源代码”:
所幸js没有被混淆加密,可以看到登录过程的源代码,分析登录源码
function login() { // 原本的js还有注释,这程序员真好!不过为了简洁,删除了原有的注释
var username = $("#username").val();
if (username == "") {
alert("请输入上网帐号,@后面去掉");
$("#username").focus();
return;
}
var password = $("#password").val();
if (password == "") {
alert("请输入密码");
$("#password").focus();
return;
}
var code = $("#code").val();
if (code == "") {
alert("请输入验证码");
$("#code").focus();
return;
}
setMaxDigits(200);
// 设置大数库的精度位数为200
var key = new RSAKeyPair("10001","","b2867727e19e1163cc084ea57b9fa8406a910c6703413fa7df96c1acdca7b983a262e005af35f9485d92cd4c622eca4a14d6fd818adca5cae73d9d228b4ef05d732b41fb85f80af578a150ebd9a2eb5ececb853372ca4731ca1c8686892987409be3247f9b26cae8e787d8c135fc0652ec0678a5eda0c3d95cc1741517c0c9c3");
// RSAKeyPair 是一个用于生成 RSA 公钥对的函数,10001是指数,后面的一大串是模数
var loginKey = encryptedString(key, '{"userName":"' + username + '","password":"' + password + '","rand":"' + code + '"}');
// loginKey是由 userName: 用户名,password: 密码,code: 验证码 组成,然后用RSA公钥加密
//console.log(loginKey)
// 下面就是数据封包的逻辑了,还有根据服务器代码返回报错的一些信息
var wlanuserip = $("#wlanuserip").val()
var wlanacip = $("#wlanacip").val()
$.ajax({
type : 'POST',
url : '/ajax/login',
cache : false,
data: {
loginKey: loginKey,
wlanuserip: wlanuserip,
wlanacip: wlanacip
},
dataType : 'json',
success : function(data, textStatus) {
if (data.resultCode == "0" || data.resultCode == "13002000" ) {
$("#login").css('display','none');
$("#success").css('display','block');
changeCode();
} else {
changeCode();
if(data.resultCode == "13018000") {
alert("已办理一人一号多终端业务的用户,请使用客户端登录");
} else {
alert(data.resultInfo);
}
}
}
});
}
所以登录的大致过程:
用户输入账号密码、验证码
→本地生成RSA公钥,加密输入的三个数据,即loginkey
→通过POST发送Json数据,包含loginKey、wlanuserip、wlanacip
→等待服务器返回结果
了解完登录过程,就可以用小学学过的Python尝试实现自动登录了!
实现自动登录
自动登录要模拟完成上面的登录流程,需要以下步骤:
生成RSA公钥
→将账号密码赋值给对应函数
→使用OCR识别验证码,识别后赋值给函数
→封装Loginkey数据包,使用RSA公钥加密
→构造请求参数
post_data = {
'loginKey': ,
'wlanuserip': ,
'wlanacip':
}
→发送POST请求
制作数据包
1、生成公钥
在“在线通过模数、指数生成公钥”网站中,通过已知的模数和指数生成RSA公钥
指数:10001
模数:b2867727e19e1163cc084ea57b9fa8406a910c6703413fa7df96c1acdca7b983a262e005af35f9485d92cd4c622eca4a14d6fd818adca5cae73d9d228b4ef05d732b41fb85f80af578a150ebd9a2eb5ececb853372ca4731ca1c8686892987409be3247f9b26cae8e787d8c135fc0652ec0678a5eda0c3d95cc1741517c0c9c3
(发现广东天翼的似乎都是这个)
生成的公钥:
rsa_public_key = """
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyhncn4Z4RY8wITqV7n6hAapEM
ZwNBP6fflsGs3Ke5g6Ji4AWvNflIXZLNTGIuykoU1v2Bitylyuc9nSKLTvBdcytB
+4X4CvV4oVDr2aLrXs7LhTNyykcxyhyGhokph0Cb4yR/mybK6OeH2ME1/AZS7AZ4
pe2gw9lcwXQVF8DJwwIDAQAB
-----END PUBLIC KEY-----
"""
在Python中读取公钥:pub_key = rsa.PublicKey.load_pkcs1_openssl_pem(rsa_public_key.encode())
2、OCR识别验证码
在login.js中,还有一段与验证码有关的代码:
function changeCode() {
var time = new Date().getTime();
$("#image_code").attr("src", "/common/image_code.jsp?time=" + time);
}
通过一个带有当前时间的URL请求获取验证码图片
小插曲:
由于刚开始认为验证码是根据请求时间返回的,并在一段时间内有效,所以在完成了后续操作后,POST发送,但得到的结果一直是验证码错误,遂在群内询问大佬,在群里发的Github链接中找到了解决办法。
Pandaft/ESurfingPy-CLI: 基于 Python 实现登录和登出广东天翼校园网的命令行工具 原来已经有前辈做过这个了,通过Session保持连接再获取验证码。
先用Session访问登录链接:response = session.get(page_url, timeout=5, headers=headers),然后获取验证码:url = re.search(r'/common/image_code\.jsp\?time=\d+',
str(response.content)).group()
获取验证码图片后,将图片二值化降噪之后使用sml2h3/ddddocr通用验证码识别OCR识别,准确率非常高
def preprocess_image(self, image):
# 转换为灰度图像
image = image.convert("L")
# 应用二值化
threshold = 128
image = image.point(lambda p: p > threshold and 255)
image = image.filter(ImageFilter.MedianFilter(size=3))
return image
3、封装loginkey,发送数据
# 登录数据
login_data = {
"userName": username,
"password": password,
"rand": code
}
# 加密login_key
login_key = self.encrypt_rsa(json.dumps(login_data), pub_key)
# 构造请求头和Cookie
headers = {
"Origin": f"http://{esurfingurl}",
"Referer": f"http://{esurfingurl}/qs/index_gz.jsp?wlanacip={wlanacip}&wlanuserip={wlanuserip}",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0",
}
# 构造请求参数
post_data = {
'loginKey': login_key,
'wlanuserip': wlanuserip,
'wlanacip': wlanacip
}
# 发送POST请求
try:
response = session.post(
f'http://{esurfingurl}/ajax/login', timeout=5, headers=headers, data=post_data)
if response.status_code == 200:
data = response.json()
if data['resultCode'] == "0" or data['resultCode'] == "13002000":
signature = response.cookies["signature"]
print("成功连接校园网!")
发现已经可以返回登录成功了
制作登录客户端
根据上述原理,制作一个登录客户端:Yish1/SEIG-Auto-Connect
原则上适用于所有广东天翼网页端账号
工具特色:
- 支持一键登录网页端校园网账号
- 支持自动获取登录参数
- 自动登录功能,在启动软件时自动登录保存的账号
- 自动识别验证码,如果失败5次,需要手动输入验证码
- 看门狗,默认每600秒检测一次网络状态,若网络不通,自动重连
- 对保存的密码低级加密
- 按下最小化按钮可以隐藏进托盘
Comments 1 条评论
博主 花火~晴雨
期待学生客户端,加油!😜