目標站點:aHR0cDovL2VpcC5jaGFuZmluZS5jb20vbG9naW4uanNw
說明: 該站點較簡單,可以作爲小白練手使用,方便理解 js 逆向常規步驟。
1. 定位代碼
- 打開調試面板,輸入手機號、密碼,點擊登錄,檢查 Network,分析後發現箭頭指向的請求,分析發現參數
j_password
已經被加密;
- 全局搜索
j_password
,下斷點,重新輸入賬號密碼,網頁在下圖位置被斷住,分析發現desEncrypt
函數返回的結果就是被解密後的數據;
2. 分析調試
- 回溯,進入
function desEncrypt(value, xForm, type)
中,分析發現_0(xForm)
結果爲false
,在else
代碼keyObj = SECURITYKEY.get();
處下斷,如下圖;
- 回溯分析
SECURITYKEY.get()
函數,在代碼str = SECURITYKEY._2();
處下斷,回溯分析SECURITYKEY._2()
函數,發現該函數是通過 ajax 發送請求得到返回結果的,請求的 url 中包含js/session.jsp?_=
,並且還跟了一個時間戳後綴,通過搜索過濾請求,找到了目標 url:http://eip.chanfine.com/resource/js/session.jsp?_=1637761979345&s_ajax=true
,該 url 尾部也跟了一個時間戳;
- 訪問上面拿到的 url,得到如下返回值,正是我們想要的。
function getSessionId(){
return "3B9B93AF595C0F909356C09468D9F041";
}
- 回退,繼續單步調試,發現
encodeType == null || encodeType == 'aes'
結果爲true
,此處 else 條件分支可以忽略。單步調試後發現,前述的返回值被分割成多個部分並賦值給了一個對象 key,最後返回了一個對象;
- 再次回退,分析
CryptoJS.AES.encrypt(value, CryptoJS.enc.Utf8.parse(keyObj.key)
,
分析發現:
-- keyObj.key
就是前面得到的 key
對象的 key
屬性值,
-- CryptoJS.enc.Utf8.parse()
函數對應的函數在122行:
parse: function(a) {
return b.parse(unescape(encodeURIComponent(a)))
}
-- 分析上面的代碼,encodeURIComponent
是內生的自帶函數,unescape()
也是內生的自帶函數,而且兩個函數都爲對傳入的參數 a
做任何修改。需要繼續回溯分析的 b.parse()
函數,對應函數代碼爲:
parse: function(a) {
for (var c = a.length, e = [], j = 0; j < c; j++)
e[j >>> 2] |= (a.charCodeAt(j) & 255) << 24 - 8 * (j % 4);
return new r.init(e,c)
}
-- 分析上面的代碼,發現其執行過程包括一個 for 循環,單步調試發現並不需要進一步回溯,需要回溯的是return 的 new r.init(e,c)
,繼續回溯得到如下代碼:
init: function(a, c) {
a = this.words = a || [];
this.sigBytes = c != p ? c : 4 * a.length
},
- 到這一步,其實已經沒有必要繼續在扣代碼了,結合前面的上下文,可以知道整個js代碼其實可以全部扣下來,直接進行調用的,因爲
value = CryptoJS.AES.encrypt(value, CryptoJS.enc.Utf8.parse(keyObj.key), {iv:CryptoJS.enc.Utf8.parse(keyObj.iv)}).toString()
這段加密代碼,全部是對CryptoJS
的調用。
-
打開 console,輸出前面分析的結果;
如上圖,得到加密後的內容,至此調試分析結束。
3. js代碼實現
- 通過上面的分析,知道核心加密步驟分爲兩步:
第一步,是通過
SECURITYKEY.get()
生成一個對象,在此過程中還需要 ajax 發送一個帶時間戳的請求;
第二步,是通過CryptoJS
中的AES
和enc
進行加密處理;
- js 代碼:
略。