深澜校园网模拟登录
1.分析api
连接到校园网,登录网站自动弹出来
http://172.16.8.6/srun_portal_pc?ac_id=1&theme=basic2
先输入错的密码,看看会发生什么,F12打开开发者工具
提示了密码错误,再看看这个过程发生了什么
get_challenge
请求方式为get,地址为
http://172.16.8.6/cgi-bin/get_challenge
请求的数据为
参数 | 分析 |
---|---|
callback | jsonp解决跨域的一个参数 |
username | 你的校园网账户 |
ip | 本机wifi自动获取的ip |
_ | 当前时间戳(13位) |
参数分析完了,看看服务器的回应吧
这里是不需要分析的。唯一需要注意的是challenge,因为太过诡异。
srun_portal
请求方式为get,地址为
http://172.16.8.6/cgi-bin/srun_portal
请求数据为
看到这一大串,可能不太好下手。确实参数有点多。
多观察几次,先找不变的参数,排除掉。
参数 | 分析 |
---|---|
callback | 和第一次的callback意思一样,可以排除 |
action | login |
username | 你的校园网账户 |
ac_id | 1 (意义不详,但每次都一样可以排除) |
ip | 本机wifi自动获取的ip |
n | 200 (和ac_id一样排除) |
type | 1 (和ac_id一样排除) |
os | windows 10 |
name | windows |
double_stack | 0 |
_ | 当前时间戳(13位) |
总共14个参数,一下子pass了11个参数,还剩下3个变化的参数,分别是
password,chksum,info
接下来开始从js文件里分析了,总共4个,不多
搜索关键字:password
这个是什么,这个不就是我们请求的参数吗,一下全都出来了,继续搜索对应关键字。
hmd5就找出来了,是经过md5加密之后的密码,还做过加盐处理
至于token就是之前的challenge
接下来是chksum,一样的方法,搜索关键字。
可以发现是简单的字符串拼接。然后用sha1加密得出chksum。
可是还有个i是什么呢。继续找吧
都出来了,对象转为字符串之后进行xEncode(天知道是什么加密方式)进行加密,反正也是加盐的加密。也用到了token。之后再进行一次base64加密。i就出来了
i出来了,chksum也出来了。顺序不能错。
处理顺序
参数 | 加密方式 |
---|---|
password | HmacMD5加密 |
info | xencode处理之后再base64 |
chksum | sha1处理 |
3个参数都需要用到token,至于加密方式更加是坑
只有sha1和md5是可以直接用的。
xencode更加不知道是什么鬼东西,base64是经过改动的。和正常的base64是不一样的。
2.分析加密
js版xencode
function xEncode(str, key) {
if (str == "") {
return "";
}
var v = s(str, true),
k = s(key, false);
if (k.length < 4) {
k.length = 4;
}
var n = v.length - 1,
z = v[n],
y = v[0],
c = 0x86014019 | 0x183639A0,
m,
e,
p,
q = Math.floor(6 + 52 / (n + 1)),
d = 0;
// console.log(q)
while (0 < q--) {
d = d + c & (0x8CE0D9BF | 0x731F2640);
e = d >>> 2 & 3;
for (p = 0; p < n; p++) {
y = v[p + 1];
m = z >>> 5 ^ y << 2;
m += (y >>> 3 ^ z << 4) ^ (d ^ y);
m += k[(p & 3) ^ e] ^ z;
z = v[p] = v[p] + m & (0xEFB8D130 | 0x10472ECF);
}
y = v[0];
m = z >>> 5 ^ y << 2;
m += (y >>> 3 ^ z << 4) ^ (d ^ y);
m += k[(p & 3) ^ e] ^ z;
z = v[n] = v[n] + m & (0xBB390742 | 0x44C6F8BD);
}
return l(v, false);
}
function s(a, b) {
var c = a.length, v = [];
for (var i = 0; i < c; i += 4) {
v[i >> 2] = a.charCodeAt(i) | a.charCodeAt(i + 1) << 8 | a.charCodeAt(i + 2) << 16 | a.charCodeAt(i + 3) << 24;
}
if (b) {
v[v.length] = c;
}
return v;
}
function l(a, b) {
var d = a.length, c = (d - 1) << 2;
if (b) {
var m = a[d - 1];
if ((m < c - 3) || (m > c))
return null;
c = m;
}
for (var i = 0; i < d; i++) {
a[i] = String.fromCharCode(a[i] & 0xff, a[i] >>> 8 & 0xff, a[i] >>> 16 & 0xff, a[i] >>> 24 & 0xff);
}
if (b) {
return a.join('').substring(0, c);
} else {
return a.join('');
}
}
本来想自己改一下,但是还是用强大的google搜索到了
在此感谢这位无名侠,直接拿来用吧
https://pastebin.com/uqsL06jF
python版的xencode
import math
def force(msg):
ret = []
for w in msg:
ret.append(ord(w))
return bytes(ret)
def ordat(msg, idx):
if len(msg) > idx:
return ord(msg[idx])
return 0
def sencode(msg, key):
l = len(msg)
pwd = []
for i in range(0, l, 4):
pwd.append(
ordat(msg, i) | ordat(msg, i + 1) << 8 | ordat(msg, i + 2) << 16
| ordat(msg, i + 3) << 24)
if key:
pwd.append(l)
return pwd
def lencode(msg, key):
l = len(msg)
ll = (l - 1) << 2
if key:
m = msg[l - 1]
if m < ll - 3 or m > ll:
return
ll = m
for i in range(0, l):
msg[i] = chr(msg[i] & 0xff) + chr(msg[i] >> 8 & 0xff) + chr(
msg[i] >> 16 & 0xff) + chr(msg[i] >> 24 & 0xff)
if key:
return "".join(msg)[0:ll]
return "".join(msg)
def get_xencode(msg, key):
if msg == "":
return ""
pwd = sencode(msg, True)
pwdk = sencode(key, False)
if len(pwdk) < 4:
pwdk = pwdk + [0] * (4 - len(pwdk))
n = len(pwd) - 1
z = pwd[n]
y = pwd[0]
c = 0x86014019 | 0x183639A0
m = 0
e = 0
p = 0
q = math.floor(6 + 52 / (n + 1))
d = 0
while 0 < q:
d = d + c & (0x8CE0D9BF | 0x731F2640)
e = d >> 2 & 3
p = 0
while p < n:
y = pwd[p + 1]
m = z >> 5 ^ y << 2
m = m + ((y >> 3 ^ z << 4) ^ (d ^ y))
m = m + (pwdk[(p & 3) ^ e] ^ z)
pwd[p] = pwd[p] + m & (0xEFB8D130 | 0x10472ECF)
z = pwd[p]
p = p + 1
y = pwd[0]
m = z >> 5 ^ y << 2
m = m + ((y >> 3 ^ z << 4) ^ (d ^ y))
m = m + (pwdk[(p & 3) ^ e] ^ z)
pwd[n] = pwd[n] + m & (0xBB390742 | 0x44C6F8BD)
z = pwd[n]
q = q - 1
return lencode(pwd, False)
js版base64
var _PADCHAR = "="
, _ALPHA = "LVoJPiCN2R8G90yg+hmFHuacZ1OWMnrsSTXkYpUq/3dlbfKwv6xztjI7DeBE45QA"
, _VERSION = "1.0";
function _getbyte64(s, i) {
var idx = _ALPHA.indexOf(s.charAt(i));
if (idx === -1) {
throw "Cannot decode base64"
}
return idx
}
function _setAlpha(s) {
_ALPHA = s;
}
function _getbyte(s, i) {
var x = s.charCodeAt(i);
if (x > 255) {
throw "INVALID_CHARACTER_ERR: DOM Exception 5"
}
return x
}
function _encode(s) {
if (arguments.length !== 1) {
throw "SyntaxError: exactly one argument required"
}
s = String(s);
var i, b10, x = [], imax = s.length - s.length % 3;
if (s.length === 0) {
return s
}
for (i = 0; i < imax; i += 3) {
b10 = (_getbyte(s, i) << 16) | (_getbyte(s, i + 1) << 8) | _getbyte(s, i + 2);
x.push(_ALPHA.charAt(b10 >> 18));
x.push(_ALPHA.charAt((b10 >> 12) & 63));
x.push(_ALPHA.charAt((b10 >> 6) & 63));
x.push(_ALPHA.charAt(b10 & 63))
}
switch (s.length - imax) {
case 1:
b10 = _getbyte(s, i) << 16;
x.push(_ALPHA.charAt(b10 >> 18) + _ALPHA.charAt((b10 >> 12) & 63) + _PADCHAR + _PADCHAR);
break;
case 2:
b10 = (_getbyte(s, i) << 16) | (_getbyte(s, i + 1) << 8);
x.push(_ALPHA.charAt(b10 >> 18) + _ALPHA.charAt((b10 >> 12) & 63) + _ALPHA.charAt((b10 >> 6) & 63) + _PADCHAR);
break
}
return x.join("")
}
这就是他和常规的base64之间的不同,踩了几次坑之后决定用python实现一下
python版base64
_PADCHAR = "="
_ALPHA = "LVoJPiCN2R8G90yg+hmFHuacZ1OWMnrsSTXkYpUq/3dlbfKwv6xztjI7DeBE45QA"
def _getbyte(s, i):
x = ord(s[i]);
if (x > 255):
print("INVALID_CHARACTER_ERR: DOM Exception 5")
exit(0)
return x
def get_base64(s):
i=0
b10=0
x = []
imax = len(s) - len(s) % 3;
if len(s) == 0:
return s
for i in range(0,imax,3):
b10 = (_getbyte(s, i) << 16) | (_getbyte(s, i + 1) << 8) | _getbyte(s, i + 2);
x.append(_ALPHA[(b10 >> 18)]);
x.append(_ALPHA[((b10 >> 12) & 63)]);
x.append(_ALPHA[((b10 >> 6) & 63)]);
x.append(_ALPHA[(b10 & 63)])
i=imax
if len(s) - imax ==1:
b10 = _getbyte(s, i) << 16;
x.append(_ALPHA[(b10 >> 18)] + _ALPHA[((b10 >> 12) & 63)] + _PADCHAR + _PADCHAR);
else:
b10 = (_getbyte(s, i) << 16) | (_getbyte(s, i + 1) << 8);
x.append(_ALPHA[(b10 >> 18)] + _ALPHA[((b10 >> 12) & 63)] + _ALPHA[((b10 >> 6) & 63)] + _PADCHAR);
return "".join(x)
再提供python版本的sha1和md5
md5
import hmac
import hashlib
def get_md5(password,token):
return hmac.new(token.encode(), password.encode(), hashlib.md5).hexdigest()
sha1
import hashlib
def get_sha1(value):
return hashlib.sha1(value.encode()).hexdigest()
流程总结
- 第一次get_challenge是获取token。
- 中间做了3个信息的处理
- 最后一步就是登录和认证了
3.模拟登录
import requests
import time
import re
from encryption.srun_md5 import *
from encryption.srun_sha1 import *
from encryption.srun_base64 import *
from encryption.srun_xencode import *
header={
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36'
}
init_url="http://172.16.8.6"
get_challenge_api="http://172.16.8.6/cgi-bin/get_challenge"
srun_portal_api="http://172.16.8.6/cgi-bin/srun_portal"
n = '200'
type = '1'
ac_id='1'
enc = "srun_bx1"
def get_chksum():
chkstr = token+username
chkstr += token+hmd5
chkstr += token+ac_id
chkstr += token+ip
chkstr += token+n
chkstr += token+type
chkstr += token+i
return chkstr
def get_info():
info_temp={
"username":username,
"password":password,
"ip":ip,
"acid":ac_id,
"enc_ver":enc
}
i=re.sub("'",'"',str(info_temp))
i=re.sub(" ",'',i)
return i
def init_getip():
global ip
init_res=requests.get(init_url,headers=header)
print("初始化获取ip")
ip=re.search('id="user_ip" value="(.*?)"',init_res.text).group(1)
print("ip:"+ip)
def get_token():
# print("获取token")
global token
get_challenge_params={
"callback": "jQuery112404953340710317169_"+str(int(time.time()*1000)),
"username":username,
"ip":ip,
"_":int(time.time()*1000),
}
get_challenge_res=requests.get(get_challenge_api,params=get_challenge_params,headers=header)
token=re.search('"challenge":"(.*?)"',get_challenge_res.text).group(1)
print(get_challenge_res.text)
print("token为:"+token)
def do_complex_work():
global i,hmd5,chksum
i=get_info()
i="{SRBX1}"+get_base64(get_xencode(i,token))
hmd5=get_md5(password,token)
chksum=get_sha1(get_chksum())
print("所有加密工作已完成")
def login():
srun_portal_params={
'callback': 'jQuery11240645308969735664_'+str(int(time.time()*1000)),
'action':'login',
'username':username,
'password':'{MD5}'+hmd5,
'ac_id':ac_id,
'ip':ip,
'chksum':chksum,
'info':i,
'n':n,
'type':type,
'os':'windows+10',
'name':'windows',
'double_stack':'0',
'_':int(time.time()*1000)
}
# print(srun_portal_params)
srun_portal_res=requests.get(srun_portal_api,params=srun_portal_params,headers=header)
print(srun_portal_res.text)
if __name__ == '__main__':
global username,password
username="201626203044@cmcc"
password="15879684798qq"
init_getip()
get_token()
do_complex_work()
login()
查看截图
登录成功!
为什么要模拟登录?
有时间可以去琢磨一下安卓的客户端版,接口和加密方式都给出来了,移植并不难。
有哪位jxnu的老铁想做的,给个apk出来
至于注销.一样的可以分析出来。我这就懒得分析了。
适用版本
我也不清楚是深澜3000还是深澜4000
反正我只知道是这个版本
项目文件
https://github.com/huxiaofan1223/jxnu_srun.git