原 文: http://www.threetails.xyz/2019/05/19/初探js逆向(二)
· 正 · 文 · 來 · 啦 ·
前言
如果你是沒有任何 js 逆向經驗的爬蟲萌新,且沒看過上篇的《JavaScript逆向教程,不來了解一下么?》,建議先移步去看,因為本篇所使用的案例相對上篇,難度會更大一些。
在上一篇中,我們學習了一個入門案例的逆向流程。然而,加密的方式有很多,不可能以一概全。在進入本篇正題前,我們先回憶一下上篇的逆向思路:
…
不妨思考一下,參數雖做了加密,但網頁畢竟要正常顯示內容,所以在網頁渲染的過程中,一定有個地方對這個參數做了解密,然后將數據寫入html。
也就是說,我們需要在網頁渲染的過程里,一步步觀察,看看到底是哪個位置對這個參數做了解密。
…
其實,這個思路有個前提:加密參數必須是請求返回的結果參數。如果網站在請求發起時就對請求參數做了加密,這個思路就不管用了。
另外,在上篇中,因為案例比較簡單,在找到解密函數之后工作就完成了 90%,所以摳代碼的部分我們一筆帶過。而本篇的案例,即便找到了加密位置,可能也只完成了一半工作。
所以本篇將以七麥數據這個網站為例,介紹‘當請求參數被加密時的逆向思路’以及‘摳代碼’的正確姿勢。
網站分析
訪問 https://www.qimai.cn/rank/marketRank/market/3/category/-2/date/2019-05-18 這個地址,可以看到:
紅框中的App榜單列表即為我們的目標數據。來看看它發起了哪些請求:
在 marketRank
這個請求的響應內容里能夠找到我們的目標數據,而且是清晰的 json 結構。但不要高興得太早,我們再看一下它的請求參數:
market(3)
、category(-2)
、date
分別表示應用商店(應用寶)、類別(全部游戲)和日期,想構造它們都很簡單,為了后文描述方便,我們暫且稱之為‘簡單三參’。
需要重點關注的是這個 analysis
,它是一個被加密的必選參數,請求時必須攜帶,否則無法正常返回數據。這時候你可能會想:“那我直接把它拷貝下來,模擬請求時再帶上不就行了?”,然而,只要你稍微分析下就會發現,這個參數並不是固定的,它會隨着簡單三參的變化而改變。且這類榜單數據通常具有時效性,如果你想進行批量、持久地爬取有效數據,這種‘提前收集 analysis ’的方式是不現實的。
說了這么多,好像有些偏離主題了,我們的目的不是爬取網站,而是學習 js 逆向。
下面進入正題。
逆向思路
我們先用上篇中提到的方式,在xhr請求里打上斷點,刷新一下網頁。
代碼執行到了 h.send(f)
,等等,好像哪里不對。send 不就是把請求發送出去嗎?那是不是意味着請求參數在這步之前就已經生成完畢?觀察一下上面幾行代碼,果然如此:
1558181366544
在 t
對象的 url 屬性中可以看到,analysis 已經生成好了。那么我們再往下執行也沒意義了,因為這個請求已經被發送到了服務端,客戶端沒必要再對它的參數進行解密。況且我們的目的是研究如何生成 analysis,而不是如何解密。
那怎樣才能找到生成 analysis 的位置呢?我們可以先把它的值記錄下來,
fGR5SX10dQ0oY3lVeGIkBH1HDQ1wExcWVlYPG1sAQltVRGJRXlMkFAxXBANUAQUHBQQFcBtV
然后把斷點打在一個比較早的地方(analysis 生成之前),一步步往下執行,這個值首次出現的位置,就是它生成的位置。
想把斷點打在 analysis 生成之前,可以在 Network 選項卡下,該請求的 Initiator 列里看到它的調用棧,調用順序由上而下:
1558182881027
在前幾條里隨便選一個,點進去打上斷點,這里我看get
比較順眼,就選了這條,並在默認位置打上斷點。打上斷點后別急着刷新網頁,這里有個坑需要先避一下。因為除了marketRank
之外,marketList
這個請求也有一個 analysis 參數,它請求的是應用商店列表(百度、應用寶、360…)。為了避免干擾,我們不要刷新網頁,而是切換類別:
通過這種方式來觸發調試界面,就不會再去請求應用商店列表了。切換類別后即可觸發彈出調試界面:
1558183179819
可以發現,簡單三參都已經出現了,但還沒發現 analysis,它應該還沒生成,讓我們繼續往下執行。注意,執行過程中時刻關注是否出現類似(切換了類別,analysis肯定會變化,所以是類似)我們之前記錄下來的那個值。
此處省略一萬步調試……,只要你耐心足夠,就能找到下圖中的代碼:
1558184945255
可以看到,r
的值與我們之前記錄的值很類似,為了進一步確定,可以在調試執行完成后看下請求中的 analysis 值是否與這個 r
的值一致。
答案是一致的。也就是說,接下來只要把 r
的生成代碼全部摳下來,我們就能生成 analysis 了。感覺也沒那么難對吧?其實這個網站,摳代碼才是重頭戲。
摳代碼
開頭中提到,本案例即便找到了加密位置,也只完成了一半工作。因為加密函數中做了大量的代碼混淆和迷惑眼球的函數調用,想把它完整摳下來也不是件容易的事。如果你沒有強大的心臟和足夠的耐心,請止步於此;但如果你就喜歡折騰,請接着往下看。
鑒於本文的目的主要在於介紹‘請求參數被加密時的逆向思路’,所以不會對‘摳代碼’的部分做詳細講解,也不會提供完整代碼,但會稍微做一些提示,希望能對你有所幫助。以下提示內容只有真正去嘗試摳代碼的人才能看懂了。
-
b
寫死。 -
摳出
e
的生成函數,生成e
。
-
時間戳與寫死的-44
-
摳出
m
的生成函數,通過簡單三參+URL 后綴+e
生成m
。
-
簡單三參 sort 與 join
-
忽略迷惑代碼
ano[Ao]…
-
進入 e 內部打斷點
-
只關注被調用並執行的代碼
-
new一個Unit8Array
-
找到寫入Unit8Array的代碼
-
轉base64
-
封裝
f[La]
,便於步驟4調用
-
摳出
r
的生成函數,通過m
和b
生成r
。
-
搞定步驟3之后心態千萬不要崩,因為勝利就在眼前
-
幾個寫死的變量
-
String["fromCharCode"]
貼張逆向成功的截圖:
總結
當請求的參數被加密時,將斷點打在加密參數生成之前,在它的值首次出現的位置找到它的加密函數。
摳代碼時,如果代碼做了大量混淆。需要辨別哪些代碼是迷惑你的,哪些代碼是真正起作用的,哪些代碼是不需要摳的,哪些代碼是可以自己用其他方式替代的。
大周末的在家研究js破解,辛苦作者了。