关于巨量算数jsvmp简单还原signature思路
關(guān)于巨量算數(shù)jsvmp簡單還原signature思路
也是頭一次處理jsvmp,簡單記錄一下。因為不是補環(huán)境,所以介紹的會比較細(xì)碎,不然可能漏掉了,結(jié)果又會有問題
先說好,只是介紹思路哈,我只是個摸魚的混子!
-
一些前提條件
-
關(guān)于jsvmp的大致思路(肝總給的原話,肝總yyds),概念就這樣,具體需要調(diào)試的時候才能真正理解!
- 按這個用心跟,找以下 3 個組件:
- 操作碼:塊相互構(gòu)建的基本指令
- 存儲:用于存儲狀態(tài)和指令的數(shù)組或列表
- 解釋器:一個步進器,將一個操作碼鏈接到下一個,讀取和寫入內(nèi)存
- 按這個用心跟,找以下 3 個組件:
-
推薦理解jsvmp:https://www.cnblogs.com/2014asm/p/13733623.html
-
按照常規(guī)的,可以直接搜到或者hook到目的參數(shù),對于jsvmp的話不太方便了,個人這個用的是插樁方法(其實就是打日志,看判斷條件,有問題就斷到上一個條件,接著分析)
-
按照這位大佬的方法: https://mp.weixin.qq.com/s/YAL4iT3ECQeK7vZRsLRFaA
-
-
分析步驟
-
抓包介紹
-
中間會有很多的請求,關(guān)注url包含 https://trendinsight.oceanengine.com/api/open/index/get_keyword_valid_date的請求
-
斷點一下xhr就能跟到crawler.js,熟悉的$jsvmprt初始化,構(gòu)建運行js的vm, 頭條系好像都是這個名
-
很顯然G,K函數(shù)就是入口,接下來開始正式分析
-
-
介紹一下函數(shù)
-
簡單看一下,K函數(shù)長這樣,從上往下看下來,可以推測這個函數(shù)就是在配置需要計算的各個參數(shù),也就是初始化,仔細(xì)可以看到前面對K函數(shù)的調(diào)用。
-
再簡單看一下,G函數(shù)長這個樣子,這就很長了,可以看到是在兩個條件下的循環(huán),很明顯這就是個用來計算的函數(shù)
-
再仔細(xì)看下來,可以發(fā)現(xiàn)中間大多把參數(shù)放在S數(shù)組當(dāng)中存放,再回到最開始說的前提那里,就會有點印象了。
-
-
分析一波
-
分析前提
- 最重要的是,加密操作肯定是拿我們提交的、動態(tài)的內(nèi)容做操作,比如 cookie,headers, params, data之類的,要是用其他的,就感覺有點扯了,所以分析的時候把這些綜合分析很有必要。
- 比較重要的是,像這種計算,大多都是取ascii碼,然后運算再轉(zhuǎn),這個時候關(guān)于ascii相關(guān)的方法就需要重點關(guān)注
- 結(jié)果字符串是有哪些組成的:從頭往后看一遍日志就很容易得出來了
- 既然G是參與運算的,那就在這里插樁。把S里的內(nèi)容打出來瞧瞧都啥玩意。
- 既然G里有兩個for循環(huán)那就分別看看,一堆測試之后發(fā)現(xiàn)只在一個條件里操作。
- 很顯然O和j是判斷的依據(jù),下兩個日志斷點,一個看S內(nèi)容,一個看條件的O和j(方便分析不動的時候調(diào)試到上一步,直接走流程)
- 把xhr發(fā)出之前的日志全打出來
- 中間應(yīng)該是會比較卡,因為計算的次數(shù)是真的很多,既然會卡很久,那盡量先保存一個,另外再開一個用做分析
- 大致是這一個樣子
-
分析具體流程: 如果不確定推斷,就按照條件斷點到上一步,自己走流程
-
可以看到這個_02B4Z6WO就是我們的最終結(jié)果,一直往上走,會發(fā)現(xiàn)最開始參數(shù)是在時間戳,至于是怎么確定的話,可以分析一組操作需要用的參數(shù),看他是從哪來的(全劇搜基本就行),細(xì)看發(fā)現(xiàn)前面那一點就是固定的版本號
-
繼續(xù)看接下來的步驟,可以看到時間戳/1000之后轉(zhuǎn)字符取ascii碼
-
目測一遍,發(fā)現(xiàn)基本是遍歷字符串,每個運算基本都是: (hash ^ ts[i].charCodeAt()) * 65599 >>> 0。像這種基本都是有算法名字的,搜一下叫sdbm,原理是一樣的,參數(shù)不一樣。
-
一輪操作下來,結(jié)果和頁面計算算一樣。但是如果不將這記錄下來的話,后續(xù)分析的時候,可能就會漏掉,或者找不到之類的,畢竟算力這么大的不可能依次拿參數(shù)做操作。所以建議是自己在ide跟著走代碼流程,再用別的記錄分析流程!
-
往下推的話,后續(xù)肯定很多也是這個運算,但不能保證所有都是用這個算法,畢竟大廠哪有這么小家子氣
-
發(fā)現(xiàn)是對href取的主體,一波分析發(fā)現(xiàn),還是上面那個算法,只是入?yún)ash改成了時間戳的計算結(jié)果
-
接著往下走,發(fā)現(xiàn)又有和之前算法不一樣的別的運算,建議是同樣把計算結(jié)果保存,從上往下計算,比從下往上追要方便很多,也不確定后續(xù)哪里需要它來做運算,所以先保存。
-
大致計算為:((href_result % 65521) * 65521) ^ ts_result
-
然后就是第四步結(jié)果轉(zhuǎn)成二進制,轉(zhuǎn)成之后發(fā)現(xiàn)有點對不上后面的,細(xì)看原來他在前面加了一串 8240二進制和0,前后走幾步發(fā)現(xiàn)這確實是固定值
-
第五步二進制轉(zhuǎn)成十進制
-
然后對十進制字符串進行sdbm_hash,入?yún)?
-
第七步之后發(fā)現(xiàn)有多行都沒用的,細(xì)看可以發(fā)現(xiàn)這是在校驗環(huán)境。
-
走到這里發(fā)現(xiàn)無依無靠的keyword,應(yīng)該是下面會用到,先計算下來存著
-
,發(fā)現(xiàn)計算還不太一樣,但是細(xì)推一下就能看出來: (hash * 65599 + str[i].charCodeAt()) >>> 0;
-
接著往下走還是一長串的環(huán)境校驗
-
一堆的瑪卡巴卡之后發(fā)現(xiàn)了canvas,經(jīng)過多次發(fā)現(xiàn)畫的這圖沒變,不同電腦畫的應(yīng)該會不一樣。
-
看了一會,發(fā)現(xiàn)這個數(shù)沒找到,搜一下發(fā)現(xiàn)是塊死牛肉。確定這是固定值。
-
推測發(fā)現(xiàn)跟之前算的有優(yōu)點區(qū)別,他并不是全部取,而是根據(jù)入?yún)⑦M行取模,但其實還是固定值
-
((num * 65599) + img_str[num % (img_str.length)].charCodeAt()) >>> 0
-
好了,整了這么多沒用的數(shù),終于看到我們想看的字符了。
- 反向走一波發(fā)現(xiàn),源頭是我們偉大的第六步結(jié)果。做了操作:(((num >> 2) >> 24) & 63) + 65
- 65怎么來的: 做走幾組發(fā)現(xiàn)這不是固定值,但出現(xiàn)的都是那幾個,所以推測是一組數(shù)據(jù),根據(jù)條件判斷給的值。
-
總結(jié)發(fā)現(xiàn):[0,26) + 65, [26, 52) + 71, ( 62 || 63) -17, 其他-4,取函數(shù)filter_special_char
- 24怎么來的:一組字符串走完發(fā)現(xiàn)都不一樣,但發(fā)現(xiàn)是按照[24, 18, 12, 6, 0]來偏移的。多走幾次返現(xiàn)還真是,但后面出現(xiàn)六個字符的咋辦?那后面再看,至少現(xiàn)在是沒啥問題
- 再想象一下,這么多字符串,不可能各走各的,應(yīng)該是每一組都是一個函數(shù)出來的,只是入?yún)⒉灰粯?#xff0c;所以寫的時候盡量按組寫函數(shù)
- 這個時候再討論 >> 2,發(fā)現(xiàn)一組數(shù)據(jù)完了,它只用了一次,那它就是這組字符外層的操作,不會影響這個函數(shù)。
- 計算規(guī)律: String.fromCharCode(filter_special_char(num >> the_offset[i]) & 63));
- 這樣看是不滿足六個字符,還是現(xiàn)階段沒問題,接著往下走,慢慢補!
看這里是28了,上下推一下發(fā)現(xiàn)是:(num << 28) ^ (8240) >> 4);,多看幾組,發(fā)現(xiàn)右邊是固定的,接著看字符,發(fā)現(xiàn)按照上面的進行計算,一毛一樣。果然,一組字符用一個函數(shù)應(yīng)該是沒問題的。
發(fā)現(xiàn)hash值為第11步的canvas計算值,但是卻沒有偏移
接著往下走發(fā)現(xiàn)了,ua,發(fā)現(xiàn)還是之前的sdbm_hash算法,只是hash值為第六步的值,一頓下來也沒有問題。
中途發(fā)現(xiàn)一個小操作,這也記錄下來,留著用。
然后再出現(xiàn)的就是body了,這一串中間的字符一看,這就是keywords的計算值呀,一同計算下來,發(fā)現(xiàn)也沒啥問題
再又是一個小操作,仍然記錄下來
然后就開始了第二輪字符串了。這里和之前又有不一樣,看來原函數(shù)又得改了。測試發(fā)現(xiàn),此時8240已經(jīng)不是固定值,需要新傳入值(todo_result)做判斷使用,但是也就兩個參數(shù)。以及第三組的入?yún)⒊闪藅odo_result。
然后就是第二輪字符串下來,接下里又會看到一組字符。對比發(fā)現(xiàn),在結(jié)果字符串里并沒有它,所以別算這個了。
接著往下走,發(fā)現(xiàn)沒啥看的了。就發(fā)現(xiàn)直接出現(xiàn)了一長串,仔細(xì)發(fā)現(xiàn)刷新刪除cookie就會變,很顯然是個根據(jù)cookie來的。所以這里也要自己推了哈
這里走完直接跟前面拼接上,還有版本號也拼上。中間過了老長一段。發(fā)現(xiàn)他居然拿拼接的字符串開始sdbm_hash,然后拿結(jié)果取16進制,截取6到8位做最后拼接的值。
至此就是:signature = _ + 版本號+第一輪字符串+第二輪字符串+根據(jù)cookie來的值+補的兩個字符
分析體會
-
可以看到,其實vmp的操作,和開始介紹的已經(jīng)很契合了。反正代碼就那些,反反復(fù)復(fù)的在用
-
操作碼:也就是一個條件下來,中間用到的那一塊操作,但其實可以看到,它大多都是一層一層條件后最后那一下才做真正操作
-
存儲:很明顯就是那個S,那些計算參數(shù)名字都一樣,計算完了一直在索引加加減減的往里塞。
-
解釋器:也就是需要在哪一段條件下,也就是O和j的值,需要執(zhí)行這一段計算。
個人體會:
- 大部分的內(nèi)容,都是在一個窗口是固定的,另外開一個窗口用來推理不順進行調(diào)試用。
- 都是從上往下推,中間可能會有很多的沒有用到的參數(shù),比如第三輪字符串,會造成不必要消耗
- 當(dāng)然逆向肯定是根據(jù)結(jié)果來推內(nèi)容的來源,由于我也是第一次正面處理jsvmp,希望先了解更細(xì)一點再這么做。
- 還有一點就是,從下往上推的話,肯定是一個小入口,慢慢補很多東西,在分析的時候,會讓邏輯有點亂,不知道里面參數(shù)、步驟到底還需要哪些。
總結(jié)
以上是生活随笔為你收集整理的关于巨量算数jsvmp简单还原signature思路的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 幼儿园管理系统c语言,【资源学习】c语言
- 下一篇: 简易大富翁游戏