浅谈代码段加密原理(防止静态分析)

在软件安全里,有一种保护手段叫加密,一般情况下都是为代码段加密,使原本的代码无法被静态分析,只能动态调试。

涉及到的知识有:PE文件结构,代码重定位,shellcode。

代码加密时可用各种算法组合起来使用,只要保证解密时用逆推的方法还原成源代码即可,下面例子当中用的是最简单的异或加密

由于博主也是第一次接触这个代码段加密,有写的不对或者不够的地方,还麻烦大佬指正。感谢!!

思路:

1.获取代码段内容,进行加密后再覆盖回去

2.把重定位去掉,目的使重定位发生的时机在解密之后,否则解密的数据是已经被重定位过的

3.新增一个节,在这个节中编写解密步骤,并且要自己实现代码节的重定位,理由同2。并将OEP改写为当前节的解密位置。  由于TLS目录运行的时机先于OEP,同理也可以编写TLS目录进行解密。

主要步骤:

步骤:

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的例子各位可以在网上搜索或者自己想办法用汇编转换成机器码输入。

有疑惑或者不对的地方欢迎在评论指出,感谢

 

posted @ 2020-09-02 02:27  __阿阿阿怪  阅读(1535)  评论(0编辑  收藏  举报