【JS 逆向百例】轉變思路盗似,少走彎路,X米加密分析

聲明

本文章中所有內容僅供學習交流篡腌,抓包內容术吝、敏感網(wǎng)址、數(shù)據(jù)接口均已做脫敏處理罩引,嚴禁用于商業(yè)用途和非法用途各吨,否則由此產生的一切后果均與作者無關,若有侵權袁铐,請聯(lián)系我立即刪除揭蜒!

逆向目標

  • 目標:X米賬號登錄
  • 主頁:aHR0cHM6Ly9hY2NvdW50LnhpYW9taS5jb20v
  • 接口:aHR0cHM6Ly9hY2NvdW50LnhpYW9taS5jb20vcGFzcy9zZXJ2aWNlTG9naW5BdXRoMg==
  • 逆向參數(shù):Form Data:hash: FCEA920F7412B5DA7BE0CF42B8C93759

逆向過程

抓包分析

來到X米的登錄頁面,隨便輸入一個賬號密碼登陸剔桨,抓包定位到登錄接口為 aHR0cHM6Ly9hY2NvdW50LnhpYW9taS5jb20vcGFzcy9zZXJ2aWNlTG9naW5BdXRoMg==

01.png

POST 請求屉更,F(xiàn)orm Data 里的參數(shù)比較多,分析一下主要參數(shù):

  • serviceParam: {"checkSafePhone":false,"checkSafeAddress":false,"lsrp_score":0.0}洒缀,從參數(shù)的字面意思來看瑰谜,似乎是在檢查手機和地址是否安全,至于具體是什么含義,暫時不得而知似舵,也不知道是在哪個地方設置的脚猾。
  • callback: http://order.xxx.com/login/callback?followup=https%3A%2F%2Fwww.xx......,回調鏈接砚哗,一般來說是固定的龙助,后面帶有 followup 和 sid 參數(shù)。
  • qs: %3Fcallback%3Dhttp%253A%252F%252Forder.xxx.com%252Flogin%252Fcallback%2......蛛芥,把 qs 的值格式化一下可以發(fā)現(xiàn)提鸟,其實是 callback、sign仅淑、sid称勋、_qrsize 四個值按照 URL 編碼進行組合得到的。
  • _sign: w1RBM6cG8q2xj5JzBPPa65QKs9w=涯竟,這個一串看起來是經過某種加密后得到的赡鲜,也有可能是網(wǎng)頁源碼中的值。
  • user: 15555555555庐船,明文用戶名银酬。
  • hash: FCEA920F7412B5DA7BE0CF42B8C93759,加密后的密碼筐钟。

參數(shù)逆向

基本參數(shù)

先來看一下 serviceParam 等基本參數(shù)揩瞪,一般思路我們是先直接搜索一下看看能不能直接找到這個值,搜索發(fā)現(xiàn) serviceParam 關鍵字在一個 302 重定向請求里:

02.png

我們注意到篓冲,當只輸入登錄的主頁 aHR0cHM6Ly9hY2NvdW50LnhpYW9taS5jb20v李破,它會有兩次連續(xù)的 302 重定向,來重點分析一下這兩次重定向壹将。

第一次重定向嗤攻,新的網(wǎng)址里有 followupcallback瞭恰、sign屯曹、sid 參數(shù),這些我們都是在后面的登錄請求中要用到的惊畏。

03.png
04.png

第二次重定向恶耽,新的網(wǎng)址里同樣有 followupcallback颜启、sign偷俭、sid 參數(shù),此外還有 serviceParam缰盏、qs 參數(shù)涌萤,同樣也是后面的登錄請求需要用到的淹遵。

05.png
06.png

找到了參數(shù)的來源,直接從第二次重定向的鏈接里提取各項參數(shù)负溪,這里用到了 response.history[1].headers['Location'] 來提取頁面第二次重定向返回頭里的目標地址透揣,urllib.parse.urlparse 來解析重定向鏈接 URL 的結構,urllib.parse.parse_qs 提取參數(shù)川抡,返回字典辐真,代碼樣例:

import requests
import urllib.parse


headers = {
    'Host': '脫敏處理,完整代碼關注 GitHub:https://github.com/kgepachong/crawler',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
index_url = '脫敏處理崖堤,完整代碼關注 GitHub:https://github.com/kgepachong/crawler'
response = requests.get(url=index_url, headers=headers)
location_url = response.history[1].headers['Location']
urlparse = urllib.parse.urlparse(location_url)
query_dict = urllib.parse.parse_qs(urlparse.query)
print(query_dict)

need_theme = query_dict['needTheme'][0]
show_active_x = query_dict['showActiveX'][0]
service_param = query_dict['serviceParam'][0]
callback = query_dict['callback'][0]
qs = query_dict['qs'][0]
sid = query_dict['sid'][0]
_sign = query_dict['_sign'][0]

print(need_theme, show_active_x, service_param, callback, qs, sid, _sign)

hash

其他參數(shù)都齊全了侍咱,現(xiàn)在還差一個加密后的密碼 hash,一般來講這種都是通過 JS 加密的密幔,老方法楔脯,全局搜索 hash 或者 hash:,可以在 78.4da22c55.chunk.js 文件里面看到有一句:hash: S()(r.password).toUpperCase()胯甩,很明顯是將明文的密碼經過加密處理后再全部轉為大寫:

07.png

重點是這個 S()昧廷,鼠標移上去會發(fā)現(xiàn)其實是調用了 78.4da22c55.chunk.js 的一個匿名函數(shù),我們在匿名函數(shù)的 return 位置埋下斷點進行調試:

08.png
e.exports = function(e, n) {
    if (void 0 === e || null === e)
        throw new Error("Illegal argument " + e);
    var r = t.wordsToBytes(u(e, n));
    return n && n.asBytes ? r : n && n.asString ? s.bytesToString(r) : t.bytesToHex(r)
}

可以看到傳進來的 e 是明文的密碼偎箫,最后的 return 語句是一個三目運算符麸粮,由于 n 是 undefined,所以最后 return 的實際上是 t.bytesToHex(r)镜廉,其值正是加密后的密碼,只不過所有字母都是小寫愚战,按照正常思維娇唯,我們肯定是開始扣 JS 了,這里傳入了參數(shù) r寂玲,var r = t.wordsToBytes(u(e, n));塔插,先跟進 u 這個函數(shù)看看:

09.png
10.png

可以看到 u 函數(shù)實際上是用到了 567 這個對象方法,在這個對象方法里面拓哟,還用到了 129想许、211、22 等非常多的方法断序,這要是挨個去扣流纹,那還不得扣到猴年馬月,而且還容易出錯违诗,代碼太多也不好定位錯誤的地方漱凝,所以這里需要轉變一下思路,先來看看 t.bytesToHex(r) 是個什么東東诸迟,跟進到這個函數(shù):

11.png
bytesToHex: function(e) {
    for (var t = [], n = 0; n < e.length; n++)
        t.push((e[n] >>> 4).toString(16)),
        t.push((15 & e[n]).toString(16));
    return t.join("")
}

解讀一下這段代碼茸炒,傳進來的 e 是一個 16 位的 Array 對象愕乎,定義了一個 t 空數(shù)組,經過一個循環(huán)壁公,依次取 Array 對象里的值感论,第一次經過無符號右移運算(>>>)后,轉為十六進制的字符串紊册,將結果添加到 t 數(shù)組的末尾比肄。第二次進行位運算(&)后,同樣轉為十六進制的字符串湿硝,將結果添加到 t 數(shù)組的末尾薪前。也就是說,原本傳進來的 16 位的 Array 對象关斜,每一個值都經過了兩次操作示括,那么最后結果的 t 數(shù)組中就會有 32 個值,最后再將 t 數(shù)組轉換成字符串返回痢畜。

結合一下調用的函數(shù)名稱垛膝,我們來捋一下整個流程,首先調用 wordsToBytes() 方法將明文密碼字符串轉為 byte 數(shù)組丁稀,無論密碼的長度如何吼拥,最后得到的 byte 數(shù)組都是 16 位的,然后調用 bytesToHex() 方法线衫,循環(huán)遍歷生成的 byte 類型數(shù)組凿可,讓其生成 32 位字符串。

無論密碼長度如何授账,最終得到的密文都是 32 位的枯跑,而且都由字母和數(shù)字組成,這些特點很容易讓人想到 MD5 加密白热,將明文轉換成 byte 數(shù)組后進行隨機哈希敛助,對 byte 數(shù)組進行摘要,得到摘要 byte 數(shù)組屋确,循環(huán)遍歷 byte 數(shù)組纳击,生成固定位數(shù)的字符串,這不就是 MD5 的加密過程么攻臀?

直接把密碼拿來進行 MD5 加密焕数,和網(wǎng)站的加密結果進行對比,可以發(fā)現(xiàn)確實是一樣的刨啸,驗證了我們的猜想是正確的:

12.png

既然如此百匆,直接可以使用 Python 的 hashlib 模塊來實現(xiàn)就 OK 了,根本不需要去死扣代碼呜投,代碼樣例:

import hashlib

password = "1234567"
encrypted_password = hashlib.md5(password.encode(encoding='utf-8')).hexdigest().upper()
print(encrypted_password)
# FCEA920F7412B5DA7BE0CF42B8C93759

總結

有的時候需要我們轉變思路加匈,不一定每次都要死扣 JS 代碼存璃,相對較容易的站點的加密方式無非就是那么幾種,有的是稍微進行了改寫雕拼,有的是把密鑰纵东、偏移量等參數(shù)隱藏了,有的是把加密解密過程給你混淆了啥寇,讓你難以理解偎球,如果你對常見的加密方式和原理比較熟悉的話,有時候只需要搞清楚他用的什么加密方式辑甜,或者拿到了密鑰衰絮、偏移量等關鍵參數(shù),就完全可以自己還原整個加密過程磷醋!

完整代碼

GitHub 關注 K 哥爬蟲猫牡,持續(xù)分享爬蟲相關代碼!歡迎 star 邓线!

https://github.com/kgepachong/

以下只演示部分關鍵代碼淌友,完整代碼倉庫地址:

https://github.com/kgepachong/crawler/

Python 登錄關鍵代碼

#!/usr/bin/env python3
# -*- coding: utf-8 -*-


import json
import hashlib
import urllib.parse

import requests


index_url = '脫敏處理,完整代碼關注 GitHub:https://github.com/kgepachong/crawler'
login_url = '脫敏處理骇陈,完整代碼關注 GitHub:https://github.com/kgepachong/crawler'
headers = {
    'Host': '脫敏處理震庭,完整代碼關注 GitHub:https://github.com/kgepachong/crawler',
    'Origin': '脫敏處理,完整代碼關注 GitHub:https://github.com/kgepachong/crawler',
    'Referer': '脫敏處理你雌,完整代碼關注 GitHub:https://github.com/kgepachong/crawler',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
session = requests.session()


def get_encrypted_password(password):
    encrypted_password = hashlib.md5(password.encode(encoding='utf-8')).hexdigest().upper()
    return encrypted_password


def get_parameter():
    response = requests.get(url=index_url, headers=headers)
    location_url = response.history[1].headers['Location']
    urlparse = urllib.parse.urlparse(location_url)
    query_dict = urllib.parse.parse_qs(urlparse.query)
    # print(query_dict)
    return query_dict


def login(username, encrypted_password, query_dict):
    data = {
        'bizDeviceType': '',
        'needTheme': query_dict['needTheme'][0],
        'theme': '',
        'showActiveX': query_dict['showActiveX'][0],
        'serviceParam': query_dict['serviceParam'][0],
        'callback': query_dict['callback'][0],
        'qs': query_dict['qs'][0],
        'sid': query_dict['sid'][0],
        '_sign': query_dict['_sign'][0],
        'user': username,
        'cc': '+86',
        'hash': encrypted_password,
        '_json': True
    }
    response = session.post(url=login_url, data=data, headers=headers)
    response_json = json.loads(response.text.replace('&&&START&&&', ''))
    print(response_json)
    return response_json


def main():
    username = input('請輸入登錄賬號: ')
    password = input('請輸入登錄密碼: ')
    encrypted_password = get_encrypted_password(password)
    parameter = get_parameter()
    login(username, encrypted_password, parameter)


if __name__ == '__main__':
    main()
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末器联,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子婿崭,更是在濱河造成了極大的恐慌主籍,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件逛球,死亡現(xiàn)場離奇詭異,居然都是意外死亡苫昌,警方通過查閱死者的電腦和手機颤绕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來祟身,“玉大人奥务,你說我怎么就攤上這事⊥嗔颍” “怎么了氯葬?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長婉陷。 經常有香客問我帚称,道長官研,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任闯睹,我火速辦了婚禮戏羽,結果婚禮上,老公的妹妹穿的比我還像新娘楼吃。我一直安慰自己始花,他們只是感情好,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布孩锡。 她就那樣靜靜地躺著酷宵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪躬窜。 梳的紋絲不亂的頭發(fā)上浇垦,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天,我揣著相機與錄音斩披,去河邊找鬼溜族。 笑死,一個胖子當著我的面吹牛垦沉,可吹牛的內容都是我干的煌抒。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼厕倍,長吁一口氣:“原來是場噩夢啊……” “哼寡壮!你這毒婦竟也來了?” 一聲冷哼從身側響起讹弯,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤况既,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后组民,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體棒仍,經...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年臭胜,在試婚紗的時候發(fā)現(xiàn)自己被綠了莫其。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡耸三,死狀恐怖乱陡,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情仪壮,我是刑警寧澤憨颠,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站积锅,受9級特大地震影響爽彤,放射性物質發(fā)生泄漏养盗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一淫茵、第九天 我趴在偏房一處隱蔽的房頂上張望爪瓜。 院中可真熱鬧,春花似錦匙瘪、人聲如沸铆铆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽薄货。三九已至,卻和暖如春碍论,著一層夾襖步出監(jiān)牢的瞬間谅猾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工鳍悠, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留税娜,地道東北人。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓藏研,卻偏偏與公主長得像敬矩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蠢挡,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內容

  • 在一個方法內部定義的變量都存儲在棧中弧岳,當這個函數(shù)運行結束后,其對應的棧就會被回收业踏,此時禽炬,在其方法體中定義的變量將不...
    Y了個J閱讀 4,413評論 1 14
  • linux資料總章2.1 1.0寫的不好抱歉 但是2.0已經改了很多 但是錯誤還是無法避免 以后資料會慢慢更新 大...
    數(shù)據(jù)革命閱讀 12,135評論 2 34
  • 第一部分 Python基礎篇(80題) 1、為什么學習Python勤家? Python相對于其他編程語言有很多優(yōu)點: ...
    清清子衿木子水心閱讀 1,697評論 0 1
  • 27腹尖、移動端響應式布局開發(fā) 響應式布局開發(fā) 1、什么是響應式布局開發(fā)伐脖?把我們開發(fā)完成的產品热幔,能夠讓其適配不同的設備...
    萌妹撒閱讀 1,036評論 0 0
  • 今天青石的票圈出鏡率最高的肿男,莫過于張藝謀的新片終于定檔了介汹。 一張滿溢著水墨風的海報一次次的出現(xiàn)在票圈里却嗡,也就是老謀...
    青石電影閱讀 10,309評論 1 2