【JS 逆向百例】层层嵌套,某加速商城 RSA 加密
文章目錄
- 聲明
- 逆向目標
- 逆向過程
- 抓包分析
- 參數逆向
- 邏輯總結
- 完整代碼
- 參數 JS 加密關鍵代碼
- Python 登錄關鍵代碼
聲明
本文章中所有內容僅供學習交流,敏感網址、數據接口均已做脫敏處理,嚴禁用于商業用途和非法用途,否則由此產生的一切后果均與作者無關,若有侵權,請聯系我立即刪除!
逆向目標
-
目標:某加速商城登錄接口
-
主頁:aHR0cDovL3d3dy4xNXl1bm1hbGwuY29tL3BjL2xvZ2luL2luZGV4
-
接口:aHR0cDovL3d3dy4xNXl1bm1hbGwuY29tL3BjL2xvZ2luL2NoZWNr
-
逆向參數:
PHPSESSID=g9jp7sfpukg99v03gj69nr9r56
Cookie:Form Data:
u[password]: 7aad70a547b016a07f2e20bee7b4936111e650aa5c419fafdfb28322...... _csrfToken: 4bea63330c5ccdd37588321d027f4c40129687b0
逆向過程
抓包分析
在首頁點擊登陸,來到登錄頁面,隨便輸入一個賬號密碼登陸,抓包定位到登錄接口為 aHR0cDovL3d3dy4xNXl1bm1hbGwuY29tL3BjL2xvZ2luL2NoZWNr ,POST 請求,Form Data 里,密碼 u[password] 被加密處理了,此外還有一個 _csrfToken 也是需要我們解決的,cookie 里面有一個 PHPSESSID,經過測試,如果不帶此參數,最終的請求也是失敗的。
參數逆向
首先看看 _csrfToken,先嘗試直接搜索一下它的值,可以發現其實在首頁的源碼里面就有,直接匹配拿過來即可:
再看一下 cookie 里面的 PHPSESSID,首先想到的,可能是第一次訪問頁面,Response Headers 返回的 Set-Cookie 得到的,查看第一次請求,確實是的,如果沒有的話,需要清除緩存再訪問(開發者工具 ——> Application ——> Storage ——> Clear site data)。
最后一個密碼參數 u[password],肯定是通過 JS 加密得到的,直接 Ctrl+Shift+F 全局搜索,可以直接在 index 首頁找到 RSA 加密的地方,埋下斷點進行調試,最后的 res 正是加密后的密碼:
我們將這段關鍵代碼進行改寫,封裝成一個函數:
function getEncryptedPassword(password) {var public_key = "00bdf3db924714b9c4ddd144910071c282e235ac51371037cf89fa08f28b9105b6326338ed211280154c645bf81bae4184c2b52e2b02b0953e7aa8b25a8e212a0b";var public_length = "10001";var rsa = new RSAKey();rsa.setPublic(public_key, public_length);return rsa.encrypt(password); }這里主要用到的三個函數 RSAKey()、setPublic()、encrypt(),在開發者工具中,鼠標放到函數上,可以看到這里都是調用的 rsa.js 里面的方法,我們直接將整個文件剝離下來進行本地調試:
本地調試會發現提示 BigInteger 未定義,鼠標移到這個函數上面,可以發現是調用了 jsbn.js 里面的方法,同樣的,直接將整個 jsbn.js 文件剝離下來進行本地調試。
這里其實在 rsa.js 文件的第一行有一句注釋:// Depends on jsbn.js and rng.js,我們可以猜測 rsa.js 是可能依賴 jsbn.js 和 rng.js 這兩個文件的。
有了 jsbn.js 的代碼,再次進行調試,會發現又提示 navigator 和 SecureRandom 未定義,navigator 我們已經非常熟悉了,是瀏覽器的相關信息,一般情況下直接定義為空即可(navigator = {};);將鼠標移到 SecureRandom 函數上面,可以發現是調用了 rng.js 里面的方法,同樣的,直接將整個 rng.js 文件剝離下來進行本地調試。這里就證實了前面我們的猜想,rsa.js 確實是依賴 jsbn.js 和 rng.js 的。
我們注意到,這里在 rng.js 文件的第一行,同樣有一句注釋:// Random number generator - requires a PRNG backend, e.g. prng4.js,表明 rng.js 是隨機數生成器,需要 PRNG 后端,例如 prng4.js,在密碼學中,PRNG 全稱是 pseudorandom number generator,即偽隨機數生成器,是指通過特定算法生成一系列的數字,使得這一系列的數字看起來是隨機的,但是實際是確定的,所以叫偽隨機數,感興趣的朋友可以深入研究一下,在這里我們知道 rng.js 可能還依賴于 prng4.js,需要進一步調試才清楚。
rsa.js、jsbn.js、rng.js 都齊全了,再次本地調試,會發現 rng.js 里面的 rng_psize 未定義,鼠標放上去看到 rng_psize 就是一個定值 256,在右邊的 Global 全局變量里也可以看到值為 256,嘗試搜索一下 rng_psize,可以發現在 prng4.js 里面有定義 var rng_psize = 256;,果然和注釋說得一樣,rng.js 是依賴 prng4.js 的,但是這里似乎直接定義一下 rng_psize 就行了。
直接在本地代碼定義一下 var rng_psize = 256;,再次進行調試,此時又會提示 rng.js 里缺少 prng_newstate() 對象,再次回到開發者工具,可以看到 prng_newstate() 是 prng4.js 里面的方法,果然 rng.js 和 prng4.js 的關系并不簡單,同樣的,我們也直接將整個 prng4.js 文件剝離下來進行本地調試。
再次調試,運行無誤,可以成功拿到加密后的密碼了:
邏輯總結
加密入口可以在 index 首頁找到,用到了 rsa.js 里面的三個加密函數 RSAKey()、setPublic()、encrypt();
rsa.js 里的 BigInteger() 函數依賴 jsbn.js,SecureRandom() 函數依賴 rng.js;
rng.js 里的變量 rng_psize 在 prng4.js 中定義,prng_newstate() 函數也依賴 prng4.js;
要將 rsa.js、jsbn.js、rng.js、prng4.js 這四個 JS 加密文件完整的剝離下來才能還原整個加密過程。
完整代碼
GitHub 關注 K 哥爬蟲:https://github.com/kgepachong,持續分享爬蟲相關代碼!歡迎 star !
以下只演示部分關鍵代碼,完整代碼倉庫地址:https://github.com/kgepachong/crawler
參數 JS 加密關鍵代碼
navigator = {};// ================== prng4.js begin ================== //function Arcfour() {}function ARC4init(key) {}function ARC4next() {}// 此處省略 N 個函數var rng_psize = 256;// ================== prng4.js end ================== //// ================== rng.js begin ================== //var rng_state; var rng_pool; var rng_pptr;function rng_seed_int(x) {}function rng_seed_time() {}// 此處省略 N 個函數function SecureRandom() {}SecureRandom.prototype.nextBytes = rng_get_bytes;// ================== rng.js end ================== //// ================== jsbn.js begin ================== //var dbits;var canary = 0xdeadbeefcafe; var j_lm = ((canary & 0xffffff) == 0xefcafe);function BigInteger(a, b, c) {}function nbi() {}// 此處省略 N 個函數// protected BigInteger.prototype.copyTo = bnpCopyTo; BigInteger.prototype.fromInt = bnpFromInt; BigInteger.prototype.fromString = bnpFromString; BigInteger.prototype.clamp = bnpClamp; BigInteger.prototype.dlShiftTo = bnpDLShiftTo; BigInteger.prototype.drShiftTo = bnpDRShiftTo; BigInteger.prototype.lShiftTo = bnpLShiftTo; BigInteger.prototype.rShiftTo = bnpRShiftTo; BigInteger.prototype.subTo = bnpSubTo; BigInteger.prototype.multiplyTo = bnpMultiplyTo; BigInteger.prototype.squareTo = bnpSquareTo; BigInteger.prototype.divRemTo = bnpDivRemTo; BigInteger.prototype.invDigit = bnpInvDigit; BigInteger.prototype.isEven = bnpIsEven; BigInteger.prototype.exp = bnpExp;// public BigInteger.prototype.toString = bnToString; BigInteger.prototype.negate = bnNegate; BigInteger.prototype.abs = bnAbs; BigInteger.prototype.compareTo = bnCompareTo; BigInteger.prototype.bitLength = bnBitLength; BigInteger.prototype.mod = bnMod; BigInteger.prototype.modPowInt = bnModPowInt;// "constants" BigInteger.ZERO = nbv(0); BigInteger.ONE = nbv(1);// ================== jsbn.js end ================== //// ================== rsa.js begin ================== //function parseBigInt(str, r) {}function linebrk(s, n) {}function byte2Hex(b) {}function pkcs1pad2(s, n) {}function RSAKey() {}function RSASetPublic(N, E) {}function RSADoPublic(x) {}function RSAEncrypt(text) {}// protected RSAKey.prototype.doPublic = RSADoPublic;// public RSAKey.prototype.setPublic = RSASetPublic; RSAKey.prototype.encrypt = RSAEncrypt; //RSAKey.prototype.encrypt_b64 = RSAEncryptB64;// ================== rsa.js end ================== //function getEncryptedPassword(password) {var public_key = "00bdf3db924714b9c4ddd144910071c282e235ac51371037cf89fa08f28b9105b6326338ed211280154c645bf81bae4184c2b52e2b02b0953e7aa8b25a8e212a0b";var public_length = "10001";var rsa = new RSAKey();rsa.setPublic(public_key, public_length);return rsa.encrypt(password); }// 測試樣例 console.log(getEncryptedPassword("123456"))Python 登錄關鍵代碼
#!/usr/bin/env python3 # -*- coding: utf-8 -*-import execjs import requestsfrom lxml import etree from PIL import Imageindex_url = '脫敏處理,完整代碼關注 GitHub:https://github.com/kuaidaili/crawler/' login_url = '脫敏處理,完整代碼關注 GitHub:https://github.com/kuaidaili/crawler/' code_url = '脫敏處理,完整代碼關注 GitHub:https://github.com/kuaidaili/crawler/'headers = {'Host': '脫敏處理,完整代碼關注 GitHub:https://github.com/kuaidaili/crawler/','Referer': '脫敏處理,完整代碼關注 GitHub:https://github.com/kuaidaili/crawler/','Origin': '脫敏處理,完整代碼關注 GitHub:https://github.com/kuaidaili/crawler/','User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36' } session = requests.session()def get_encrypted_password(password):with open('encrypt.js', 'r', encoding='utf-8') as f:yunmall_js = f.read()encrypted_password = execjs.compile(yunmall_js).call('getEncryptedPassword', password)return encrypted_passworddef get_csrf_token_cookie():response = session.get(url=index_url, headers=headers)tree = etree.HTML(response.text)csrf_token = tree.xpath("//input[@name='_csrfToken']/@value")[0]cookies = response.cookies.get_dict()# print(csrf_token, cookies)return csrf_token, cookiesdef get_very_code(cookies):response = session.get(url=code_url, cookies=cookies, headers=headers)with open('code.png', 'wb') as f:f.write(response.content)image = Image.open('code.png')image.show()very_code = input('請輸入驗證碼: ')return very_codedef login(csrf_token, very_code, cookies, username, encrypted_password):data = {'u[loginType]': 'name','u[phone]': username,'u[password]': encrypted_password,'u[veryCode]': very_code,'u[token]': '','_csrfToken': csrf_token}header_info = {'X-Requested-With': 'XMLHttpRequest',}headers.update(header_info)response = session.post(url=login_url, data=data, cookies=cookies, headers=headers)response.encoding = 'utf-8-sig'response_code = response.text# print(response_code)status_code = {'31': '恭喜,登陸成功。','32': '登陸失敗。','33': '用戶名或密碼錯誤。','35': '該用戶已被管理員鎖定。','311': '該賬號已綁定設備,請在綁定的設備登陸。','20001': '驗證碼填寫錯誤!'}try:print(status_code[response_code])except KeyError:print('請求超時!')def main():username = input('請輸入登錄賬號: ')password = input('請輸入登錄密碼: ')if len(password) > 32:raise Exception('請輸入正確的密碼!')encrypted_password = get_encrypted_password(password)csrf_token, cookies = get_csrf_token_cookie()very_code = get_very_code(cookies)login(csrf_token, very_code, cookies, username, encrypted_password)if __name__ == '__main__':main()總結
以上是生活随笔為你收集整理的【JS 逆向百例】层层嵌套,某加速商城 RSA 加密的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微信办浦发信用卡额度一般是多少
- 下一篇: 【JS 逆向百例】当乐网登录接口参数逆向