├── .gitignore ├── README.md ├── config.default.json └── fchinanet.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | config.json 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Linux_ChinaNet 2 | 全平台可使用的天翼电信校园网客户端 3 | 4 | 暂时只修改了了核心的上网功能,用户交互问题,下线功能等还没有修改。 5 | to be continued... 6 | 7 | ### 近期接口换的太频繁,加上忙着毕业的事情,先不更新了。 8 | -------------------------------------------------------------------------------- /config.default.json: -------------------------------------------------------------------------------- 1 | { 2 | "account": "<填入掌上大学用户名>", 3 | "passwd": "<填入掌上大学密码>", 4 | "id": "", 5 | "server_id": "", 6 | "status": "", 7 | "last_ip": "", 8 | "bras_ip": "", 9 | "wan_ip": "" 10 | } -------------------------------------------------------------------------------- /fchinanet.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python3 2 | # test1.0 3 | import json 4 | import base64 5 | import requests 6 | import time 7 | import hashlib 8 | import re 9 | import socket 10 | # 作为全局变量 11 | configDict={} 12 | 13 | def initial(): 14 | global configDict 15 | with open('config.json','r') as f: 16 | configDict=json.load(f) 17 | res=requests.get('http://test.f-young.cn') 18 | #!!!!!!这里的list可能长度为0,会溢出.... 19 | if len(res.history)>0: 20 | realUrl=res.history[-1].url 21 | print(realUrl) 22 | args=realUrl.split('?')[1].split('&') 23 | wan_ip=args[0].split('=')[1] 24 | bras_ip=args[1].split('=')[1] 25 | # 存储到config文件 26 | configDict['wan_ip']=wan_ip 27 | configDict['bras_ip']=bras_ip 28 | with open('config.json','w') as f: 29 | json.dump(configDict,f) 30 | 31 | def getAppVersion(): 32 | res=requests.get('http://pre.f-young.cn/js/conf.js') 33 | pattern=re.compile(r'\d\.\d\.\d') 34 | version=pattern.search(res.text).group() 35 | app_version='Android_college_'+version 36 | return app_version 37 | 38 | def get8bin(s): 39 | rawLenth=len(s) 40 | result='' 41 | for i in range(8-rawLenth): 42 | result+='0' 43 | result+=s 44 | return result 45 | 46 | def getUserId(): 47 | global configDict 48 | # 先转化为bytes 49 | rawStr=configDict['account']+':'+configDict['passwd'] 50 | bytesAuth=rawStr.encode(encoding='utf-8') 51 | # base64编码 52 | Auth=str(base64.b64encode(bytesAuth),'utf-8') 53 | head=head={'Authorization':'Basic %s'%Auth} 54 | 55 | res=requests.get('https://cps.loocha.cn:9607/anony/login?1=%s'%getAppVersion(),headers=head,stream=True) 56 | bufferstr=str(res.raw.read()) 57 | 58 | b1=bufferstr[16:18] 59 | b2=bufferstr[20:22] 60 | b3=bufferstr[24:26] 61 | b4=bufferstr[28:30] 62 | 63 | A=get8bin(bin(int(b4,16))[2:])[1:] 64 | B=get8bin(bin(int(b3,16))[2:])[1:] 65 | C=get8bin(bin(int(b2,16))[2:])[1:] 66 | D=get8bin(bin(int(b1,16))[2:])[1:] 67 | 68 | USER_ID=str(int(A+B+C+D,2)) 69 | return USER_ID 70 | 71 | def doFirstVerify(): 72 | global configDict 73 | with open('config.json','r') as f: 74 | configDict=json.load(f) 75 | 76 | if ('account' in configDict.keys())&('passwd' in configDict.keys()): 77 | if (configDict['account']=='')|(configDict['passwd']==''): 78 | # 需要先填写密码 79 | return 0 80 | if 'auth' not in configDict.keys(): 81 | # 先转化为bytes 82 | rawStr=configDict['account']+':'+configDict['passwd'] 83 | bytesAuth=rawStr.encode(encoding='utf-8') 84 | # base64编码 85 | Auth=str(base64.b64encode(bytesAuth),'utf-8') 86 | 87 | head=head={'Authorization':'Basic %s'%Auth} 88 | res=requests.get('https://www.loocha.com.cn:8443/login',headers=head) 89 | if res.status_code!=200: 90 | # 密码错误 91 | return 1 92 | did_id=json.loads(res.text)['user']['did'].split('#')[0] 93 | user_id=json.loads(res.text)['user']['id'] 94 | configDict['id']=user_id 95 | configDict['server_id']=did_id 96 | configDict['auth']=Auth 97 | del(configDict['account']) 98 | del(configDict['passwd']) 99 | with open('config.json','w') as f: 100 | json.dump(configDict,f) 101 | print('验证成功添加账户成功') 102 | # 成功 103 | return 2 104 | 105 | def loginChinaNet(): 106 | # global configDict 107 | # head={'Authorization':'Basic %s'%configDict['auth']} 108 | # res=requests.get('https://www.loocha.com.cn:8443/login',headers=head) 109 | # did_id=json.loads(res.text)['user']['did'].split('#')[0] 110 | # user_id=json.loads(res.text)['user']['id'] 111 | # configDict['id']=user_id 112 | # configDict['server_id']=did_id 113 | # with open('config.json','w') as f: 114 | # json.dump(configDict,f) 115 | doOnline() 116 | 117 | def doOnline(): 118 | global configDict 119 | head={'Authorization':'Basic %s'%configDict['auth']} 120 | user_id=configDict['id'] 121 | nowtime=str(int(time.time()*1000)) 122 | 123 | passWd=getPwd() 124 | print('本次登录密码为:',passWd) 125 | Qrcode=getQr() 126 | print('本次QrCode为:',Qrcode) 127 | 128 | 129 | params='&server_did='+configDict['server_id']+'&time='+nowtime+'&type=1' 130 | sign = getMD5('mobile='+'17712918215'+ '&model='+'callmesp-PC'+params) 131 | param = "1=Android_college_100.100.100&qrcode=" + Qrcode + "&code=" + passWd + "&type=1" + "&mm=" + 'callmesp-PC' + "&server_did=" + configDict['server_id'] + "&time=" + nowtime + "&sign=" + sign 132 | res=requests.post('https://wifi.loocha.cn/' + user_id + '/wifi/telecom/auto/login?' + param,headers=head) 133 | 134 | resDict=json.loads(res.text) 135 | if resDict.get('status')=='0': 136 | print('连接成功') 137 | else: 138 | print('连接失败') 139 | 140 | 141 | def getMD5(rawStr): 142 | myMd5=hashlib.md5() 143 | myMd5.update(rawStr.encode('utf-8')) 144 | result=myMd5.hexdigest() 145 | return result.upper() 146 | 147 | def getPwd(): 148 | global configDict 149 | head={'Authorization':'Basic %s'%configDict['auth']} 150 | # user_id=configDict['id'] 151 | user_id='2364100' 152 | # did_id=configDict['server_id'] 153 | did_id='0' 154 | nowtime=str(int(time.time()*1000)) 155 | 156 | params='&server_did=' + did_id + '&time=' + nowtime + '&type=1' 157 | sign = getMD5('mobile='+'17712918215'+ '&model='+'callmesp-PC'+params) 158 | res=requests.get('https://wifi.loocha.cn/'+user_id +'/wifi/telecom/pwd?1=Android_college_100.100.100'+'&mm='+'callmesp-PC'+params+'&sign='+sign,headers=head) 159 | resDict=json.loads(res.text) 160 | if resDict['status']=='0': 161 | return resDict['telecomWifiRes']['password'] 162 | else: 163 | print('获取失败:'+resDict) 164 | return 0 165 | 166 | 167 | 168 | def getQr(): 169 | global configDict 170 | initial() 171 | # 暂时还没有写直接读取文件不用initial 172 | wan_ip=configDict['wan_ip'] 173 | bras_ip=configDict['bras_ip'] 174 | 175 | res=requests.get('https://wifi.loocha.cn/0/wifi/qrcode?brasip=%s&ulanip=%s&wlanip=%s'%(bras_ip,wan_ip,wan_ip)) 176 | resDict=json.loads(res.text) 177 | 178 | if resDict['status']=='0': 179 | return resDict['telecomWifiRes']['password'] 180 | else: 181 | print('获取失败:',resDict) 182 | return 0 183 | 184 | 185 | def realStart(): 186 | rescode=int(doFirstVerify()) 187 | if rescode==0: 188 | print('填写账号密码后重新尝试') 189 | elif rescode==1: 190 | print('账号或密码错误') 191 | elif rescode==2:#初步验证通过,下一步是登录 192 | loginChinaNet() 193 | 194 | def str2hex(s): 195 | return ' '.join([hex(ord(c)).replace('0x','') for c in s]) 196 | 197 | def str2bin(s): 198 | return ''.join([bin(ord(c)).replace('0b','') for c in s]) 199 | 200 | def bin2str(s): 201 | return ''.join([chr(i) for i in [int(b,2) for b in s.split(' ')]]) 202 | 203 | def bin2dec(s): 204 | return int(s,2) 205 | 206 | def getPwdtest(): 207 | # 以下是临时测试数据... 208 | global configDict 209 | # 先转化为bytes 210 | rawStr=configDict['account']+':'+configDict['passwd'] 211 | bytesAuth=rawStr.encode(encoding='utf-8') 212 | # base64编码 213 | Auth=str(base64.b64encode(bytesAuth),'utf-8') 214 | head={'Authorization':'Basic %s'%Auth} 215 | user_id=getUserId() 216 | did_id='0' 217 | APP_VERSION= getAppVersion() 218 | MODEL=socket.gethostname() 219 | # .................. 220 | 221 | nowtime=str(int(time.time()*1000)) 222 | params='&server_did=' + did_id + '&time=' + nowtime + '&type=1' 223 | sign = getMD5('mobile='+configDict['account']+ '&model='+MODEL+params) 224 | res=requests.get('https://wifi.loocha.cn/%s/wifi/telecom/pwd?1=%s&%s&sign=%s&mm=%s'%(user_id,APP_VERSION,params,sign,MODEL),headers=head) 225 | resDict=json.loads(res.text) 226 | if resDict['status']=='0': 227 | print('code:'+resDict['telecomWifiRes']['password']) 228 | return resDict['telecomWifiRes']['password'] 229 | else: 230 | print('pwd获取失败',resDict) 231 | return '823144' 232 | 233 | def getQrtest(): 234 | # 以下是临时测试数据... 235 | user_id=getUserId() 236 | MODEL=socket.gethostname() 237 | did_id='0' 238 | wan_ip=configDict['wan_ip'] 239 | bras_ip=configDict['bras_ip'] 240 | APP_VERSION= getAppVersion() 241 | # .................. 242 | res=requests.get('https://wifi.loocha.cn/0/wifi/qrcode?1=%s&brasip=%s&ulanip=%s&wlanip=%s&mm=default'%(APP_VERSION,bras_ip,wan_ip,wan_ip)) 243 | resDict=json.loads(res.text) 244 | 245 | if resDict['status']=='0': 246 | print('qrcode:'+resDict['telecomWifiRes']['password']) 247 | return resDict['telecomWifiRes']['password'] 248 | else: 249 | print('qr获取失败') 250 | return 0 251 | 252 | def doOnlineTest(): 253 | # 以下是临时测试数据... 254 | global configDict 255 | # 先转化为bytes 256 | rawStr=configDict['account']+':'+configDict['passwd'] 257 | bytesAuth=rawStr.encode(encoding='utf-8') 258 | # base64编码 259 | Auth=str(base64.b64encode(bytesAuth),'utf-8') 260 | head={'Authorization':'Basic %s'%Auth} 261 | user_id=getUserId() 262 | MODEL=socket.gethostname() 263 | did_id='0' 264 | wan_ip=configDict['wan_ip'] 265 | bras_ip=configDict['bras_ip'] 266 | APP_VERSION= getAppVersion() 267 | # .................. 268 | nowtime=str(int(time.time()*1000)) 269 | params='&server_did=' + did_id + '&time=' + nowtime + '&type=1' 270 | sign = getMD5('mobile='+configDict['account']+ '&model='+MODEL+params) 271 | PARAM='1=%s&qrcode=%s&code=%s&mm=%s&%s&sign=%s&type=1&server_did=%s&time=%s'%(APP_VERSION,getQrtest(),getPwdtest(),MODEL,params,sign,did_id,nowtime) 272 | finalUrl='https://wifi.loocha.cn/%s/wifi/telecom/auto/login?%s'%(user_id,PARAM) 273 | res=requests.post(finalUrl,headers=head) 274 | print(res.text) 275 | 276 | initial() 277 | doOnlineTest() --------------------------------------------------------------------------------