C++寫殼之高級篇

以前在寫了寫殼基礎篇,如今就來完成寫殼高級篇。沒有基礎篇的知識,那理解高級篇就比較困難。有了寫殼基礎後,才能在其基礎上逐步實現高級功能,加殼的目的主要是防止別人破0解,而想要別人很難破0解,我認爲要在花指令、混淆和指令虛擬化上大量的時間及腦力才能作到,這個比較費腦力費時間。我在此就說說一些能快速入門的反調試技術,下面說的難度將逐漸提高。ios

主要工具: VS201七、x64dbg、OD算法

實驗平臺:win10 64位
實現功能:反調試、IAT加密、Hash加密、動態解密。windows

1、反調試

顧名思義,就是阻止別人調試程序,在PEB結構中有一個BegingDebugged標誌位專門用於檢測是否處於調試狀態,爲1則處於調試狀態,用VS2017測試下列程序:數組

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include "pch.h"
#include <iostream>
#include <windows.h>
 
/ / 反調試 1
bool PEB_BegingDebugged()
{
     bool BegingDebugged = false;
     __asm
     {
         mov eax, fs:[ 0x30 ];               / / 獲取PEB
         mov al, byte ptr ds : [eax + 0x2 ]; / / 獲取Peb.BegingDebugged
         mov BegingDebugged, al;
     }   
     return BegingDebugged;                / / 若是爲 1 則說明正在被調試
}
 
int main()
{
     if (PEB_BegingDebugged())
     {
         MessageBoxA( 0 , "正在被調試" , 0 , 0 );
         return 0 ;
     }
     std::cout << "Hello World!\n" ;
     getchar();
}

敲完代碼後按Ctrl+F5直接運行能夠輸出Hello World!,按F5以調試方式運行程序則會彈出反調試窗口,說明這種方法檢測是否正在被調試成功!
安全

 

可是壞消息是用某些論壇的OD運行這程序依然能正常運行,檢測不到反調試,如今的各個平臺的OD基本都有一個插件是StrongOD,他能幹掉PEB中全部檢測反調試的標誌位!因此以上方法基本被淘汰了。函數

 

因此這裏介紹一個能夠在OD中也能反調試的方法,有一個能夠同時在0環和3環運行的函數NtQueryInformationProcess,它的主要做用是查看進程相關的各類信息,在這把它用於檢測調試。
咱們給它第二個參數傳入ProcessDebugPort,當輸出的查詢信息爲0xFFFFFFFF時,則此程序處於被調試狀態。代碼以下:工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include "pch.h"
#include <iostream>
#include <windows.h>
#include <winternl.h>
#pragma comment(lib,"ntdll.lib")
 
/ / 反調試 2
bool NQIP_ProcessDebugPort()
{
     int nDebugPort = 0 ;
     NtQueryInformationProcess(
         GetCurrentProcess(), / / 目標進程句柄
         ProcessDebugPort,   / / 查詢信息的類型
         &nDebugPort,        / / 輸出查詢的信息
         sizeof(nDebugPort), / / 查詢類型的大小
         NULL);             
     return nDebugPort = = 0xFFFFFFFF ? true : false;
}
 
int main()
{
     if (NQIP_ProcessDebugPort())
     {
         MessageBoxA( 0 , "正在被調試" , 0 , 0 );
         return 0 ;
     }
     std::cout << "Hello World!\n" ;
     getchar();
}

編譯生成程序後使用OD打開再運行,彈出窗口,反調試成功,我在這測試了15PB的OD和吾愛破n解的OD均能有效。掌握了反調試的方法,咱們能夠把它放在殼代碼的各個角落,檢測到調試就立刻退出程序,多放置幾個陰人位置,這樣就能增長破n解的難度了!
oop

2、IAT加密

要對IAT加密前提條件是對PE文件比較熟悉。
IAT也就是導入函數的地址表,程序在加載到內存後IAT中填充的都是函數的地址,使用OD打開隨意exe文件,我這就打開QQ.exe,找到第一個HEX數據是FF15開頭的CALL代碼,右鍵查看內存地址。

從圖中知:若是IAT沒加密反彙編代碼一目瞭然就能看見用的的什麼API,加密的目的就是即便調用函數也不能一眼就看出調用的是什麼函數(有字符串提示)。
內存窗口的地址是IAT每一個元素的地址,數值一列是IAT存儲的數據,註釋中也解釋了它們是什麼API的地址。
IAT加密原理就是:測試

  1. 遍歷導入表獲取每一個函數的IAT地址(對應上圖內存欄中地址的值)
  2. 取出IAT地址的內容,就是函數的地址(上圖內存欄中數值的值),把該函數地址進行加密後獲得一個數據(我這就是異或了一個值0x13973575進行了加密)
  3. 申請一段內存,其中存放解密上述的數據獲得真地址,而後調用該地址的代碼。
  4. 把申請的內存地址放入IAT地址對應的數值中。
    完成以上步驟後IAT就被加密了,固然第3步當中能夠進行適當的混淆和加花指令別人就更加看不出來了。

加密後再查看反彙編代碼就沒有字符串提示了,再查看該地址內存數據也沒有字符串註釋了,效果圖以下:
優化

 

在彙編窗口Ctrl+G輸入04A90000查看,其中的代碼有花指令以下:

 

去掉花指令後其彙編代碼最終成功調用真正函數的地址了,就是:

 

具體操做就是在殼代碼解壓縮,解密後,再進行IAT修復加密,遍歷IAT代碼我就不解釋了。
IAT加密源碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/ / 修復並加密IAT
void MendIAT()
{
     HMODULE hBase = (HMODULE)g_hModule;
     auto pImport = (PIMAGE_IMPORT_DESCRIPTOR)(g_Sc.dwImportRVA + (DWORD)hBase);
 
     / / 外層遍歷模塊
     while (pImport - >Name)
     {
         / / 獲取當前模塊地址
         HMODULE hModule = MyLoadLibraryExA((char * )(pImport - >Name + (DWORD)hBase), 0 , 0 );
         if (pImport - >FirstThunk)
         {
             / / IAT的地址
             PDWORD IAT = PDWORD(pImport - >FirstThunk + (DWORD)hBase);
             DWORD ThunkRva = 0 ;
             if (pImport - >OriginalFirstThunk = = 0 )
                 ThunkRva = pImport - >FirstThunk;
             else
                 ThunkRva = pImport - >OriginalFirstThunk;
             PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)(ThunkRva + (DWORD)hBase);
 
             / / 函數的名字
             char * dwFunName = 0 ;
             / / 內層遍歷模塊中的函數
             while (pThunk - >u1.Ordinal)
             {
                 / / 序號導入
                 if (pThunk - >u1.Ordinal & 0x80000000 )
                 {
                     dwFunName = (char * )(pThunk - >u1.Ordinal & 0x7fffffff );
                 }
                 / / 名稱導入
                 else
                 {
                     PIMAGE_IMPORT_BY_NAME pImportByName = (PIMAGE_IMPORT_BY_NAME)
                         (pThunk - >u1.Ordinal + (DWORD)hBase);
                     dwFunName = pImportByName - >Name;
                 }
                 / / 獲取每一個函數的地址
                 DWORD dwFunAddr = (DWORD)SysGetProcAddress(hModule, dwFunName);
                 / / * * 加密函數地址 * *
                 dwFunAddr ^ = 0x13973575 ;
                 LPVOID AllocMem = (PDWORD)MyVirtualAlloc(NULL, 0x20 , MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
 
                 / / 構造一段花指令解密的ShellCode
                 byte OpCode[] = { 0xe8 , 0x01 , 0x00 , 0x00 ,
                                   0x00 , 0xe9 , 0x58 , 0xeb ,
                                   0x01 , 0xe8 , 0xb8 , 0x8d ,
                                   0xe4 , 0xd8 , 0x62 , 0xeb ,
                                   0x01 , 0x15 , 0x35 , 0x75 ,
                                   0x35 , 0x97 , 0x13 , 0xeb ,
                                   0x01 , 0xff , 0x50 , 0xeb ,
                                   0x02 , 0xff , 0x15 , 0xc3 };
                 / / 把dwFunAddr寫入到解密的ShellCode中
                 OpCode[ 11 ] = dwFunAddr;
                 OpCode[ 12 ] = dwFunAddr >> 0x8 ;
                 OpCode[ 13 ] = dwFunAddr >> 0x10 ;
                 OpCode[ 14 ] = dwFunAddr >> 0x18 ;
 
                 / / 拷貝數據到申請的內存
                 MyRtlMoveMemory(AllocMem, OpCode, 0x20 );
 
                 / / 修改保護屬性
                 DWORD dwProtect = 0 ;
                 MyVirtualProtect(IAT, 4 , PAGE_EXECUTE_READWRITE, &dwProtect);
                 / / 把獲取到的加密函數地址填充在導入地址表裏面
                 * (IAT) = (DWORD)AllocMem;
                 MyVirtualProtect(IAT, 4 , dwProtect, &dwProtect);
 
                 + + IAT;
                 + + pThunk;
             }
         }
         + + pImport;
     }
}

3、Hash加密

爲何要進行Hash加密?由於在逆向工做者逆向的過程當中,字符串信息對他們來講很重要,若是看見了一個API函數的字符串,那麼他大概就能知道這段代碼大概的功能了,垂手可得就能破0解掉,爲了阻止這種事件發生,那麼Hash加密在這就能發揮出很大做用。

 

衆所周知1個字節是8位,這表明他表示2的8次方個數,也就是256種可能,若是咱們把它的一個數據表明一個系統中的函數(API),至關於給函數一個序號,那麼1個字節就能存儲256個函數的信息,那2個字節就能存儲2的16次方也就是65536個API函數,這真是大大的好消息, windows系統中的API函數也就幾千個,2個字節存儲其所有API函數信息真是綽綽有餘。

 

而讓這2個字節的數據表明一個函數,這個數據咱們稱它爲Hash值,所以須要設計一個算法。我在這設計是方法是定義一個2字節類型(short)的數據,分別把nHash值先左移11位再右移5位後相加,再加上API函數中一個字符的Ascii碼,以此循環遍歷完整個API函數的全部字符,獲得一個咱們須要的Hash值。在以前寫殼基礎篇中提到過殼代碼中的API是動態獲取的,那麼咱們在動態獲取的時候使用Hash值更能提升隱蔽性,使破0解者不易發現咱們所要使用的是哪一個函數。
具體Hash加密代碼以下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include "pch.h"
#include <iostream>
 
int main()
{
     while (true)
     {
         / / 用於保存 Hash
         unsigned short nHash = 0 ;
         char arr[ 50 ] = {}, * p;
         p = arr;
         printf( "請輸入API: " );
         scanf_s( "%s" , arr, 50 );
         while ( * p)
         {
             / / 先左移 11 位再右移 5 位相加後再加上該字符的Ascii
             nHash = ((nHash << 11 ) | (nHash >> 5 ));
             nHash = nHash + * p;
             p + + ;
         }
         printf( "Hash值爲:0x%X\n" , nHash);
     }
     return 0 ;
}

使用方法是首先使用上述代碼對咱們須要使用API函數進行Hash加密獲得Hash值,而後再寫一個Hash值對比字符串的函數(解密),使用該值和系統中的API函數對比,和誰相等,咱們就把這個函數的地址獲取取出。這樣咱們就隱晦的獲得了所需的函數的地址。
Hash解密代碼以下,須要傳入2個參數,1是對比函數的地址,2是Hash值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/ * * * * * * * * * * * * * * * * Hash 對比 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
     _Hash_CmpString: / / (char * strFunName, int nDigest)
         push ebp;
         mov ebp, esp;
         sub esp, 0x4 ;
         push ebx;
         push ecx;
         push edx;
         mov dword ptr[ebp - 0x4 ], 0 ;
         xor eax, eax;
         xor ecx, ecx;
         mov esi, [ebp + 0x8 ]; / / strFunName
     _Start:
         mov al, [esi + ecx];
         test al, al;
         jz _End;
         mov edi, [ebp - 0x4 ];
         shl edi, 0xb ; / / 11 16 進製爲b
         mov edx, [ebp - 0x4 ];
         shr edx, 0x5 ;
         or edi, edx;
         add edi, eax;
         mov[ebp - 0x4 ], edi;
         inc ecx;
         jmp _Start;
     _End:
         mov esi, [ebp + 0xc ]; / / 獲取 hash
         and edi, 0xffff ; / / 取低 16
         cmp edi, esi; / / 對比 hash
         mov eax, 0x1 ;
         je _Over;
         xor eax, eax;
     _Over:
         pop edx;
         pop ecx;
         pop ebx;
         mov esp, ebp;
         pop ebp;
         ret 0x8 ;

上述代碼有大大的優化空間,比較懶我就不弄了。

 

有了Hash加解密,就能夠本身實現一個GetProcAddress函數了,在這以後須要獲取任何API函數就用本身實現的GetProcAddress函數,這樣就是達到更加隱蔽的獲取API函數的目的,學會了Hash加解密咱也就脫離了小白的行列了。
代碼以下,參數1是所需API的模塊基址,參數2是Hash值:(純彙編獲取更能鍛鍊基本功!)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/ / 自寫的GetProcAddress
DWORD MyGetProcAddress(HMODULE hModule, int nDigest)
{
     DWORD GetProcAddr = 0 ;
     __asm
     {
         jmp _Start_Fun;
         / * * * * * * * * * * * * * * * * * 自寫的GetProcAddress * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
     _Fun_GetProcAddress: / / (dword ImageBase, int nDiegst)
         push ebp;
         mov ebp, esp;
         sub esp, 0xc ;
         push edx; / / 保存寄存器
         push ebx;
         mov edx, [ebp + 0x8 ];  / / DLL基地址如kernel32
         mov esi, [edx + 0x3c ]; / / Dos頭的e_lfanew
         lea esi, [edx + esi];  / / PE頭VA(NT頭)
         mov esi, [esi + 0x78 ]; / / Import表的RVA
         lea esi, [edx + esi];  / / Import表的VA
         mov edi, [esi + 0x1c ]; / / EAT的RVA .AddressOfFunctions
         lea edi, [edx + edi];  / / EAT的VA
         mov[ebp - 0x4 ], edi;   / / EAT的VA保存到局部變量 1
         mov edi, [esi + 0x20 ]; / / ENT的RVA  AddressOfNames
         lea edi, [edx + edi];  / / ENT的VA
         mov[ebp - 0x8 ], edi;   / / ENT的VA - >Local2
         mov edi, [esi + 0x24 ]; / / EOT的RVA
         lea edi, [edx + edi];  / / EOT的VA
         mov[ebp - 0xc ], edi;   / / EOT的VA - >Local3
 
         xor ecx, ecx;
         jmp _First;
     _Begin:
         inc ecx;
     _First:
         mov esi, [ebp - 0x8 ]; / / ENT
         mov esi, [esi + ecx * 4 ]; / / EN RVA
         lea esi, [edx + esi]; / / EN VA
         push[ebp + 0xc ];
         push esi;
         call _Hash_CmpString;
         test eax, eax;
         jz _Begin; / / 不是則循環
 
         mov esi, [ebp - 0xc ]; / / EOT
         xor ebx, ebx;
         mov bx, [esi + ecx * 2 ]; / / 函數所對應的序號
 
         mov esi, [ebp - 0x4 ]; / / EAT
         mov esi, [esi + ebx * 4 ]; / / EA RVA
         lea eax, [edx + esi]; / / 函數的地址
         pop ebx;
         pop edx;
         mov esp, ebp;
         pop ebp;
         retn 0x8 ;
 
         / * * * * * * * * * * * * * * * * Hash 對比 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
     _Hash_CmpString: / / (char * strFunName, int nDigest)
         push ebp;
         mov ebp, esp;
         sub esp, 0x4 ;
         push ebx;
         push ecx;
         push edx;
         mov dword ptr[ebp - 0x4 ], 0 ;
         xor eax, eax;
         xor ecx, ecx;
         mov esi, [ebp + 0x8 ]; / / strFunName
     _Start:
         mov al, [esi + ecx];
         test al, al;
         jz _End;
         mov edi, [ebp - 0x4 ];
         shl edi, 0xb ;
         mov edx, [ebp - 0x4 ];
         shr edx, 0x5 ;
         or edi, edx;
         add edi, eax;
         mov[ebp - 0x4 ], edi;
         inc ecx;
         jmp _Start;
     _End:
         mov esi, [ebp + 0xc ]; / / 獲取 hash
         and edi, 0xffff ;
         cmp edi, esi; / / 對比 hash
         mov eax, 0x1 ;
         je _Over;
         xor eax, eax;
     _Over:
         pop edx;
         pop ecx;
         pop ebx;
         mov esp, ebp;
         pop ebp;
         ret 0x8 ;
 
     _Start_Fun:
         pushad;
         push nDigest;
         push hModule;
         call _Fun_GetProcAddress;
         mov GetProcAddr, eax;
         popad;
     }
     return GetProcAddr;
}

動態獲取函數例子(下面的Hash值是亂填的,意思意思下):

1
2
3
MyLoadLibraryExA = (FuLoadLibraryExA)MyGetProcAddress(g_hKernel32, 0xC0D8 );
g_hUser32 = MyLoadLibraryExA( "user32.dll" , 0 , 0 );
MyMessageBoxW = (FuMessageBoxW)MyGetProcAddress(g_hUser32, 0x1E38 );

4、動態解密

加入動態解密的殼,這無疑是強度較高的殼了,它可以在目標程序運行起來以後,動態的對代碼段進行解密。先運行一段代碼解密後一部分的代碼,而後再運行解密後的代碼,能夠往復循環,這樣破0解者只能看見運行着的代碼的附近的代碼,隔得遠的代碼處於加密狀態,這樣就須要花費大量的時間才能破0解了,固然想要實現這種高強度,仍是須要花費不少時間去設計的,並且要求咱們對x86彙編語言有比較深入理解,這我就分享下我對動態解密理解。

 

下面我直接根據一個案列來分析動態解密流程:
爲了方便演示效果,我在VS中用匯編以動態獲取API方式寫了一段功能是彈窗的代碼,效果如圖

將生成EXE文件用0x32dbg(或者OD)打開後找到彈窗功能的彙編代碼,扣取出該段代碼16進制字節。扣取字節的操做是在x32dbg中選中該段代碼後,右鍵->複製->數據->C樣式ShellCode字符串

 

OD中選中代碼後右鍵->數據轉換->C++->字節

 

把這些字節存在一個字符串數組中,直接用我這個就好了:

1
char ShellCode[] = "\x60\x83\xEC\x60\xEB\x55\x4D\x65\x73\x73\x61\x67\x65\x42\x6F\x78\x41\x00\x45\x78\x69\x74\x50\x72\x6F\x63\x65\x73\x73\x00\x4C\x6F\x61\x64\x4C\x69\x62\x72\x61\x72\x79\x45\x78\x41\x00\x47\x65\x74\x50\x72\x6F\x63\x41\x64\x64\x72\x65\x73\x73\x00\x75\x73\x65\x72\x33\x32\x2E\x64\x6C\x6C\x00\x48\x65\x6C\x6C\x6F\x20\x47\x72\x65\x61\x74\x20\x4E\x61\x74\x75\x72\x65\x21\x00\xD9\xEE\xD9\x74\x24\xF4\x5A\x64\x8B\x35\x30\x00\x00\x00\x8B\x76\x0C\x8B\x76\x1C\x8B\x36\x8B\x5E\x08\x52\x53\xE8\x5A\x00\x00\x00\x8B\xC8\x51\x52\x8D\x42\xC3\x50\x53\xFF\xD1\x5A\x59\x52\x50\x51\x53\xE8\x01\x00\x00\x00\x61\x55\x8B\xEC\x83\xEC\x0C\x8B\x55\x14\x33\xC9\x8D\x72\xE1\x51\x51\x56\xFF\x55\x10\x8B\x55\x14\x8D\x4A\xAB\x51\x50\xFF\x55\x0C\x33\xC9\x8B\x55\x14\x8D\x5A\xEC\x51\x53\x53\x51\xFF\xD0\x8B\x55\x14\x8D\x72\xB7\x56\xFF\x75\x08\xFF\x55\x0C\x51\xFF\xD0\x8B\xE5\x5D\xC2\x10\x00\x55\x8B\xEC\x83\xEC\x0C\x52\x53\x8B\x55\x08\x8B\x72\x3C\x8D\x34\x32\x8B\x76\x78\x8D\x34\x32\x8B\x7E\x1C\x8D\x3C\x3A\x89\x7D\xFC\x8B\x7E\x20\x8D\x3C\x3A\x89\x7D\xF8\x8B\x7E\x24\x8D\x3C\x3A\x89\x7D\xF4\x33\xC0\xEB\x01\x40\x8B\x75\xF8\x8B\x34\x86\x8D\x34\x32\x8B\x5D\x0C\x8D\x7B\xD2\xB9\x0E\x00\x00\x00\xF3\xA6\x75\xE7\x8B\x75\xF4\x33\xDB\x66\x8B\x1C\x46\x8B\x75\xFC\x8B\x34\x9E\x8D\x04\x32\x5B\x5A\x8B\xE5\x5D\xC2\x08\x00" ;

在0x32dbg(或者OD)中選中彙編代碼段後右下角會顯示選中的字節大小。

 

下面寫一個加密該字符串的代碼,編譯的時候VS項目屬性中配置「C/C++ -> 代碼生成 -> 安全檢查(禁用GS)」,「鏈接器 -> 高級 -> 數據執行保護DEP(關閉)」。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/ / 加密函數
void Encoder(char * pData, int nSize)
{
     / / 加密密鑰
     int nOutKey = 0x15 ;
     / / 加密後的緩衝區
     unsigned char * pBuffer = NULL;
     pBuffer = (unsigned char * )new char[nSize + 1 ];
     / / 對每一個字節進行加密
     for ( int j = 0 ; j < nSize; j + + )
     {
         pBuffer[j] = pData[j] ^ nOutKey;   
     }
     / / 打印出每一個字節
     for ( int i = 0 ; i < nSize; i + + )
     {
         printf( "\\x%02X" ,pBuffer[i]);
     }   
}
 
int main()
{
     / / 調用ShellCode查看是否能正常運行
     __asm {
         lea eax, ShellCode;
         push eax;
         ret;
     }
     / / 加密
     Encoder(ShellCode, 257 );
     getchar();
}

加密後會獲得一個字節數組,咱們把它複製下來存到另外一個數組中,而後把他放在解密代碼的屁股後面。
開始寫解密代碼,解密代碼纔是動態解密中的核心點,重中之重。
這裏要說一下GetPC技術,GetPC技術翻譯爲中文也就是獲取指針計數器。在x86彙編中實際上就是獲取當前代碼EIP的技術。我這用的是call 指令,call xxx指令至關於 push 下一行代碼的EIP + jmp xxx。 那麼咱們直接把XXX改成下一行指令的地址就能獲取當前EIP 內聯彙編代碼爲:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/ / 解密函數
     __asm {
         call _Next; / / 跳到下一行,並把EIP壓入棧
     _Next:       
         pop eax; / / 得到當前EIP
         lea esi, [eax + 0x22 ]; / / 生成後在OD中查看上一行到最後一行解密代碼的長度
         xor ecx, ecx;
         mov cx, 0x13e ; / / 要解密的字節長度
     _DeCode:
         mov al, byte ptr ds : [esi + ecx];
         xor al, 0x15 ; / / 解密密鑰
         mov byte ptr ds : [esi + ecx], al;
         loop _DeCode;
         xor[esi + ecx], 0x15 ;
         jmp esi;
     }

這裏要注意的是這段代碼後面要緊跟加密後的代碼。 在實際的殼代碼中,先把須要加密的代碼加密後和解密代碼組裝起來就能夠達到動態解密的功能。

相關文章
相關標籤/搜索