参考图:
具体相关代码
核心相关代码
1 // gbpeall.cpp: implementation of the gbpeall class. 2 // 3 ////////////////////////////////////////////////////////////////////// 4 5 #include "stdafx.h" 6 #include "gbpeall.h" 7 8 9 ////////////////////////////////////////////////////////////////////// 10 // Construction/Destruction 11 ////////////////////////////////////////////////////////////////////// 12 13 14 //定义一个全局变量 15 BYTE ShellCode[] = 16 { 17 0x6A,00,0x6A,00,0x6A,00,0x6A,00, //MessageBox push 0的硬编码 18 0xE8,00,00,00,00, // call汇编指令E8和后面待填充的硬编码 19 0xE9,00,00,00,00 // jmp汇编指令E9和后面待填充的硬编码 20 }; 21 22 //ExeFile->FileBuffer 返回值为计算所得文件大小 23 //读取一个exe文件,然后输出为FileBuffer 24 DWORD ReadPEFile(IN LPSTR lpszFile, OUT LPVOID* pFileBuffer) 25 { 26 //下面有个IN和OUT,大致意思就是参数的类型传入进来之后不进行宏扩展; 27 //啥也不干,即使理解成干,也是扩展成空白,这个是C++语法中允许的; 28 //LPSTR ----> typedef CHAR *LPSTR, *PSTR; 意思就是char* 指针;在WINNT.H头文件里面 29 FILE* pFile = NULL; 30 //定义一个FILE结构体指针,在标准的Stdio.h文件头里面 31 //可参考:https://blog.csdn.net/qq_15821725/article/details/78929344 32 DWORD fileSize = 0; 33 // typedef unsigned long DWORD; DWORD是无符号4个字节的整型 34 LPVOID pTempFileBuffer = NULL; 35 //LPVOID ----> typedef void far *LPVOID;在WINDEF.H头文件里面;别名的void指针类型 36 37 //打开文件 38 pFile = fopen(lpszFile,"rb"); //lpszFile是当作参数传递进来 39 if (!pFile) 40 { 41 printf("打开文件失败! "); 42 return 0; 43 } 44 /* 45 关于在指针类型中进行判断的操作,下面代码出现的情况和此一样,这里解释下: 46 1.因为指针判断都要跟NULL比较,相当于0,假值,其余都是真值 47 2.if(!pFile)和if(pFile == NULL), ----> 为空,就执行语句;这里是两个等于号不是一个等于号 48 3.if(pFile)就是if(pFile != NULL), 不为空,就执行语句; 49 */ 50 51 //读取文件内容后,获取文件的大小 52 fseek(pFile,0,SEEK_END); 53 fileSize = ftell(pFile); 54 fseek(pFile,0,SEEK_SET); 55 56 /* 57 fseek 通过使用二进制的方式打开文件,移动文件读写指针的位置,在stdio.h头文件里 58 59 int fseek(FILE * stream, long offset, int fromwhere); 60 61 上面是fseek的函数原型 62 第一个参数stream 为文件指针 63 第二个参数offset 为偏移量,整数表示正向偏移,负数表示负向偏移 64 第三个参数fromwhere 为指针的起始位置,设定从文件的哪里开始偏移,可能取值为:SEEK_CUR,SEEK_END,SEEK_SET 65 SEEK_SET 0 文件开头 66 SEEK_CUR 1 当前读写的位置 67 SEEK_END 2 文件尾部 68 69 下面是相关用法和例子: 70 fseek(fp,100L,0);把fp指针移动到离文件开头100字节处; 71 fseek(fp,100L,1);把fp指针移动到离文件当前位置100字节处; 72 fseek(fp,100L,2);把fp指针退回到离文件结尾100字节处。 73 fseek(fp,0,SEEK_SET);将读写位置移动到文件开头; 74 fseek(fp,0,SEEK_END);将读写位置移动到文件尾时; 75 fseek(fp,100L,SEEK_SET);将读写位置移动到离文件开头100字节处; 76 fseek(fp,100L,SEEK_CUR);将读写位置移动到离文件当前位置100字节处; 77 fseek(fp,-100L,SEEK_END);将读写指针退回到离文件结尾100字节处; 78 fseek(fp,1234L,SEEK_CUR);把读写位置从当前位置向后移动1234字节; 79 fseek(fp,0L,2);把读写位置移动到文件尾; 80 其中 ---> L后缀表示长整数 81 82 ftell()用于返回文件当前指针指向的位置,与fseek配合可以算出文件元素数据总数。 83 参考:http://c.biancheng.net/cpp/html/2519.html 84 85 ftell()函数用来获取文件读写指针的当前位置,其原型为:long ftell(FILE * stream); 同样在stdio.h头文件里 86 参数:stream 为已打开的文件指针。 87 */ 88 89 //动态申请内存空间 90 pTempFileBuffer = malloc(fileSize); 91 92 /* 93 参考:http://c.biancheng.net/cpp/html/137.html 94 原型:void* malloc (size_t size); 95 size_t ---> typedef unsigned int size_t; 无符号整型别名是size_t 96 void* ---> 函数的返回值类型是 void* ;void并不是说没有返回值或者返回空指针,而是返回的指针类型未知; 97 所以在使用 malloc() 时通常需要进行强制类型转换,将 void 指针转换成我们希望的类型; 98 例如:char *ptr = (char *)malloc(10); //分配10个字节的内存空间,用来存放字符 99 参数说明 ---> size 为需要分配的内存空间的大小,以字节(Byte)计。 100 函数说明 ---> malloc()在堆区分配一块指定大小的内存空间,用来存放数据。这块内存空间在函数执行完成后不会被初始化; 101 它们的值是未知的,所以分配完成内存之后需要初始化; 102 返回值:分配成功返回指向该内存的地址,失败则返回 NULL。 103 */ 104 105 if (!pTempFileBuffer) 106 { 107 printf("内存分配失败! "); 108 fclose(pFile); 109 return 0; 110 } 111 112 //根据申请到的内存空间,读取数据 113 114 size_t n = fread(pTempFileBuffer,fileSize,1,pFile); 115 if (!n) 116 { 117 printf("读取数据失败! "); 118 free(pTempFileBuffer); // 释放内存空间 119 fclose(pFile); // 关闭文件流 120 return 0; 121 } 122 123 //数据读取成功,关闭文件 124 *pFileBuffer = pTempFileBuffer; // 将读取成功的数据所在的内存空间的首地址放入指针类型pFileBuffer 125 pTempFileBuffer = NULL; // 初始化清空临时申请的内存空间 126 fclose(pFile); // 关闭文件 127 return fileSize; // 返回获取文件的大小 128 } 129 130 //CopyFileBuffer --> ImageBuffer 131 //将读取的FileBuffer拉伸加载到ImageBuffer,用作测试验证文件拉伸; 132 DWORD CopyFileBufferToImageBuffer(IN LPVOID pFileBuffer,OUT LPVOID* pImageBuffer) 133 { 134 //LPVOID ----> typedef void far *LPVOID;在WINDEF.H头文件里面;别名的void指针类型 135 PIMAGE_DOS_HEADER pDosHeader = NULL; 136 PIMAGE_NT_HEADERS pNTHeader = NULL; 137 PIMAGE_FILE_HEADER pPEHeader = NULL; 138 PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL; 139 PIMAGE_SECTION_HEADER pSectionHeader = NULL; 140 LPVOID pTempImageBuffer = NULL; 141 /* 142 上面都是PE里面的相关结构体类型,使用其类型进行自定义变量,并初始化值为NULL 143 PIMAGE_DOS_HEADER ---> 指向结构体,别名为这两个 IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER 144 PIMAGE_NT_HEADERS ---> 指向结构体,typedef PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS; 145 PIMAGE_FILE_HEADER ---> 指向结构体,别名为这两个 IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; 146 PIMAGE_OPTIONAL_HEADER32 ---> 指向结构体,别名为这两个 IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; 147 PIMAGE_SECTION_HEADER ---> 指向结构体,别名为这两个 IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; 148 */ 149 150 if (pFileBuffer == NULL) 151 { 152 printf("FileBuffer 获取失败! "); 153 return 0; 154 } 155 156 //判断是否是有效的MZ标志 157 if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE) 158 { 159 printf("无效的MZ标识 "); 160 return 0; 161 } 162 /* 163 IMAGE_DOS_SIGNATURE 这个在头文件WINNT.H里面,对应是个无参数宏; 164 #define IMAGE_DOS_SIGNATURE 0x5A4D // MZ 165 在宏扩展的时候就会替换为0x5A4D ,然后根据架构的不同进行排序存储,分大端和小端模式; 166 使用上面方式进行比对是否是有效的MZ头是非常有效; 167 而且IMAGE_DOS_SIGNATURE存储的值是两个字节,刚好就是PWORD ---> typedef WORD near *PWORD; 168 所以在进行比较的时候需要强制类型转换为相同的类型进行比较 169 */ 170 171 pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; 172 //这里的定义,就相当于已经确定了,其头肯定是MZ了,然后强制转换类型为PIMAGE_DOS_HEADER,就是Dos头 173 174 //判断是否是有效的PE标志 175 if (*((PDWORD)((DWORD)pFileBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) 176 { 177 printf("无效的PE标记 "); 178 return 0; 179 } 180 /* 181 IMAGE_NT_SIGNATURE ---> #define IMAGE_NT_SIGNATURE 0x00004550 // PE00 182 上述同样是个宏扩展,在头文件WINNT.H里面; 183 在进行比对的时候因为在Dos头里面有个值是 e_lfanew 对应的时候DWORD类型,所以在进行指针相加的时候 184 需要先进行强制类型转换,然后相加,即移动指针位置;然后最终需要比对的结果是0x4550站两个字节 185 所以又要强制转换类型为PWORD; 186 */ 187 //定位NT头 188 pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew); 189 //上面偏移完成之后pFileBuffer的指针偏移到了NT头---> pNTHeader 190 //**************************************************************************************** 191 //定位PE文件头 192 pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader)+4); 193 //根据PE头的结构体内容,PE文件头位置在NT头首地址偏移4个字节即可得到pPEHeader 194 //**************************************************************************************** 195 //定位可选PE头 196 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER); 197 /* 198 要得到可选PE的首地址位置,就根据上面得到的PE文件头位置里面的IMAGE_SIZEOF_FILE_HEADER来定位; 199 IMAGE_SIZEOF_FILE_HEADER也是个宏扩展,里面字节描述了PE文件头的大小是20个字节; 200 #define IMAGE_SIZEOF_FILE_HEADER 20,所以只要在PE文件头的首地址偏移20个字节即可移动到可选PE头; 201 指针相加的时候,此处的类型依然是DWORD 202 */ 203 //**************************************************************************************** 204 //第一个节表指针 205 pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader); 206 /* 207 这里要移动到第一个节表指针的首地址,就需要根据上面标准PE文件头中的SizeOfOptionalHeader获取具体可选PE 208 头的大小,然后根据这个大小进行偏移即可; 209 */ 210 //**************************************************************************************** 211 212 /* 213 到了节表的首地址位置之后,因为需要将FileBuffer复制到ImageBuffer,这个过程中,节表之前的Dos头,NT头 214 PE文件头,可选PE头,她们的大小都是不变的,所以定位出来之后,到后面的操作中直接复制即可,而节表不一样 215 她在FileBuffer状态和ImageBuffer状态是不相同的,她们节表之间复制转换到ImageBuffer是需要拉长节表,所以 216 在操作的时候是需要确定FileBuffer到ImageBuffer之后ImageBuffer的大小是多少,而这个大小,已经在可选PE头 217 里面的某一个值中已经给出来了 ---> SizeOfImage ; 218 注意:FileBuffer和ImageBuffer都是在内存中的展示,只不过FileBuffer是使用winhex等类似的形式打开查看其 219 二进制的形式,而ImageBuffer则是双击打开应用程序,将其加载至内存中显示的二进制的形式; 220 */ 221 //**************************************************************************************** 222 223 //根据SizeOfImage申请新的内存空间 224 pTempImageBuffer = malloc(pOptionHeader->SizeOfImage); 225 226 if (!pTempImageBuffer) 227 { 228 printf("再次在堆中申请一块内存空间失败 "); 229 return 0; 230 } 231 232 //因为下面要开始对内存空间进行复制操作,所以需要初始化操作,将其置为0,避免垃圾数据,或者其他异常 233 //初始化新的缓冲区 234 memset(pTempImageBuffer,0,pOptionHeader->SizeOfImage); 235 /* 236 参考:http://c.biancheng.net/cpp/html/157.html 237 238 在头文件string.h里面 239 240 void* memset( void* ptr,int value,size_t num ); 241 memset()函数用来将指定内存的前n个字节设置为特定的值; 242 243 参数说明: 244 ptr 为要操作的内存的指针; 245 value 为要设置的值;既可以向value传递int类型的值,也可以传递char类型的值,int和char可以根据ASCII码相互转换; 246 num 为ptr的前num个字节,size_t就是unsigned int。 247 函数说明:memset()会将ptr所指的内存区域的前num个字节的值都设置为value,然后返回指向ptr的指针; 248 */ 249 //**************************************************************************************** 250 251 //根据SizeOfHeaders大小的确定,先复制Dos头 252 memcpy(pTempImageBuffer,pDosHeader,pOptionHeader->SizeOfHeaders); 253 /* 254 参考:http://c.biancheng.net/cpp/html/155.html 255 256 在头文件string.h里面 257 258 void* memcpy (void* dest,const void* src,size_t num); 259 memcpy()函数功能用来复制内存的;她会复制src所指向内容的首地址,作为起始位置,然后偏移num个字节到dest所指的内存地址 260 的位置;此函数有个特征就是,她并不关心被复制的数据类型,只是逐字节地进行复制,这给函数的使用带来了很大的灵活性, 261 可以面向任何数据类型进行复制; 262 263 需要注意的是: 264 dest 指针要分配足够的空间,也就是要大于等于num字节的空间,如果没有分配足够的空间会出现错误; 265 dest和src所指的内存空间不能重叠(如果发生了重叠,使用 memmove() 会更加安全)。 266 267 所以上面的代码的含义如下: 268 (1)pDosHeader ---> 是指向pFileBuffer的首地址,也就是内存复制的时候从这里开始; 269 (2)pTempImageBuffer ---> 这里是表示上面要复制的目的,要把内容复制到这块内存来; 270 (3)pOptionHeader->SizeOfHeaders ---> 这里表示复制多大的内容到pTempImageBuffer里面去; 271 (4)从上面看来我们就知道复制到目标pOptionHeader->SizeOfHeaders所在的内存空间一定要比pTempImageBuffer大; 272 */ 273 //**************************************************************************************** 274 275 //上面把已经确定的头都复制好了,那么下面就可以开始复制节的里面的内容,因为节不仅仅是一个,所以需要用到for循环进行操作 276 //根据节表循环copy节的内容 277 PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader; 278 //定义一个临时节表的指针 279 for (int i=0;i<pPEHeader->NumberOfSections;i++,pTempSectionHeader++) 280 { 281 memcpy((void*)((DWORD)pTempImageBuffer + pTempSectionHeader->VirtualAddress), 282 (void*)((DWORD)pFileBuffer + pTempSectionHeader->PointerToRawData),pTempSectionHeader->SizeOfRawData); 283 } 284 /* 285 上面的大概操作就是根据标准PE文件头里面的值 NumberOfSections确定有几个节,然后不断的计算并增加指针偏移位置,不停的复制 286 287 PointerToRawData ---> 节在文件中的偏移地址; 288 VirtualAddress ---> 节在内存中的偏移地址; 289 SizeOfRawData ---> 节在文件中对齐后的尺寸; 290 291 (void*)((DWORD)pTempImageBuffer + pTempSectionHeader->VirtualAddress) ---> Dest(目的地) 292 上面我们已经知道了函数memcpy是怎么复制操作的,所以这里我们依依解释下: 293 首先我们知道,上面展示的是目的地,而且我们的目的是要从FileBuffer节内容复制到ImageBuffer节的内容, 294 那么要使用到的是文件被双击打开之后在内存中的偏移地址,这个地址就是VirtualAddress;这里举个例子: 295 正常打开notepad.exe,然后使用winhex加载这个notepad.exe的内存数据,同时使用PE解析工具得到两个值的信息如下: 296 可选PE头 ---> ImageBase ---> 0x01000000 297 第一个节表显示的VirtualAddress ---> 00001000 298 上面两个值相加就得到了文件被打开在内存中第一个节的真实数据的起始位置 ---> 0x01001000 299 查看winhex对应的地址,确认是对的; 300 301 (void*)((DWORD)pFileBuffer + pTempSectionHeader->PointerToRawData) ---> Src(源复制的起始内存地址) 302 同样是上面的例子: 303 PointerToRawData是节在文件中的偏移地址,而我们知道,在文件中和在内存中是不一样的,因为在内存中有ImageBase的说法, 304 但在文件中没有,所以她的起始位置就是文件存储在硬盘的时候使用winhex打开的开头位置,为这里同样使用winhex以二进制的形式 305 打开notepad.exe(非双击打开),发现文件的起始位置是0x00000000,同时使用PE解析工具确认出了PointerToRawData的值 306 PointerToRawData ---> 0x00000400 ; 起始位置为0x00000000 ,她们相加就得到第一个节表的起始位置为0x00000400 307 查看winhex对应的地址,确认是对的; 308 所以这里总结下来的Src,就是内存复制的时候,从这个偏移地址开始拿数据开始复制; 309 310 pTempSectionHeader->SizeOfRawData 311 这里就是告诉我们上面复制要复制多大的内容到 (void*)((DWORD)pTempImageBuffer + pTempSectionHeader->VirtualAddress) 312 SizeOfRawData ---> 节在文件中对齐后的尺寸; 313 例子还是以上面的为例: 314 通过PE解析工具确认SizeOfRawData的大小为:0x00007800 315 316 总结: 317 memcpy((void*)((DWORD)pTempImageBuffer + pTempSectionHeader->VirtualAddress), 318 (void*)((DWORD)pFileBuffer + pTempSectionHeader->PointerToRawData), 319 pTempSectionHeader->SizeOfRawData); 320 321 上面代码就是在文件中的形式找到要复制的位置0x00000400的起始位置开始复制,要复制0x00007800个字节大小,也就是从 322 0x00000400这个地址开始向后偏移7800个字节,将这些数据复制到文件双击被打开时候的内存地址0x01001000为起点向后覆盖复制 323 完成即可,为这里测试算了下;0x00000400+0x00007800=0x00007C00 ; 0x00007C00这个地址刚好是第二个节的PointerToRawData 324 这样就可以很好的理解for循环对第二个节的复制; 325 */ 326 327 //**************************************************************************************** 328 //返回数据 329 *pImageBuffer = pTempImageBuffer; 330 //将复制好后节的首地址保存到指针pImageBuffer中 331 pTempImageBuffer = NULL; 332 //初始化清空临时使用的pTempImageBuffer 333 334 return pOptionHeader->SizeOfImage; 335 } 336 337 //FileBuffer ---> NewImageBuffer(新增节操作)? 338 //通过复制FileBuffer并增加1000H到新的NewImageBuffer,用作新增节; 339 DWORD CopyFileBufferToNewImageBuffer(IN LPVOID pFileBuffer,IN size_t fileSize,OUT LPVOID* pNewImageBuffer) 340 { 341 PIMAGE_DOS_HEADER pDosHeader = NULL; 342 PIMAGE_NT_HEADERS pNTHeader = NULL; 343 PIMAGE_FILE_HEADER pPEHeder = NULL; 344 PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL; 345 PIMAGE_SECTION_HEADER pSectionHeader = NULL; 346 PIMAGE_SECTION_HEADER pLastSectionHeader = NULL; 347 LPVOID pTempNewImageBuffer = 0; 348 DWORD sizeOfFile = 0; 349 DWORD numberOfSection = 0; 350 DWORD okAddSections = 0; 351 352 353 //判断读取pFileBuffer读取是否成功 354 if (!pFileBuffer) 355 { 356 printf("缓冲区指针无效 "); 357 return 0; 358 } 359 360 //判断是否为MZ标志 361 362 if ((*(PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)// IMAGE_DOS_SIGNATURE --> MZ 363 { 364 printf("不是一个有效的MZ标志 "); 365 return 0; 366 } 367 368 //判断是否为PE标志 369 pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; 370 if (*((PWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) // IMAGE_NT_SIGNATURE --> PE 371 { 372 printf("不是有效的PE标志 "); 373 return 0; 374 } 375 376 //*********************申请开辟内存空间***************************************************************** 377 378 sizeOfFile = fileSize+0x1000; 379 pTempNewImageBuffer = malloc(sizeOfFile); 380 381 //判断内存空间开辟是否成功 382 if (!pTempNewImageBuffer) 383 { 384 printf("pTempNewImageBuffer开辟内存空间失败 "); 385 return 0; 386 } 387 388 //初始化内存内容 389 memset(pTempNewImageBuffer,0,sizeOfFile); 390 391 //初始化完成之后,先把为修改的内存空间全部拷贝到新的内存空间 392 memcpy(pTempNewImageBuffer,pFileBuffer,fileSize); 393 394 //定位Dos头地址 395 pDosHeader = (PIMAGE_DOS_HEADER)(pTempNewImageBuffer); 396 397 //定位NT头的地址 398 pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pTempNewImageBuffer+pDosHeader->e_lfanew); 399 400 //定位标志PE头地址 401 pPEHeder = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader)+0x04);//PE SIGNATURE 站4个字节 402 403 //定位可选PE头地址 404 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)(((DWORD)pPEHeder)+IMAGE_SIZEOF_FILE_HEADER);//IMAGE_SIZEOF_FILE_HEADER -> 20个字节 405 406 //定位第一个节表地址 407 pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeder->SizeOfOptionalHeader); 408 409 //定位最后一个节表的地址 410 pLastSectionHeader = &pSectionHeader[pPEHeder->NumberOfSections-1]; 411 412 //判断是否有足够的空间添加一个节表 413 //判断条件: 414 /* 415 SizeOfHeader - (DOS + 垃圾数据 + PE标记 + 标准PE头 + 可选PE头 + 已存在节表) >= 2个节表的大小 416 SizeOfHeader在可选PE头里面 417 */ 418 419 okAddSections = (DWORD)(pOptionHeader->SizeOfHeaders - (pDosHeader->e_lfanew + 0x04 + 420 sizeof(PIMAGE_FILE_HEADER) + pPEHeder->SizeOfOptionalHeader + sizeof(PIMAGE_SECTION_HEADER) 421 * pPEHeder->NumberOfSections)); 422 423 if (okAddSections < 2*sizeof(PIMAGE_SECTION_HEADER)) 424 { 425 printf("这个exe文件头不剩余空间不够 "); 426 free(pTempNewImageBuffer); 427 return 0; 428 } 429 430 //上面没问题,就开始修改内容了 431 //*************************修改内容******************************************************************* 432 433 //初始化新节表信息 434 PWORD pNumberOfSection = &pPEHeder->NumberOfSections; 435 PDWORD pSizeOfImage = &pOptionHeader->SizeOfImage; 436 437 numberOfSection = pPEHeder->NumberOfSections; 438 PVOID pSecName = &pSectionHeader[numberOfSection].Name; 439 PDWORD pSecMisc = &pSectionHeader[numberOfSection].Misc.VirtualSize; 440 PDWORD pSecVirtualAddress = &pSectionHeader[numberOfSection].VirtualAddress; 441 PDWORD pSecSizeOfRawData = &pSectionHeader[numberOfSection].SizeOfRawData; 442 PDWORD pSecPointToRawData = &pSectionHeader[numberOfSection].PointerToRawData; 443 PDWORD pSecCharacteristics = &pSectionHeader[numberOfSection].Characteristics; 444 445 //修改PE文件头里面的节数量信息 446 447 printf("*pNumberOfSection:%#X ",pPEHeder->NumberOfSections); 448 *pNumberOfSection = pPEHeder->NumberOfSections + 1; 449 printf("*pNumberOfSection:%#X ",pPEHeder->NumberOfSections); 450 451 //修改PE可选头里面SizeOfImage信息 452 printf("*pSizeOfImage:%#X ",pOptionHeader->SizeOfImage); 453 *pSizeOfImage = pOptionHeader->SizeOfImage + 0x1000; 454 printf("*pSizeOfImage:%#X ",pOptionHeader->SizeOfImage); 455 456 //向节表中添加数据 457 458 memcpy(pSecName,".newSec",8); 459 *pSecMisc = 0x1000; 460 //这里VirtualAddress的地址需要根据最后一个节表中对齐前内存中的实际大小? 461 //和文件中对齐后的大小,分别使用VirtualAddress加上她们的值,哪个大,就是 462 //哪个; 463 //VirtualAddress+max(VirtualSize,SizeOfRawData) 464 //就是上面的公式 465 466 //判断出要添加的值 467 DWORD add_size = pLastSectionHeader->Misc.VirtualSize > pLastSectionHeader->SizeOfRawData? 468 pLastSectionHeader->Misc.VirtualSize:pLastSectionHeader->SizeOfRawData; 469 //上面是个三目运算符 470 471 printf("pLastSectionHeader: %#X ",pLastSectionHeader); 472 printf("add_size: %#X ",add_size); 473 printf("numberOfSection: %#X ",pPEHeder->NumberOfSections); 474 printf("pLastSectionHeader->Misc.VirtualSize: %#X ",pLastSectionHeader->Misc.VirtualSize); 475 printf("pLastSectionHeader->SizeOfRawData: %#X ",pLastSectionHeader->SizeOfRawData); 476 printf("add_size: %#X ",add_size); 477 478 *pSecVirtualAddress = pLastSectionHeader->VirtualAddress + add_size; 479 480 //SectionAlignment对齐 481 482 if (*pSecVirtualAddress % pOptionHeader->SectionAlignment) 483 { 484 *pSecVirtualAddress = *pSecVirtualAddress / pOptionHeader->SectionAlignment * 485 pOptionHeader->SectionAlignment + pOptionHeader->SectionAlignment; 486 } 487 488 *pSecSizeOfRawData = 0x1000; 489 *pSecPointToRawData = pLastSectionHeader->PointerToRawData + pLastSectionHeader->SizeOfRawData; 490 491 //FileAlignment对齐 492 493 if (*pSecPointToRawData % pOptionHeader->FileAlignment) 494 { 495 *pSecPointToRawData = *pSecPointToRawData / pOptionHeader->FileAlignment * 496 pOptionHeader->FileAlignment + pOptionHeader->FileAlignment; 497 } 498 499 *pSecCharacteristics = 0xFFFFFFFF; 500 501 *pNewImageBuffer = pTempNewImageBuffer; 502 pTempNewImageBuffer = NULL; 503 504 return sizeOfFile; 505 } 506 507 //求对齐后的大小 508 //Actuall_size ---> 内存中对齐前实际的大小 ---> VirtualSize 509 //Align_size ---> 文件中对齐后的大小 ---> SizeOfRawData 510 DWORD AlignLength(DWORD Actuall_size,DWORD Align_size) 511 { 512 if (Actuall_size % Align_size == 0) 513 { 514 return Actuall_size; 515 } 516 else 517 { 518 DWORD n = Actuall_size / Align_size; 519 return Align_size*(n+1); 520 } 521 } 522 523 //ImageBuffer ---> NewImageBuffer 524 //将拉伸后加载到内存的ImageBuffer存入到NewImageBuffer,修改数据完成之后,准备存盘操作 525 DWORD FileBufferToModifyImageBuffer(IN LPVOID pFileBuffer,OUT LPVOID* pNewImageBuffer) 526 { 527 PIMAGE_DOS_HEADER pDosHeader = NULL; 528 PIMAGE_NT_HEADERS pNTHeader = NULL; 529 PIMAGE_FILE_HEADER pPEHeader = NULL; 530 PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL; 531 PIMAGE_SECTION_HEADER pSectionHeader = NULL; 532 PIMAGE_SECTION_HEADER pLastSectionHeader = NULL; 533 LPVOID pTempNewImageBuffer = NULL; 534 DWORD ImageBuffer_Size = 0; 535 DWORD numberOfSection = 0; 536 537 //判断读取pImageBuffer是否成功 538 if (!pFileBuffer) 539 { 540 printf("缓冲区指针无效 "); 541 } 542 543 //判断是否是有效的MZ头 544 if ((*(PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE) 545 { 546 printf("不是有效的MZ头 "); 547 return 0; 548 } 549 550 //判断是否是有效的PE头 551 pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; 552 if (*((PWORD)((DWORD)pFileBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) 553 { 554 printf("不是有效的PE头 "); 555 return 0; 556 } 557 558 //定位NT头 559 pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew); 560 561 //定位标准的PE文件头 562 pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader+0x04); 563 564 //定位可选PE头 565 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER); 566 567 //定位第一个节表地址 568 numberOfSection = pPEHeader->NumberOfSections; 569 pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader+pPEHeader->SizeOfOptionalHeader); 570 571 //定位最后一个节表地址 572 pLastSectionHeader = &pSectionHeader[numberOfSection-1]; 573 // printf("numberOfSection --> %#X ",numberOfSection); 574 // printf("*pSectionHeader --> %#X ",pSectionHeader->Misc.VirtualSize); 575 // printf("*pLastSectionHeader --> %#X ",&pLastSectionHeader); 576 577 //开始操作需要修改的部分 578 579 //最后一个节中内存中对齐前的大小; 580 PDWORD pVirtualSize = &pLastSectionHeader->Misc.VirtualSize; 581 //最后一个节在文件中对齐后的大小; 582 PDWORD pSizeOfRawData = &pLastSectionHeader->SizeOfRawData; 583 //文件中SizeOfImage的大小; 584 PDWORD pSizeOfImage = &pOptionHeader->SizeOfImage; 585 586 //扩展修改之前的数据 587 // printf("&pLastSectionHeader->Misc.VirtualSize --> %#X ",pVirtualSize); 588 // printf("*pLastSectionHeader->Misc.VirtualSize --> %#X ",*pVirtualSize); 589 // 590 // printf("&pLastSectionHeader->SizeOfRawData --> %#X ",pSizeOfRawData); 591 // printf("*pLastSectionHeader->SizeOfRawData --> %#X ",*pSizeOfRawData); 592 // 593 // printf("&pOptionHeader->SizeOfImage --> %#X ",pSizeOfImage); 594 // printf("*pOptionHeader->SizeOfImage --> %#X ",*pSizeOfImage); 595 596 //扩展修改pVirtualSize 597 *pVirtualSize = AlignLength(*pVirtualSize,pOptionHeader->SectionAlignment)+0x1000; 598 // printf("&pLastSectionHeader->Misc.VirtualSize --> %#X ",pVirtualSize); 599 // printf("*pLastSectionHeader->Misc.VirtualSize --> %#X ",*pVirtualSize); 600 // printf("&pLastSectionHeader->SizeOfRawData --> %#X ",pSizeOfRawData); 601 // printf("*pLastSectionHeader->SizeOfRawData --> %#X ",*pSizeOfRawData); 602 // 603 //扩展修改pSizeOfRawData 604 *pSizeOfRawData = AlignLength(*pSizeOfRawData,pOptionHeader->SectionAlignment)+0x1000; 605 // printf("&pLastSectionHeader->Misc.VirtualSize --> %#X ",pVirtualSize); 606 // printf("*pLastSectionHeader->Misc.VirtualSize --> %#X ",*pVirtualSize); 607 // printf("&pLastSectionHeader->SizeOfRawData --> %#X ",pSizeOfRawData); 608 // printf("*pLastSectionHeader->SizeOfRawData --> %#X ",*pSizeOfRawData); 609 // printf("&pOptionHeader->SizeOfImage --> %#X ",pSizeOfImage); 610 // printf("*pOptionHeader->SizeOfImage --> %#X ",*pSizeOfImage); 611 612 //修改SizeOfImage 613 *pSizeOfImage += 0x1000; 614 // printf("&pLastSectionHeader->Misc.VirtualSize --> %#X ",pVirtualSize); 615 // printf("*pLastSectionHeader->Misc.VirtualSize --> %#X ",*pVirtualSize); 616 // printf("&pLastSectionHeader->SizeOfRawData --> %#X ",pSizeOfRawData); 617 // printf("*pLastSectionHeader->SizeOfRawData --> %#X ",*pSizeOfRawData); 618 // printf("&pOptionHeader->SizeOfImage --> %#X ",pSizeOfImage); 619 // printf("*pOptionHeader->SizeOfImage --> %#X ",*pSizeOfImage); 620 621 //得到修改之后的大小准备申请内存空间 622 623 ImageBuffer_Size = pOptionHeader->SizeOfImage; 624 pTempNewImageBuffer = malloc(ImageBuffer_Size); 625 626 if (!pTempNewImageBuffer) 627 { 628 printf("分配内存空间失败 "); 629 return 0; 630 } 631 632 //初始化内存空间 633 memset(pTempNewImageBuffer,0,ImageBuffer_Size); 634 635 //复制SizeOfHeaders 636 memcpy(pTempNewImageBuffer,pDosHeader,pOptionHeader->SizeOfHeaders); 637 638 //创建临时节的结构体指针,遍历数据 639 PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader; 640 641 for (DWORD i = 0;i<pPEHeader->NumberOfSections;i++,pTempSectionHeader++) 642 { 643 memcpy((PVOID)((DWORD)pTempNewImageBuffer+pTempSectionHeader->VirtualAddress), 644 (void*)((DWORD)pFileBuffer+pTempSectionHeader->PointerToRawData),pTempSectionHeader->SizeOfRawData); 645 } 646 647 *pNewImageBuffer = pTempNewImageBuffer; 648 pTempNewImageBuffer = NULL; 649 return *pSizeOfImage; 650 } 651 652 //将ImageBuffer更改为一个节表和节然后压缩为NewBuffer,供存盘准备 653 DWORD FileBufferToModifyOneImageBuffer(IN LPVOID pImageBuffer,OUT LPVOID* pNewBuffer) 654 { 655 PIMAGE_DOS_HEADER pDosHeader = NULL; 656 PIMAGE_NT_HEADERS pNTHeader = NULL; 657 PIMAGE_FILE_HEADER pPEHeader = NULL; 658 PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL; 659 PIMAGE_SECTION_HEADER pSectionHeader = NULL; 660 PIMAGE_SECTION_HEADER pLastSectionHeader = NULL; 661 LPVOID pTempNewBuffer = NULL; 662 DWORD finalSections_Size = 0; 663 DWORD numberOfSection = 0; 664 DWORD lastSectionsMax_Size = 0; 665 DWORD sizeOfFile = 0; 666 667 //判断读取pImageBuffer是否成功 668 if (pImageBuffer == NULL) 669 { 670 printf("缓冲区指针无效 "); 671 } 672 //判断是否是有效的MZ标志 673 if (*((PWORD)pImageBuffer) != IMAGE_DOS_SIGNATURE) 674 { 675 printf("不是有效的MZ头 "); 676 return 0; 677 } 678 //判断是否是有效的PE标志 679 680 pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer; 681 if (*((PDWORD)((DWORD)pImageBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) 682 { 683 printf("不是有效的PE标志 "); 684 return 0; 685 } 686 687 688 //定位NT头 689 pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer+pDosHeader->e_lfanew); 690 691 //定位标准的PE文件头 692 pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader+0x04); 693 694 //定位可选PE头 695 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER); 696 697 //定位第一个节表地址 698 numberOfSection = pPEHeader->NumberOfSections; 699 pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader+pPEHeader->SizeOfOptionalHeader); 700 701 //定位最后一个节表地址 702 pLastSectionHeader = &pSectionHeader[numberOfSection-1]; 703 704 705 //最后一个节中内存对齐前的大小; 706 PDWORD pVirtualSize = &pLastSectionHeader->Misc.VirtualSize; 707 //最后一个节在文件中对齐后的大小; 708 PDWORD pSizeOfRawData = &pLastSectionHeader->SizeOfRawData; 709 710 //计算最后一个节中最大值是SizeOfRawData还是VirtualSize 711 lastSectionsMax_Size = *pSizeOfRawData > *pVirtualSize ? *pSizeOfRawData : *pVirtualSize; 712 713 //计算最终合并后节对应SizeOfRawData或VirtualSize的大小 714 finalSections_Size = pLastSectionHeader->VirtualAddress + lastSectionsMax_Size - pOptionHeader->SizeOfHeaders; 715 716 //修改第一个节的SizeOfRawData和VirtualSize的值,将其更改为上面计算出来的最终值 717 pSectionHeader->Misc.VirtualSize = finalSections_Size; 718 pSectionHeader->SizeOfRawData = finalSections_Size; 719 720 //修改pPEHeader->NumberOfSections的属性为0x01 721 numberOfSection = 0x01; 722 723 sizeOfFile = pOptionHeader->SizeOfHeaders + finalSections_Size; 724 //使用winhex打开notepad.exe 是0x00000400,这是第一个节之前的所有大小 725 // for(DWORD i = 0;i<numberOfSection;i++) 726 // { 727 // sizeOfFile += pSectionHeader[i].SizeOfRawData; // pSectionHeader[i]另一种加法 728 // } 729 730 //根据SizeOfImage申请新的空间 731 pTempNewBuffer = malloc(sizeOfFile); 732 733 if (!pTempNewBuffer) 734 { 735 printf("申请内存空间失败 "); 736 return 0; 737 } 738 //初始化新的缓冲区 739 memset(pTempNewBuffer,0,sizeOfFile); 740 //根据SizeOfHeaders 先copy头 741 memcpy(pTempNewBuffer,pDosHeader,sizeOfFile); 742 //根据节表循环复制节 743 //PIMAGE_SECTION_HEADER pTempSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader); 744 // PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader; 745 // for (int j=0;j<pPEHeader->NumberOfSections;j++,pTempSectionHeader++) 746 // { 747 // memcpy((PDWORD)((DWORD)pTempNewBuffer+pTempSectionHeader->PointerToRawData), 748 // (PDWORD)((DWORD)pImageBuffer+pTempSectionHeader->VirtualAddress), 749 // pTempSectionHeader->SizeOfRawData); 750 // } 751 752 //返回数据 753 *pNewBuffer = pTempNewBuffer; 754 pTempNewBuffer = NULL; 755 return sizeOfFile; 756 } 757 758 //ImageBuffer ---> NewBuffer 759 //将拉伸后加载到内存的ImageBuffer存入到NewBuffer里面,然后准备存盘; 760 DWORD CopyImageBufferToNewBuffer(IN LPVOID pImageBuffer,OUT LPVOID* pNewBuffer) 761 { 762 //下面大部分操作都是跟上面一样的,这里就不再赘述了 763 PIMAGE_DOS_HEADER pDosHeader = NULL; 764 PIMAGE_NT_HEADERS pNTHeader = NULL; 765 PIMAGE_FILE_HEADER pPEHeader = NULL; 766 PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL; 767 PIMAGE_SECTION_HEADER pSectionHeader = NULL; 768 LPVOID pTempNewBuffer = NULL; 769 DWORD sizeOfFile = 0; 770 DWORD numberOfSection = 0; 771 772 if (pImageBuffer == NULL) 773 { 774 printf("缓冲区指针无效 "); 775 } 776 //判断是否是有效的MZ标志 777 if (*((PWORD)pImageBuffer) != IMAGE_DOS_SIGNATURE) 778 { 779 printf("不是有效的MZ头 "); 780 return 0; 781 } 782 pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer; 783 //判断是否是有效的PE标志 784 if (*((PDWORD)((DWORD)pImageBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) 785 { 786 printf("不是有效的PE标志 "); 787 return 0; 788 } 789 //NT头地址 790 pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer + pDosHeader->e_lfanew); 791 //标准PE文件头 792 pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4); 793 //可选PE头 794 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER); 795 //第一个节表地址 796 pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader); 797 798 //计算文件需要的空间--最后一个节的文件偏移+节对齐后的长度 799 /* 800 numberOfSection = pPEHeader->NumberOfSections; 801 pSectionHeader = pSectionHeader[numberOfSection-1]; 802 sizeOfFile = (pSectionHeader->PointerToRawData + pSectionHeader->Misc.VirtualSize + pOptionHeader->FileAlignment); 803 printf("sizeOfFile %X ",sizeOfFile); 804 805 for (DWORD i=0;i<=numberOfSection;i++) 806 { 807 sizeOfFile += sizeOfFile[i]; 808 } 809 */ 810 811 sizeOfFile = pOptionHeader->SizeOfHeaders; 812 //使用winhex打开notepad.exe 是0x00000400,这是第一个节之前的所有大小 813 for(DWORD i = 0;i<pPEHeader->NumberOfSections;i++) 814 { 815 sizeOfFile += pSectionHeader[i].SizeOfRawData; // pSectionHeader[i]另一种加法 816 } 817 818 /* 819 上面的for循环大概意思就是基于几个节的数量依次循环叠加sizeOfFile的值;因为SizeOfRawData是文件中对齐后的大小; 820 所以循环计算如下: 821 sizeOfFile = 0x00000400 + 0x00007800 = 0x00007C00 822 sizeOfFile = 0x00007C00 + 0x00000800 = 0x00008400 823 sizeOfFile = 0x00008400 + 0x00008000 = 0x00010400 824 825 */ 826 827 //根据SizeOfImage申请新的空间 828 pTempNewBuffer = malloc(sizeOfFile); 829 830 if (!pTempNewBuffer) 831 { 832 printf("申请内存空间失败 "); 833 return 0; 834 } 835 //初始化新的缓冲区 836 memset(pTempNewBuffer,0,sizeOfFile); 837 //根据SizeOfHeaders 先copy头 838 memcpy(pTempNewBuffer,pDosHeader,pOptionHeader->SizeOfHeaders); 839 //根据节表循环复制节 840 //PIMAGE_SECTION_HEADER pTempSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader); 841 PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader; 842 for (int j=0;j<pPEHeader->NumberOfSections;j++,pTempSectionHeader++) 843 { 844 /*memcpy((LPVOID)((DWORD)pTempNewBuffer + pTempSectionHeader->PointerToRawData), 845 (LPVOID)((DWORD)pImageBuffer + pTempSectionHeader->VirtualAddress), 846 pTempSectionHeader->SizeOfRawData);*/ 847 //PointerToRawData节区在文件中的偏移,VirtualAddress节区在内存中的偏移地址,SizeOfRawData节在文件中对齐后的尺寸 848 memcpy((PDWORD)((DWORD)pTempNewBuffer+pTempSectionHeader->PointerToRawData), 849 (PDWORD)((DWORD)pImageBuffer+pTempSectionHeader->VirtualAddress), 850 pTempSectionHeader->SizeOfRawData); 851 //printf("%X --> PoniterToRadata ",pTempSectionHeader->PointerToRawData); 852 //printf("%X --> VirtualAddress ",pTempSectionHeader->VirtualAddress); 853 //printf("%X --> VirtualSize ",pTempSectionHeader->Misc.VirtualSize); 854 } 855 856 //返回数据 857 *pNewBuffer = pTempNewBuffer; 858 pTempNewBuffer = NULL; 859 return sizeOfFile; 860 } 861 862 //将上面得到的MemBuffer存盘到本地; 863 BOOL MemeryTOFile(IN LPVOID pMemBuffer,IN size_t size,OUT LPSTR lpszFile) 864 { 865 FILE* fp = NULL; 866 fp = fopen(lpszFile, "wb+"); 867 if (!fp) // 这里我刚开始写漏了一个等于号,变成复制NULL了,导致错误 868 // if(fp == NULL) 可以这么写,没问题 869 { 870 fclose(fp); 871 return FALSE; 872 } 873 fwrite(pMemBuffer,size,1,fp); 874 fclose(fp); 875 fp = NULL; 876 return TRUE; 877 } 878 879 //RVA格式转换FOA --- RvaToFileOffset 880 DWORD RvaToFileOffset(IN LPVOID pFileBuffer,IN DWORD dwRva) 881 { 882 PIMAGE_DOS_HEADER pDosHeader = NULL; 883 PIMAGE_NT_HEADERS pNTHeader = NULL; 884 PIMAGE_FILE_HEADER pPEHeader = NULL; 885 PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL; 886 PIMAGE_SECTION_HEADER pSectionHeader = NULL; 887 DWORD numberOfSection = 0; 888 DWORD dwFOAValue = 0; 889 890 //判断指针是否有效 891 if (!pFileBuffer) 892 { 893 printf("pFileBuffer 指针无效 "); 894 return 0; 895 } 896 //判断是否是有效的MZ标志 897 if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE) 898 { 899 printf("pFileBuffer不是有效的MZ标志 "); 900 return 0; 901 } 902 //判断是否是一个有效的PE标志 903 pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; 904 if (*((PWORD)((DWORD)pFileBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) 905 { 906 printf("pFileBuffer不是一个有效的PE标志 "); 907 return 0; 908 } 909 910 printf("当前的Rva地址: %#X ",dwRva); 911 pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew); 912 pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader+0x04); 913 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER); 914 pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader); 915 916 //定义个临时节表指针进行下面的计算操作 917 numberOfSection = pPEHeader->NumberOfSections; 918 PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader; 919 920 921 //判断dwRva所处的节 922 if (dwRva <= pOptionHeader->SizeOfHeaders) 923 { 924 return (DWORD)dwRva; 925 } 926 //上面是判断如果rva地址所处的节在第一个节之前那么直接返回rva的地址; 927 //否则下面就是开始遍历查找节; 928 else 929 { 930 for (DWORD n = 0; n < numberOfSection; n++, pTempSectionHeader++) 931 {//下面是判断在哪个节的范围,然后根据rva所在的地址减去所在节的VirtualAddress得到的偏移值加上文件中对应节的偏移值PointerToRawData 932 if ((dwRva >= pTempSectionHeader->VirtualAddress) && (dwRva < pTempSectionHeader->VirtualAddress + pTempSectionHeader->Misc.VirtualSize)) 933 { 934 dwFOAValue = dwRva - pTempSectionHeader->VirtualAddress + pTempSectionHeader->PointerToRawData; 935 } 936 else 937 { 938 printf("RvaToFoa 转换失败! "); 939 return 0; 940 } 941 } 942 } 943 return dwFOAValue; 944 } 945 946 //FOA格式转换RVA --- ImageOffset 947 DWORD FoaToImageOffset(IN LPVOID pFileBuffer,IN DWORD dwFoa) 948 { 949 PIMAGE_DOS_HEADER pDosHeader = NULL; 950 PIMAGE_NT_HEADERS pNTHeader = NULL; 951 PIMAGE_FILE_HEADER pPEHeader = NULL; 952 PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL; 953 PIMAGE_SECTION_HEADER pSectionHeader = NULL; 954 DWORD numberOfSection = 0; 955 DWORD dwRVAValue = 0; 956 957 //判断指针是否有效 958 if (!pFileBuffer) 959 { 960 printf("pFileBuffer 指针无效 "); 961 return 0; 962 } 963 //判断是否是有效的MZ标志 964 if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE) 965 { 966 printf("pFileBuffer不是有效的MZ标志 "); 967 return 0; 968 } 969 //判断是否是一个有效的PE标志 970 pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; 971 if (*((PWORD)((DWORD)pFileBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) 972 { 973 printf("pFileBuffer不是一个有效的PE标志 "); 974 return 0; 975 } 976 977 printf("当前的Foa地址: %#X ",dwFoa); 978 pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew); 979 pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader+0x04); 980 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER); 981 pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader); 982 983 //定义个临时节表指针进行下面的计算操作 984 numberOfSection = pPEHeader->NumberOfSections; 985 PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader; 986 987 if (dwFoa <= pOptionHeader->SizeOfHeaders) 988 { 989 return (DWORD)dwFoa; 990 } 991 else 992 { 993 for (DWORD n = 0; n < numberOfSection; n++, pTempSectionHeader++) 994 { 995 if ((dwFoa >= pTempSectionHeader->PointerToRawData) && (dwFoa < pTempSectionHeader->SizeOfRawData)) 996 { 997 dwRVAValue = dwFoa - pTempSectionHeader->PointerToRawData + pTempSectionHeader->VirtualAddress; 998 } 999 else 1000 { 1001 printf("FoaToRva 转换失败! "); 1002 return 0; 1003 } 1004 } 1005 } 1006 return dwRVAValue; 1007 } 1008 1009 //在原有的exe文件中开始操作添加ShellCode代码; 1010 1011 VOID AddCodeInCodeSec() 1012 { 1013 LPVOID pFileBuffer = NULL; 1014 LPVOID pImageBuffer = NULL; 1015 LPVOID pNewBuffer = NULL; 1016 PIMAGE_DOS_HEADER pDosHeader = NULL; 1017 PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL; 1018 PIMAGE_SECTION_HEADER pSectionHeader = NULL; 1019 PBYTE codeBegin = NULL; 1020 BOOL isOK = FALSE; 1021 DWORD size = 0; 1022 1023 //File-->FileBuffer 1024 ReadPEFile(FilePath_In,&pFileBuffer); 1025 if (!pFileBuffer) 1026 { 1027 printf("文件-->缓冲区失败 "); 1028 return ; 1029 } 1030 1031 //FileBuffer-->ImageBuffer 1032 CopyFileBufferToImageBuffer(pFileBuffer,&pImageBuffer); 1033 if (!pImageBuffer) 1034 { 1035 printf("FileBuffer-->ImageBuffer失败 "); 1036 free(pFileBuffer); 1037 return ; 1038 } 1039 1040 //判断代码段空闲区域是否能够足够存储ShellCode代码 1041 pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer; 1042 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)(((DWORD)pImageBuffer + pDosHeader->e_lfanew) + 4 + IMAGE_SIZEOF_FILE_HEADER); 1043 pSectionHeader = (PIMAGE_SECTION_HEADER)(((DWORD)pImageBuffer + pDosHeader->e_lfanew) + 4 + IMAGE_SIZEOF_FILE_HEADER + IMAGE_SIZEOF_NT_OPTIONAL32_HEADER); 1044 if (((pSectionHeader->SizeOfRawData) - (pSectionHeader->Misc.VirtualSize)) < SHELLCODELENGTH) 1045 { 1046 printf("代码区域空闲空间不够 "); 1047 free(pFileBuffer); 1048 free(pImageBuffer); 1049 } 1050 1051 //将代码复制到空闲区域 1052 codeBegin = (PBYTE)((DWORD)pImageBuffer + pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize); 1053 printf("pSectionHeader->VirtualAddress: %#010X ", pSectionHeader->VirtualAddress); 1054 printf("pSectionHeader->Misc.VirtualSize: %#010X ", pSectionHeader->Misc.VirtualSize); 1055 printf("codeBegin: %#010X ", codeBegin); 1056 1057 memcpy(codeBegin,ShellCode,SHELLCODELENGTH); 1058 1059 //修正E8-->call后面的代码区域 1060 DWORD callAddr = (MESSAGEBOXADDR - (pOptionHeader->ImageBase + ((DWORD)(codeBegin + 0xD) - (DWORD)pImageBuffer))); 1061 printf("callAddr ---> %#010X ",callAddr); 1062 *(PDWORD)(codeBegin + 0x09) = callAddr; 1063 printf("*(PWORD)(codeBegin + 0x09) ---> %#010X ",*(PDWORD)(codeBegin + 0x09)); 1064 /* 1065 关于修正E8的理解,公式:X = 要跳转的地址 - (E8当前的地址 + 5); 1066 要跳转的地址,这里是毋庸置疑的,就是我们要加入代码MessageBox的地址; 1067 然后要减去E8当前的地址+5的位置,这里不是太好理解; 1068 我们的目的是要将E8后面的4个字节计算出来,然后写入到E8后面,也就是公式中X; 1069 上面公式E8当前地址+5 ,而在此情况要定位到这个位置就要从代码的Dos开始通过指针相加; 1070 进行位置偏移到E8当前地址+5的位置; 1071 所以定位codeBegin的位置是:pImageBuffer指针最开始的位置(Dos头位置)通过内存中偏移的宽度移动到第一个节表的位置; 1072 也就是上面的pSectionHeader->VirtualAddress 操作形式; 1073 然后再偏移第一个节表在内存中对齐前实际的宽度(尺寸)pSectionHeader->Misc.VirtualSize; 1074 上述一番操作之后就到了第一个节表没有对齐前的位置,这个位置就是我们可以添加ShellCode代码的起始位置; 1075 到了添加ShellCode代码的起始位置之后,就要想办法添加E8位置后面的4个字节,此时根据ShellCode代码的宽度; 1076 进行计算,确认0x6A 00 0x6A 00 0x6A 00 0x6A 00 E8 00 00 00 00 刚好向后面数13个位置,按照十六进制看; 1077 就是0xD,所以在codeBegin偏移0xD个位置即可到达E9的位置,这也就是我们说的(E8当前的地址 + 5); 1078 到了上面的位置之后,由于我们最终是需要在程序运行之后在内存中添加ShellCode代码;所以这里一定要计算出; 1079 其准确的偏移地址,这样不管怎么拉伸到哪个位置,都能准确找到位置; 1080 注意:这里需要注意一点理解,上面说的pImageBuffer这个是我们加载程序到我们申请的内存中,绝不是程序在; 1081 运行中的那个内存,这里一定要理解清楚,她们是不一样的,理解了这个就能理解上面代码为什么要减去Dos头的; 1082 首地址,(DWORD)(codeBegin + 0xD) - (DWORD)pImageBuffer) 1083 */ 1084 1085 //修正E9-->jmp后面的代码区域 1086 DWORD jmpAddr = ((pOptionHeader->ImageBase + pOptionHeader->AddressOfEntryPoint) - (pOptionHeader->ImageBase + ((DWORD)(codeBegin + SHELLCODELENGTH) - (DWORD)pImageBuffer))); 1087 printf("jmpAddr ---> %#010X ",jmpAddr); 1088 *(PDWORD)(codeBegin + 0x0E) = jmpAddr; 1089 printf("*(PWORD)(codeBegin + 0x0E) ---> %#010X ",*(PDWORD)(codeBegin + 0x0E)); 1090 /* 1091 公式:X = 要跳转的地址 - (E9当前的地址 + 5) 1092 这里同样是要计算出E9后面4个字节的地址,我们的目的是在这里添加OEP的地址,让其执行完成MessageBox之后跳转; 1093 OEP的地址,那么这里就要先计算出OEP地址,就是pOptionHeader->ImageBase + pOptionHeader->AddressOfEntryPoint; 1094 再减去(E9当前的地址 + 5) 0x6A 00 0x6A 00 0x6A 00 0x6A 00 E8 00 00 00 00 E9 00 00 00 00; 1095 (DWORD)codeBegin + SHELLCODELENGTH 就是加上ShellCode总长度,偏移完成之后减去ImageBuffer首地址再加上ImageBase; 1096 */ 1097 1098 //修正OEP 1099 printf("pOptionHeader->AddressOfEntryPoint ---> %#010X ",pOptionHeader->AddressOfEntryPoint); 1100 printf("(DWORD)codeBegin ---> %#010X ",((DWORD)codeBegin - (DWORD)pImageBuffer)); 1101 pOptionHeader->AddressOfEntryPoint = (DWORD)codeBegin - (DWORD)pImageBuffer; 1102 printf("pOptionHeader->AddressOfEntryPoint ---> %#010X ",pOptionHeader->AddressOfEntryPoint); 1103 //修正OEP好理解,就是定位到OEP地址,然后直接通过codeBegin地址减去pImageBuffer的首地址即可; 1104 1105 //ImageBuffer-->NewBuffer 1106 size = CopyImageBufferToNewBuffer(pImageBuffer,&pNewBuffer); 1107 if (size == 0 || !pNewBuffer) 1108 { 1109 printf("ImageBuffer-->NewBuffer失败 "); 1110 free(pFileBuffer); 1111 free(pImageBuffer); 1112 return ; 1113 } 1114 1115 //NewBuffer-->文件 1116 isOK = MemeryTOFile(pNewBuffer,size,FilePath_Out); 1117 if (isOK) 1118 { 1119 printf("修改代码添加SHELLCODE 存盘成功 "); 1120 return ; 1121 } 1122 1123 //释放内存 1124 free(pFileBuffer); 1125 free(pImageBuffer); 1126 free(pNewBuffer); 1127 } 1128 1129 //调用函数新增节表和节操作,成功之后并存盘到本地; 1130 VOID NewSectionsInCodeSec() 1131 { 1132 LPVOID pFileBuffer = NULL; 1133 LPVOID pNewImageBuffer = NULL; 1134 BOOL isOK = FALSE; 1135 DWORD size1 = 0; 1136 DWORD size2 = 0; 1137 1138 //File-->FileBuffer 1139 size1 = ReadPEFile(FilePath_In,&pFileBuffer); 1140 if (size1 == 0 || !pFileBuffer) 1141 { 1142 printf("文件-->缓冲区失败 "); 1143 return ; 1144 } 1145 printf("fileSize - Final: %#X ",size1); 1146 1147 //FileBuffer-->NewImageBuffer 1148 size2 = CopyFileBufferToNewImageBuffer(pFileBuffer,size1,&pNewImageBuffer); 1149 if (size2 == 0 || !pFileBuffer) 1150 { 1151 printf("FileBuffer-->NewImageBuffer失败 "); 1152 free(pFileBuffer); 1153 return ; 1154 } 1155 printf("sizeOfFile - Final: %#X ",size2); 1156 //NewImageBuffer-->文件 1157 isOK = MemeryTOFile(pNewImageBuffer,size2,FilePath_Out); 1158 if (isOK) 1159 { 1160 printf("新增节表和节存盘成功 "); 1161 return ; 1162 } 1163 1164 //释放内存 1165 free(pFileBuffer); 1166 free(pNewImageBuffer); 1167 } 1168 1169 //调用函数扩大一个节 1170 VOID ExtendLastSectionsInCodeSec() 1171 { 1172 //ReadPEFile CopyFileBufferToImageBuffer CopyImageBufferToNewImageBuffer 1173 1174 LPVOID pFileBuffer = NULL; 1175 LPVOID pImageBuffer = NULL; 1176 LPVOID pNewImageBuffer = NULL; 1177 BOOL isOK = FALSE; 1178 DWORD FileBufferSize = 0; 1179 DWORD ImageBufferSize = 0; 1180 DWORD size = 0; 1181 1182 //File-->FileBuffer 1183 FileBufferSize = ReadPEFile(FilePath_In,&pFileBuffer); 1184 if (FileBufferSize == 0 || !pFileBuffer) 1185 { 1186 printf("文件-->缓冲区失败 "); 1187 return ; 1188 } 1189 printf("FileBufferSize - Final: %#X ",FileBufferSize); 1190 1191 //FileBuffer-->ImageBuffer 1192 ImageBufferSize = FileBufferToModifyImageBuffer(pFileBuffer,&pImageBuffer); 1193 if (ImageBufferSize == 0 || !pFileBuffer) 1194 { 1195 printf("FileBuffer-->ImageBuffer失败 "); 1196 free(pFileBuffer); 1197 return ; 1198 } 1199 printf("ImageBufferSize - Final: %#X ",ImageBufferSize); 1200 1201 size = CopyImageBufferToNewBuffer(pImageBuffer,&pNewImageBuffer); 1202 if (size == 0 || !pImageBuffer) 1203 { 1204 printf("pImageBuffer-->pNewImageBuffer失败 "); 1205 free(pFileBuffer); 1206 return ; 1207 } 1208 //pNewImageBuffer-->文件 1209 isOK = MemeryTOFile(pNewImageBuffer,size,FilePath_Out); 1210 if (isOK) 1211 { 1212 printf("扩大一个节成功,并存盘 "); 1213 return ; 1214 } 1215 1216 //释放内存 1217 free(pFileBuffer); 1218 free(pImageBuffer); 1219 free(pNewImageBuffer); 1220 } 1221 1222 1223 //调用函数扩大一个节 1224 VOID ModifySectionsOneInCodeSec() 1225 { 1226 //ReadPEFile CopyFileBufferToImageBuffer FileBufferToModifyOneImageBuffer MemeryTOFile 1227 1228 LPVOID pFileBuffer = NULL; 1229 LPVOID pImageBuffer = NULL; 1230 LPVOID pNewImageBuffer = NULL; 1231 BOOL isOK = FALSE; 1232 DWORD FileBufferSize = 0; 1233 DWORD ImageBufferSize = 0; 1234 DWORD size = 0; 1235 1236 //File-->FileBuffer 1237 FileBufferSize = ReadPEFile(FilePath_In,&pFileBuffer); 1238 if (FileBufferSize == 0 || !pFileBuffer) 1239 { 1240 printf("文件-->缓冲区失败 "); 1241 return ; 1242 } 1243 printf("FileBufferSize - Final: %#X ",FileBufferSize); 1244 1245 //FileBuffer-->ImageBuffer 1246 ImageBufferSize = CopyFileBufferToImageBuffer(pFileBuffer,&pImageBuffer); 1247 if (ImageBufferSize == 0 || !pFileBuffer) 1248 { 1249 printf("FileBuffer-->ImageBuffer失败 "); 1250 free(pFileBuffer); 1251 return ; 1252 } 1253 printf("ImageBufferSize - Final: %#X ",ImageBufferSize); 1254 1255 size = FileBufferToModifyOneImageBuffer(pImageBuffer,&pNewImageBuffer); 1256 if (size == 0 || !pImageBuffer) 1257 { 1258 printf("pImageBuffer-->pNewImageBuffer失败 "); 1259 free(pFileBuffer); 1260 return ; 1261 } 1262 //pNewImageBuffer-->文件 1263 isOK = MemeryTOFile(pNewImageBuffer,size,FilePath_Out); 1264 if (isOK) 1265 { 1266 printf("合并节完成,存盘成功 "); 1267 return ; 1268 } 1269 1270 //释放内存 1271 free(pFileBuffer); 1272 free(pImageBuffer); 1273 free(pNewImageBuffer); 1274 } 1275 1276 VOID RvaAndFoaConversion() 1277 { 1278 LPVOID pFileBuffer = NULL; 1279 DWORD FileBufferSize = 0; 1280 DWORD FoaFinalVaule = 0; 1281 DWORD RvaFinalVaule = 0; 1282 size_t pRVA = 1180; 1283 size_t pFOA = 2279; 1284 1285 //File-->FileBuffer 1286 FileBufferSize = ReadPEFile(FilePath_In,&pFileBuffer); 1287 if (FileBufferSize == 0 || !pFileBuffer) 1288 { 1289 printf("文件-->缓冲区失败 "); 1290 return ; 1291 } 1292 printf("FileBufferSize: %#X ",FileBufferSize); 1293 1294 FoaFinalVaule = RvaToFileOffset(pFileBuffer,pRVA); 1295 if (FoaFinalVaule == 0 || !pFileBuffer) 1296 { 1297 printf("pFileBuffer-->读取失败 "); 1298 free(pFileBuffer); 1299 return ; 1300 } 1301 printf("转换成功 --> FoaFinalVaule 传进来的pRVA值: : %#X %#X ",FoaFinalVaule,pRVA); 1302 1303 RvaFinalVaule = FoaToImageOffset(pFileBuffer,pFOA); 1304 if (RvaFinalVaule == 0 || !pFileBuffer) 1305 { 1306 printf("pFileBuffer-->读取失败 "); 1307 free(pFileBuffer); 1308 return ; 1309 } 1310 printf("转换成功 --> RvaFinalVaule 传进来的pFOA值 : %#X %#X ",RvaFinalVaule,pFOA); 1311 1312 1313 free(pFileBuffer); 1314 }
头文件代码
// gbpeall.h: interface for the gbpeall class. // ////////////////////////////////////////////////////////////////////// #if !defined(AFX_GBPEALL_H__C24C6881_E003_41F7_BE14_24DDA1702CCD__INCLUDED_) #define AFX_GBPEALL_H__C24C6881_E003_41F7_BE14_24DDA1702CCD__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #include <string.h> #include <windows.h> #include <stdlib.h> //#define FILEPATH_IN "C:\WINDOWS\system32\kernel32.dll" // #define FilePath_In "C:\cntflx\notepad.exe" #define FilePath_In "C:\cntflx\ipmsg.exe" //#define FilePath_Out "C:\cntflx\notepadnewpes.exe" //#define FilePath_Out "C:\cntflx\ipmsgnewpeaddcodes.exe" //#define FilePath_Out "C:\cntflx\ipmsgnewaddsections.exe" //#define FilePath_Out "C:\cntflx\ipmsgextendsections.exe" #define FilePath_Out "C:\cntflx\ipmsgemerge.exe" #define MESSAGEBOXADDR 0x77D5050B #define SHELLCODELENGTH 0x12 //16进制的,转换为十进制就是18 extern BYTE ShellCode[]; //读文件 --->FileBuffer DWORD ReadPEFile(IN LPSTR lpszFile,OUT LPVOID* pFileBuffer); //写到ImageBuffer,FileBuffer ---> ImageBuffer DWORD CopyFileBufferToImageBuffer(IN LPVOID pFileBuffer,OUT LPVOID* pImageBuffer); //写到NewImageBuffer, FileBuffer ---> NewImageBuffer DWORD CopyFileBufferToNewImageBuffer(IN LPVOID pFileBuffer,IN size_t fileSize,OUT LPVOID* pNewImageBuffer); //写到NewImageBuffer, 这里供扩大节使用; DWORD FileBufferToModifyImageBuffer(IN LPVOID pFileBuffer,OUT LPVOID* pNewImageBuffer); //写入到NewBuffer,目的是将拉伸后的ImageBuffer再缩回来,为存盘准备 DWORD CopyImageBufferToNewBuffer(IN LPVOID pImageBuffer,OUT LPVOID* pNewBuffer); //写入到NewImageBuffer,这里供合并节使用? DWORD FileBufferToModifyOneImageBuffer(IN LPVOID pImageBuffer,OUT LPVOID* pNewBuffer); //写到pNewBuffer里面,从pNewImageBuffer写入 ---> pNewBuffer //DWORD ModifyImageBufferToNewBuffer(IN LPVOID pNewImageBuffer,OUT LPVOID* pNewBuffer); //Rva转Foa DWORD RvaToFileOffset(IN LPVOID pFileBuffer,IN DWORD dwRva); //Foa转RVA DWORD FoaToImageOffset(IN LPVOID pFileBuffer,IN DWORD dwFoa); //对齐大小 DWORD AlignLength(DWORD Actuall_size,DWORD Align_size); //将MemBuffer写入到硬盘,这里就是将各种修改好的内存文件,存入到本地硬盘中; BOOL MemeryTOFile(IN LPVOID pMemBuffer,IN size_t size,OUT LPSTR lpszFile); //DWORD RvaToFileOffset(IN LPVOID pFileBuffer,IN DWORD dwRva); //调用函数,添加ShellCode代码 VOID AddCodeInCodeSec(); //这个调用函数用到下面的4个函数 //ReadPEFile CopyFileBufferToImageBuffer CopyImageBufferToNewBuffer MemeryTOFile //调用函数,新增节表和节操作; VOID NewSectionsInCodeSec(); //这个调用函数用到下面的3个函数 //ReadPEFile CopyFileBufferToNewImageBuffer MemeryTOFile //调用函数,扩大最后一个节 VOID ExtendLastSectionsInCodeSec(); //这个调用函数用到下面的4个函数 //ReadPEFile FileBufferToModifyImageBuffer CopyImageBufferToNewImageBuffer MemeryTOFile //调用函数,合并节 VOID ModifySectionsOneInCodeSec(); //这个调用函数用到下面的4个函数 //ReadPEFile CopyFileBufferToImageBuffer FileBufferToModifyOneImageBuffer MemeryTOFile //调用函数,Rva和Foa之间的相互转换; VOID RvaAndFoaConversion(); //这个调用函数用到下面的3个函数 //ReadPEFile RvaToFileOffset FoaToImageOffset #endif // !defined(AFX_GBPEALL_H__C24C6881_E003_41F7_BE14_24DDA1702CCD__INCLUDED_)
main函数入口代码
// allpelx.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "gbpeall.h" int main(int argc, char* argv[]) { //Fun(); //AddCodeInCodeSec(); //NewSectionsInCodeSec(); //ExtendLastSectionsInCodeSec(); //ModifySectionsOneInCodeSec(); RvaAndFoaConversion(); printf("Hello World! Cntf "); system("pause"); return 0; }
执行结果