这里我们拥有到一个工具:winhex 15.8的版本,十六进制编辑器
我们看一下源代码 ,源代码中在代码段中只调用了两个函数,1:messagebox(),这里传四个参数,2:ExitProcess(),传一个参数
对源代码进行编辑,得到PE文件(helloworld.exe)
这里写自定义目录标题
图1

用winhex打开helloworld.exe,如图2,3,4,5 是整个文件(其中填充完全是0的没有截图,关键位置的截图了)
不要急,下面我们一步一步拆分图2,3,4,5部分
图2
图3
图4
图5
在这里先做一个总的说明:
查找PE资料,得到如图PE的结构,所有我们先看PE头部(从下面图2开始)
查结构体IMAGE_DOS_HEADER得到PE头的结构
下面我们从dos头开始
dos头
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; // Magic number
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
在PE的头部中只有两个字段是最重要的,其他的可以不用管,这两个分别是e_magic,e_lfanew,前者指明是DOS文件,后者指明PE的首地址,或者说PE标志
PE头
PE有由三个部分组成,又叫NT头
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
004000B0 50 45 00 00 ASCII "PE" ; PE signature (PE)
其中 DWORD Signature; 就是PE的头地址
IMAGE_FILE_HEADER FileHeader;为文件头结构体
IMAGE_OPTIONAL_HEADER32 OptionalHeader;为扩展头结构体
文件头结构体
typedef struct _IMAGE_FILE_HEADER {
WORD Machine; 2H
WORD NumberOfSections; 4H
DWORD TimeDateStamp; 8H
DWORD PointerToSymbolTable; 12H
DWORD NumberOfSymbols; 10H
WORD SizeOfOptionalHeader; 12H
WORD Characteristics; 14H
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
004000B4 4C01 DW 014C ; Machine = IMAGE_FILE_MACHINE_I386
004000B6 0300 DW 0003 ; NumberOfSections = 0x3
004000B8 FD002465 DD 652400FD ; TimeDateStamp = 0x652400FD
004000BC 00000000 DD 00000000 ; PointerToSymbolTable = 0x0
004000C0 00000000 DD 00000000 ; NumberOfSymbols = 0x0
004000C4 E000 DW 00E0 ; SizeOfOptionalHeader = E0 (224.)
004000C6 0F01 DW 010F ; Characteristics = EXECUTABLE_IMAGE|32BIT_MACHINE|RELOCS_STRIPPED|LINE_NUMS_STRIPPED|LOCAL_SYMS_STRIPPED
扩展头结构体
扩展头结构体32位和64位的还不一样这里暂时介绍32位的结构体(64位的多16个字节并且还有点小小的区别)
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint; 程序执行入口RVA地址 必须
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; 需要 64位必须要16个全要,32位的序号2个也上
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; 60H
004000C8 0B01 DW 010B ; MagicNumber = PE32 ROM映像文件:(0107h)
004000CA 05 DB 05 ; MajorLinkerVersion = 0x5
004000CB 0C DB 0C ; MinorLinkerVersion = C (12.)
004000CC 00020000 DD 00000200 ; SizeOfCode = 200 (512.)
004000D0 00040000 DD 00000400 ; SizeOfInitializedData = 400 (1024.) 12h
004000D4 00000000 DD 00000000 ; SizeOfUninitializedData = 0x0 10h
004000D8 00100000 DD 00001000 ; AddressOfEntryPoint = 0x1000 14
004000DC 00100000 DD 00001000 ; BaseOfCode = 0x1000
004000E0 00200000 DD 00002000 ; BaseOfData = 0x2000
004000E4 00004000 DD 00400000 ; ImageBase = 0x400000
004000E8 00100000 DD 00001000 ; SectionAlignment = 0x1000
004000EC 00020000 DD 00000200 ; FileAlignment = 0x200
004000F0 0400 DW 0004 ; MajorOSVersion = 0x4
004000F2 0000 DW 0000 ; MinorOSVersion = 0x0
004000F4 0000 DW 0000 ; MajorImageVersion = 0x0
004000F6 0000 DW 0000 ; MinorImageVersion = 0x0
004000F8 0400 DW 0004 ; MajorSubsystemVersion = 0x4
004000FA 0000 DW 0000 ; MinorSubsystemVersion = 0x0
004000FC 00000000 DD 00000000 ; Reserved
00400100 00400000 DD 00004000 ; SizeOfImage = 4000 (16384.)
00400104 00040000 DD 00000400 ; SizeOfHeaders = 400 (1024.)
00400108 00000000 DD 00000000 ; CheckSum = 0x0
0040010C 0200 DW 0002 ; Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI
0040010E 0000 DW 0000 ; DLLCharacteristics = 0x0
00400110 00001000 DD 00100000 ; SizeOfStackReserve = 100000 (1048576.)
00400114 00100000 DD 00001000 ; SizeOfStackCommit = 1000 (4096.)
00400118 00001000 DD 00100000 ; SizeOfHeapReserve = 100000 (1048576.)
0040011C 00100000 DD 00001000 ; SizeOfHeapCommit = 1000 (4096.)
00400120 00000000 DD 00000000 ; LoaderFlags = 0x0
00400124 10000000 DD 00000010 ; NumberOfRvaAndSizes = 10 (16.)(数据目录项,这里32位可可以等于2,但64位不可以)
以上就是PE节表之前的内容,DOS头+PE头的全部内容
下面看节表的内容
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;
004001A8 2E 74 65 78 74 0>ASCII ".text" ; SECTION (可以改的)
004001B0 24000000 DD 00000024 ; VirtualSize = 24 (36.);内存中大小(内存对齐前的长度)可以改的
004001B4 00100000 DD 00001000 ; VirtualAddress = 0x1000;内存中偏移(必须要)
004001B8 00020000 DD 00000200 ; SizeOfRawData = 200 (512.)文件中大小(文件对齐后的长度)这个不能改为0即可以
004001BC 00040000 DD 00000400 ; PointerToRawData = 0x400;文件中大小(文件对齐后的长度)必须要
004001C0 00000000 DD 00000000 ; PointerToRelocations = 0x0
004001C4 00000000 DD 00000000 ; PointerToLineNumbers = 0x0
004001C8 0000 DW 0000 ; NumberOfRelocations = 0x0
004001CA 0000 DW 0000 ; NumberOfLineNumbers = 0x0
004001CC 20000060 DD 60000020 ; Characteristics = CODE|EXECUTE|READ
004001D0 2E 72 64 61 74 6>ASCII ".rdata" ; SECTION
004001D8 92000000 DD 00000092 ; VirtualSize = 92 (146.)
004001DC 00200000 DD 00002000 ; VirtualAddress = 0x2000
004001E0 00020000 DD 00000200 ; SizeOfRawData = 200 (512.)
004001E4 00060000 DD 00000600 ; PointerToRawData = 0x600
004001E8 00000000 DD 00000000 ; PointerToRelocations = 0x0
004001EC 00000000 DD 00000000 ; PointerToLineNumbers = 0x0
004001F0 0000 DW 0000 ; NumberOfRelocations = 0x0
004001F2 0000 DW 0000 ; NumberOfLineNumbers = 0x0
004001F4 40000040 DD 40000040 ; Characteristics = INITIALIZED_DATA|READ
004001F8 2E 64 61 74 61 0>ASCII ".data" ; SECTION
00400200 0B000000 DD 0000000B ; VirtualSize = B (11.)
00400204 00300000 DD 00003000 ; VirtualAddress = 0x3000
00400208 00020000 DD 00000200 ; SizeOfRawData = 200 (512.)
0040020C 00080000 DD 00000800 ; PointerToRawData = 0x800
00400210 00000000 DD 00000000 ; PointerToRelocations = 0x0
00400214 00000000 DD 00000000 ; PointerToLineNumbers = 0x0
00400218 0000 DW 0000 ; NumberOfRelocations = 0x0
0040021A 0000 DW 0000 ; NumberOfLineNumbers = 0x0
0040021C 400000C0 DD C0000040 ; Characteristics = INITIALIZED_DATA|READ|WRITE
暂时只介绍到这里不过现在只是粗略的介绍了一下还没仔细分析这里面的逻辑