-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathweb.py
210 lines (178 loc) · 7.27 KB
/
web.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
import base64
import time
import random
from html.parser import HTMLParser
from io import BytesIO
import requests
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from PIL import Image
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Encoding': 'gzip, deflate, br',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.5 Safari/605.1.15',
'Accept-Language': 'en-gb',
'Connection': 'keep-alive'
}
class LoginPageParser(HTMLParser):
def __init__(self):
super().__init__()
self.body = {}
self.salt = b''
def handle_starttag(self, tag: str, attrs: list):
if tag == 'input':
attrs = dict(attrs)
html_id = attrs.get('id')
html_name = attrs.get('name')
html_value = attrs.get('value')
if html_name in ['lt', 'dllt', 'execution', '_eventId', 'rmShown']:
self.body[html_name] = html_value
elif html_id == 'pwdDefaultEncryptSalt':
self.salt = html_value.encode('utf-8', 'ignore')
def error(self, message):
pass
@staticmethod
def create_body(html: str, username: str, password: str) -> dict:
def random_bytes(length: int):
result = ''
chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'
for i in range(length):
result += random.choice(chars)
return result.encode('utf-8', 'ignore')
# parse html page
parser = LoginPageParser()
parser.feed(html)
parser.body['username'] = username
# AES password encryption
if parser.salt != b'':
iv = random_bytes(16)
plain_text = pad(random_bytes(
64) + password.encode('utf-8', 'ignore'), 16, 'pkcs7')
aes_cipher = AES.new(parser.salt, AES.MODE_CBC, iv)
cipher_text = aes_cipher.encrypt(plain_text)
parser.body['password'] = base64.b64encode(
cipher_text).decode('utf-8', 'ignore')
else:
parser.body['password'] = password
return parser.body
def get_modified_form_data(form_data: list, form_template: list):
form_data_dict = {}
for item in form_data:
value = {}
name = item['name']
hide = bool(item['hide'])
title = item['title']
if '本人是否承诺所填报的全部内容' in title:
value['stringValue'] = '是 Yes'
elif '学生本人是否填写' in title:
value['stringValue'] = '是'
elif item['value']['dataType'] == 'STRING':
value['stringValue'] = item['value']['stringValue']
elif item['value']['dataType'] == 'ADDRESS_VALUE':
value['addressValue'] = item['value']['addressValue']
form_data_dict[name] = {'hide': hide, 'title': title, 'value': value}
form_data_modified = []
for item in form_template:
name = item['name']
if name in form_data_dict:
form_data_modified.append({
'name': name,
'title': form_data_dict[name]['title'],
'value': form_data_dict[name]['value'],
'hide': form_data_dict[name]['hide']
})
else:
form_data_modified.append({
'name': name,
'title': item['title'],
'value': {},
'hide': 'label' not in name
})
return form_data_modified
def checkin(username: str, password: str, vpn_username: str = None, vpn_password: str = None):
session = requests.Session()
# get front page
url = 'https://xmuxg.xmu.edu.cn'
res = session.get(url, headers=headers, allow_redirects=True)
time.sleep(1)
# applg (or webvpn) bypass
if not res.url.startswith('https://xmuxg.xmu.edu.cn/'):
if vpn_username is None:
vpn_username = username
if vpn_password is None:
vpn_password = password
while True:
# get chapta
url = 'https://applg.xmu.edu.cn/wengine-auth/login/image'
headers['Referer'] = res.url
res = session.get(url, headers=headers)
img = Image.open(BytesIO(base64.b64decode(res.json()['p'][22:])))
px = img.load()
time.sleep(2)
# cacluate captcha answer
w = img.width
for i in range(img.width):
for j in range(img.height):
if px[i, j][3] < 255:
w = min(w, i)
w -= 2
# verify captcha
url = 'https://applg.xmu.edu.cn/wengine-auth/login/verify'
body = {'w': str(w),
't': '0',
'locations[0][x]': '604',
'locations[0][y]': '410',
'locations[1][x]': str(w + 604),
'locations[1][y]': '410'}
res = session.post(url, body, headers=headers)
# check if need to retry
if res.json()['success']:
break
time.sleep(3)
# post applg login form
url = 'https://applg.xmu.edu.cn/wengine-auth/do-login'
body = {'auth_type': 'local', 'username': vpn_username,
'sms_code': '', 'password': vpn_password}
res = session.post(url, body, headers=headers, allow_redirects=True)
# get login page
url = 'https://ids.xmu.edu.cn/authserver/login?service=https://xmuxg.xmu.edu.cn/login/cas/xmu'
res = session.get(url, headers=headers)
time.sleep(2)
# post login form
headers['Referer'] = res.url
body = LoginPageParser.create_body(res.text, username, password)
res = session.post(url, body, headers=headers, allow_redirects=True)
time.sleep(2)
# incorrect password
if '您提供的用户名或者密码有误' in res.text or 'username or password is incorrect' in res.text:
return False, f'登录失败,用户名 {username} 或密码 {password} 错误'
# need captcha
cookie = res.cookies.get('SAAS_U')
if cookie is None:
return False, '登录失败,需要验证码'
# get business id
url = 'https://xmuxg.xmu.edu.cn/api/app/214/business/now'
headers['Referer'] = 'https://xmuxg.xmu.edu.cn/app/214'
headers['X-Requested-With'] = 'XMLHttpRequest'
res = session.get(url, headers=headers)
business_id = str(res.json()['data'][0]['business']['id'])
time.sleep(1)
# get form template
url = f'https://xmuxg.xmu.edu.cn/api/formEngine/business/{business_id}/formRenderData?playerId=owner'
res = session.get(url, headers=headers)
form_template = res.json()['data']['components']
time.sleep(1)
# get my form instance
url = f'https://xmuxg.xmu.edu.cn/api/formEngine/business/{business_id}/myFormInstance'
res = session.get(url, headers=headers)
form = res.json()['data']
form_id = form['id']
form_data = form['formData']
time.sleep(2)
# post changes
url = f'https://xmuxg.xmu.edu.cn/api/formEngine/formInstance/{form_id}'
headers['Connection'] = 'close'
body = {'formData': get_modified_form_data(
form_data, form_template), 'playerId': 'owner'}
res = session.post(url, json=body, headers=headers)
return True, cookie