GandCrab2.0 脫殼筆記分析記錄
有人在2018年的時候就分析過了惡意樣本分析,相關資料可以查看參考部分。由於裏邊有很多可以學習和總結的脫殼技術,因此重新分析這個樣本,這篇文章暫時不做加密和勒索實際功能分析,如果要看分析樣本形成勒索的過程以及相關分析報告,可以直接跳到參考部分,這篇文章主要關注的是脫殼相關的一些技術細節的分析,由於文章是我個人的一些分析和總結,因此會有一些不足,比較趨向於像我這樣剛接觸惡意軟件分析,由於很多樣本在分析前需要脫殼之後才能看到核心功能,因此這裏重點關注脫殼過程,主要內容如下
-
解密並dump出shellCode
-
解密並dump出PE1
-
解密並dump出encrpt.dll
解密並dump出shellCode
使用IDA打開病毒母體,來到入口函數,經過分析後程序會解密出資源文件的內容
調用VirtualProtect
函數修改shell_code
的屬性爲可讀寫,這裏的VirtualPtotect
函數是通過動態獲取的函數指針
之後調用函數sub_401014
對shell_code
解密並調用
爲了能進一步分析shell_code
,需要在動態調試階段dump
下來,因此這裏使用x32dbg
調試母體病毒,在程序調用shell_code
的時候,單步進入後dump
下來,如下是來到執行調用shell_code
的位置
此時選擇單步進入,來到shell_code
的入口處,如下
此時將內存dump
下,步驟爲在內存窗口點擊右鍵-->選擇 在內存佈局中轉到
之後跳轉到內存佈局,之後選中對應的內存位置一般使用這個方式跳轉到內存佈局文件,默認會選擇指定的內存
,然後右鍵保存爲文件,之後命名爲shell_code.bin
,如下
使用ida分析shell_code
的功能。
ShellCode功能分析
現在主要對shellcode的功能分析,這部分主要會完成如下功能
- 解密PE1文件
- 動態填充encrypt.dll文件
- 反射調用encrypt.dll
解密並dump出PE1
在執行解密PE1這個文件之前,需要弄清楚幾個問題
[ebp-0A0h]
保存的內容是什麼- 動態獲取
kernel32.dll
函數是如何保存的 - 解密的PE1文件什麼時候選擇dump
初始化數據和導入APIs
[ebp-0A0h]
保存的內容是什麼
在動態調試階段注意如下指令
mov eax, [ebp-8Ch]
mov ecx, [ebp-3Ch]
lea eax, [ecx+eax-38h]
mov [ebp-8], eax
mov eax, [ebp-8]
mov [ebp-0A0h], eax ;這裏賦值給[ebp-0xA0]
動態調試階段內內存內容如下
根據內存中的內容可以知道,這裏保存的是一個PE文件,不過是被加密了在後面會執行解密,在執行解密前,程序動態保存了kernel32.dll GetProcAddress
函數在棧上使用。如下
通過這個手段,分別將需要的函數保存在了當前的棧上,主要獲取的函數包括
mov dword ptr ss:[ebp-38],eax ;VirtualProtect
mov dword ptr ss:[ebp-48],eax ;VirutalAlloc
mov dword ptr ss:[ebp-24],eax ;VirutalProtect
mov dword ptr ss:[ebp-5c],eax ;VirutalFree
mov dword ptr ss:[ebp-44],eax ;GetVersionExA
mov dword ptr ss:[ebp-38],eax ;TerminateProcess
這些字符串在dump出的shell_code中靜態分析顯示如下
查看字符串內容
由此可以知道這個主要是爲了避免殺毒軟件所查殺,因此在解密shellc_code並運行後,動態階段解密後導入。
繼續往下來到如下指令
push dword ptr [ebp-44h] ; ptr_GetVersionExA
push 100000h
push dword ptr [ebp-0A8h] ; buff_1
call sub_4497
這裏的[ebp-0x0A8]
實際指向的是新的一個程序塊,這部分的內容還沒有被修復
dump下這個文件命名爲stage2.bin
拖入ida分析後保存,這部分的節區等信息沒有被填充,如下
這部分也是需要在後面的修復中使用的。
動態獲取kernel32.dll
函數是如何保存的
根據上面的分析shell_code
在運行階段動態導入了需要使用的API保存在棧上。
解密的PE1文件什麼時候選擇dump
在前面的準備階段完成後開始調用VirtualAlloc
分配一塊內存
將返回的內存指針保存在了[ebp-0x10]
,這裏命名內存塊爲new_pe1_buff
,然後調用sub_4170
函數執行解密
執行解密前的new_pe1_buff
的內存內容
解密後
此時將內存的數據保存解密後的內存爲PE1.exe
,這樣就能獲取解密後的PE1數據了。
下面的過程主要是分析內存修復PE文件的過程
往下繼續分析,在解密PE1文件後,接着調用VirtualProtect
函數修改了[ebp-0xA8]
內存屬性爲0x40
,對照了MSDN
的文檔後知道,修改爲可讀寫屬性,如下
#參考https://docs.microsoft.com/zh-cn/windows/win32/memory/memory-protection-constants
PAGE_EXECUTE_READWRITE
0x40
修改完屬性後就將[ebp-0xA8]
的數據清空,目的是接下來就會對這個內存重新導入新的數據塊 call sub_435D
函數實際是調用了memset
函數,如下
在清空前的[ebp-0xA8]
內存
執行後清空掉這塊內存,目的是後續重組一個PE文件
重組一個PE文件,需要的數據包括
- [ebp-0x10]
- [ebp-0x90]
開始組裝新的PE文件,調用sub_3EE函數
複製頭部信息,內容保存在[ebp-0xA8]
push dword ptr [eax+14h];指向section->PointerToRawData
push dword ptr [ebp-34h];指向了 new_pe1_dos_header
push dword ptr [ebp-90h];這裏指向的[ebp-0xA8]
call sub_43EE
這裏的[eax+0x14]
是.text節區在文件內的大小映射到[ebp-0xA8]
內。如下是動態調試時參數
之後完成後就將[ebp-0x10]的節區按照文件大小映射到內存中,執行前
執行復制函數後,
這裏就將複製完了第一部分內容,後面還要繼續複製節區數據和IAT修復。
複製section
在複製完header部分後後面,接着開始修復節區和IAT表。首先獲取節區大小
根據[ebp-0xB0]
當做節區索引,如果小於當前的節區表大小,則跳入複製節區表
讀取下一個節區
重複複製完成後,開始修復IAT表
修復IAT
設置完成導入表和導出表後,執行讀取符號
這裏將[ebp-0x90]命名爲payload.dll
修復完成所有的函數後,最後跳轉執行PE1.exe
,來到最後跳轉位置
這個過程其實不用去過分關注,因爲程序的核心程序並不在後面的節區複製和修復IAT過程,我們dump出的PE1.exe已經可以用作靜態分析和動態調試了,經過分析後發現在解密完成PE1.exe之後的操作屬於擾亂分析的一個手段實際程序會跳轉到PE1.exe內執行解密encrypt.dll,這個在後面的動態調試PE1.exe中可以得到證明,分析的好處就是對內存加載PE以及修復PE的過程有了更熟悉了一些。
解密並dump出encrpt.dll
根據前面的分析可以知道,shell_code的主要功能是解密出PE1程序然後執行,釋放的PE1並沒有被寫入到本地,這樣就可以減少被查殺的概率,直接是無文件方式執行,同樣的encrypt.dll也不會被寫入到本地中,而是解密後反射運行,如下是dump的過程。
首先程序先解密出encrypt.dll
程序
之後獲取dll內的ReflectLoader
函數
利用反射調用的方式執行encrypt.dll
,關於反射調用可以看stephenfewer開源的項目,和一般的利用注射器(進程注入dll)方式利用VirtualAlloc,WriteProcessMemory,CreateRemoteThread,LoadLibrary
等當時的注入不同,注射器方式需要寫入dll到本地然後利用LoadLibrary
函數加載dll觸發dll內的DllMain
會被執行,反射調用會在當前運行的進程加載dll然後執行。
在獲取到ReflectiveLoader
之後調用VirtualProtect
函數修改encrypt.dll
的屬性爲可執行
這裏就完成了PE1
的核心功能
- 解密encrypt.dll
- 調用encrypt.dll內的導出符號
ReflectiveLoader
函數然後執行encyrpt.dll
接下來就是核心encrypt.dll
的分析,在分析之前需要dump出這個dll文件。這一步我在分析和調試的時候走了很多彎路,例如一直調試母體病毒,分析shell_code
的解密功能,包括解密出了PE1
之後的程序,不過在多次調試和分析中,對這種內存解密和加載PE文件的方式也熟悉了不少。
dump核心的encrypt.dll
可以建立在第二階段獲取的PE1.exe
這個文件內,直接使用x32dbg調試PE1.exe
文件,在分析PE1.exe
時可以知道,程序要執行encrypt.dll
前會調用VirtualProtect
函數修改內存屬性,由此可以知道此事的encrypt.dll
是解密完成了,因此我們直接在調試的PE1.exe
的VirtualProtect
函數上設置斷點,需要注意的是這個函數會被調用兩次,因此我們只需要在第一次命中斷點後選擇dump下來即可,設置斷點的選擇在如下位置方便獲取到參數
設置好斷點後運行程序,在第一次斷下來後查看內存,如下
此時選擇dump下來保存,即可獲取到了核心的encrypt.dll
總結
首先程序會解密出shellCode
來解密出第一階段的PE1
程序,PE1
負責解密出encrypt.dll
,之後通過反射調用的方式來運行惡意行爲。從這個過程中可以看出這個過程是一個無文件方式,相比較於注射器來完成的進程注入方式,反射調用更加隱蔽。
一些常用的脫殼點可以在脫殼階段使用:
關鍵內存Api斷點
如果是進程注入的方式會選擇在VirtualProtect,WriteProcessMemeory VirtualAlloc
函數設置斷點分別獲取解密的PE文件,這種脫殼方式可以稱爲關鍵內存Api斷點;例如上面的脫殼過程,可以設置斷點在VirtualProtect,VirtualAlloc
上,程序運行後分別關注每個次斷點內存中的內存,然後每次都dump下來,然後做分析即可,可以不用去關心實際的一些解密過程。
ESP定律
如果是壓縮殼類的,可以考慮ESP定律手段,這個資料比較多,也容易上手,這個我會在分析兩個壓縮殼的樣本。