溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Python怎樣模擬登陸某網(wǎng)教師教育網(wǎng)

發(fā)布時間:2021-12-04 10:28:41 來源:億速云 閱讀:131 作者:柒染 欄目:網(wǎng)絡(luò)安全

這篇文章給大家介紹Python怎樣模擬登陸某網(wǎng)教師教育網(wǎng),內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

不得不說,最近的 Python 蠻火的,我也稍稍了解了下,并試著用 Python 爬取網(wǎng)站上的數(shù)據(jù)

不過有些數(shù)據(jù)是要登陸后才能獲取的,我們每年都要到某教師教育網(wǎng)學(xué)習(xí)一些公需科目,就拿這個網(wǎng)站試試,關(guān)鍵是對網(wǎng)站的分析

打開瀏覽器,輸入網(wǎng)站網(wǎng)址 http://www.jste.net.cn ,按F12調(diào)出瀏覽器的開發(fā)者工具,選中 Network ,并勾選 Preserve log,防止切換網(wǎng)頁時信息丟失

Python怎樣模擬登陸某網(wǎng)教師教育網(wǎng)

網(wǎng)頁上輸入賬號,密碼輸入“123456”,驗證碼輸入“abcde”,驗證碼不要輸正確的,否則密碼錯5次,會被網(wǎng)站鎖定賬號30個小時,驗證碼倒是可以隨便錯

登陸后(當(dāng)然登陸不上,會跳轉(zhuǎn)到另一個登陸頁面),在開發(fā)者工具中看到與服務(wù)器的數(shù)據(jù)交換

Python怎樣模擬登陸某網(wǎng)教師教育網(wǎng)

第一個是get驗證碼圖片的,第二個就是向網(wǎng)站提交數(shù)據(jù)的,點一下第二個信息

Python怎樣模擬登陸某網(wǎng)教師教育網(wǎng)

這是個 Post 請求,重點看紅框中的提交數(shù)據(jù),randomCode就是輸入的驗證碼了,x,y應(yīng)該是點擊的按鈕控件的位置了,有cookie后就沒有提交這個數(shù)據(jù)了,可以忽視,returnURL、appId,encrypt每次都是一樣的,也不用管他,重點是 reqId 和 req 這兩個 key 的值了,reqId猜想是點擊按鈕時取到的時間戳,可以復(fù)制這個數(shù)據(jù)到驗證下 Unix時間戳(Unix timestamp)轉(zhuǎn)換工具 單位選毫秒,確實是剛剛提交數(shù)據(jù)的時間,就剩下一個數(shù)據(jù)了,這個key的數(shù)值很長,下面來尋找這個數(shù)據(jù)是從哪里的來的

可以看到 login.jsp 下可以看到 encode.js、string.js、des.js 從名字上就能看出這幾個是用來加密提交數(shù)據(jù)的,右鍵 login.jsp,選擇 “Open in Sources panel”

Python怎樣模擬登陸某網(wǎng)教師教育網(wǎng)

Python怎樣模擬登陸某網(wǎng)教師教育網(wǎng)

可以跳轉(zhuǎn)到 "源" 選項卡,看到 ’login.jsp‘ 的源碼,如果格式混亂,比如所有代碼在一行中,不便于觀看,可以點擊界面下方

Python怎樣模擬登陸某網(wǎng)教師教育網(wǎng)

的中括號,開發(fā)者工具會自動給你重新格式化代碼。

仔細(xì)分析 login.jsp 的代碼,看到

function doOk(frm) {            var el = frm.elements["loginName"];            var loginName = el.value.replace(/ /g, "");            el.value = loginName;            if (isEmpty(loginName)) {                alert("請輸入登錄名");                el.focus();                return false;            }            el = frm.elements["pwd"];            el.value = el.value.replace(/ /g, "");            var pwd=el.value;            if (isEmpty(el.value)) {                alert("請輸入登錄密碼");                el.focus();                return false;            }            var d = new Date();            pwd = encode(loginName, pwd);//密碼第一次加密,可以跟進            frm.elements["encrypt"].value = "1";            var validCode="";            el=frm.elements["randomCode"];            if(el){                el.value=el.value.replace(/ /g,"");                if (isEmpty(el.value)) {                    alert("請輸入登錄密碼");                    el.focus();                    return false;                }                validCode=el.value;            }            loginName=encodeURI(loginName);//避免中文問題 進行URL編碼            var reqId=(new Date()).getTime()+"";//獲取時間戳給 reqId            var str=strEnc(loginName+"\n"+pwd,reqId,validCode);//關(guān)鍵加密代碼,可以跟進分析            frm.elements["loginName"].disabled="true";            frm.elements["pwd"].value=pwd;            frm.elements["pwd"].disabled="true";            frm.elements["req"].value=str;            frm.elements["reqId"].value=reqId;            return true;        }

找到這段代碼,其中主要是對輸入檢查的部分,重點看這兩處

pwd = encode(loginName, pwd);

此處對密碼進行第一次加密

loginName=encodeURI(loginName);//避免中文問題var reqId=(new Date()).getTime()+"";var str=strEnc(loginName+"\n"+pwd,reqId,validCode);

第一行:將用戶名進行 URL 的格式編碼

第二行,取時間戳賦值給 reqId

第三行傳入用戶名,加密后的密碼和驗證碼進行驗證,函數(shù)返回值賦給變量 str,正是提交數(shù)據(jù)的 req 的值

在兩個加密函數(shù)入口設(shè)置斷點,開發(fā)者工具設(shè)置斷點的,只要在這個代碼的行號上點擊鼠標(biāo)就行了,設(shè)好斷點后,再次輸入用戶名密碼和驗證碼,重新提交,程序被斷下:

Python怎樣模擬登陸某網(wǎng)教師教育網(wǎng)

F11單步進入第一個斷點,這里需要點擊界面下面的中括號重新格式化下代碼,單步跟進后看到:

var _$_7151 = ["encode", "ABCDEFGHIJKLMNOP", "QRSTUVWXYZabcdef", "ghijklmnopqrstuv", "wxyz0123456789+/", "=", "", "charCodeAt", "charAt", "length", "join", "reverse", "split"];window[_$_7151[0]] = function(c, e) {    function a(p) {        var q = _$_7151[1] + _$_7151[2] + _$_7151[3] + _$_7151[4] + _$_7151[5];        p = encodeURI(p);        var r = _$_7151[6];        var g, h, j = _$_7151[6];        var k, l, m, o = _$_7151[6];        var b = 0;        do {            g = p[_$_7151[7]](b++);//第一個字符            h = p[_$_7151[7]](b++);//第二個字符            j = p[_$_7151[7]](b++);//第三個字符            k = g >> 2; //得到 k            l = ((g & 3) << 4) | (h >> 4);//得到 i            m = ((h & 15) << 2) | (j >> 6);//得到 m            o = j & 63; //得到 o            if (isNaN(h)) {  //如果沒有第二個字符                m = o = 64 //則取表中的第64個字符替換            } else {                if (isNaN(j)) { //如果沒有第三個字符                    o = 64 //則取表中的第64個字符替換                }            }            ;r = r + q[_$_7151[8]](k) + q[_$_7151[8]](l) + q[_$_7151[8]](m) + q[_$_7151[8]](o);            g = h = j = _$_7151[6];            k = l = m = o = _$_7151[6]        } while (b < p[_$_7151[9]]);;return r    }    var d = c[_$_7151[9]];    var f = a(e)[_$_7151[12]](_$_7151[6])[_$_7151[11]]()[_$_7151[10]](_$_7151[6]);    for (var b = 0; b < (d % 2 == 0 ? 1 : 2); b++) {        f = a(f)[_$_7151[12]](_$_7151[6])[_$_7151[11]]()[_$_7151[10]](_$_7151[6])    }    ;return f}

這個函數(shù)返回的 f 就是密碼第一次加密后的結(jié)果了,這個代碼是用什么工具變成這樣的不太清楚,如果出現(xiàn) _$_7151[n] 這樣的字符可以查詢代碼最上面的列表

代換,大致過程不詳說,跟一遍就知道了,就是循環(huán)從密碼中取三個字符 g、h、j,然后將三個字符的ascii碼左移或右移,或和其他結(jié)果加加減減,得到的結(jié)果 k、l、m、o 查詢表格替換字符,如果密碼長度不是 3 的整數(shù)倍,則查表結(jié)果用 “=” 替換,將循環(huán)得到的查表結(jié)果依次連接,并反序,得到一個密碼加密后的密碼

至少將密碼進行兩次這樣的加密計算,如果用戶名的長度是奇數(shù),再進行一次加密,加密的過程只需要復(fù)制代碼到 python 中,修改成  python 的格式就可以了。

步過了對密碼的第一次加密后,繼續(xù)步進上面設(shè)下的第二個斷點

function strEnc(data,firstKey,secondKey,thirdKey){    var leng = data.length;//取 data 的長度    var encData = "";    var firstKeyBt,secondKeyBt,thirdKeyBt,firstLength,secondLength,thirdLength;    if(firstKey != null && firstKey != ""){        firstKeyBt = getKeyBytes(firstKey);//取 firstkey 在每個字符之間插入一個字節(jié)的 0        firstLength = firstKeyBt.length;//取得插入 0 后的長度    }    if(secondKey != null && secondKey != ""){        secondKeyBt = getKeyBytes(secondKey);//取 secondkey 在每個字符之間插入一個字節(jié)的 0        secondLength = secondKeyBt.length;//取得插入 0 后的長度    }    if(thirdKey != null && thirdKey != ""){ //登陸過程中,并沒用到 thirdkey,即 thirdKey = None        thirdKeyBt = getKeyBytes(thirdKey);//取 thirdkey 在每個字符之間插入一個字節(jié)的 0        thirdLength = thirdKeyBt.length;//取得插入 0 后的長度    }    if(leng > 0){         if(leng < 4){ 如果 data 的長度<4,因為跳過,代碼用省略號替換            //省去一些代碼……            }else{            var iterator = parseInt(leng/4);//data 的長度除 64,得到循環(huán)次數(shù)            var remainder = leng%4; //data 的長度是否是 64 位的整數(shù)倍,保存余數(shù)            var i=0;            for(i = 0;i < iterator;i++){ //開始循環(huán)                var tempData = data.substring(i*4+0,i*4+4); //循環(huán)取 data 的64 位                var tempByte = strToBt(tempData);//轉(zhuǎn)換成 bits                var encByte ;                if(firstKey != null && firstKey !="" && secondKey != null && secondKey != "" ){                    var tempBt;                    var x,y;                    tempBt = tempByte;                    for(x = 0;x < firstLength ;x ++){                        tempBt = enc(tempBt,firstKeyBt[x]);//循環(huán)從firstkey 中取得64 位做密鑰,依次對 data 中的某一段加密                    }                    for(y = 0;y < secondLength ;y ++){                        tempBt = enc(tempBt,secondKeyBt[y]);//循環(huán)從second中取得64 位做密鑰,依次對 data 中的某一段加密                    }                    encByte = tempBt;//保存加密結(jié)果                }            //…………            if(remainder > 0){  //如果 data 有多余的長度,不足64 位                var remainderData = data.substring(iterator*4+0,leng);                var tempByte = strToBt(remainderData);//將余下的分到4個16位的數(shù)組中                var encByte ;                if(firstKey != null && firstKey !="" && secondKey != null && secondKey != "" && thirdKey != null ){                    var tempBt;                    var x,y,z;                    tempBt = tempByte;                    for(x = 0;x < firstLength ;x ++){                        tempBt = enc(tempBt,firstKeyBt[x]);循環(huán)從firstkey 中取得64 位做密鑰,依次對 data 中的某一段加密                    }                    for(y = 0;y < secondLength ;y ++){                        tempBt = enc(tempBt,secondKeyBt[y]);循環(huán)從secondkey中取得64 位做密鑰,依次對 data 中的某一段加密                    }                    encByte = tempBt;//保存加密結(jié)果                }                encData += bt64ToHex(encByte);//將加密后的文本轉(zhuǎn)為16進制文本            }        }    }    return encData;//返回加密結(jié)果}

這是一段循環(huán)進行 DES 加密的代碼,先將data, firstkey, secondkey進行字符間插入一個字節(jié)的0, 然后不是 64 位整數(shù)倍長度的從上面代碼看,相當(dāng)于在后面補上 0 了
從data中取出一段64位數(shù)據(jù),循環(huán)用 firstkey 和 second 中的 64 位做密鑰,層層加密,得到的結(jié)果和 data 中其他 64 位加密的結(jié)果串聯(lián)后就是 req 的值了
因為 key 都是 64 位的,再加上本身 sources 中也看到了 DES.js 文件,所以 enc(tempBt,secondkeyBt)應(yīng)該就是 DES 算法了。
但是自己寫代碼模擬登陸確發(fā)現(xiàn)結(jié)果和自己跟的結(jié)果不同,從代碼中看,DES 采用了 ECB 模式,不是 CBC 模式,PAD_mode 也沒問題,都64位,不需要 DES 自己填充啊。沒辦法,只得硬著頭皮繼續(xù)跟進 DES 加密的代碼

我們知道,DES 加密需要先對 key 進行 置換,得到 56 位密鑰,標(biāo)準(zhǔn)的 DES 都有個置換表,正常的 DES 置換表是這樣的

Permutation and translation tables for DES        __pc1 = [56, 48, 40, 32, 24, 16, 8,               0, 57, 49, 41, 33, 25, 17,               9, 1, 58, 50, 42, 34, 26,               18, 10, 2, 59, 51, 43, 35,               62, 54, 46, 38, 30, 22, 14,               6, 61, 53, 45, 37, 29, 21,               13, 5, 60, 52, 44, 36, 28,               20, 12, 4, 27, 19, 11, 3               ]

即將 key 的第 56 位放到第 0 位,第 48 位放到第 1 位…………最后置換出 56 位的 key,再分成 2 個28 密鑰,循環(huán)左移和右移,然后 對 IP 置換后的 data 加密,進行 Sbox 盒替換 和 Pbox 替換,再進行一次 IP-1 置換得到密文,解密算法一樣。

但跟進 DES 加密函數(shù)沒多久就發(fā)現(xiàn)問題了,找到密鑰置換的函數(shù)

var keys = generateKeys(keyByte);

并跟進:

function generateKeys(keyByte){    var key   = new Array(56);    var keys = new Array();    keys[ 0] = new Array();    keys[ 1] = new Array();    keys[ 2] = new Array();    keys[ 3] = new Array();    keys[ 4] = new Array();    keys[ 5] = new Array();    keys[ 6] = new Array();    keys[ 7] = new Array();    keys[ 8] = new Array();    keys[ 9] = new Array();    keys[10] = new Array();    keys[11] = new Array();    keys[12] = new Array();    keys[13] = new Array();    keys[14] = new Array();    keys[15] = new Array();    var loop = [1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1];//看到了循環(huán)移位的表,沒看到置換表    for(i=0;i<7;i++){        for(j=0,k=7;j<8;j++,k--){            key[i*8+j]=keyByte[8*k+i];//用了這個循環(huán)生成 56 位         }    }//省略代碼}

這里修改了標(biāo)準(zhǔn)的置換表,用了一個嵌套循環(huán)生成 56 位密鑰,即把

原來 key 的 56 位 --> 第 0 位,48 位 --> 第 1 位,40 位 --> 第 2 位,…………0 位--> 第 7 位

原來 key 的 57 位 --> 第 8 位,49 位 --> 第 9 位,41 位 --> 第 10 位,………… 1 位 -->第 15 位

…………

最后丟棄原 key 的第 63,55,47,39,31,23,15,7 位(位置號從 0 開始)

在 python 中不能直接使用標(biāo)準(zhǔn)的 DES庫了,可以把標(biāo)準(zhǔn)庫中的 pyDes.py 文件拷貝到工程同目錄下,改名為 Des,py,并導(dǎo)入工程

from Des import *

另外在 Des.py 中找到 key 的置換表,修改成

__pc1 = [        56, 48, 40, 32, 24, 16, 8, 0,        57, 49, 41, 33, 25, 17, 9, 1,        58, 50, 42, 34, 26, 18, 10, 2,        59, 51, 43, 35, 27, 19, 11, 3,        60, 52, 44, 36, 28, 20, 12, 4,        61, 53, 45, 37, 29, 21, 13, 5,        62, 54, 46, 38, 30, 22, 14, 6    ]

就可以正常使用 Des 了

最后附上 python 代碼:

from Des import *from urllib.parse import quotefrom time import time, sleepfrom PIL import Imageimport requestsimport sysfrom bs4 import BeautifulSoups = requests.session()headers = {    'Cache-Control': 'max-age=0',    'Connection': 'keep-alive',    'Referer': 'http://www.jste.net.cn/uids/login.jsp',    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) \    Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0'}def custom_encode(data):  # 懶得注釋了,直接從js中拷貝出來,改成python的代碼    tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='    data_bytes = list(data.encode())    while len(data_bytes) % 3 != 0:        data_bytes.append(0)    b = 0    length = len(data_bytes)    r = ''    while b < length:        g = data_bytes[b]        h = data_bytes[b + 1]        j = data_bytes[b + 2]        k = g >> 2        m = ((g & 3) << 4) | (h >> 4)        n = ((h & 15) << 2) | (j >> 6)        o = j & 63        third_char = '=' if h == 0 else tab[n]        fourth_char = '=' if j == 0 else tab[o]        r = r + tab[k] + tab[m] + third_char + fourth_char        b = b + 3    return r[::-1]  # 反序輸出def encode_pwd(str_name, str_pwd):    encoded_pwd = custom_encode(str_pwd)    encoded_pwd = custom_encode(encoded_pwd)  # 先連續(xù)對密碼加密兩次    if len(str_name) % 2 == 1:        encoded_pwd = custom_encode(encoded_pwd)  # 如果用戶名長度是奇數(shù),則再加密一次    return encoded_pwddef strenc(data, firstkey, secondkey):    bts_data = extend_to_16bits(data)  # 將data長度擴展成64位的倍數(shù)    bts_firstkey = extend_to_16bits(firstkey)  # 將 first_key 長度擴展成64位的倍數(shù)    bts_secondkey = extend_to_16bits(secondkey)  # 將 second_key 長度擴展成64位的倍數(shù)    i = 0    bts_result = []    while i < len(bts_data):        bts_temp = bts_data[i:i + 8]  # 將data分成每64位一段,分段加密        j, k = 0, 0        while j < len(bts_firstkey):            des_k = des(bts_firstkey[j: j + 8], ECB)  # 分別取出 first_key 的64位作為密鑰            bts_temp = list(des_k.encrypt(bts_temp))            j += 8        while k < len(bts_secondkey):            des_k = des(bts_secondkey[k:k + 8], ECB)  # 分別取出 second_key 的64位作為密鑰            bts_temp = list(des_k.encrypt(bts_temp))            k += 8        bts_result.extend(bts_temp)        i += 8    str_result = ''    for each in bts_result:        str_result += '%02X' % each  # 分別加密data的各段,串聯(lián)成字符串    return str_resultdef extend_to_16bits(data):  # 將字符串的每個字符前插入 0,變成16位,并在后面補0,使其長度是64位整數(shù)倍    bts = data.encode()    filled_bts = []    for each in bts:        filled_bts.extend([0, each])  # 每個字符前插入 0    while len(filled_bts) % 8 != 0:  # 長度擴展到8的倍數(shù)        filled_bts.append(0)  # 不是8的倍數(shù),后面添加0,便于DES加密時分組    return filled_btsdef get_rand_code():    random_code_url = r'http://www.jste.net.cn/uids/genImageCode?rnd='    time_stamp = str(int(time() * 1000))    random_code_url += time_stamp    try:        req = s.get(random_code_url, headers=headers, stream=True)        with open('rand_code.jpg', 'wb') as f:            for chunk in req.iter_content(chunk_size=1024):                f.write(chunk)    except requests.RequestException:        print('網(wǎng)絡(luò)鏈接錯誤,請稍后重試/(ㄒoㄒ)/~~')        sys.exit()    with Image.open('rand_code.jpg')as img:        img.show()def login_site(reqid, randomcode, reqkey):    post_data = {        'randomCode': randomcode,        'returnURL': None,        'appId': 'uids',        'site': None,        'encrypt': 1,        'reqId': reqid,        'req': reqkey    }    try:        req = s.post('http://www.jste.net.cn/uids/login.jsp', headers=headers, data=post_data)        print('Status Code:%s' % req.status_code)  # 不知道為什么瀏覽器上登陸成功返回的是302,這里返回200        if 'Set-Cookie' in req.headers.keys():  # 還好,看到response中出現(xiàn)Set-Cookie,就登陸成功了            return True        else:            return False    except requests.RequestException:        print('網(wǎng)絡(luò)鏈接錯誤,請稍后重試/(ㄒoㄒ)/~~')        return Falsedef main():    print(''.center(100, '-'))    uname = input('請輸入你的用戶名:')    pwd = input('請輸入你的登陸密碼:')    get_rand_code()    secondkey = input('請輸入看到的驗證碼:')  # 取得驗證碼,作為second_key,提交數(shù)據(jù)時作為 randomCode 的值    firstkey = str(int(time() * 1000))  # 取得提交時的時間戳,作為first_key,提交數(shù)據(jù)時候作為 reqId 的值    crypt_pwd = encode_pwd(uname, pwd)  # 對輸入的密碼進行第一次加密    data = quote(uname) + '\n' + crypt_pwd  # 用戶名URI編碼后和密碼加密后的文本鏈接等待被DES加密    post_req = strenc(data, firstkey, secondkey)  # 主要是DES計算,作為 req 的值提交數(shù)據(jù)    if login_site(reqid=firstkey, randomcode=secondkey, reqkey=post_req) is True:        print(''.center(100, '-'))        print('登陸成功,O(∩_∩)O哈哈~...')        try:            req = s.get('http://www.jste.net.cn/train/credit_hour/top.jsp')  # 打開一個網(wǎng)頁測試一下            soup = BeautifulSoup(req.text, 'html5lib')  # 網(wǎng)頁為多框架,測試下訪問TOP框架中的文本            print(soup.select('.b')[0].text.replace('\n', '').replace(' ', ''))        except requests.RequestException:            print('網(wǎng)絡(luò)鏈接錯誤,請稍后重試/(ㄒoㄒ)/~~')if __name__ == '__main__':  # 啟動程序    main()

測試效果:

Python怎樣模擬登陸某網(wǎng)教師教育網(wǎng)

最后思考了下,很多網(wǎng)站的數(shù)據(jù)都是明碼提交的,或者是簡單的加密提交的,這個網(wǎng)站在加密上花了一些工夫

但是js腳本最大的問題就是別人可以看到源碼,雖然網(wǎng)站登陸成功后立即刪除了js文件,但是只要出現(xiàn)了就會被發(fā)現(xiàn),我網(wǎng)上搜索了下隱藏源碼的辦法,但是水平才菜了,沒學(xué)過 java ,也沒看懂。

最后補充下:DES加密的數(shù)據(jù) data 是用戶名的" URL格式 + 換行 + 密碼第一次加密得到的文本“

firstkey 是提交時得到的時間戳,secondkey 就是輸入的驗證碼

關(guān)于Python怎樣模擬登陸某網(wǎng)教師教育網(wǎng)就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI