PE PE文件格式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 PE: IMAGE_DOS_HEADER e_lfanew (OD) DOS_STUB[176] IMAGE_NT_HEADERS; NT头 DWORD Signature IMAGE_FILE_HEADER; IMAGE_OPTIONAL_HEADER32 IMAGE_DATA_DIRECTORY [] IMAGE_SECTION_HEADER[] 数据
DOS头:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 typedef struct _IMAGE_DOS_HEADER { WORD e_magic; WORD e_cblp; WORD e_cp; WORD e_crlc; WORD e_cparhdr; WORD e_minalloc; WORD e_maxalloc; WORD e_ss; WORD e_sp; WORD e_csum; WORD e_ip; WORD e_cs; WORD e_lfarlc; WORD e_ovno; WORD e_res[4 ]; WORD e_oemid; WORD e_oeminfo; WORD e_res2[10 ]; LONG e_lfanew; } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
magic number :
魔数是文件的前几位,它唯一地标识了文件的类型。这使得编程更容易,因为不需要搜索复杂的文件结构来识别文件类型。
例如Windows的exe:
HEADER所占的位置及大小
正常32程序无法兼容16位 在16位中运行就会出现this program。。。
指向新的文件格式
除了magic number 以及F0 00 00 00(新文件格式头)的都可以修改 直到PE(nt的magic number)
若修改新文件格式头的代码 会使OD无法调试 认为是无效文件格式(拒绝创建进程)
NT头:
1 2 3 4 5 6 typedef struct _IMAGE_NT_HEADERS { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER32 OptionalHeader; } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
在一个有效的 PE 文件里,Signature 字段被设置为00004550h , ASCII 码字符是“PE00 ”。标志这 PE 文件头的开始。 “PE00 ” 字符串是 PE 文件头的开始,DOS 头部的 e_lfanew 字段正是指向这里。
文件头(标准PE头): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 typedef struct _IMAGE_FILE_HEADER { WORD Machine; WORD NumberOfSections; DWORD TimeDateStamp; DWORD PointerToSymbolTable; DWORD NumberOfSymbols; WORD SizeOfOptionalHeader; WORD Characteristics; } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; #define IMAGE_FILE_RELOCS_STRIPPED 0x0001 #define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 #define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 #define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 #define IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010 #define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 #define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 #define IMAGE_FILE_32BIT_MACHINE 0x0100 #define IMAGE_FILE_DEBUG_STRIPPED 0x0200 #define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 #define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 #define IMAGE_FILE_SYSTEM 0x1000 #define IMAGE_FILE_DLL 0x2000 #define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 #define IMAGE_FILE_BYTES_REVERSED_HI 0x8000
选项头: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 typedef struct _IMAGE_OPTIONAL_HEADER { WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; DWORD BaseOfData; DWORD ImageBase; DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; DWORD SizeOfStackReserve; DWORD SizeOfStackCommit; DWORD SizeOfHeapReserve; DWORD SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } IMAGE_OPTIONAL_HEADER32, * PIMAGE_OPTIONAL_HEADER32;
节表: PE节表位于PE头的下面,PE节表中记录了各个节表的起始位置、大小,以及在内存中偏移位置和属性。
一个PE文件有多少个节,就有多少个节表 !每一个节表的大小是确定的,40字节
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #define IMAGE_SIZEOF_SHORT_NAME 8 typedef struct _IMAGE_SECTION_HEADER { BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; union { DWORD PhysicalAddress; DWORD VirtualSize; } Misc; DWORD VirtualAddress; DWORD SizeOfRawData; DWORD PointerToRawData; DWORD PointerToRelocations; DWORD PointerToLinenumbers; WORD NumberOfRelocations; WORD NumberOfLinenumbers; DWORD Characteristics; } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
定位节表位置 :一个PE文件从哪里开始是节表(硬盘上的地址):DOS头大小 + 垃圾空位 + PE签名大小 + 标准PE头大小 + 可选PE头大小(需要查);
DOS头大小固定为64字节;
PE签名大小为4字节;
标准PE头大小固定为20字节;
可选PE头大小可以通过标准PE头中的SizeOfOptionalHeader字段的值来确定
1 e_lfanew + 4 + 20 + SizeOfOptionalHeader = 节表开始地址
Name :
8个字节一般是以“\0”结尾的ASCII码字符串来标识的名称,内容可以自定义,该名称并不遵守必须以“\0”结尾的规律,如果不是以“\0”结尾,系统会截取8个字节的长度进行处理,所以不能以名称作为定位的标准和依据,正确的方法是按照IMAGE_OPTIONAL_HEADER32结构中的数据目录字段结合进行定位(当按照char *来定位时若结尾无“\0”则会持续一直读取)
FileBuffer->ImageBuffer 一个文件在硬盘要运行的时候是要经过拉伸的,如果简单拉伸后,程序就能够运行吗?
答案是否定的 ImageBuffer已经离运行非常接近了
Misc.VirtualSize (在内存中拉伸后的实际大小)和 SizeOfRawData谁更大?
因为可能会存在一些未初始化数据,而这些数据在文件中是不会体现的,但是放在内存的时候,会被计算出来,会在内存里会留出空间。
因此有可能Misc.VirtualSize 比 SizeOfRawData大 VirtualSize是加载到内存对齐前的大小,SizeOfRawData是磁盘文件中对齐后的大小。
FileBuffer拷贝到ImageBuffer 将FileBuffer放到ImageBuffer 得先读取sizeofimage大小 然后开辟相应大小的空间 SIzeofheaders大小在FileBuffer和ImageBuffer中是一致的 可以直接拷贝
ImageBuffer空间申请完后需要全部初始化为0
将节的内容拷贝到ImageBuffer中 PointerToRawData决定从什么地方拷贝 VirtualAddress决定拷贝到什么地方
拷贝的时候应该按照那个较小的数值(或者选择SizeOfRawData来确定需要复制的节的大小)
选择SizeOfRawData的原因: 在极端情况下,当节中存在足够大未初始化的数据时,按照Misc.VirtualSize值将FileBuffer中的数据复制到ImageBuffer中,很可能会把FileBuffer中下一个节的数据也复制过去,这样就会造成复制错误。
计算内存中节存储的某个地址在文件中的地址: 在某个节中地址为0x501234 基地址为0x500000
节表的信息: 节1,PointerToRawData 400: VirtualAddress 1000
节2:PointerToRawData 600: VirtualAddress 2000
节3,PointerToRawData 800: VirtualAddress 3000
0x501234-0x500000=0x1234
0x1234<VirtualAddress 1000 && 0x1234>VirtualAddress 2000
所以可以判断位于第一个节中
判断:循环判断条件使得偏移>VirtualAddress同时<VirtualAddress+Misc.VirtualSize
计算位于节中的偏移: 0x1234-0x1000=0x234
所以在文件中的地址就是在文件中第一个节表的地址+0x234
VA,虚拟地址,也就是程序被加载到内存中的地址
RVA,相对虚拟地址
将VA减去MODULE的BASE就是RVA的值。
FOA:在文件中的对应的地址
代码节空白区添加代码 在exe中添加messagebox 并获取其地址
在messagebox处下一个断点 然后在断点中查看
messagebox地址为0x772AAC60
硬编码:E8 E9(call jmp)
硬编码:
硬编码是指将具体的数值、路径、参数等直接写入程序代码中,而不通过变量或配置文件来表示。这样的做法使得程序中的这些数值和参数变得固定,不容易修改,且缺乏灵活性。硬编码的值通常被称为”魔法数”(Magic Numbers)或”魔法字符串”,因为它们没有直观的含义,只能通过查看代码来了解。
举例 :
分析可以发现,无论是E8还是E9,后面的地址貌似都不是直接小端序转化过来的地址
真正要跳转的地址=E8这条指令的下一条指令的地址 + X X=真正要跳转的地址 - E8要要跳转的地址的下一条指令的地址
X为E8后面的地址
例如(上图中jmp):
1 2 772 AAD77 /E9 A9000000 jmp user32.772 AAE25772 AAD7C 8B 5C24 14 mov ebx,dword ptr ss:[esp+0x14 ]
772AAE25为真正要跳转的地址
772AAD7C E9这条指令的下一条指令的地址
X=772AAE25-772AAD7C=00 00 00 A9
转为小端序即是E9 A9 00 00 00
也可以这样计算X = 要跳转的地址 - (E9的地址 +5)
E9的地址为拉伸后的地址
将代码添加到空白区不需要修改节的属性
在任意代码空白区添加代码
计算NewBuffer的大小 :
通过最后一个节表的信息知道最后一个节表的偏移地址+ImageBase+节的大小,就是NewBuffer的大小
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 #include <iostream> #include <windows.h> PIMAGE_DOS_HEADER dosHeader = nullptr; PIMAGE_FILE_HEADER fileHeader = nullptr; PIMAGE_OPTIONAL_HEADER32 optionalHeader = nullptr; PIMAGE_SECTION_HEADER* sectionArr = nullptr; LPVOID MemoryData = nullptr; LPVOID ReadProgramData (LPCSTR programPath) { FILE* program = nullptr; size_t size = NULL ; LPVOID data = nullptr; fopen_s(&program, programPath, "rb" ); if (program == nullptr) { printf ("failed to open by program!\n" ); goto END; } if (fseek(program, 0 , SEEK_END) == 0 ) { size = ftell(program); if (fseek(program, 0 , SEEK_SET) != 0 ) { printf ("failed to move the pointer to begin!\n" ); goto END; } } else { printf ("failed to move the pointer to end!\n" ); goto END; } data = malloc (size); if (data != nullptr) { memset (data, '\0' , size); fread_s(data, size, 1 , size, program); } else { printf ("failed to apply by memory\n" ); goto END; } END: if (program) fclose(program); return data; } void AnalyzePeStruct (PCHAR fileData, PIMAGE_DOS_HEADER& dos, PIMAGE_FILE_HEADER& file, PIMAGE_OPTIONAL_HEADER32& optional, PIMAGE_SECTION_HEADER*& section) { dos = (PIMAGE_DOS_HEADER)fileData; fileData = &fileData[dos->e_lfanew + 4 ]; file = (PIMAGE_FILE_HEADER)fileData; fileData = &fileData[20 ]; optional = (PIMAGE_OPTIONAL_HEADER32)fileData; fileData = &fileData[file->SizeOfOptionalHeader]; section = (PIMAGE_SECTION_HEADER*)malloc (file->NumberOfSections * sizeof (IMAGE_SECTION_HEADER)); if (section != nullptr) { for (size_t index = 0 ; index < file->NumberOfSections; index++) { section[index] = (PIMAGE_SECTION_HEADER)fileData; fileData = &fileData[40 ]; } } } LPVOID ShrinkData (LPVOID memoryData) { LPVOID fileData = nullptr; LPVOID tempMemoryDataPointer = nullptr; LPVOID tempFileDataPointer = nullptr; int copyCharNumber = NULL ; size_t fileDataSize = optionalHeader->SizeOfHeaders; for (size_t index = 0 ; index < fileHeader->NumberOfSections; index++) { fileDataSize += sectionArr[index]->SizeOfRawData; } fileData = malloc (fileDataSize); if (fileData != nullptr) { memset (fileData, '\0' , fileDataSize); tempMemoryDataPointer = memoryData; tempFileDataPointer = fileData; copyCharNumber = optionalHeader->FileAlignment; while (copyCharNumber < optionalHeader->SizeOfHeaders) { copyCharNumber *= 2 ; } memcpy_s(tempFileDataPointer, copyCharNumber, tempMemoryDataPointer, optionalHeader->SizeOfHeaders); for (size_t index = 0 ; index < fileHeader->NumberOfSections; index++) { copyCharNumber = optionalHeader->FileAlignment; tempFileDataPointer = (LPVOID)((UINT_PTR)fileData + sectionArr[index]->PointerToRawData); tempMemoryDataPointer = (LPVOID)((UINT_PTR)memoryData + sectionArr[index]->VirtualAddress); while (copyCharNumber < sectionArr[index]->SizeOfRawData) { copyCharNumber *= 2 ; } memcpy_s(tempFileDataPointer, copyCharNumber, tempMemoryDataPointer, sectionArr[index]->SizeOfRawData); } } return fileData; } LPVOID StretchData (PCHAR fileData) { LPVOID tempMemoryDataPointer = nullptr; PCHAR tempFileDataPointer = nullptr; int copyCharNumber = NULL ; AnalyzePeStruct(fileData, dosHeader, fileHeader, optionalHeader, sectionArr); MemoryData = malloc (optionalHeader->SizeOfImage); if (MemoryData != nullptr) { memset (MemoryData, '\0' , optionalHeader->SizeOfImage); tempMemoryDataPointer = MemoryData; tempFileDataPointer = fileData; copyCharNumber = optionalHeader->SectionAlignment; while (copyCharNumber < optionalHeader->SizeOfHeaders) { copyCharNumber *= 0x2 ; } memcpy_s(tempMemoryDataPointer, copyCharNumber, tempFileDataPointer, optionalHeader->SizeOfHeaders); for (size_t index = 0 ; index < fileHeader->NumberOfSections; index++) { copyCharNumber = optionalHeader->SectionAlignment; tempMemoryDataPointer = (LPVOID)((UINT_PTR)MemoryData + sectionArr[index]->VirtualAddress); tempFileDataPointer = (PCHAR)((UINT_PTR)fileData + sectionArr[index]->PointerToRawData); while (copyCharNumber < sectionArr[index]->SizeOfRawData) { copyCharNumber *= 2 ; } memcpy_s(tempMemoryDataPointer, copyCharNumber, tempFileDataPointer, sectionArr[index]->SizeOfRawData); } } return MemoryData; } bool WriteMyFilePeData (LPVOID fileData, LPCSTR newProgramPath) { FILE* newProgram = nullptr; bool result = true ; size_t size = NULL ; fopen_s(&newProgram, newProgramPath, "wb" ); if (newProgram == nullptr) { printf ("failed to create by program!\n" ); result = false ; goto END; } size = _msize(fileData); fwrite(fileData, 1 , size, newProgram); END: if (newProgram) fclose(newProgram); return result; } UINT_PTR RvaToFoa (UINT_PTR address) { UINT_PTR thisSectionBegin = NULL ; UINT_PTR thisSectionEnd = NULL ; UINT_PTR offset = NULL ; for (size_t index = 0 ; index < fileHeader->NumberOfSections; index++) { thisSectionBegin = (UINT_PTR)MemoryData + sectionArr[index]->VirtualAddress; thisSectionEnd = thisSectionBegin + sectionArr[index]->Misc.VirtualSize; if (thisSectionBegin <= address && thisSectionEnd >= address) { offset = address - (UINT_PTR)MemoryData; offset = offset - sectionArr[index]->VirtualAddress; offset = offset + sectionArr[index]->PointerToRawData; break ; } } return offset; } bool WriteNewDataToSection (char * memoryData) { char data[] = { 0x6A , 0x00 ,0x6A , 0x00 , 0x6A , 0x00 , 0x6A , 0x00 , 0xE8 ,0x00 ,0x00 ,0x00 ,0x00 , 0xE9 ,0x00 ,0x00 ,0x00 ,0x00 }; char * pointer = nullptr; char * tempDataPointer = nullptr; UINT_PTR entryPointer = NULL ; UINT_PTR E8_X = NULL ; UINT_PTR E9_X = NULL ; UINT_PTR message = (UINT_PTR)&MessageBoxA; UINT_PTR newEntryPointer = NULL ; for (DWORD index = 0 ; index < fileHeader->NumberOfSections; index++) { if (sectionArr[index]->Characteristics != 0x60000020 ) { continue ; } if (sectionArr[index]->SizeOfRawData - (sectionArr[index]->Misc.VirtualSize + sizeof (data)) >= 0 ) { pointer = (char *)((UINT_PTR)memoryData + sectionArr[index]->VirtualAddress + sectionArr[index]->Misc.VirtualSize); entryPointer = (UINT_PTR)pointer; tempDataPointer = &data[9 ]; E8_X = message - (optionalHeader->ImageBase + sectionArr[index]->VirtualAddress + ((UINT_PTR)&pointer[13 ] - (entryPointer - sectionArr[index]->Misc.VirtualSize))); tempDataPointer[0 ] = E8_X; tempDataPointer[1 ] = E8_X >> 8 ; tempDataPointer[2 ] = E8_X >> 16 ; tempDataPointer[3 ] = E8_X >> 24 ; tempDataPointer = &data[14 ]; E9_X = (optionalHeader->ImageBase + optionalHeader->AddressOfEntryPoint) - (optionalHeader->ImageBase + sectionArr[index]->VirtualAddress + ((UINT_PTR)&pointer[18 ] - (entryPointer - sectionArr[index]->Misc.VirtualSize)));; tempDataPointer[0 ] = E9_X; tempDataPointer[1 ] = E9_X >> 8 ; tempDataPointer[2 ] = E9_X >> 16 ; tempDataPointer[3 ] = E9_X >> 24 ; memcpy_s(pointer, sizeof (data), data, sizeof (data)); newEntryPointer = (entryPointer - (entryPointer - sectionArr[index]->Misc.VirtualSize)) + sectionArr[index]->VirtualAddress; optionalHeader->AddressOfEntryPoint = newEntryPointer; pointer = &memoryData[dosHeader->e_lfanew + 4 + sizeof (*fileHeader)]; memcpy_s(pointer, sizeof (*optionalHeader), optionalHeader, sizeof (*optionalHeader)); return true ; } } return false ; } int main () { char * filePe = (char *)ReadProgramData("C:\\Users\\64107\\source\\repos\\PE\\PE\\Release\\PE.exe" ); char * memoryPe = (char *)StretchData(filePe); if (WriteNewDataToSection(memoryPe) != false ) { LPVOID meFilePe = ShrinkData(memoryPe); WriteMyFilePeData(meFilePe, "C:\\Users\\64107\\source\\repos\\PE\\PE\\Release\\PE.exe" ); } else { printf ("程序可执行节区没有空白区域可供写入!\n" ); } system("pause" ); free (filePe); free (memoryPe); return 0 ; }
新增节-添加代码 新增节需要满足的条件:
确保新增节后,已存在的节表区域仍有剩余空间以容纳一个节表(遵循Windows规则,若不遵守规则,可能导致程序异常。)。计算公式如下:
1 SizeOfHeader - DOS文件头 - 可选PE头 - 节表数量 * 0x28(40) > 80
新增节步骤:
添加新的节表
在现有节表末尾新增一个节,可以直接复制已有节表数据进行初始化。
填充新增节后的空间
在新增节后,填充一个节大小的 0x00
,确保数据完整。
修改PE头中的节表数量
找到PE头中的NumberOfSections
字段,增加1,更新节表数量。
更新SizeOfImage
字段
修改PE头中的SizeOfImage
字段,增加内存对齐后的新增节大小。
在文件末尾新增节数据
在原有数据末尾,添加一个内存对齐后的新增节数据区域。
修正新增节的属性
确保新增节的属性字段设置正确(如只读、可执行等)。根据需要调整其Characteristics
字段。
填充新增节数据
在新增节区域内写入有效数据,避免数据为空导致程序运行失败。
当节表区空间不足时的处理方法:
方法:前移NT头
将NT头整体前移 0x20
个字节,覆盖DOS头后面的字符串部分,为节表区腾出额外空间。
注意事项:
确保调整后,PE结构完整且头部的偏移量正确。
修改后及时校验文件是否能够正常运行。
例:
找到节表数量为5
如图为已存在的节表 往下开始全是空白区(空间足够大 可以添加新的节)
复制40个字节即第一个节表到空白区,创建新的节表,同时将节数量改为6
假设新增大小为1000字节的节表,找到sizeofimage加上1000
在文件结尾插入1000h大小的字节
填写新节表信息
Name即节表的名字。可以随便改但不要超过限制大小;
Virtual Size即内存中的大小(对齐前的长度),这里直接填1000即可;
Virtual Offset即内存中的偏移(Virtual Address),根据前一个节表的信息可知这里应填00000E00+00005000=00005E00;
Raw Size即文件中大小(对齐后的长度),这里和Virtual Size填一样即可(1000),因为这个文件对齐前后没有拉伸;
同理Raw Offset即文件中偏移,和Virtual Offset填一样即可(00005E00);
Characteristics即块属性(标志),看需要填写,这里保持和第一个节表相同即可
扩大节-合并节-数据目录 扩大节 注意事项 :只能扩大最后一个节。
操作步骤:
拉伸到内存
分配新的空间
增加Ex
的空间,修改 SizeOfImage
:
1 SizeOfImage = SizeOfImage + Ex
修改最后一个节的大小
更新SizeOfImage
大小
合并节
拉伸到内存
计算合并后的大小
合并后的大小取决于所有节的大小:
1 2 Max = max(VirtualSize, SizeOfRawData) 内存对齐后的大小 合并节大小 = VirtualAddress + Max - SizeOfHeader 内存对齐后的值
修改第一个节的大小
更新第一个节的 VirtualSize
和 SizeOfRawData
,两者设为合并后的大小。
调整节的属性
修改第一个节的属性,使其包含所有节的属性(如读、写、执行权限)。
修改节表数量
更新 PE 头中的 NumberOfSections
字段,将其设为 1
。
数据目录 基础概念 :
数据目录位置:
数据目录位于可选 PE 头的最后一项,包含 PE 文件运行时需要的重要信息。
数据目录的作用:
定位程序的资源信息,例如:
程序图标位置。
使用的系统函数。
提供给其他程序的函数。
数据目录结构:
1 2 3 4 5 6 7 c typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; // 数据目录的内存偏移(经过内存拉伸) DWORD Size; // 数据目录的大小 } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
数据目录的 16 项:
与程序运行相关的主要目录项:
导出表 (Export Table)
导入表 (Import Table)
重定位表 (Relocation Table)
IAT 表 (Import Address Table)
其他目录项:资源表、异常信息表、安全证书表、调试信息表、版权所以表、全局指针表、TLS表、加载配置表、绑定导入表、延迟导入表、COM信息表、最后一个表保留未使用。