脫殼基礎知識以及簡單應用
一 、殼概述
1 . 殼的概念
正常的軟體開發者不對開發的軟體進行保護的話,會導致開發的軟體原始碼很容易的被心懷不軌的人竊取到,而程式的殼便是抵抗第一過程的手段,就如自然界中的昆蟲殼的作用一樣,程式殼便是保護程式不被非法分子獲取甚至破壞的。
2 . 殼的工作過程
加殼過程是用加殼程式對源程式進行壓縮、加密、轉換指令等操作,然後一般是在程式的開頭加上一段殼程式。當加殼程式執行時,在程式開頭的殼程式會對加殼程式進行解壓縮、解密程式碼或者資料、解釋執行相應程式碼等過程,殼程式執行完之後一般會將加殼程式恢復成源程式,加殼程式便依舊可以執行相應的功能。
3 . 殼的幾種分類
壓縮殼:UPX 、 ASPack
壓縮殼主要是為了讓程式檔案佔用更小的體積,對於程式的加密保護方面側重性不是很強。
加密殼 : ASProtect 、Armadillo 、EXECryptor 、Themida
加密殼的功能更加側重安全性,對於加殼後程式的體積大小並不是十分關注,它能夠將程式的重要程式碼、資料等進行加密,甚至可以提供額外的功能,比如序號產生器制、使用次數、時間限制等。有的加密殼也涉及到了虛擬機器保護技術。
虛擬機器保護軟體: VMProtect
虛擬機器保護技術最近經常聽到,但是也是剛剛才弄明白是什麼意思,簡單來說也是軟體保護技術的一種,他可以將我們的程式程式碼轉換成位元組碼,然後跳轉到虛擬機器解釋執行,這樣程式碼保護程式就是相當高的了。《加密與解密》裡面的一句話形容的我覺得十分恰當而且有趣,“就好比把一篇文章從英文翻譯成中文後,發現文章裡的很多段落是用孟加拉語寫的一樣。”當然這樣的破解成本也是相當的高了。但是虛擬機器保護技術的運用會使得程式程式碼的效率降低,對此一般只會對於特定的關鍵部分程式碼進行虛擬機器保護處理,這是對安全和效率的權衡。
4 . 其他
雖然殼的保護效果很好,但是目前大多的商業軟體並不會透過殼來保護他們的軟體。首先因為加殼程式的相容性問題,某些程式加殼後可能因為相容性不能在某些平臺上使用;其次也因為目前大多數的防毒軟體會對程式軟體進行安全監測,其可以自動識別並脫去公開的商業殼,然後分析程式的安全性,但是如果是防毒軟體都能脫去的殼那麼對於軟體的保護也就是微乎其微了,如果使用防毒軟體不能識別的自己開發的個人的殼,防毒軟體出於安全考慮會自動識別軟體為病毒,因為對於程式加殼似乎並不能實現程式的推廣。
因此目前大多數廠商會設計包含複雜演算法的序列號、設定多個“暗樁”等技術來實現程式保護。
其實照我說,開源就完了,你開源我開源大家都開源,那樣就省的破解了。
二、脫殼技術
1 . 殼的載入過程
1. 儲存入口引數(pushad/popad pushfd/popfd)
2. 獲取殼本身使用的API地址
關鍵函式:
LoadLibraryA(W)、LoadLibraryExA(W) : 將制定的DLL檔案對映進記憶體,返回模組的控制程式碼
GetmoduleHandle: 獲取已經對映進記憶體的dll檔案的控制程式碼,返回值控制程式碼
GetProcAddress : 獲取dll內製定的函式地址,返回函式地址
3.解密源程式的各個區塊的資料
4. IAT的初始化
5. 重定位項的處理(DLL檔案脫殼)
6. HOOK API
7. 跳轉到OEP
2 . 脫殼三部曲
1. 尋找EOP
2. 抓取記憶體映像
3. 重建PE檔案
3 . 脫殼一般工具
4 . 尋找OEP
4.1 . 按照跨段指令尋找OEP
加殼後
加殼前
程式開頭
00413000 > 60 pushad # 儲存暫存器的值 00413001 E8 C2000000 call 004130C8 00413006 2E:3001 xor byte ptr cs:[ecx], al 00413009 0000 add byte ptr [eax], al 0041300B 0000 add byte ptr [eax], al 0041300D 0000 add byte ptr [eax], al 0041300F 0000 add byte ptr [eax], al 00413011 003E add byte ptr [esi], bh 00413013 3001 xor byte ptr [ecx], al 00413015 002E add byte ptr [esi], ch 00413017 3001 xor byte ptr [ecx], al 00413019 0000 add byte ptr [eax], al 0041301B 0000 add byte ptr [eax], al 0041301D 0000 add byte ptr [eax], al 0041301F 0000 add byte ptr [eax], al
申請記憶體
004130FE 50 push eax 004130FF FF55 2E call dword ptr [ebp+2E] 00413102 8985 B8000000 mov dword ptr [ebp+B8], eax 00413108 6A 04 push 4 0041310A 68 00100000 push 1000 0041310F FFB5 8F000000 push dword ptr [ebp+8F] 00413115 6A 00 push 0 00413117 FF95 B8000000 call dword ptr [ebp+B8] ; KERNEL32.VirtualAlloc 0041311D 50 push eax 0041311E 8985 C4000000 mov dword ptr [ebp+C4], eax 00413124 8B9D 8B000000 mov ebx, dword ptr [ebp+8B]
跳轉到殼的第二部分
0041310A 68 00100000 push 1000 0041310F FFB5 8F000000 push dword ptr [ebp+8F] 00413115 6A 00 push 0 00413117 FF95 B8000000 call dword ptr [ebp+B8] 0041311D 50 push eax 0041311E 8985 C4000000 mov dword ptr [ebp+C4], eax 00413124 8B9D 8B000000 mov ebx, dword ptr [ebp+8B] 0041312A 03DD add ebx, ebp 0041312C 50 push eax 0041312D 53 push ebx 0041312E E8 04000000 call 00413137 00413133 5A pop edx 00413134 55 push ebp 00413135 - FFE2 jmp edx
獲取oep並跳轉
00020263 C742 50 0010000>mov dword ptr [edx+50], 1000 0002026A FF85 59030000 inc dword ptr [ebp+359] 00020270 8B85 89020000 mov eax, dword ptr [ebp+289] 00020276 0385 51030000 add eax, dword ptr [ebp+351] 0002027C 0185 84020000 add dword ptr [ebp+284], eax 00020282 61 popad 00020283 68 30114000 push 401130 00020288 C3 retn
跳轉到oep開啟正常程式邏輯
00401130 /. 55 push ebp 00401131 |. 8BEC mov ebp, esp 00401133 |. 6A FF push -1 00401135 |. 68 B8504000 push 004050B8 0040113A |. 68 FC1D4000 push 00401DFC ; SE 處理程式安裝 0040113F |. 64:A1 0000000>mov eax, dword ptr fs:[0] 00401145 |. 50 push eax 00401146 |. 64:8925 00000>mov dword ptr fs:[0], esp 0040114D |. 83EC 58 sub esp, 58 00401150 |. 53 push ebx
檢視程式的整個section資訊位於.text內。
Memory map 地址 大小 屬主 區段 包含 型別 訪問 初始訪問 已對映為 00010000 00010000 Map RW RW 00020000 00001000 Priv RW RW 00030000 00001000 Priv RW RW 00040000 00016000 Map R R 00095000 0000B000 Priv RW 保護 RW 0019C000 00002000 Priv RW 保護 RW 0019E000 00002000 堆疊 於 主? Priv RW 保護 RW 001A0000 00004000 Map R R 001B0000 00002000 Priv RW RW 001F5000 0000B000 Priv RW 保護 RW 00274000 00004000 Priv RW RW 00278000 00003000 資料塊 於 ? Priv RW RW 0027B000 00003000 資料塊 於 ? Priv RW RW 0027E000 00001000 資料塊 於 ? Priv RW RW 00400000 00001000 RebPE PE 檔案頭 Imag R RWE 00401000 00004000 RebPE .text 程式碼 Imag R RWE 00405000 00001000 RebPE .rdata Imag R RWE 00406000 00003000 RebPE .data 資料 Imag R RWE 00409000 0000A000 RebPE .rsrc 資源 Imag R RWE 00413000 00007000 RebPE .pediy SFX,輸入表 Imag R RWE
4.2 用記憶體訪問斷點尋找oep
OD可以對程式設定記憶體訪問斷點,這樣當程式開始讀取或者執行相應程式碼時,程式會中斷,並且把斷點清除,利用這個辦法我們可以尋找oep。
因為普通的壓縮、加密殼等是按區段處理資料的,一般的順序是.text 、 .rdate、.data 、 .rsrc的順序處理資料,我們可以現在.rsrc處設定按F2設定記憶體訪問斷點,當程式斷下後可表明.text段已經處理完畢,那麼我們可以在接著對.text段設定記憶體訪問斷點,這樣當程式執行.text程式碼時便會中斷在oep處。
對data端設定記憶體訪問斷點
然後F9執行到中斷處
00413145 A4 movs byte ptr es:[edi], byte ptr [esi> 00413146 B3 02 mov bl, 2 00413148 E8 6D000000 call 004131BA 0041314D ^ 73 F6 jnb short 00413145 0041314F 33C9 xor ecx, ecx 00413151 E8 64000000 call 004131BA 00413156 73 1C jnb short 00413174 00413158 33C0 xor eax, eax 0041315A E8 5B000000 call 004131BA 0041315F 73 23 jnb short 00413184 00413161 B3 02 mov bl, 2 00413163 41 inc ecx
在對text端設定記憶體訪問斷點
按F9執行到中斷處,會停在oep的位置。
00401130 /. 55 push ebp 00401131 |. 8BEC mov ebp, esp 00401133 |. 6A FF push -1 00401135 |. 68 B8504000 push 004050B8 0040113A |. 68 FC1D4000 push 00401DFC ; SE 處理程式安裝 0040113F |. 64:A1 0000000>mov eax, dword ptr fs:[0] 00401145 |. 50 push eax 00401146 |. 64:8925 00000>mov dword ptr fs:[0], esp 0040114D |. 83EC 58 sub esp, 58 00401150 |. 53 push ebx 00401151 |. 56 push esi 00401152 |. 57 push edi 00401153 |. 8965 E8 mov dword ptr [ebp-18], esp 00401156 |. FF15 28504000 call dword ptr [405028] ; KERNEL32.GetVersion
4.3 根據棧平衡原理尋找OEP
方法一 :殼程式在執行前,會將程式的暫存器的值利用push指令壓入棧內,那麼我可以對壓入的棧地址設定硬體訪問斷點,當訪問這個地址時,說明殼程式正在恢復暫存器的值,也就是說即將跳轉到OEP處。
首先執行殼程式的push指令,使得暫存器現場壓入棧內。
00413000 > 60 pushad 00413001 E8 C2000000 call 004130C8 00413006 2E:3001 xor byte ptr cs:[ecx], al 00413009 0000 add byte ptr [eax], al 0041300B 0000 add byte ptr [eax], al
此時棧內的資訊如下
0019FF64 00413000 ASCII "`杪"
0019FF68 00413000 ASCII "`杪"
0019FF6C 0019FF94
0019FF70 0019FF84
0019FF74 003A9000
0019FF78 00413000 ASCII "`杪"
0019FF7C 00413000 ASCII "`杪"
對0x19ff64設定硬體訪問斷點
然後F9
0002027C 0185 84020000 add dword ptr [ebp+284], eax 00020282 61 popad 00020283 68 30114000 push 401130 00020288 C3 retn
程式在執行完popad後停下,可以看到已經來到了OEP附近。
方法二 : 我們已經知道殼程式執行前後的esp地址是不變的,而且正常程式的第一條指令通常是push ebp, 那麼我們可以在殼程式剛開始時記錄stack地址,然後對於stack-4地址處設定硬體寫入斷點,這樣程式執行到 中斷時,便會在OEP處停下。
程式剛開始執行時的stack地址如下
0019FF84 747762C4 返回到 KERNEL32.747762C4 0019FF88 00205000 0019FF8C 747762A0 KERNEL32.BaseThreadInitThunk 0019FF90 F648F892 0019FF94 /0019FFDC
我們隊0x19ff80地址設定硬體寫入斷點。
然後F9程式便會停在oep處。
00401130 /. 55 push ebp 00401131 |. 8BEC mov ebp, esp 00401133 |. 6A FF push -1 00401135 |. 68 B8504000 push 004050B8 0040113A |. 68 FC1D4000 push 00401DFC ; SE 處理程式安裝
程式直接停在了 mov ebp , esp處。
4.4 根據編譯語言特點尋找OEP
各類語言編譯的檔案入口點都有自己的特點。
比如在VC6的啟動部分有GetCommandLineA(W)、GetVersion函式等。
我們對GetVersion函式設定斷點,中斷兩次後,就可以回到OEP附近。
4.5 最後一次異常法
程式在解密或者解壓縮時,會產生許多次異常,那麼最後一次異常的後面往往不遠處就會跳轉到OEP。
5 . 抓取記憶體映像
也稱dump,指的是把指定記憶體地址的映像轉存起來。
工具 : LordPE , 將記憶體中的資料與磁碟中的檔案PE頭連結起來。
基本用法 :
圖中紅框框出來的部分要勾選,表示連結磁碟檔案中的檔案頭
然後準備dump的時候 , 選擇dump full選項。
針對一些Anti-dump技術:
糾正SizeOfImage
我們可以使用LordPE的corrct ImageSize功能,其功能室直接在PE檔案頭中的SizeOfImage來實現的。
修改記憶體屬性
某些Anti-dump技術對於檔案的PE檔案頭等做了許可權限制,比如不允許訪問之類的,這樣LordPE便不能正常dump程式,那麼我們就可以利用OD載入程式,然後切換到Alt + M視窗, 對於沒有指定許可權區段賦予指定許可權。
然後再利用LordPE正常進行dump即可。
6 . 重建輸入表
一般做法是指,跟蹤加殼程式對IAT的處理過程,修改相關指令,阻止外殼加密API,獲得未加密的IAT。
6.1 確定IAT的地址以及大小
隨便檢視一個call 函式 , 檢視[00405020] = 0x7477cfc0 , 相當於elf檔案的got表。
然後我們檢視資料欄,搜尋0x405020 , IAT的結尾處是一個DOWRD的\x00。
所以我們確定IAT的大小是0xB8。
6.2 根據IAT重建輸入表(手動)
有點繁瑣,沒有動手去試,但是原理還是要鬧清楚的嗎,建議大家去看一下相關的資料,但是要注意的是 , 最好不要把改變FirstThunk指向的IAT的地址。
6.3 用ImportREC重建輸入表
前提條件: 目標程式已經被dump,目標程式正在執行,已知OEP或者IAT的偏移量以及大小。
首先我們讓程式執行到OEP,然後dump記憶體映像,記得要修正imageSize。
然後用ImportREC開啟程式,輸入OEP使得程式自動獲取IAT。
然後選擇我們dump出來程式進行修復。
6.4 處理不連續的IAT
有些程式的IAT有可能被斷成幾份,不連續。
例子 : TestWin.exe
查殼
這一個沒殼,直接不用脫,直接dump就可以直接用,也沒有dump的必要。就是介紹一下不連續的IAT怎麼辦。
查詢IAT的起始地址與size
因為有多個不連續IAT,這裡只能找到最開始的IAT,最好的辦法就是將size改的足夠大,然後讓Im.portREC自動分析。
然後點選show invalid , 分析無效資訊 , 在無效資訊處點選右鍵cut chunk ,得到正確的IAT表資訊,然後選擇dump進行fix。
6.5 修復函式
有時會出現函式名稱識別出錯的問題,利用ImportICE可以修改。
相關文章
- 脫殼基本知識2015-11-15
- Android應用基礎知識2017-02-18Android
- 一次簡單的脫殼2024-08-30
- ZooKeeper 基礎知識、部署和應用程式2018-07-28
- CSS基礎知識簡介2020-04-06CSS
- 用例基礎知識2024-08-28
- nginx應用總結(1)-- 基礎知識和應用配置梳理2016-11-23Nginx
- 資料庫安裝以及基礎知識2019-02-26資料庫
- C#反射基礎知識和實戰應用2014-07-14C#反射
- 用JAVA轉換簡繁體的基礎知識 (轉)2008-03-31Java
- Python爬蟲筆記(一)——基礎知識簡單整理2018-07-08Python爬蟲筆記
- Java基礎知識強化(用於自我鞏固)以及審查2020-12-15Java
- ELF 格式簡述 - eBPF 基礎知識2023-03-05eBPF
- FlashSnap 1.0的簡單脫殼與演算法探析2015-11-15演算法
- ASP.NET Core基礎知識(二)【應用啟動】2019-02-17ASP.NET
- 基礎知識2018-03-29
- 【基礎知識】Flex-彈性佈局原來如此簡單!!2018-04-02Flex
- 第一講 你應該瞭解這些簡單的計算機基礎知識2018-03-05計算機
- 【Python基礎知識】Django框架簡介2020-05-16PythonDjango框架
- AMBA匯流排基礎知識簡介2017-12-27
- CRF基礎知識以及如何實現Learning,Inference2020-10-13CRF
- 逆向基礎——軟體手動脫殼技術入門2020-08-19
- 6. Oracle開發和應用——6.1. 基礎知識2020-02-14Oracle
- Python技術基礎知識點:OS模組的應用2021-06-22Python
- TCP/IP基礎知識&Oracle的TCP/IP網路應用2009-10-16TCPOracle
- 鴻蒙初學002-應用程式包基礎知識2024-10-11鴻蒙
- 你應該知道的HTTP基礎知識2017-05-08HTTP
- 響應式排版中的基礎知識2013-10-21
- AI 基礎知識2020-01-04AI
- Webpack 基礎知識2019-04-06Web
- Dart基礎知識2019-05-25Dart
- RabbitMQ基礎知識2019-05-11MQ
- webpack基礎知識2019-07-25Web
- javascript基礎知識2021-09-09JavaScript
- ThinkPHP基礎知識2021-04-07PHP
- Laravel基礎知識2021-06-09Laravel
- Redis基礎知識2021-07-08Redis
- Docker基礎知識2018-08-08Docker