羽夏壳世界—— PE 结构(上) - 寂静的羽夏
source link: https://www.cnblogs.com/wingsummer/p/16125690.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
羽夏壳世界—— PE 结构(上)
此系列是本人一个字一个字码出来的,包括代码实现和效果截图。 如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我。
你如果是从中间插过来看的,请仔细阅读 羽夏壳世界——序 ,方便学习本教程。
在学习PE
结构之前,我们来大体看一下它的整体结构:
初步学习PE
文件,可能看的比较头大。如果你学习过编程语言的话,可以这么说:PE 文件是由一堆结构体和其他数据堆积起来的 。在学习本教程时,建议安装010 Editor
可以更方便的学习PE
结构,不过该软件是商用的。使用该软件主要是用了它的模板功能,如果是指定类型的文件,它也会主动匹配类型并提示使用相应的模板,如下所示是使用模板之后的效果:
PE
文件有两种状态,一个是在磁盘以文件的形式进行保存,另一个就是在内存中,如下图所示:
为什么这么说呢?是因为有对齐
这个概念,什么是对齐如果不会建议自己进行查阅,这个无论是编程语言还是操作系统基础中非常重要的概念,我就不在这里絮叨了。对于为什么有两种状态,之后的博文将会介绍。
在PE
结构中,有一个十分重要的概念就是RVA
和FOA
。RVA
英文全称为Relative Virtual Address
,即相对虚拟地址;FOA
英文全称为File Offset Address
,即文件偏移地址。就是因为PE
有两种状态,所以会有这两种偏移地址。当然还有一个VA
,英文全称为Virtual Address
,意为绝对的虚拟地址,也就是绝对值,是几就是几。而RVA
需要基址才能准确定位到VA
。有关RVA
、FOA
和VA
之间的相互转化,将会到下一篇继续。
IMAGE_DOS_HEADER
该结构体的结构如下:
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;
这个结构体是历史遗留的产物,因为开始的时候是DOS
系统,上面除了最后一个成员e_lfanew
,其他的都是给16位DOS
程序看的。不过第一个成员e_magic
被作为合法PE
文件的校验值,值为IMAGE_DOS_SIGNATURE
/0x5A4D
/"MZ"
。
e_lfanew
为文件偏移,指向了IMAGE_NT_HEADERS
结构,这个就是我们真正可以跑在现在图形界面的Windows
程序必须有的结构,我们来看一个程序与此部分相关的结构示意图如下:
DosStub
接在IMAGE_DOS_HEADER
结构体后面是被称之为DosStub
的,我们能够看到一个字符串This program cannot be run in DOS mode
,意为该程序不能够在Dos
模式下运行。这里面其实不是垃圾数据,而是有意义的汇编代码,我们用16位反汇编器来看一下:
seg000:0000 seg000 segment byte public 'CODE' use16
seg000:0000 assume cs:seg000
seg000:0000 assume es:nothing, ss:seg000, ds:nothing, fs:nothing, gs:nothing
seg000:0000
seg000:0000 ; =============== S U B R O U T I N E =======================================
seg000:0000
seg000:0000 public start
seg000:0000 start proc near
seg000:0000 push cs
seg000:0001 pop ds
seg000:0002 assume ds:seg000
seg000:0002 mov dx, 0Eh
seg000:0005 mov ah, 9
seg000:0007 int 21h ; DOS - PRINT STRING
seg000:0007 ; DS:DX -> string terminated by "$"
seg000:0009 mov ax, 4C01h
seg000:000C int 21h ; DOS - 2+ - QUIT WITH EXIT CODE (EXIT)
seg000:000C start endp ; AL = exit code
seg000:000C
seg000:000C ; ---------------------------------------------------------------------------
seg000:000E aThisProgramCan db 'This program cannot be run in DOS mode.',0Dh,0Dh,0Ah
seg000:000E db '$',0
seg000:003A align 8
seg000:0040 db 0A9h, 2Ch, 93h, 71h, 0EDh, 4Dh, 0FDh, 22h, 0EDh, 4Dh
seg000:0040 db 0FDh, 22h, 0EDh, 4Dh, 0FDh, 22h, 8Fh, 35h, 0FCh, 23h
seg000:0040 db 0EEh, 4Dh, 0FDh, 22h, 8Fh, 35h, 0F8h, 23h, 0F5h, 4Dh
seg000:0040 db 0FDh, 22h, 8Fh, 35h, 0F9h, 23h, 0E7h, 4Dh, 0FDh, 22h
seg000:0040 db 8Fh, 35h, 0FEh, 23h, 0EEh, 4Dh, 0FDh, 22h, 3Eh, 3Fh
seg000:0040 db 0FCh, 23h, 0E9h, 4Dh, 0FDh, 22h, 0EDh, 4Dh, 0FCh, 22h
seg000:0040 db 0AAh, 4Dh, 0FDh, 22h, 6Dh, 34h, 0F9h, 23h, 0ECh, 4Dh
seg000:0040 db 0FDh, 22h, 6Dh, 34h, 2, 22h, 0ECh, 4Dh, 0FDh, 22h, 6Dh
seg000:0040 db 34h, 0FFh, 23h, 0ECh, 4Dh, 0FDh, 22h, 52h, 69h, 63h
seg000:0040 db 68h, 0EDh, 4Dh, 0FDh, 22h, 8 dup(0)
seg000:00A8 db 10h dup(?)
seg000:00A8 seg000 ends
seg000:00A8
seg000:00A8
seg000:00A8 end start
注意一定要使用16位
的反汇编器,否则无论是在32位是64位下,所有的代码都是错误的。
上面的代码如果没有《微机原理》的基础可能有点难懂,这里就补充一下:int 21h
是Dos
下的中断,类似表示我要调用API
了,不同的AH
寄存器的值表示调用的函数是不一样的。如果是9
,表示在屏幕上打印字符串,不过这个字符串不是用\0
结尾的,而是$
,和我们现代操作系统的\0
的作用是一样的。如果AH
的值为0x4C
,则表示将AL
作为返回值结束程序,和我们C语言
编写main
函数中的return 0;
是一个道理。
有关该部分就介绍到这里。
IMAGE_NT_HEADERS
该结构体在32位和64位程序是有所不同的,只不过是结构体的成员大小的区别,如下所示:
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
typedef struct _IMAGE_NT_HEADERS64 {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
Signature
是一个十分重要的成员。PE
指纹的第二部分,值为IMAGE_NT_SIGNATURE
/0x00004550
/"PE"
。如果值是错误的,同样被判定为非法PE
文件。
FileHeader
是标准PE头,大小为20个字节,可以通过IMAGE_SIZEOF_FILE_HEADER
宏获取,具体细节后面将会介绍。
OptionalHeader
是扩展PE头,虽然名字带着可选,但它是必需结构。
我们再来看看它在二进制文件下的位置:
IMAGE_FILE_HEADER
该结构体在32位和64位的程序是一样的,如下所示:
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;
Machine
指示可以运行在什么样的CPU
上,它的值如下:
任意CPU
:IMAGE_FILE_MACHINE_UNKNOWN
/0x0000
;Intel 386以及后续CPU
:IMAGE_FILE_MACHINE_I386
/0x014C
;x64
:IMAGE_FILE_MACHINE_AMD64
/0x8664
。
下面是其中的所有成员:
#define IMAGE_FILE_MACHINE_UNKNOWN 0
#define IMAGE_FILE_MACHINE_TARGET_HOST 0x0001 // Useful for indicating we want to interact with the host and not a WoW guest.
#define IMAGE_FILE_MACHINE_I386 0x014c // Intel 386.
#define IMAGE_FILE_MACHINE_R3000 0x0162 // MIPS little-endian, 0x160 big-endian
#define IMAGE_FILE_MACHINE_R4000 0x0166 // MIPS little-endian
#define IMAGE_FILE_MACHINE_R10000 0x0168 // MIPS little-endian
#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 // MIPS little-endian WCE v2
#define IMAGE_FILE_MACHINE_ALPHA 0x0184 // Alpha_AXP
#define IMAGE_FILE_MACHINE_SH3 0x01a2 // SH3 little-endian
#define IMAGE_FILE_MACHINE_SH3DSP 0x01a3
#define IMAGE_FILE_MACHINE_SH3E 0x01a4 // SH3E little-endian
#define IMAGE_FILE_MACHINE_SH4 0x01a6 // SH4 little-endian
#define IMAGE_FILE_MACHINE_SH5 0x01a8 // SH5
#define IMAGE_FILE_MACHINE_ARM 0x01c0 // ARM Little-Endian
#define IMAGE_FILE_MACHINE_THUMB 0x01c2 // ARM Thumb/Thumb-2 Little-Endian
#define IMAGE_FILE_MACHINE_ARMNT 0x01c4 // ARM Thumb-2 Little-Endian
#define IMAGE_FILE_MACHINE_AM33 0x01d3
#define IMAGE_FILE_MACHINE_POWERPC 0x01F0 // IBM PowerPC Little-Endian
#define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1
#define IMAGE_FILE_MACHINE_IA64 0x0200 // Intel 64
#define IMAGE_FILE_MACHINE_MIPS16 0x0266 // MIPS
#define IMAGE_FILE_MACHINE_ALPHA64 0x0284 // ALPHA64
#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366 // MIPS
#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 // MIPS
#define IMAGE_FILE_MACHINE_AXP64 IMAGE_FILE_MACHINE_ALPHA64
#define IMAGE_FILE_MACHINE_TRICORE 0x0520 // Infineon
#define IMAGE_FILE_MACHINE_CEF 0x0CEF
#define IMAGE_FILE_MACHINE_EBC 0x0EBC // EFI Byte Code
#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8)
#define IMAGE_FILE_MACHINE_M32R 0x9041 // M32R little-endian
#define IMAGE_FILE_MACHINE_ARM64 0xAA64 // ARM64 Little-Endian
#define IMAGE_FILE_MACHINE_CEE 0xC0EE
NumberOfSections
指示节的数量,它十分重要。
TimeDateStamp
指示编译器填写的时间戳与文件属性里面创建时间
/修改时间
无关,计算的是当前时间与1970年0时0点0分
差的秒数。
PointerToSymbolTable
/NumberOfSymbols
与调试相关,不做关注。
SizeOfOptionalHeader
表示扩展PE头的大小,可以修改合适的数值。在默认情况下,32位PE
文件:0xE0
,64位PE
文件:0xF0
。
Characteristics
指示了文件属性,它的值有如下:
#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 // Relocation info stripped from file.
#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 // File is executable (i.e. no unresolved external references).
#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 // Line nunbers stripped from file.
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 // Local symbols stripped from file.
#define IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010 // Aggressively trim working set
#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 // App can handle >2gb addresses
#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 // Bytes of machine word are reversed.
#define IMAGE_FILE_32BIT_MACHINE 0x0100 // 32 bit word machine.
#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 // Debugging info stripped from file in .DBG file
#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 // If Image is on removable media, copy and run from the swap file.
#define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 // If Image is on Net, copy and run from the swap file.
#define IMAGE_FILE_SYSTEM 0x1000 // System File.
#define IMAGE_FILE_DLL 0x2000 // File is a DLL.
#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 // File should only be run on a UP machine
#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 // Bytes of machine word are reversed.
我们再来看看它在二进制文件下的位置:
IMAGE_OPTIONAL_HEADER
这个结构体在32位和64位是有区别的,是某些成员的大小区别,它们的结构如下:
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; //目录表的个数(决定DataDirectory数组长度)
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
typedef struct _IMAGE_OPTIONAL_HEADER64 {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
ULONGLONG 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;
ULONGLONG SizeOfStackReserve;
ULONGLONG SizeOfStackCommit;
ULONGLONG SizeOfHeapReserve;
ULONGLONG SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;
#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
Magic
标识程序的位数。32位程序为值为IMAGE_NT_OPTIONAL_HDR32_MAGIC
/0x10b
,64位程序为值为IMAGE_NT_OPTIONAL_HDR64_MAGIC
/0x20b
。它是判断程序位数的关键属性。
AddressOfEntryPoint
的值表示程序入口RVA
,十分重要。
ImageBase
表示该PE
比较倾向的内存镜像加载基址,比较重要。对于DLL
这个值通常并不被操作系统采纳,如果开启了随机基址的EXE
也是如此。
SectionAlignment
表示内存对齐值,十分重要。
FileAlignment
表示文件对齐值,十分重要。
SizeOfImage
表示内存中整个PE
文件的映射的尺寸,可比实际的值大,必须是SectionAlignment
的整数倍,十分重要。
SizeOfHeaders
所有头和节表按照文件对齐后的大小,否则加载会出错,这十分重要。
CheckSum
表示校验和,一些系统文件有要求用来判断文件是否被修改。
Subsystem
意为子系统,驱动程序值为1
,图形界面值为2
,控制台、DLL值为3
。下面是其枚举:
#define IMAGE_SUBSYSTEM_UNKNOWN 0 // Unknown subsystem.
#define IMAGE_SUBSYSTEM_NATIVE 1 // Image doesn't require a subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 // Image runs in the Windows GUI subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 // Image runs in the Windows character subsystem.
#define IMAGE_SUBSYSTEM_OS2_CUI 5 // image runs in the OS/2 character subsystem.
#define IMAGE_SUBSYSTEM_POSIX_CUI 7 // image runs in the Posix character subsystem.
#define IMAGE_SUBSYSTEM_NATIVE_WINDOWS 8 // image is a native Win9x driver.
#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9 // Image runs in the Windows CE subsystem.
#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10 //
#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 //
#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 //
#define IMAGE_SUBSYSTEM_EFI_ROM 13
#define IMAGE_SUBSYSTEM_XBOX 14
#define IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION 16
#define IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG 17
DllCharacteristics
是文件特性,不是针对DLL
文件的,该成员是比较重要的,基址重定位就是从这个成员进行设置的。下面是其枚举:
// IMAGE_LIBRARY_PROCESS_INIT 0x0001 // Reserved.
// IMAGE_LIBRARY_PROCESS_TERM 0x0002 // Reserved.
// IMAGE_LIBRARY_THREAD_INIT 0x0004 // Reserved.
// IMAGE_LIBRARY_THREAD_TERM 0x0008 // Reserved.
#define IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA 0x0020 // Image can handle a high entropy 64-bit virtual address space.
#define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040 // DLL can move.
#define IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY 0x0080 // Code Integrity Image
#define IMAGE_DLLCHARACTERISTICS_NX_COMPAT 0x0100 // Image is NX compatible
#define IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200 // Image understands isolation and doesn't want it
#define IMAGE_DLLCHARACTERISTICS_NO_SEH 0x0400 // Image does not use SEH. No SE handler may reside in this image
#define IMAGE_DLLCHARACTERISTICS_NO_BIND 0x0800 // Do not bind this image.
#define IMAGE_DLLCHARACTERISTICS_APPCONTAINER 0x1000 // Image should execute in an AppContainer
#define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000 // Driver uses WDM model
#define IMAGE_DLLCHARACTERISTICS_GUARD_CF 0x4000 // Image supports Control Flow Guard.
#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
DataDirectory
是存储表位置大小的数组。不同的索引代表不同的表的数据,下面是其枚举:
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory
// IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage)
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor
我们再来看看它在二进制文件下的位置:
IMAGE_SECTION_HEADER
IMAGE_SECTION_HEADER
是节区头,是重要的结构体,在32位和64位下的程序没有区别。大小可通过IMAGE_SIZEOF_SECTION_HEADER
获取,在内存的展开大小 = Max(Misc,SizeOfRawData)
。如果此节为已初始化的变量,则 Misc > SizeOfRawData
;若节为未初始化的变量则 Misc < SizeOfRawData
。如下是结构体成员:
#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;
Name
表示节区名称,ASCII
字符串,可自定义,只截取8个。
Misc
表示该节在没有对齐前的真实尺寸,该值可以不准确。
VirtualAddress
是在内存中的偏移地址,加上lmageBase
才是在内存中的真正地址,十分重要。
PointerToRawData
是节区在文件中的偏移,十分重要。
PointerToRelocations
/PointerToRelocations
/NumberOfRelocations
/PointerToLinenumbers
与调试相关,不关注。
Characteristics
是节的属性。下面是其枚举:
// IMAGE_SCN_TYPE_REG 0x00000000 // Reserved.
// IMAGE_SCN_TYPE_DSECT 0x00000001 // Reserved.
// IMAGE_SCN_TYPE_NOLOAD 0x00000002 // Reserved.
// IMAGE_SCN_TYPE_GROUP 0x00000004 // Reserved.
#define IMAGE_SCN_TYPE_NO_PAD 0x00000008 // Reserved.
// IMAGE_SCN_TYPE_COPY 0x00000010 // Reserved.
#define IMAGE_SCN_CNT_CODE 0x00000020 // Section contains code.
#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 // Section contains initialized data.
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 // Section contains uninitialized data.
#define IMAGE_SCN_LNK_OTHER 0x00000100 // Reserved.
#define IMAGE_SCN_LNK_INFO 0x00000200 // Section contains comments or some other type of information.
// IMAGE_SCN_TYPE_OVER 0x00000400 // Reserved.
#define IMAGE_SCN_LNK_REMOVE 0x00000800 // Section contents will not become part of image.
#define IMAGE_SCN_LNK_COMDAT 0x00001000 // Section contents comdat.
// 0x00002000 // Reserved.
// IMAGE_SCN_MEM_PROTECTED - Obsolete 0x00004000
#define IMAGE_SCN_NO_DEFER_SPEC_EXC 0x00004000 // Reset speculative exceptions handling bits in the TLB entries for this section.
#define IMAGE_SCN_GPREL 0x00008000 // Section content can be accessed relative to GP
#define IMAGE_SCN_MEM_FARDATA 0x00008000
// IMAGE_SCN_MEM_SYSHEAP - Obsolete 0x00010000
#define IMAGE_SCN_MEM_PURGEABLE 0x00020000
#define IMAGE_SCN_MEM_16BIT 0x00020000
#define IMAGE_SCN_MEM_LOCKED 0x00040000
#define IMAGE_SCN_MEM_PRELOAD 0x00080000
#define IMAGE_SCN_ALIGN_1BYTES 0x00100000 //
#define IMAGE_SCN_ALIGN_2BYTES 0x00200000 //
#define IMAGE_SCN_ALIGN_4BYTES 0x00300000 //
#define IMAGE_SCN_ALIGN_8BYTES 0x00400000 //
#define IMAGE_SCN_ALIGN_16BYTES 0x00500000 // Default alignment if no others are specified.
#define IMAGE_SCN_ALIGN_32BYTES 0x00600000 //
#define IMAGE_SCN_ALIGN_64BYTES 0x00700000 //
#define IMAGE_SCN_ALIGN_128BYTES 0x00800000 //
#define IMAGE_SCN_ALIGN_256BYTES 0x00900000 //
#define IMAGE_SCN_ALIGN_512BYTES 0x00A00000 //
#define IMAGE_SCN_ALIGN_1024BYTES 0x00B00000 //
#define IMAGE_SCN_ALIGN_2048BYTES 0x00C00000 //
#define IMAGE_SCN_ALIGN_4096BYTES 0x00D00000 //
#define IMAGE_SCN_ALIGN_8192BYTES 0x00E00000 //
// Unused 0x00F00000
#define IMAGE_SCN_ALIGN_MASK 0x00F00000
#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 // Section contains extended relocations.
#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 // Section can be discarded.
#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 // Section is not cachable.
#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 // Section is not pageable.
#define IMAGE_SCN_MEM_SHARED 0x10000000 // Section is shareable.
#define IMAGE_SCN_MEM_EXECUTE 0x20000000 // Section is executable.
#define IMAGE_SCN_MEM_READ 0x40000000 // Section is readable.
#define IMAGE_SCN_MEM_WRITE 0x80000000 // Section is writeable.
我们再来看看它在二进制文件下的位置:
IMAGE_DATA_DIRECTORY
IMAGE_DATA_DIRECTORY
是十分重要的结构体,具体重要性的体现已经在IMAGE_OPTIONAL_HEADER
介绍过了,如下是其结构:
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
VirtualAddress
是指表在内存的RVA
,十分重要。
Size
是指IMAGE_DATA_DIRECTORY
结构体和其使用的所有数据之和,该值不会影响程序的运行。
我们再来看看它在二进制文件下的位置:
本篇我们只介绍了基本结构体,对于后面比较复杂的几个表和地址转化,考虑到比较复杂,挪到下一篇进行。
看完本篇文章一定要把在16进制下看明白结构,最好用自己熟悉的编程语言写一个解析上面所述结构体的解析器,以巩固自己的学习成果。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK