在一些電腦軟體里有一段專門負責保護軟體不被非法修改或反編譯的程式。它們一般都是先於程式運行拿到控制權,然後完成它們保護軟體的任務。就像動植物的殼一般都是在身體外面一樣理所當然(但後來也出現了所謂的“殼中帶籽”的殼)。由於這段程式和自然界的殼在功能上有很多相同的地方,基於命名的規則,大家就把這樣的程式稱為“殼”了。就像電腦病毒和自然界的病毒一樣,其實都是命名上的方法罷了。從功能上抽象,軟體的殼和自然界中的殼相差無幾。無非是保護、隱蔽殼內的東西。而從技術的角度出發,殼是一段執行於原始程式前的代碼。原始程式的代碼在加殼的過程中可能被壓縮、加密……。當加殼後的檔案執行時,殼這段代碼先於原始程式運行,他把壓縮、加密後的代碼還原成原始程式代碼,然後再把執行權交還給原始代碼。 軟體的殼分為加密殼、壓縮殼、偽裝殼、多層殼等類,目的都是為了隱藏程式真正的OEP(入口點,防止被破解)。關於“殼”以及相關軟體的發展歷史請參閱吳先生的《一切從“殼”開始》。
基本介紹
- 中文名:脫殼破解
- 外文名:Shelling crack
- 解決方法:忽略所有異常,隱藏好OD
- 變化:多種組合
近日對Armadillo殼很感興趣,緣於它的多種組合的變化,但仔細看來,其保護的解決方法又有相對固定。方法無外乎那么幾種脫殼方法(當然排除有key和cc),本人在本論壇已對標準殼的脫殼方法發貼,但後來有所更新,乾脆總結在一塊吧,以方便大家。大家可以複製下來,放在手邊,脫殼時按步驟來。我還是一小鳥,同大家一樣在逐漸成長中,有不對的地方和不成熟的地方,望大家批評指正,共同進步。
一、基本知識:
該殼有如下保護:
(1)Debug-Blocker(阻止調試器)--解決方法就是忽略所有異常,隱藏好OD,如果載入時,老出錯,就多換幾個OD試試。
(2)CopyMem-II(雙進程保護)---解決方法是:用手動或者腳本使雙變單。
(3) Enable Import Table Elimination(IAT保護) –解決方法是用工具ArmaDetach再次載入加殼程式,記下子進程ID,用另一OD載入,利用斷點GetModuleHandleA,找到Magic Jump,修改Magic Jump,得到正確的IAT。
(4)Enable Strategic Code Splicing(遠地址跳) ,解決方法就是用Arminline工具。
(5) Enable Nanomites Processing(簡稱CC),就是把一些retn代碼變成CC(INT型),解決方法:用Arminline工具或Enjoy工具。
(6)Enable Memory-Patching Protections(記憶體保護)
二、脫此類殼常用的斷點:
1、WaitForDebugEvent(用於尋找非標準OEP和做補丁用)
2、WriteProcessMemory(用於尋找非標準OEP)
3、DebugActiveProcess(找子程)
4、OpenMutexA(雙進程轉單進程)
5、GetSystemTime(補丁KEY)
6、VirtualProtect(用於5.x)
7、CreateFileMappingA(用於5.x)
8、GetModuleHandleA/LoadLibraryA (用於找Magic Jump)
9、CreateThread(尋找OEP)
三、種類及脫殼方法
說明:對於此殼一般要隱藏OD。如果按以下方法下斷OD斷不下來,出錯,就多換幾個OD試試,在我脫殼中,就有個情況,下斷點後,老斷不下來,多換了幾個OD就成功了。
(一)單執行緒標準方式
具體方法:2次斷點法加修改Magic Jump。
1、找Magic Jump
方法有二:
方法一、下斷點Bp GetModuleHandleA/he GetModuleHandleA/bp GetModuleHandleA+5/he GetModuleHandleA+5,按shift+f9運行,當經過一個call緩衝有點大時,一般是在堆疊視窗出現ASCII "kernel32.dll"和ASCII "VirtualFree“後,再運行一次,出現"kernel32.dll",就是返回時機,取消斷點,按alt+f9執行到返回。
方法二、也可以下bp LoadLibraryA斷點,當在堆疊視窗出現MSVBVM60.Dll函式時,返回時機,在kernel32.LoadLibraryA下面有一個跳轉,一般情況下,這個跳轉比較大的話,就改為jmp,而跳轉比較小的話,就改nop)。[註:下此斷點的目的是找到Magic Jump,修改Magic Jump目的是避開的IAT的加密。]
2、找OEP
下斷點bp GetCurrentThreadId/bp CreateThread,shift+f9運行,中斷後,取消斷點,Alt+F9返回,單步執行,看到一個call edi之類的。F7進入,即到oep。OD不要關!打開import--選擇進程--OEP輸入va--自動搜尋IAT--獲取輸入表--顯示無效函式--CUT!
(二) 雙執行緒的標準殼
總體步驟:
1、要雙變單;
2、處理IAT,
修改Magic Jump;
3、尋找OEP;
4、修復
該殼有如下保護:
(1)Debug-Blocker(阻止調試器)--解決方法就是忽略所有異常,隱藏好OD,如果載入時,老出錯,就多換幾個OD試試。
(2)CopyMem-II(雙進程保護)---解決方法是:用手動或者腳本使雙變單。
(3) Enable Import Table Elimination(IAT保護) –解決方法是用工具ArmaDetach再次載入加殼程式,記下子進程ID,用另一OD載入,利用斷點GetModuleHandleA,找到Magic Jump,修改Magic Jump,得到正確的IAT。
(4)Enable Strategic Code Splicing(遠地址跳) ,解決方法就是用Arminline工具。
(5) Enable Nanomites Processing(簡稱CC),就是把一些retn代碼變成CC(INT型),解決方法:用Arminline工具或Enjoy工具。
(6)Enable Memory-Patching Protections(記憶體保護)
二、脫此類殼常用的斷點:
1、WaitForDebugEvent(用於尋找非標準OEP和做補丁用)
2、WriteProcessMemory(用於尋找非標準OEP)
3、DebugActiveProcess(找子程)
4、OpenMutexA(雙進程轉單進程)
5、GetSystemTime(補丁KEY)
6、VirtualProtect(用於5.x)
7、CreateFileMappingA(用於5.x)
8、GetModuleHandleA/LoadLibraryA (用於找Magic Jump)
9、CreateThread(尋找OEP)
三、種類及脫殼方法
說明:對於此殼一般要隱藏OD。如果按以下方法下斷OD斷不下來,出錯,就多換幾個OD試試,在我脫殼中,就有個情況,下斷點後,老斷不下來,多換了幾個OD就成功了。
(一)單執行緒標準方式
具體方法:2次斷點法加修改Magic Jump。
1、找Magic Jump
方法有二:
方法一、下斷點Bp GetModuleHandleA/he GetModuleHandleA/bp GetModuleHandleA+5/he GetModuleHandleA+5,按shift+f9運行,當經過一個call緩衝有點大時,一般是在堆疊視窗出現ASCII "kernel32.dll"和ASCII "VirtualFree“後,再運行一次,出現"kernel32.dll",就是返回時機,取消斷點,按alt+f9執行到返回。
方法二、也可以下bp LoadLibraryA斷點,當在堆疊視窗出現MSVBVM60.Dll函式時,返回時機,在kernel32.LoadLibraryA下面有一個跳轉,一般情況下,這個跳轉比較大的話,就改為jmp,而跳轉比較小的話,就改nop)。[註:下此斷點的目的是找到Magic Jump,修改Magic Jump目的是避開的IAT的加密。]
2、找OEP
下斷點bp GetCurrentThreadId/bp CreateThread,shift+f9運行,中斷後,取消斷點,Alt+F9返回,單步執行,看到一個call edi之類的。F7進入,即到oep。OD不要關!打開import--選擇進程--OEP輸入va--自動搜尋IAT--獲取輸入表--顯示無效函式--CUT!
(二) 雙執行緒的標準殼
總體步驟:
1、要雙變單;
2、處理IAT,
修改Magic Jump;
3、尋找OEP;
4、修復
1、雙變單,方法有三。
方法一:PATCH代碼法
下斷bp OpenMutexA,斷下後,ctrl+g到00401000,將空數據改為如下代碼:
00401000 60 pushad
00401001 9C pushfd
00401002 68 A0FD1200 push xxxx (註:此處的 xxxx為斷下後mutex name前的數值。)
00401007 33C0 xor eax,eax
00401009 50 push eax
0040100A 50 push eax
0040100B E8 E694A677 call KERNEL32.CreateMutexA
00401010 9D popfd
00401011 61 popad
00401012 - E9 8F9FA777 jmp KERNEL32.OpenMutexA
點右鍵選擇重建eip,f9運行,斷下後,取消斷點, ctrl+g到00401000,恢復修改。
方法二,下斷點:bp OpenMutexA,SHIFT+F9,斷下後, ALT+F9返回,返回後,將返回代碼下面的第一個跳轉改為相反跳轉,再次SHIFT+F9,斷下後,ALT+F9返回,再次將返回代碼下面的第一個跳轉改為相反跳轉。然後再一次SHIFT+F9,取消斷點,至此,同樣,雙進程轉單進程完畢!
方法三、除了用雙變單的腳本外,還可以用一個工具ARMADETACH,將帶殼的程式拖入,記下子進程的ID。
方法一:PATCH代碼法
下斷bp OpenMutexA,斷下後,ctrl+g到00401000,將空數據改為如下代碼:
00401000 60 pushad
00401001 9C pushfd
00401002 68 A0FD1200 push xxxx (註:此處的 xxxx為斷下後mutex name前的數值。)
00401007 33C0 xor eax,eax
00401009 50 push eax
0040100A 50 push eax
0040100B E8 E694A677 call KERNEL32.CreateMutexA
00401010 9D popfd
00401011 61 popad
00401012 - E9 8F9FA777 jmp KERNEL32.OpenMutexA
點右鍵選擇重建eip,f9運行,斷下後,取消斷點, ctrl+g到00401000,恢復修改。
方法二,下斷點:bp OpenMutexA,SHIFT+F9,斷下後, ALT+F9返回,返回後,將返回代碼下面的第一個跳轉改為相反跳轉,再次SHIFT+F9,斷下後,ALT+F9返回,再次將返回代碼下面的第一個跳轉改為相反跳轉。然後再一次SHIFT+F9,取消斷點,至此,同樣,雙進程轉單進程完畢!
方法三、除了用雙變單的腳本外,還可以用一個工具ARMADETACH,將帶殼的程式拖入,記下子進程的ID。
2、處理IAT,修改Magic Jump。
用OD附加該子進程,載入後,ALT+F9返回,修改前兩個位元組為加殼程式載入時的前兩個位元組。
下斷點bp GetModuleHandleA, Shift+F9運行,一般是在堆疊視窗出現ASCII "kernel32.dll"和ASCII "VirtualFree“後,再運行一次,出現
"kernel32.dll",就是返回時機,中斷後alt+f9返回,在KERNEL32.LoadLibraryA下面找到Magic Jump!修改為jmp。再下斷點bp GetCurrentThreadId/bp CreateThread(或往下拉找到兩個salc,在其上面的jmp上下斷,Shift+F9,斷下!如果檔案有校驗,則要撤消Magic Jump處的修改!打開記憶體鏡像,在00401000段下斷。運行,中斷後,刪除斷點,alt+f9返回),F8單步走,到第一個CALL ECX之類的東西時,F7進入。到oep。
註:(對於有些OD有一個字元串溢出漏洞,儘量用一些修正些錯誤的OD,有些程式需要處理Anti,方法如下:下斷點he OutputDebugStringA
斷下後,選中%s%之類的字元,在數據視窗跟隨,點右鍵->二進制->使用00填充,中斷2次!都如上修改,刪除此斷點!)
用OD附加該子進程,載入後,ALT+F9返回,修改前兩個位元組為加殼程式載入時的前兩個位元組。
下斷點bp GetModuleHandleA, Shift+F9運行,一般是在堆疊視窗出現ASCII "kernel32.dll"和ASCII "VirtualFree“後,再運行一次,出現
"kernel32.dll",就是返回時機,中斷後alt+f9返回,在KERNEL32.LoadLibraryA下面找到Magic Jump!修改為jmp。再下斷點bp GetCurrentThreadId/bp CreateThread(或往下拉找到兩個salc,在其上面的jmp上下斷,Shift+F9,斷下!如果檔案有校驗,則要撤消Magic Jump處的修改!打開記憶體鏡像,在00401000段下斷。運行,中斷後,刪除斷點,alt+f9返回),F8單步走,到第一個CALL ECX之類的東西時,F7進入。到oep。
註:(對於有些OD有一個字元串溢出漏洞,儘量用一些修正些錯誤的OD,有些程式需要處理Anti,方法如下:下斷點he OutputDebugStringA
斷下後,選中%s%之類的字元,在數據視窗跟隨,點右鍵->二進制->使用00填充,中斷2次!都如上修改,刪除此斷點!)
3、修復。
(三)CopyMem-ll +Debug-Blocke保護方式
1、先找OEP,兩個斷點。(1)斷點bp WaitForDebugEvent,運行,中斷後看堆疊,在一行有“pDebugEvent”字樣的那一行右鍵點擊“數據視窗跟隨”,取消斷點。(2)bp WriteProcessMemory,運行,中斷後,在數據視窗(要地址顯示)發現oep。
2、patch代碼,解碼。方法:重新載入,bp WaitForDebugEvent,運行,中斷,取消斷點,alt+f9返回,CTRL+F搜尋命令:or eax,0FFFFFFF8,找到後,先往上看,可以看到兩個CMP,一個是“cmp dword ptr ss:[XXXX],0”在這裡下硬體執行斷點,Shift+F9運行,中斷後取消斷點。這時看信息視窗:SS[XXXXX]=00000000,如果這個值不為0的話,需要將其清0,如果為0就不用了。第二個CMP,cmp ecx,dword ptr ds:[XXXX],下面開始解碼。在這裡要記下幾個地址:(1)cmp dword ptr ss:[XXXX],0前的地址和[]內的值。(2)第二個CMP中[]中的值。接著or eax,0FFFFFFF8處往下,可以找到一處為and eax,0FF的代碼,從這裡開始Patch,代碼如下:
2、patch代碼,解碼。方法:重新載入,bp WaitForDebugEvent,運行,中斷,取消斷點,alt+f9返回,CTRL+F搜尋命令:or eax,0FFFFFFF8,找到後,先往上看,可以看到兩個CMP,一個是“cmp dword ptr ss:[XXXX],0”在這裡下硬體執行斷點,Shift+F9運行,中斷後取消斷點。這時看信息視窗:SS[XXXXX]=00000000,如果這個值不為0的話,需要將其清0,如果為0就不用了。第二個CMP,cmp ecx,dword ptr ds:[XXXX],下面開始解碼。在這裡要記下幾個地址:(1)cmp dword ptr ss:[XXXX],0前的地址和[]內的值。(2)第二個CMP中[]中的值。接著or eax,0FFFFFFF8處往下,可以找到一處為and eax,0FF的代碼,從這裡開始Patch,代碼如下:
inc dword ptr ds:[] //第一個CMP內的值
mov dword ptr ds:[XXXX+4],1 //XXXX為第二個CMP[]內的值
jmp XXXX //第一個CMP前的地址
mov dword ptr ds:[XXXX+4],1 //XXXX為第二個CMP[]內的值
jmp XXXX //第一個CMP前的地址
修改好後,去掉所有斷點,向下找到第一個CMP下面的跳轉所跳到的地址,來到這個地址,下硬體執行斷點,Shift+F9運行,此時代碼解壓完畢
,可以脫殼。
3、脫殼。運行LordPE,將子進程dump出來,這裡的子進程就是LordPE第2個進程(有2個同名進程)。Dump後用LordPE修改入口點為在第一步中查到的OEP。
4、修復輸入表、IAT的尋找
脫殼後不要急著去修復輸入表,得先把RVA數據獲取,用OD載入Dump出來的程式,右鍵搜尋二進制字元串,輸入FF25,找到一個函式,在數據視窗跟隨,在數據視窗向上找到全是0的地方,記下地址,再向下找到全是0的地方,記下地址。
脫殼後不要急著去修復輸入表,得先把RVA數據獲取,用OD載入Dump出來的程式,右鍵搜尋二進制字元串,輸入FF25,找到一個函式,在數據視窗跟隨,在數據視窗向上找到全是0的地方,記下地址,再向下找到全是0的地方,記下地址。
5、載入子程式。
(1)找子程式pid的方法有二:
方法一:用OD載入原程式(脫殼前的程式),下斷點:bp DebugActiveProcess,中斷後看堆疊,記下Processid後面的值(這個值不是每次都
相同的)。OD不要關。
第二種方法就是用工具ArmaDetach,將加殼程式拖入。記下子程式的pid和前兩個位元組。
(1)找子程式pid的方法有二:
方法一:用OD載入原程式(脫殼前的程式),下斷點:bp DebugActiveProcess,中斷後看堆疊,記下Processid後面的值(這個值不是每次都
相同的)。OD不要關。
第二種方法就是用工具ArmaDetach,將加殼程式拖入。記下子程式的pid和前兩個位元組。
(2)另開一個OD,附加Processid後面的值進程或用工具ArmaDetach所記下的進程id,附加後,ALT+F9返回程式,將前兩個位元組改為原程式載入時的前兩個位元組。
6、下面的做法就和標準殼的一樣了,在附加的OD中:
雙變單:方法有二。其一:patch法。其二修改跳轉法。
雙變單:方法有二。其一:patch法。其二修改跳轉法。
方法一:下斷點BP OpenMutexA(雙變單),F9運行,斷下後,ctrl+g到00401000,將空數據改為如下代碼:
00401000 60 pushad
00401001 9C pushfd
00401002 68 A0FD1200 push xxxx (註:此處的 xxxx為斷下後name前的數值。)
00401007 33C0 xor eax,eax
00401009 50 push eax
0040100A 50 push eax
0040100B E8 E694A677 call KERNEL32.CreateMutexA
00401010 9D popfd
00401011 61 popad
00401012 - E9 8F9FA777 jmp KERNEL32.OpenMutexA
點右鍵選擇重建eip,f9運行,斷下後,取消斷點, ctrl+g到00401000,恢復修改。
00401000 60 pushad
00401001 9C pushfd
00401002 68 A0FD1200 push xxxx (註:此處的 xxxx為斷下後name前的數值。)
00401007 33C0 xor eax,eax
00401009 50 push eax
0040100A 50 push eax
0040100B E8 E694A677 call KERNEL32.CreateMutexA
00401010 9D popfd
00401011 61 popad
00401012 - E9 8F9FA777 jmp KERNEL32.OpenMutexA
點右鍵選擇重建eip,f9運行,斷下後,取消斷點, ctrl+g到00401000,恢復修改。
方法二:下斷點BP OpenMutexA,SHIFT+F9運行,斷下後,ALT+F9返回,返回後,將返回代碼下面的第一個跳轉改為相反跳轉,再次SHIFT+F9
,斷下後,ALT+F9返回,再次將返回代碼下面的第一個跳轉改為相反跳轉。然後再一次SHIFT+F9,取消斷點,至此,同樣,雙進程轉單進程完畢!
此法相對簡單,另外適用於00401000空數據不能修改的程式。
,斷下後,ALT+F9返回,再次將返回代碼下面的第一個跳轉改為相反跳轉。然後再一次SHIFT+F9,取消斷點,至此,同樣,雙進程轉單進程完畢!
此法相對簡單,另外適用於00401000空數據不能修改的程式。
(2)修改Magic Jump 。
下斷BP GetModuleHandleA+5,運行,一般是在堆疊視窗出現ASCII "kernel32.dll"和ASCII "VirtualFree後,再運行一次,就是返回時機,中
斷後alt+f9返回,在KERNEL32.LoadLibraryA下面找到Magic Jump!修改為jmp。清除所有斷點,再直接按F9運行,出現暫停。這時大功告成。
下斷BP GetModuleHandleA+5,運行,一般是在堆疊視窗出現ASCII "kernel32.dll"和ASCII "VirtualFree後,再運行一次,就是返回時機,中
斷後alt+f9返回,在KERNEL32.LoadLibraryA下面找到Magic Jump!修改為jmp。清除所有斷點,再直接按F9運行,出現暫停。這時大功告成。
(3)用Imprec1.6f選擇進程附加的那個進程,填入OEP地址,填第4步所記下的RAV,SIZE=1000,不要按自動搜尋IAT,直接按獲取輸入表,再按顯示無效地址,剪掉修復抓取檔案就OK了。
(四)全保護脫殼脫殼方法
1、雙變單可以用以下方法,也可以用腳本,目的是雙變單。記下程式載入時的前兩個位元組。運行腳本後,記下OEP的前兩個位元組,OEP地址和子進程ID。
2、用OD附加子進程,載入後,ALT+F9返回,修改前兩個位元組為腳本記載的OEP的前兩個位元組。
3、處理IATL輸入表
2、用OD附加子進程,載入後,ALT+F9返回,修改前兩個位元組為腳本記載的OEP的前兩個位元組。
3、處理IATL輸入表
方法:
(1)用工具ARMADETACH,將帶殼的程式拖入,記下子進程的ID。
(2)另開一OD,附加該子進程,載入後,ALT+F9返回,修改前兩個位元組為加殼程式載入時的前兩個位元組。
(3)修改Magic Jump ,CTRL+G,輸入GetModuleHandleA,在其下面的第一個跳轉下硬體執行斷點,Shift+F9運行,一般是在堆疊視窗出現ASCII "kernel32.dll"和ASCII "VirtualFree後,再運行一次,就是返回時機,中斷後alt+f9返回,在KERNEL32.LoadLibraryA下面找到Magic Jump!修改為jmp。往下拉找到兩個salc,在其上面的jmp上下斷,Shift+F9,斷下!撤消Magic Jump處的修改!CTRL+G,輸入GetCurrentThreadId/ CreateThread,下斷,運行,中斷後,刪除斷點,alt+f9返回,F8單步走,到第一個CALL ECX之類的東西時,F7進入。(此時的OEP是偽OEP,但此時的IAT是正確的。先開的OEP是對的,但IAT是錯誤的)。
(4)打開先前的OD,CTRL+B,搜尋FF25,在數據視窗跟隨,找到函式的起始位置,選定1-3個,二進制複製,打開後面所開的OD,打開記憶體鏡像,在此界面中點一下,二進制搜尋,將剛才複製的貼上,找到後,在轉存視窗,找到函式的起始地址,選定到末尾,將選定的函式複製到先前開的OD的數據視窗,要對整齊(起點要一樣),這時,後開的OD的任務完成(目的就是將未加密的IAT找到)。
(1)用工具ARMADETACH,將帶殼的程式拖入,記下子進程的ID。
(2)另開一OD,附加該子進程,載入後,ALT+F9返回,修改前兩個位元組為加殼程式載入時的前兩個位元組。
(3)修改Magic Jump ,CTRL+G,輸入GetModuleHandleA,在其下面的第一個跳轉下硬體執行斷點,Shift+F9運行,一般是在堆疊視窗出現ASCII "kernel32.dll"和ASCII "VirtualFree後,再運行一次,就是返回時機,中斷後alt+f9返回,在KERNEL32.LoadLibraryA下面找到Magic Jump!修改為jmp。往下拉找到兩個salc,在其上面的jmp上下斷,Shift+F9,斷下!撤消Magic Jump處的修改!CTRL+G,輸入GetCurrentThreadId/ CreateThread,下斷,運行,中斷後,刪除斷點,alt+f9返回,F8單步走,到第一個CALL ECX之類的東西時,F7進入。(此時的OEP是偽OEP,但此時的IAT是正確的。先開的OEP是對的,但IAT是錯誤的)。
(4)打開先前的OD,CTRL+B,搜尋FF25,在數據視窗跟隨,找到函式的起始位置,選定1-3個,二進制複製,打開後面所開的OD,打開記憶體鏡像,在此界面中點一下,二進制搜尋,將剛才複製的貼上,找到後,在轉存視窗,找到函式的起始地址,選定到末尾,將選定的函式複製到先前開的OD的數據視窗,要對整齊(起點要一樣),這時,後開的OD的任務完成(目的就是將未加密的IAT找到)。
4、用AMINLINE工具修復,選擇第一個OD所用的子進程,在Code Splicing中開始Code的地址自動給填好了,但長度需要設定一下,大一些,20000左右就可以了,修復。在IAT的長度要根據實際情況,一般選1000即可,依次進行修復,修復後,在OD中修復的地方都變成紅色。
5、用Imprec修復,無效指針CUT。
6、如果有類似自校驗的,則用AMINLINE工具中的nanomites選項修復。
5、用Imprec修復,無效指針CUT。
6、如果有類似自校驗的,則用AMINLINE工具中的nanomites選項修復。