浅谈代码段加密原理(防止静态分析)
在软件安全里,有一种保护手段叫加密,一般情况下都是为代码段加密,使原本的代码无法被静态分析,只能动态调试。
涉及到的知识有: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的例子各位可以在网上搜索或者自己想办法用汇编转换成机器码输入。
有疑惑或者不对的地方欢迎在评论指出,感谢