1. 程式人生 > >淺談程式碼段加密原理(防止靜態分析)

淺談程式碼段加密原理(防止靜態分析)

在軟體安全裡,有一種保護手段叫加密,一般情況下都是為程式碼段加密,使原本的程式碼無法被靜態分析,只能動態除錯。

涉及到的知識有:PE檔案結構,程式碼重定位,shellcode。

程式碼加密時可用各種演算法組合起來使用,只要保證解密時用逆推的方法還原成原始碼即可,下面例子當中用的是最簡單的異或加密

由於博主也是第一次接觸這個程式碼段加密,有寫的不對或者不夠的地方,還麻煩大佬指正。感謝!!

思路:

1.獲取程式碼段內容,進行加密後再覆蓋回去

2.把重定位去掉,目的使重定位發生的時機在解密之後,否則解密的資料是已經被重定位過的

3.新增一個節,在這個節中編寫解密步驟,並且要自己實現程式碼節的重定位,理由同2。並將OEP改寫為當前節的解密位置。

主要步驟:

步驟:

1.通過讀取內容,找到程式碼段位置(如果沒被修改過,用oep所在的節作為程式碼節來判斷比較科學,而不是單單靠.text),儲存程式碼段的偏移和長度,後續解密需要,然後進行異或加密。

2.通過可選PE頭的資料目錄成員,找到儲存重定位表的資料  (直接宣告一個IMAGE_SECTION_HEADER來存放),再分別申請2個記憶體來存放屬於程式碼節的重定位資料和非程式碼節的資料,並記錄其長度。

3.刪除重定位相關的資料,刪除節表,檔案內容,各頭裡面的相關資料

4.獲取到當前節表的末尾,建立一個新的節,以當前檔案的對映大小(SizeOfImage)為節記憶體起點,以當前檔案大小為節檔案起點。節屬性為可讀可學可執行已被初始化((0x20000000 | 0x40000000 | 0x80000000 | 0x40)  = E0000040)

5.根據2儲存的程式碼節的重定位資料長度+4(在該節找到一個4位元組的位置(可緊接著重定位資料末尾)作為標記位,存放4個0x00進去,以便後面shellcode用來獲取目標基址與當前基址的差值,以便重定位時使用))擴充記憶體,並把增加了標誌位的重定位資料賦值進去。

6.把原oep儲存下來,並把新增節的RVA+程式碼段的重定位資料長度+4(標誌位)作為新的OEP。

7.編寫shellcode:先通過E8,00,00,00,00方法來獲取當前新增節所在的地址,再通過節地址獲得當前程序的基址(1.當前指令地址-指令偏移;2.根據重定位後的資料),然後通過偏移找到程式碼段進行解密(注意是記憶體偏移而不是檔案偏移),解密後,獲取程式碼節的重定位資料(該節開始資料就是重定位資料,長度為需要重定位的程式碼節資料長度+4),進行重定位,重定位完成後跳轉到原OEP進行檔案正常執行流程(此處要自己進行程式碼段的重定位是因為程式碼段的資料已經自己加密過了,如果不自己實現解密後再重定位,那麼解密出來的資料是加密後且重定位的資料,無法正常執行)

8.完善新增節後的檔案資料(檔案大小、檔案對映大小、檔案程式碼大小(SizeOfCode,這個節將作為解密使用,所以也是程式碼類)、節表數量等),並將原先存放檔案內容的空間進行擴容realloc,擴容大小為原大小+這個節的檔案大小。(擴容後可能返回一個新的地址,原地址被收回,所以要使用各種頭的資料的時候,要重新獲取)

注意新增節的長度為程式碼段重定位資料長度+4 +5(e8 00 00 00 00)+shellcode長度,且注意記憶體對齊和檔案對齊。

9.完善完新節後,用同樣的方法,修復原重定位節,並增加5.中的標誌位的位置到重定位表資料中(重定位塊為4位元組塊RVA+4位元組塊長度+2位元組塊中偏移(0x3XXX開頭有效)組成,但重定位塊為4位元組對齊,所以最小的重定位塊為12位元組),根據2.中所求得的非程式碼段的重定位的資料長度,再+12+8(8位元組0,用來結尾)的長度進行非程式碼節的重定位資料的擴容,並根據上一個節的大小,完善該節的相關資料。記得檔案大小和記憶體大小的對齊。並更新可選PE頭當中的相關資料。

10.將8.的非程式碼段的重定位資料,拼接到檔案內容的末尾。拼接前記得先對檔案內容大小進行擴容。

 

附上程式碼:

  1 #include <Windows.h>
  2 #include <iostream>
  3 #include <stdio.h>
  4 #include <tlhelp32.h>
  5 #include <io.h>
  6 #include <string.h>
  7 #include "Asmjit\\asmjit.h"
  8 
  9 using namespace std;
 10 using namespace asmjit;
 11 using namespace asmjit::x86;
 12 
 13 char *g_pFileSrc = NULL;
 14 int g_iTextVirtualAddr = 0; //程式碼節在記憶體中的地址
 15 int g_iTextVirtualLen = 0;     //程式碼節在記憶體中的長度
 16 
 17 
 18 int main()
 19 {
 20     char szFileName[] = { "C:\\Users\\Admin\\Desktop\\彈窗.exe" };
 21     HANDLE hFile = CreateFileA(szFileName,
 22         GENERIC_READ | GENERIC_WRITE,
 23         FILE_SHARE_READ | FILE_SHARE_WRITE,
 24         NULL,
 25         OPEN_EXISTING,   //獲取檔案內容
 26         FILE_ATTRIBUTE_ARCHIVE,
 27         NULL);
 28 
 29     if (hFile == INVALID_HANDLE_VALUE )
 30     {
 31         printf("檔案開啟失敗\n");
 32         return 1;
 33     }
 34 
 35     //獲取檔案大小
 36     DWORD dwFileSize = GetFileSize(hFile, NULL);
 37 
 38     //申請空間將exe讀取到記憶體中
 39     g_pFileSrc = new char[dwFileSize];
 40     if (NULL == g_pFileSrc)
 41     {
 42         printf("空間申請失敗\n");
 43         return false;
 44     }
 45     ReadFile(hFile, g_pFileSrc, dwFileSize, NULL, NULL);
 46     CloseHandle(hFile);
 47 
 48     PIMAGE_DOS_HEADER pDosHead = (PIMAGE_DOS_HEADER)g_pFileSrc;
 49     PIMAGE_NT_HEADERS pNtHead = (PIMAGE_NT_HEADERS)((DWORD)pDosHead + pDosHead->e_lfanew);
 50     DWORD dwSectionCount = pNtHead->FileHeader.NumberOfSections;//節表數量
 51     //通過可選PE頭的地址+可選PE頭的長度,得到節表位置
 52     PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)((DWORD)(&pNtHead->OptionalHeader) + pNtHead->FileHeader.SizeOfOptionalHeader);
 53 
 54 
 55 
 56     /////////////////////////////////////
 57     /////////////加密程式碼節//////////////
 58     /////////////////////////////////////
 59     for (int i = 0; i < dwSectionCount; i++)
 60     {
 61         //通過OEP的地址所在的節確定程式碼節位置
 62         if (pNtHead->OptionalHeader.AddressOfEntryPoint >= pSection->VirtualAddress
 63             && pNtHead->OptionalHeader.AddressOfEntryPoint < (pSection->VirtualAddress + pSection->Misc.VirtualSize))
 64             //if (!strcmp((char*)pSection->Name,".text"))  //找到程式碼節  
 65         {
 66             g_iTextVirtualAddr = pSection->VirtualAddress;  //後續解密需要
 67             g_iTextVirtualLen = pSection->Misc.VirtualSize;
 68             cout << pSection->Name;
 69             for (int iCount = 0; iCount < pSection->Misc.VirtualSize; iCount++)
 70             {
 71                 *(char*)(g_pFileSrc + pSection->PointerToRawData + iCount) ^= 0x35;
 72             }
 73             pSection->Characteristics |= 0x80000000;  //讓程式碼節可寫,以便後續解密
 74             break;
 75         }
 76         pSection++;
 77     }
 78 
 79 
 80 
 81     /////////////////////////////////////
 82     ///////////儲存重定位表資訊//////////
 83     /////////////////////////////////////
 84     DWORD dwRelocRVA = pNtHead->OptionalHeader.DataDirectory[5].VirtualAddress; //得到重定位表的RVA
 85     pNtHead->OptionalHeader.DataDirectory[5].VirtualAddress = 0;
 86     DWORD dwRelocVirtualSize = 0;//重定位表的長度
 87     pNtHead->OptionalHeader.DataDirectory[5].Size = 0;
 88     DWORD dwRelocFileAddr = 0;//重定位表的檔案位置
 89     DWORD dwRelocFileLen = 0;//重定位表的檔案長度
 90     char szRelocName[8] = { 0 }; //重定位表名字(可能被改名混淆,做記錄)
 91     DWORD dwRelocTab = 0;        //重定位節屬性
 92 
 93 
 94     int iTextRelocSize = 0;  //程式碼節塊的大小
 95     int iNotTextRelocSize = 0;//非程式碼節塊的大小
 96     char *szNotTextRelocSrc = NULL;//重定位表非程式碼段資料
 97     char *szTextRelocSrc = NULL;    //重定位表程式碼段資料
 98     
 99     DWORD dwReloctOffset = 0; //當前遍歷的偏移
100 
101 
102 
103 
104     /////////////////////////////////////
105     ////////拷貝並刪除重定位表資料///////
106     /////////////////////////////////////
107     pSection = (PIMAGE_SECTION_HEADER)((DWORD)(&pNtHead->OptionalHeader) + pNtHead->FileHeader.SizeOfOptionalHeader);//重新找到表頭
108     for (int i = 0; i < dwSectionCount; i++)
109     {
110         if (pSection->VirtualAddress == dwRelocRVA) //找到重定位節表,保留重定位表相關資料到記憶體中,隨後刪除重定位表
111         {
112             //儲存重定位表資料
113             dwRelocVirtualSize = pSection->Misc.VirtualSize;
114             dwRelocFileAddr = pSection->PointerToRawData;
115             dwRelocFileLen = pSection->SizeOfRawData;
116             dwRelocTab = pSection->Characteristics;
117             strcpy(szRelocName, (char *)pSection->Name);
118 
119             PIMAGE_BASE_RELOCATION pBaseRelocation = (PIMAGE_BASE_RELOCATION)(g_pFileSrc + dwRelocFileAddr);//重定位表當前塊
120             while ((pBaseRelocation->VirtualAddress != 0) && (pBaseRelocation->SizeOfBlock != 0))  //遍歷到重定位表末尾為止
121             {
122                 //如果當前重定位範圍在程式碼段內  則記錄起來
123                 if (pBaseRelocation->VirtualAddress >= g_iTextVirtualAddr & pBaseRelocation->VirtualAddress < g_iTextVirtualAddr + g_iTextVirtualLen)
124                 {
125                     while (pBaseRelocation->VirtualAddress < g_iTextVirtualAddr + g_iTextVirtualLen) //由於程式碼段是連續的,一直遍歷到非程式碼段
126                     {
127                         iTextRelocSize += pBaseRelocation->SizeOfBlock; //記錄長度
128                         pBaseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)pBaseRelocation + pBaseRelocation->SizeOfBlock);
129                     }
130                     szTextRelocSrc = (char *)realloc(szTextRelocSrc, iTextRelocSize);
131                     //從末尾-重定位長度=重定位資料  再+遍歷的偏移  得到需要複製的資料的地址  複製長度為所以程式碼節範圍的重定位資料長度
132                     memcpy(szTextRelocSrc, g_pFileSrc + dwFileSize - dwRelocFileLen + dwReloctOffset, iTextRelocSize);
133                     dwReloctOffset += iTextRelocSize;//複製完之後  更新偏移(由於這個這段資料連續的  全部複製完再更新)
134                 }
135                 else
136                 {
137                     szNotTextRelocSrc = (char*)realloc(szNotTextRelocSrc, iNotTextRelocSize + pBaseRelocation->SizeOfBlock); //將原本的存放重定位的空間擴容
138                     /*非程式碼塊的複製源起點=重定位表位置(起始位置+末尾-重定位長度)+偏移
139                     目標位置為剛申請出來的空間的空閒起點(起點+已賦值的長度)*/
140                     memcpy(szNotTextRelocSrc + iNotTextRelocSize ,
141                         g_pFileSrc + dwFileSize - dwRelocFileLen + dwReloctOffset, 
142                         pBaseRelocation->SizeOfBlock);
143                     dwReloctOffset += pBaseRelocation->SizeOfBlock;//此時檔案偏移增加
144                     iNotTextRelocSize += pBaseRelocation->SizeOfBlock;//記錄非程式碼段的長度
145                     pBaseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)pBaseRelocation + pBaseRelocation->SizeOfBlock);                    
146                 }
147                 
148             }
149             /*此時,原重定位的資料已經分成了非程式碼段(szNotTextRelocSrc)和程式碼段(szTextRelocSrc)兩部分
150             還原時,還原非程式碼段的資料即可*/
151 
152             //檔案內容+檔案長度 = 取到檔案末尾  再-當前重定位表的長度,取到重定位表的起始位置
153             memset(g_pFileSrc + dwFileSize - dwRelocFileLen, 0, dwRelocFileLen);//把重定位表資料用0覆蓋
154             memset(pSection, 0, sizeof(IMAGE_SECTION_HEADER));
155             pNtHead->FileHeader.NumberOfSections--;
156             //調整檔案記憶體大小
157             int iAlignmentSize = dwRelocVirtualSize % pNtHead->OptionalHeader.SectionAlignment;
158             if (!iAlignmentSize)
159             {
160                 pNtHead->OptionalHeader.SizeOfImage -= dwRelocVirtualSize;        //調整檔案記憶體大小
161             }
162             else
163             {
164                 pNtHead->OptionalHeader.SizeOfImage =
165                     pNtHead->OptionalHeader.SizeOfImage
166                     - (dwRelocVirtualSize - iAlignmentSize + pNtHead->OptionalHeader.SectionAlignment);
167             }
168             dwFileSize -= dwRelocFileLen;
169             break;
170         }
171         pSection++;
172     }
173 
174 
175 
176     //找到最後一個節表,得到新增節的檔案起點和對映到記憶體中的起點
177     pSection = (PIMAGE_SECTION_HEADER)((DWORD)(&pNtHead->OptionalHeader) + pNtHead->FileHeader.SizeOfOptionalHeader);
178     pSection += (pNtHead->FileHeader.NumberOfSections - 1);
179     //檔案起點   //檔案長度原本就已經對齊  無需再對齊
180     int iNewSectionFileAddr = pSection->PointerToRawData + pSection->SizeOfRawData;
181 
182     //檔案對映起點
183     int iNewSectionFileMapAddr = 0;
184     int iMemoryAlignment = pSection->Misc.VirtualSize % pNtHead->OptionalHeader.SectionAlignment;
185     if (!iMemoryAlignment)
186     {
187         iNewSectionFileMapAddr = pSection->VirtualAddress + pSection->Misc.VirtualSize;
188     }
189     else
190     {
191         iNewSectionFileMapAddr = pSection->VirtualAddress
192             + pSection->Misc.VirtualSize
193             + (pNtHead->OptionalHeader.SectionAlignment - iMemoryAlignment);
194     }
195 
196 
197     /////////////////////////////////////
198     ////////////開始新增節///////////////
199     /////////////////////////////////////
200     pSection++;
201     /*記錄當前屬性 方便除錯*/
202     memset(pSection, 0, sizeof(IMAGE_SECTION_HEADER));    //初始化當前節屬性
203     memcpy(pSection->Name, ".kd", 3);                        //節名字
204     pSection->VirtualAddress = iNewSectionFileMapAddr;                //節的記憶體起始地址
205     pSection->PointerToRawData = iNewSectionFileAddr;                //節的檔案起始地址
206     
207     pSection->Characteristics = (0x20000000 | 0x40000000 | 0x80000000 | 0x40); //節屬性 可讀可寫可執行已被初始化
208     pNtHead->FileHeader.NumberOfSections += 1;            //節表數量+1
209     
210     
211     //作為標誌位  後面獲取記憶體基址使用
212     int iTabOffset = iTextRelocSize;            //標誌位偏移
213     int iTabRva = pSection->VirtualAddress;//標誌位所在的RVA起點
214     int iTextAlignment = iTextRelocSize + 4 ; //站位後的程式碼節重定位資料長度
215     
216     szTextRelocSrc = (char*)realloc(szTextRelocSrc,iTextAlignment);  
217     memset(szTextRelocSrc + iTextRelocSize, 0, 4);//重定位標記
218     //g_pFileSrc = (char*)realloc(g_pFileSrc, dwFileSize + iTextAlignment);
219     //memcpy(g_pFileSrc + dwFileSize , szTextRelocSrc, iTextAlignment);//新增程式碼節的重定位資料
220     //dwFileSize += iTextAlignment;
221 
222     int iOldOEP = pNtHead->OptionalHeader.AddressOfEntryPoint;//原來的oep
223     //將OEP改為新增節偏移   (原始的記憶體長度(對映末尾)-當前對映基址)  
224     pNtHead->OptionalHeader.AddressOfEntryPoint = pSection->VirtualAddress + iTextAlignment;
225     int iNewOEP = pNtHead->OptionalHeader.AddressOfEntryPoint;
226     /////////////////////////////////////
227     ////////////編寫shellcode///////////
228     /////////////////////////////////////
229     JitRuntime        _x86RunTimeObject;
230     X86Assembler    a(&_x86RunTimeObject); //重定位
231 
232 
233     // 動態編譯過程
234     // 也就是組織shellcode的過程
235     //得到實際載入基址:1.利用重定位 (還可以直接利用shellcode自定位獲得)
236     int ioldIB = pNtHead->OptionalHeader.ImageBase;//目標基址
237     extern int g_iTextVirtualAddr;//解密需要
238     extern int g_iTextVirtualLen;
239 
240     //獲取程序基址
241     char szGetOEP[] = { 0xE8,0x00,0x00,0x00,0x00 }; //同理jit.call(0x00000000);
242     a.pop(eax);
243     a.sub(eax, 5);//此時得到oep所在的地址 oep距離基址隔著
244     a.mov(ebx, eax);//備份一個OEP所在程序地址
245     a.sub(eax, iNewOEP);//OEP所在的基址-偏移  得到基址
246     a.mov(ebp, eax);    //儲存程序基址到ebp
247 
248     a.sub(ebx, iTextAlignment - iTabOffset);//得到標誌塊的位置
249     a.mov(edx, dword_ptr(ebx));//得到重定位後該地址的值  該值為目標基址與實際基址的差值(由於以前是0,重定位後的值=0-目標+實際)
250     
251      
252     //解密程式碼段
253     Label begin = a.newLabel();
254     Label over = a.newLabel();
255     //此時ebp為實際載入基址
256     a.mov(esi, ebp);
257     a.add(esi, g_iTextVirtualAddr);//esi為程式碼段的起點
258     a.mov(edi, 0);//edi為迴圈偏移
259     a.bind(begin);
260     a.cmp(edi, g_iTextVirtualLen);
261     a.jnl(over);
262     a.mov(ecx, esi);
263     a.add(ecx, edi); //開始解密
264     a.movsx(al, byte_ptr(ecx));
265     a.xor_(al, 0x35);
266     a.mov(byte_ptr(ecx), al);
267     a.inc(edi);
268     a.jmp(begin);
269     a.bind(over);//解密完畢
270     
271 
272     //程式碼節重定位
273     /*基址為程式碼節的重定位資料、iTextRelocSize程式碼節的長度、ebp程式基址、edi遍歷偏移*/
274     Label TextBegin = a.newLabel(); //程式碼節遍歷起點
275     Label TextOver = a.newLabel();  //程式碼節遍歷終點    
276     Label LumpBegin =a.newLabel(); //程式碼節的塊遍歷起點
277     Label LumpOver = a.newLabel();  //程式碼節的塊的遍歷終點
278 
279     /*重定位資料:基址+iTextbAddr、資料長度:iTextRelocSize
280     edi程式基址、esi重定位資料基址(基址+iTextbAddr)*/
281     int iTextOffset = pSection->VirtualAddress; //重定位所在的偏移
282     
283     
284     a.push(ebp);
285     a.mov(ebp, esp);
286     a.sub(esp, 16);//-16塊RVA -12塊長度 -8塊迴圈下標 -4塊內偏移(0x3XXX) ebp即程式基址
287     a.mov(dword_ptr(ebp, -4), edx);//把差值保留在edx中
288 
289     a.cmp(dword_ptr(ebp), ioldIB);
290     a.je(TextOver);//當前程序基址=預計基址 則無需重定位
291     a.mov(edi, 0); //edi遍歷偏移
292     a.bind(TextBegin);//節迴圈起點
293     a.cmp(edi, iTextRelocSize);//遍歷下標大於等於重定位長度  則遍歷完成
294     a.jnl(TextOver);
295     a.mov(esi, dword_ptr(ebp));//得到程序基址
296     a.add(esi, iTextOffset);//esi為重定位資料的基址
297     a.mov(eax, dword_ptr(esi, edi));
298     a.cmp(eax, 0);  //如果為0 則遍歷完畢
299     a.je(TextOver);
300     a.mov(dword_ptr(ebp, -16), eax);//塊RVA
301     a.mov(edx, 4);
302     a.add(edx, edi);
303     a.mov(eax, dword_ptr(esi, edx));
304     a.mov(dword_ptr(ebp, -12), eax);//塊長度
305     a.mov(dword_ptr(ebp,-8), 8); //塊內迴圈偏移   從第8位開始 4RVA+4SIZE
306 
307     a.bind(LumpBegin);//節迴圈起點
308     a.mov(ebx, dword_ptr(ebp, -8)); //ebx塊內偏移
309     a.cmp(ebx, dword_ptr(ebp, -12));
310     a.jnl(LumpOver);
311     a.mov(ecx, esi);//重定位資料基址
312     a.add(ecx, edi);//當前遍歷偏移
313     a.movzx(eax, word_ptr(ecx, ebx));
314     a.add(dword_ptr(ebp, -8), 2);//塊內偏移+2
315     a.cmp(eax, 0); //為0 則遍歷完畢當前塊
316     a.je(LumpOver);
317     a.xor_(eax, 0x3000);
318     a.add(eax, dword_ptr(ebp, -16));//+塊RVA
319     a.add(eax, dword_ptr(ebp));//得到重定位地址
320     a.mov(edx, dword_ptr(eax));//獲得裡面的資料
321     a.add(edx, dword_ptr(ebp, -4));
322     /*a.sub(edx, ioldIB);
323     a.add(edx, dword_ptr(ebp));*/
324     a.mov(dword_ptr(eax), edx);//重定位成功
325     a.jmp(LumpBegin);
326     a.bind(LumpOver);//節迴圈終點
327     a.add(edi, dword_ptr(ebp, -12));
328     a.jmp(TextBegin);
329     a.bind(TextOver);//節迴圈終點
330     
331     a.add(esp, 16);//-16
332     a.pop(ebp);
333     //回到原OEP  此時ebp為程式基址
334     a.mov(ebx, ebp);
335     a.add(ebx, iOldOEP);//當前程序基址+原OEP
336     a.jmp(ebx);
337 
338     // 生成機器碼
339     // 也叫生成shellcode
340     // 使用一個函式指標指向
341     PVOID pCode = a.make();
342     int iJitSize = a.getCodeSize() + sizeof(szGetOEP);
343     iTextAlignment += iJitSize;
344 
345     if (!(iTextAlignment % pNtHead->OptionalHeader.FileAlignment))  //節檔案長度   根據程式碼段重定位資料長度
346     {
347         pSection->SizeOfRawData = iTextAlignment;
348     }
349     else
350     {
351         pSection->SizeOfRawData = iTextAlignment
352             + pNtHead->OptionalHeader.FileAlignment
353             - (iTextAlignment % pNtHead->OptionalHeader.FileAlignment);
354     }
355     if (!(iTextAlignment % pNtHead->OptionalHeader.SectionAlignment))  //節記憶體長度
356     {
357         pSection->Misc.VirtualSize = iTextAlignment;
358     }
359     else
360     {
361         pSection->Misc.VirtualSize = iTextAlignment
362             + pNtHead->OptionalHeader.SectionAlignment
363             - (iTextAlignment % pNtHead->OptionalHeader.SectionAlignment);
364     }
365     pNtHead->OptionalHeader.SizeOfCode += pSection->SizeOfRawData;        //新增節的內容同樣為程式碼  
366     pNtHead->OptionalHeader.SizeOfImage += pSection->Misc.VirtualSize;
367 
368     int iNewSecionFileSize = pSection->SizeOfRawData;
369     g_pFileSrc = (char*)realloc(g_pFileSrc, dwFileSize + iNewSecionFileSize);
370     memcpy(g_pFileSrc + dwFileSize, szTextRelocSrc, iTextRelocSize + 4);//新增程式碼節的重定位資料
371     memcpy(g_pFileSrc + dwFileSize + iTextRelocSize + 4, szGetOEP, sizeof(szGetOEP));//壓入求基址指令
372     memcpy(g_pFileSrc + dwFileSize + iTextRelocSize + 4 + sizeof(szGetOEP), pCode, a.getCodeSize()); //壓入shellcode
373     dwFileSize += iNewSecionFileSize;
374 
375 
376 
377     /////////////////////////////////////
378     //////////修復重定位表//////////////
379     /////////////////////////////////////
380     pDosHead = (PIMAGE_DOS_HEADER)g_pFileSrc;
381     pNtHead = (PIMAGE_NT_HEADERS)((DWORD)pDosHead + pDosHead->e_lfanew);
382     dwSectionCount = pNtHead->FileHeader.NumberOfSections;//節表數量
383     //通過可選PE頭的地址+可選PE頭的長度,得到節表位置
384     pSection = (PIMAGE_SECTION_HEADER)((DWORD)(&pNtHead->OptionalHeader) + pNtHead->FileHeader.SizeOfOptionalHeader);
385     pSection += dwSectionCount;  //pSection的首地址+(節表數量*40)
386     memset(pSection, 0, sizeof(IMAGE_SECTION_HEADER));            //初始化
387 
388     //重定位表屬性
389     strcpy((char*)pSection->Name, szRelocName);                    //節名
390     /*如果原有的重定位表的虛擬空間大小比實際存放資料的空間大10位元組(用於新增新的重定位塊),則無需擴大,否則則要按節對齊尺寸擴大
391     前12位元組為資料,後40為一個重定位塊的長度 (以全為0的重定位塊為結尾)  */
392     pSection->PointerToRawData = dwFileSize;
393     int iRelocSize = iNotTextRelocSize + 20;
394     if (!(iRelocSize % pNtHead->OptionalHeader.FileAlignment))
395     {
396         pSection->SizeOfRawData = iRelocSize;
397     }
398     else
399     {
400         pSection->SizeOfRawData = iRelocSize
401             + pNtHead->OptionalHeader.FileAlignment
402             - (iRelocSize % pNtHead->OptionalHeader.FileAlignment);
403     }
404     int iAddrSize = pSection->SizeOfRawData; //這個節的檔案長度
405     pSection->VirtualAddress = pNtHead->OptionalHeader.SizeOfImage;//節RVA起始位置
406     if (!(iRelocSize % pNtHead->OptionalHeader.SectionAlignment))
407     {
408         pSection->Misc.VirtualSize = iRelocSize;
409     }
410     else
411     {
412         pSection->Misc.VirtualSize = iRelocSize
413             + pNtHead->OptionalHeader.SectionAlignment
414             - (iRelocSize % pNtHead->OptionalHeader.SectionAlignment);
415     }
416     pSection->Characteristics = dwRelocTab | 0x80000000;
417     pNtHead->OptionalHeader.DataDirectory[5].VirtualAddress = pSection->VirtualAddress;
418     pNtHead->OptionalHeader.DataDirectory[5].Size = iNotTextRelocSize + 12 + 8;
419     pNtHead->FileHeader.NumberOfSections++; 
420     pNtHead->OptionalHeader.SizeOfImage += pSection->Misc.VirtualSize;
421     
422     
423     //重定位的塊以4位元組對齊 
424     char szAddReloc[12] = { 0 };
425     int iTabLen = sizeof(szAddReloc);
426     int iTabAddr = iTabRva + iTabOffset;
427     iTabRva = iTabAddr & 0xfff000;
428     iTabOffset = (iTabAddr & 0xfff) | 0x3000;
429     memcpy(szAddReloc, &iTabRva, 12);//RVA
430     memcpy(szAddReloc + 4, &iTabLen, 4);//size
431     memcpy(szAddReloc + 8, &iTabOffset, 4);//偏移  位於第8個位元組後面
432 
433     szNotTextRelocSrc = (char *)realloc(szNotTextRelocSrc, iAddrSize);//增加重定位資料
434     memcpy(szNotTextRelocSrc + iNotTextRelocSize, szAddReloc, sizeof(szAddReloc));//把新的重定位塊資料加到原重定位塊資料上
435     memset(szNotTextRelocSrc + iNotTextRelocSize + sizeof(szAddReloc), 0, 8);
436 
437     g_pFileSrc = (char*)realloc(g_pFileSrc, dwFileSize + iAddrSize);//把空間大小增加重定位節所佔檔案大小
438     
439     memcpy(g_pFileSrc + dwFileSize, szNotTextRelocSrc, iAddrSize);
440     dwFileSize += iAddrSize;//更新檔案大小
441 
442 
443     HANDLE hNewFile = CreateFileA("C:\\Users\\Admin\\Desktop\\彈窗測試.exe",
444         GENERIC_READ | GENERIC_WRITE,
445         FILE_SHARE_READ | FILE_SHARE_WRITE,
446         NULL,
447         CREATE_ALWAYS,   //建立並覆蓋上一個檔案
448         FILE_ATTRIBUTE_ARCHIVE,
449         NULL);
450     
451     if (hNewFile == INVALID_HANDLE_VALUE)
452     {
453         printf("檔案儲存失敗\n");
454         return 1;
455     }
456     int iError = GetLastError();
457     LPDWORD iNum = NULL;
458     WriteFile(hNewFile, g_pFileSrc, dwFileSize, iNum, NULL); //寫入檔案
459     CloseHandle(hNewFile);
460 
461     MessageBoxA(0, "by:阿怪\n          2020.8.25", "加密成功", 0);
462     system("pause");
463 }

執行效果:

 

 

 

shellcode的例子各位可以在網上搜索或者自己想辦法用匯編轉換成機器碼輸入。

有疑惑或者不對的地方歡迎在評論指出,感謝

&n