PE文件结构:NT头部

NT 头部(NT Header)是 PE 文件格式的核心部分之一,它包含了有关程序如何加载、执行以及一些重要的文件属性。NT 头部常被认为是 PE 头部 的核心或“真正的”PE 头部,因为操作系统加载 PE 文件时,首先会查找 DOS 头部的 e_lfanew 字段,定位到 NT 头部,即 PE 文件的有效头部(这也是我在上一篇文章《PE文件结构-PE文件结构-DOS头部&DOS stub》中的PE文件结构部分NT header为PE头部的原因)。

但实际上,在 PE 文件格式(Portable Executable format)中,PE 头部 和 NT 头部 是同一个概念的不同部分,实际上 NT 头部 是 PE 头部 的一个子结构。它们共同定义了可执行文件的加载、执行方式以及其它重要信息。PE 头部包含了从 DOS 头部开始到 节表(Section Header)部分之间的所有内容,而 NT 头部则是 PE 头部的核心部分,负责描述文件的结构和属性。

NT 头部具体包括:PE 标识符(PE Signature)、文件头(File Header)、可选头(Optional Header)这些部分共同构成了 NT 头部,在 PE 文件结构中起着关键作用。下面我会分别介绍这三个部分:

NT头部的结构

PE 标识符(PE Signature)
PE 标识符通常位于 NT 头部的最前面,它是NT头部的第一个字段是一个标识符,用来标识该文件为PE格式,它是一个4字节的值,通常是字符串PE\0\0。这个签名用于告诉操作系统该文件是一个有效的PE文件。
②文件头(File Header)
文件头(File Header)是 NT 头部的第一部分,包含了 PE 文件的一些基本信息,描述了目标机器的类型、节的数量、文件的创建时间等。
可选头(Optional Header)
可选头包含了更详细的文件加载和执行信息,实际上是 PE 文件中最重要的一部分,尽管名字中有“可选”字样,但它是必须的。可选头中包含了程序的入口点地址、图像基地址、节的对齐要求、堆栈大小等信息,操作系统通过这些信息来正确加载和执行程序。

接着通过Visual Studio来进一步查看NT头部的结构,查看方式与前一篇文章《DOS头部&DOS stub》中描述的一样,在随意的一个C/Cpp文件中,敲入NT头部结构体:IMAGE_NT_HEADERS

接着按住ctrl键点击结构体,查看结构体内部结构:

typedef struct _IMAGE_NT_HEADERS {DWORD Signature;IMAGE_FILE_HEADER FileHeader;IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

可以看到NT头部中就包含有上述介绍的PE 标识符(PE Signature)、文件头(File Header)、可选头(Optional Header)。这个时候可以通过010Editor进一步查看文件的结构此处的程序使用的是上一篇文章中生成的程序

在DOS头部、DOS stub后的内容就是NT头部;那么结合上述NT头部的结构,就可以很清楚地看出在NT头部开头的4个字节就是PE Signature:

而PE标识是不允许更改的,若我们更改了PE标识,那么此时程序就没法运行了:

接着我们往下继续说一下NT头部中的其他内容。

文件头(FileHeader)结构

在PE(Portable Executable)文件格式中,FileHeader 是 NT 头部的一部分,包含了关于文件的基本元数据,FileHeader 结构描述了文件的基本信息,如文件类型、机器架构、节区数量、时间戳等内容,它位于 NT 头部中的第二部分,在 IMAGE_NT_HEADERS 结构中。

长按ctrl点击NT头部中的IMAGE_FILE_HEADER进行跳转,查看文件头部的内容:

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 (WORD)

该字段指示目标机器的架构类型,即PE文件所支持的硬件平台;这个时候在FILE_HEADER结构体所在位置往下拉,我们就可以看到MACHINE字段的取值:

具体的取值如下:

#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

这个时候我们在010Editor中的位置,找到实例文件的MACHINE取值:014C(小端序存储)

那么通过上述我们就可以直接知道目标机器的架构类型为:Intel 386;这个值代表了该 PE 文件是针对 32 位 x86 架构(即 i386 处理器)的目标平台。

②NumberOfSections(WORD)

该字段指定PE文件中节区(section)的数量。PE文件由多个节区组成,每个节区存储了特定类型的数据或代码(例如 .text.data.rsrc 等)。在010 Editor中可以看到该示例文件的值为00 09:

如果一个 PE 文件的 NumberOfSections 字段的值为 9,这表示该文件包含 9 个节,每个节可能代表不同的程序部分。例如,文件可能包含以下节:

①.text — 代码段
②.data — 数据段
③.rdata — 只读数据段
④.bss — 未初始化数据段
⑤.edata — 导出符号表
⑥.idata — 导入符号表
⑦.rsrc — 资源段
⑧.reloc — 重定位表
⑨.debug — 调试信息(可选)

NumberOfSections也不能随意修改:在 PE 文件中,每个节都有一个对应的节头(IMAGE_SECTION_HEADER)。节头结构包含有关该节的元数据(例如节的名称、大小、内存地址等),NumberOfSections 字段指示节头的数量,因此,如果你修改 NumberOfSections,你必须确保节头的数量也正确更新。

③TimeDateStamp(DWORD)

该字段表示文件的时间戳。它通常是编译或链接时生成的时间,表示PE文件创建的具体时刻;时间戳的值是一个32位的时间戳,表示自1970年1月1日起的秒数。010Editor中的位置:

该字段的修改并不会影响程序的运行:

④PointerToSymbolTable(DWORD)

该字段是指向符号表的指针,但在现代Windows程序中通常不再使用,因为符号表主要用于调试信息。在010 Editor中查看该字段位置:

该字段的修改也不会影响程序的运行:

在旧版的PE文件(例如Windows 95及早期)中,符号表包含了调试符号,帮助开发者进行反汇编和调试。现代的PE文件通常不会使用该字段,且符号信息通常与程序文件分离(例如使用.pdb文件存储符号)。
⑤NumberOfSymbols(DWORD)

指定符号表中的符号数量;与PointerToSymbolTable字段结合使用,表示符号表中存储的符号数量。在现代PE文件中,这个字段通常被设为零,因为不再使用符号表。查看其位置,并以CC字符填充该字段:

再次运行程序,发现字段中值的修改并不影响程序的正常运行。

⑥SizeOfOptionalHeader(WORD)

该字段表示可选头(Optional Header)的大小,可选头包含了程序的更多信息,如入口点、映像基址、堆栈大小等。除此之外,PE文件的加载和执行相关的关键信息都存储也存储在可选头中。

该字段不可随意修改,CC字符填充后运行程序:

⑦Characteristics(WORD)

该字段表示PE文件的特性,用于描述文件的类型、功能和运行要求。该字段的值是一个位掩码,每一位代表不同的特性。该字段的具体取值在该结构体下面被定义:

#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.

具体位置如下:

看到该字段的值为:01 02这个时候通过对照上面的取值列表(0x0102 = 0x0100 + 0x0002)可以知道:该程序为一个32位的可执行程序。

同样的,该字段不可随意修改:

至此NT头部中的文件头部已经全部介绍完毕,接着就对NT头部的可选头部(OptionalHeader)进行说明:

可选头(OptonalHeader)结构

同样的,在VS中按住ctrl点击结构体进行跳转:

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;
①Magic(WORD)

该字段用于标识PE文件的类型;对于32位的PE文件,其值通常是0x010B,而对于64位的PE文件,其值是0x020B。这个时候在010 Editor中查看样例程序中该字段的位置以及值:

由此可以直接得出该程序位32位得PE文件。同样,该字段不可随意修改:

②MajorLinkerVersion(Byte)和MinorLinkerVersion(Byte)

这两个字段分别表示链接器的主版本号和副版本号,用于标识创建PE文件的链接器版本。这两个字段通常与生成 PE 文件的工具(例如 Microsoft Visual Studio 的 link.exe)的版本相关,帮助标识链接器的版本,通常用于追踪文件生成的工具和版本。

1.主版本号(MajorLinkerVersion):这个字段表示链接器的主要版本。它通常用于标识链接器的大版本更新,这些更新可能包含不兼容的更改或重要的新功能。例如,如果链接器的版本从10升级到11,这将通过主版本号的变化来反映。
​
2.副版本号(MinorLinkerVersion):这个字段表示链接器的次要版本或修订号。它用于标识链接器的小版本更新,这些更新通常是向后兼容的,可能包含错误修复、性能改进或小的新功能。例如,从10.0到10.1的更新将通过副版本号的变化来反映。

不同版本的 Microsoft Visual Studiolink.exe 会生成不同的版本号。例如:

  • Visual Studio 2005 (VS 8.0):链接器版本号为 8.0

  • Visual Studio 2010 (VS 10.0):链接器版本号为 10.0

  • Visual Studio 2015 (VS 14.0):链接器版本号为 14.0

  • Visual Studio 2019 (VS 16.0):链接器版本号为 14.0 或更高(具体版本取决于修订)

MajorLinkerVersion = 0x0E (14)
MinorLinkerVersion = 0x01 (1)

在 样例程序的PE 文件结构 可选头(IMAGE_OPTIONAL_HEADER 中可以看出MajorLinkerVersionMinorLinkerVersion 分别为 0x0E(14)和 0x01(1),这意味着该文件是使用 link.exe 链接器版本 14.1 生成的。Microsoft 的 link.exe 工具会随着 Visual Studio 版本的变化而更新,因此 MajorLinkerVersion 和 MinorLinkerVersion 会根据 Visual Studio 的版本有所不同。以下是一些常见的示例:

1. 版本 8.0(Visual Studio 2005)MajorLinkerVersion = 8MinorLinkerVersion = 0
2. 版本 10.0(Visual Studio 2010)MajorLinkerVersion = 10MinorLinkerVersion = 0
3. 版本 14.0(Visual Studio 2015)MajorLinkerVersion = 14MinorLinkerVersion = 0
4. 版本 14.28(Visual Studio 2019)MajorLinkerVersion = 14MinorLinkerVersion = 28

最后,在Editor中使用CC字符填充这两个字段,发现这两个字段并不影响程序的正常运行:

③SizeOfCode(DWORD)、SizeOfInitializedData(DWORD)和SizeOfUninitializedData(DWORD)

这三个DWORD类型的字段分别表示代码段、初始化数据和未初始化数据的大小。

Ⅰ.SizeOfCode:在PE文件的可选头(Optional Header)中,SizeOfCode字段表示代码段(也称为.text段)的总大小。代码段包含了程序的可执行指令。这个字段的单位是字节。它表示程序中所有代码段的总字节数,包括任何填充(padding)数据,以确保内存对齐。

SizeOfCode在文件结构中的对应位置:

该字段的值为:00 05 84 00,随意修改该字段的值,发现该字段并不影响程序正常运行。

Ⅱ.SizeOfInitializedData:在PE文件的可选头(Optional Header)中,SizeOfInitializedData字段表示已初始化数据段(即.data段)的总大小。已初始化数据段包含了程序的全局变量和静态变量,这些变量在程序加载到内存时已经有初始值。该字段的在文件结构中的位置:

该字段的值为:00 01 20 00,随意修改该字段的值,发现该字段并不影响程序正常运行。

Ⅲ.SizeOfUninitializedData:在PE文件的可选头(Optional Header)中,SizeOfUninitializedData字段表示未初始化数据段(即.bss段)的总大小。未初始化数据段包含程序中在定义时没有被赋予初始值的全局变量和静态变量。该字段的在文件结构中的位置:

该字段的值为:00 00 00 00,随意修改该字段的值,发现该字段并不影响程序正常运行。

④AddressOfEntryPoint(DWORD)

AddressOfEntryPoint字段是PE文件结构中可选头(IMAGE_OPTIONAL_HEADER)的一个重要成员,它定义了程序执行的入口点。这是一个DWORD类型的字段,表示程序的入口点在内存中的相对虚拟地址(RVA)。AddressOfEntryPoint字段指示程序执行的起始点,当PE文件被加载到内存中时,这个字段指定了操作系统应该首先执行的代码的位置。对于EXE文件,这通常是WinMainmain函数的地址;对于DLL文件,则是DllMain函数的地址。

什么是相对虚拟地址(RVA)?
RVA,全称为Relative Virtual Address(相对虚拟地址),是PE(Portable Executable,可移植执行文件)格式中用于指定地址的一种方式。在PE文件中,RVA是一种相对于PE文件的基地址(ImageBase)的偏移量,用于在内存中定位各个部分(如代码、数据、资源等)的位置。
基地址(ImageBase):PE文件在内存中的首选加载地址称为基地址。对于大多数可执行文件,默认的基地址是0x00400000,但这个值可以在PE文件的可选头中被修改;基地址在PE文件结构中的位置如下。

计算实际地址AddressOfEntryPoint是一个RVA值,它是相对于PE文件的ImageBase(镜像基址)的偏移量。因此,要计算入口点在内存中的绝对地址,需要将ImageBaseAddressOfEntryPoint相加。例如,如果ImageBase0x00400000,而AddressOfEntryPoint0x00001000,则程序入口的虚拟地址(VA)为0x00401000

此时我的样例程序的AddressOfEntryPoint值为0002BC565ImageBase的值为00400000

程序入口的虚拟机地址 = ImageBase+ AddressOfEntryPoint

程序入口的虚拟地址=00400000+0002BC56==0042BC56,这个时候我们就得到了程序的入口点在内存中的实际地址;但是需要注意的一点是如果程序和操作系统中都启用了地址空间布局随机化(ASLR),则ImageBase会在每次执行时随机化,那么此时计算得到的程序入口地址就不准确了。这个程序入口的虚拟地址是可以修改的,后续可以再单独写篇文章。

程序执行中的ASLR功能的条件

①操作系统启用ASLR: 操作系统需要全局启用ASLR(如Windows、Linux等),否则即使程序支持ASLR,操作系统也不会进行地址布局随机化。
②程序支持ASLR: 程序需要在编译时明确启用ASLR支持(如Windows中的IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE标志,或者Linux中的PIE支持)。

Visual Studio生成程序(链接)时决定程序是否支持ASLR功能的开关:

右击项目-->选择属性-->链接器-->高级-->随机基址

⑤BaseOfCode(DWORD)和BaseOfData(DWORD)

这两个字段分别表示代码段和数据段在内存中的基地址

Ⅰ.BaseOfCode:BaseOfCode字段是一个DWORD值,它指向代码段(通常称为.text段)在内存中的起始相对虚拟地址(RVA)。也就是说,当PE文件被加载到内存中时,BaseOfCode就是代码段的开始地址。

Ⅱ.BaseOfData:BaseOfData字段也是一个DWORD值,它指向数据段(通常称为.data段)在内存中的起始相对虚拟地址(RVA)。对于32位PE文件,这个字段表示数据段的开始地址,而在64位PE文件中,这个字段被合并到ImageBase字段中,因此64位PE文件中不存在BaseOfData字段。

随意修改这两个字段,发现程序正常运行

⑥ImageBase(DWORD)

ImageBase字段表示PE文件在内存中的默认加载地址。换句话说,当操作系统加载一个可执行文件或DLL时,它会尝试将其映射到这个指定的虚拟内存地址。如果这个地址已经被其他资源占用,操作系统会选择另一个合适的地址并进行重定位(Relocation)。通常情况下,可执行文件的默认地址为0x00400000,而DLL文件则通常位于更高的地址,如0x10000000。开发者可以在编译时指定这个地址。

右击项目-->选择属性-->链接器-->高级-->固定基址

该字段在上面介绍AddressOfEntryPoint时已经介绍到了,这边就不做更多赘述。该字段的位置:

该字段无法随意更改:

⑦SectionAlignment(DWORD)和FileAlignment(DWORD)
SectionAlignment

SectionAlignment 字段定义了节(Section)在内存中的对齐边界,它告诉操作系统在加载PE文件时,节的开始位置应该对齐到内存中的多大边界上。具体来说,SectionAlignment 指定了内存中加载节时需要遵循的对齐规则。这个值影响程序加载到内存后,如何将文件中的不同节(如代码段、数据段等)映射到内存中。通常,这个值通常设置为操作系统的页面大小,常见的如 0x1000(即 4KB)。

当程序被加载到内存时,节的实际内存地址应该是 SectionAlignment 倍数的地址,也就是说,操作系统会确保每个节的起始地址与 SectionAlignment 对齐。例如,若 SectionAlignment0x1000,则程序中的每个节(Section)在内存中的起始地址将是 0x1000 的倍数(如 0x1000, 0x2000, 0x3000 等)。

该字段不可以随意修改:

FileAlignment

FileAlignment 这 个值指定了在PE文件的磁盘上,每个节(Section)数据在文件中存储时应该遵循的对齐规则。换句话说,它告诉操作系统文件中的节数据在文件中的存储位置应该如何对齐。这个字段决定了节在PE文件中的存储格式,以及如何优化文件大小或读取性能。FileAlignment 值通常会小于或等于 SectionAlignment,因为文件本身并不一定需要像内存那样严格地对齐。

当PE文件的节(Section)被写入磁盘时,它们的起始地址将会对齐到 FileAlignment 的倍数。这意味着节在文件中的位置会遵循 FileAlignment 的对齐规则。例如,若 FileAlignment0x200,那么每个节在文件中的起始地址将是 0x200 的倍数(如 0x200, 0x400, 0x600 等)。

同样的,该字段无法随意修改:

⑧MajorOperatingSystemVersion(WORD)和MinorOperatingSystemVersion(WORD)

Ⅰ.MajorOperatingSystemVersion 指定程序所要求的操作系统的主版本号。操作系统的主版本号通常是指操作系统的主要版本,通常对应操作系统的系列版本。例如,Windows XP、Windows 7、Windows 10 等都有不同的主版本号。

Windows 版本和主版本号的关系:

Windows XP: MajorOperatingSystemVersion = 5
Windows Vista: MajorOperatingSystemVersion = 6
Windows 7: MajorOperatingSystemVersion = 6
Windows 8/8.1: MajorOperatingSystemVersion = 6
Windows 10: MajorOperatingSystemVersion = 10

Ⅱ.MinorOperatingSystemVersion 指定程序所要求的操作系统的次版本号。操作系统的次版本号通常表示某个操作系统版本的细节修订版本。例如,Windows 7 可能有多个次版本号,如 Windows 7 SP1。

Windows 版本和次版本号的关系:

Windows XP (没有 SP): MinorOperatingSystemVersion = 0
Windows XP (SP1): MinorOperatingSystemVersion = 1
Windows 7 (SP1): MinorOperatingSystemVersion = 1
Windows 10 (版本 1507): MinorOperatingSystemVersion = 0
Windows 10 (版本 1903): MinorOperatingSystemVersion = 3

兼容性: 当程序加载到操作系统时,如果操作系统检测到程序的版本要求与当前操作系统不匹配,操作系统可以弹出错误提示,或者程序根本无法启动。通过设置这些字段,程序开发者可以控制其程序是否在特定的操作系统版本上运行。这两个字段的经典取值有:

操作系统MajorOperatingSystemVersionMinorOperatingSystemVersion
Windows XP51
Windows 761
Windows 10100
Windows 10101

例如,假设 MajorOperatingSystemVersion = 6MinorOperatingSystemVersion = 1,表示该程序要求至少 Windows Vista 或 Windows 7 及更高版本。如果操作系统版本低于 6.1(如 Windows XP),则该程序无法运行。此时找到样例程序中的这两个字段的值:

此时我的样例程序这两个字段的值为:00 0600 00。主版本号为 6 通常表示该程序要求运行在 Windows Vista更高版本的 Windows 操作系统(如 Windows 7、Windows 8、Windows 10);次版本号为 0 通常表示 没有特定的次版本要求。这意味着该程序应该能够在主版本号为 6 的任何操作系统上运行。该程序要求至少运行在 Windows Vista 或更高版本的操作系统上。如果你尝试在 Windows XP 或更早版本的操作系统上运行此程序,操作系统可能会拒绝启动该程序,或者该程序可能在运行时遇到问题。这两个字段不可随意更改:

⑨MajorImageVersion(WORD)和MinorImageVersion(WORD)

在 PE 文件(Portable Executable)格式中,MajorImageVersionMinorImageVersionIMAGE_OPTIONAL_HEADER 部分的两个字段,它们用于描述程序的 版本信息

Ⅰ.MajorOperatingSystemVersion

MajorImageVersion 字段定义了程序的 主版本号。它用于标识程序的主要版本。在软件开发中,主版本号通常用于指示程序的重要变化或突破性的更新,通常与大的功能更新、改进或架构更改有关。

Ⅱ.MinorOperatingSystemVersion

inorImageVersion 字段定义了程序的 次版本号。它通常表示程序的一些较小更新、改进或者修复。与 MajorImageVersion 一起使用,MinorImageVersion 可以描述程序从一个版本到另一个版本的小范围变化。

在软件的开发和发布过程中,开发者会使用这两个字段来标识程序的版本。主版本号通常随着大的功能更新或重构而增加,而次版本号则随着小的更新或修复而变化。

假设 PE 文件中以下字段的值:

  • MajorImageVersion = 1

  • MinorImageVersion = 5

这表示该程序的版本号为 1.5,这意味着程序处于第一个大版本,并且可能在 1.0 版本之后有了一些小的更新或修复,版本号的次版本号为 5。

示例程序中这两个字段的值:00 00(表示该程序版本为0.0)

修改这俩字段的值,重新运行程序,发现这两个字段的修改并不影响程序运行:

⑩MajorSubsystemVersion(WORD)和MinorSubsystemVersion(WORD)

Ⅰ.MajorSubsystemVersion

MajorSubsystemVersion 字段指定程序所要求的 主子系统版本号。操作系统根据这个字段来确认程序所需的子系统版本。如果程序所要求的子系统版本较高,操作系统会确保加载并提供该子系统环境。

Ⅱ.MinorSubsystemVersion

MinorSubsystemVersion 字段指定程序所要求的 次子系统版本号。与 MajorSubsystemVersion 一起,MinorSubsystemVersion 描述了程序的完整子系统版本要求。

什么是子系统?

子系统是操作系统提供的环境,用于支持不同类型的程序。PE 文件中的子系统信息决定了程序将运行在什么样的环境中。常见的子系统类型包括:

①Windows GUI:图形用户界面应用程序,通常不使用控制台(如大多数桌面应用程序)。
②Windows CUI:控制台应用程序(如命令行程序)。
③POSIX:用于支持 POSIX 标准的子系统,通常在 Windows 上使用类似 Cygwin 的环境。
④EFI:用于支持 UEFI(统一可扩展固件接口)环境下的程序。

经检验,这两个字段不可随意修改。

⑪Win32VersionValue(DWORD)

该字段是一个DWORD类型的字段,Win32VersionValue 字段在 PE 文件格式中并不是非常常用,它的值通常被设置为 0x00000000,但也可能存储其他值来表示特定的版本信息。这个字段主要在早期版本的 Windows 操作系统中被使用,用于标识 32 位 Windows 版本,但在大多数情况下,它并不对程序的运行产生影响。

⑫ SizeOfImage(DWORD)

SizeOfImage 是一个关键字段,它告诉操作系统或加载器在将程序加载到内存时需要为该程序分配多大的内存空间。这个大小不仅包括程序代码和数据,还包括由操作系统加载和初始化程序时需要的其他信息(如导入表、重定位表等)。

SizeOfImage 的计算通常包括以下内容:

所有节(Section)的内存占用:PE 文件通常包含多个节,如 .text、.data、.rdata 等,每个节的大小决定了映像占用的内存空间。
页对齐(Page Alignment):操作系统在加载程序时,通常会对节进行页对齐(即按系统页面大小对齐)。因此,SizeOfImage 可能会比文件的实际大小大一些,因为它包含了对齐后的内存空间。

经验证,该值不可随意修改:

此时若是将该值调大:则程序正常运行。

若此时将该字段值调小(小于原来的值):则程序无法正常运行:

⑬ SizeOfHeaders(DWORD)

SizeOfHeaders 主要用于标识文件头部部分的总大小。操作系统在加载程序时,首先会读取文件头(包括 DOS 头、PE 头、可选头和节表头等),并根据这些头部信息来判断如何加载和执行该程序。

该字段的值通常会帮助操作系统加载器确定文件头部分的结束位置,并从文件中读取其余部分(即程序的代码和数据段等)。

SizeOfHeaders 计算公式为:

SizeOfHeaders = DOS header size + PE header size + File header size + Optional header size + Section header size

SizeOfHeaders 字段表示 PE 文件头部分的总大小,包含了所有与程序加载相关的元数据。这个字段的值包括 DOS 头、PE 头、文件头、可选头和节头的大小,并帮助操作系统确定程序的加载方式。SizeOfHeaders 的大小通常是固定的,但如果节数量增加,节头部分的大小会随之变化。

经检验,该字段不可随意更改。

⑭CheckSum(DWORD)

CheckSum 字段的主要作用是验证 PE 文件的完整性。在文件传输、下载、拷贝或其他操作后,可以使用 CheckSum 来检查文件是否发生了任何错误或损坏。如果文件被修改或损坏,计算出的校验和将与原始的 CheckSum 值不同。

实际例子:

在某些情况下,操作系统或防病毒软件可能会检查 CheckSum 来确定 PE 文件是否被篡改,或者在加载文件时验证文件的完整性。

例如,在安装一个程序时,安装程序可能会计算下载的安装包的校验和并与存储在文件中的校验和进行比较。如果校验和不匹配,说明文件可能在下载过程中损坏,或者文件被篡改,安装程序会提示用户。

经检验:该字段在修改后不影响程序运行(win11中)

⑮ Subsystem(WORD)

Subsystem 字段,用于指定程序的子系统类型,子系统类型决定了该程序在操作系统中运行时所依赖的环境和特性。例如,它决定程序是作为控制台应用程序运行,还是作为图形用户界面(GUI)应用程序运行。该字段的值是一个常量,指定了程序所依赖的操作系统功能和库。

在Visual Studio中可以找到Subsystem的相关取值:

#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

此时样例程序该字段的取值为:0002,通过对照该表我们就可以清楚该程序是一个GUI程序,运行在Windows的GUI子系统。

若此时我们将样例程序该字段的取值改为:0003(Image runs in the Windows character subsystem.)

再次运行后就发现该程序会自动弹出黑窗口。最后经过验证发现,该程序不可随意修改:

⑯ DllCharacteristics(WORD)

DllCharacteristics 字段用于指示 DLL 文件(动态链接库)的一些特性和行为。它是一个位掩码字段,包含一组特性标志,描述了该 DLL 的一些重要属性,特别是在加载、链接和执行时的行为。在Visual Studio查看NT头结构题的页面,向下拉,就可以看到对应的DllCharacteristics字段的取值。

#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.(表示该 DLL 支持地址空间布局随机化)
#define IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY    0x0080     // Code Integrity Image(表示强制 DLL 完整性检查)
#define IMAGE_DLLCHARACTERISTICS_NX_COMPAT    0x0100     // Image is NX compatible(表示该 DLL 支持执行禁用(NX, No eXecute)功能。)
#define IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200     // Image understands isolation and doesn't want it(表示该 DLL 不使用 Windows 的 DLL 隔离机制。)
#define IMAGE_DLLCHARACTERISTICS_NO_SEH       0x0400     // Image does not use SEH.  No SE handler may reside in this image(表示该 DLL 不使用结构化异常处理)
#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     0x8000 //表示该 DLL 是终端服务器兼容的。  

此时样例程序该字段的值为0x8140

通过对照该表可知:表示该 DLL 支持地址空间布局随机化(0x40)、该 DLL 支持执行禁用功能(0x100)、该 DLL 是终端服务器兼容的(8000x)。

IMAGE_DLLCHARACTERISTICS_NX_COMPAT
IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE

最后,经过尝试,发现该字段不可随意更改。

SizeOfStackReserve (DWORD)和 SizeOfStackCommit(DWORD)、SizeOfHeapReserve(DWORD) 和 SizeOfHeapCommit(DWORD)**(可以修改,但是不能随意修改)

Ⅰ.SizeOfStackReserve

该字段表示栈的预留大小,即操作系统为程序预留的栈空间的总大小。这个值是操作系统在加载程序时保留的虚拟内存空间的大小,但是这些内存页并不会立即被物理内存填充,直到程序开始使用它们。如果 SizeOfStackReserve0x00100000(1MB),则表示操作系统为该程序预留了 1MB 的虚拟内存空间,用于栈的使用。

Ⅱ.MinorSubsystemVersion

该字段表示栈的提交大小,即操作系统为程序分配并加载到物理内存中的栈空间的大小。这个值表示栈的初始物理内存分配量,在程序运行时,栈空间的初始部分会被分配给程序。如果 SizeOfStackCommit0x00020000(128KB),则表示操作系统在程序启动时会为栈分配 128KB 的物理内存。这部分内存是立即可用的,而不是仅仅在虚拟内存中预留的。

Ⅲ.SizeOfHeapReserve 和 SizeOfHeapCommit

这两个字段分别表示为程序的堆保留和提交的内存大小。

SizeOfHeapReserve 指定了操作系统为堆保留的虚拟内存大小。此空间是程序可以使用的,但初始时并不会映射到实际的物理内存中。SizeOfHeapCommit 指定了初始为程序分配的物理内存大小,通常较小,只为程序的堆提供基础内存,只有在程序需要更多堆空间时,操作系统才会进一步分配更多内存。

操作系统通常会使用两种策略来管理堆栈的内存分配:

预留大小:一般来说,SizeOfStackReserve 的值较大,因为它表示操作系统为栈保留的地址空间。这个值通常会设置得足够大,以便程序能够使用较大的栈空间。
提交大小:SizeOfStackCommit 的值通常较小,因为它表示最初分配给程序的物理内存。操作系统会根据程序的需求,逐步扩大提交的内存区域。

⑱LoaderFlags(DWORD)

LoaderFlags 的标志值通常用来指定一些特殊的加载选项,特别是关于程序加载的方式。不过这些标志在许多实际的应用中并未被广泛使用,很多时候它的值为 0。随意修改该字段的值,并不影响程序正常运行。

⑲ NumberOfRvaAndSizes(DWORD)

该字段表示数据目录(Data Directories)数组中有效条目的数量。Data Directories 数组包含了 PE 文件中多个重要信息的 RVA 和大小。NumberOfRvaAndSizes 定义了数组中实际使用的条目数。它的值是一个小于或等于 16 的数字,因为 Data Directories 数组的最多只能有 16 个元素。

如果 NumberOfRvaAndSizes 为 10,那么数据目录中前 10 个条目是有效的,之后的条目没有被使用,可能是 0 或者无效的。如果 NumberOfRvaAndSizes 为 16,那么数据目录中的所有条目都是有效的,表示所有的 16 个数据目录项都有对应的 RVA 和大小。

此时样例程序中NumberOfRvaAndSizes值为00 00 00 01这个时候则表示数据目录中的只有一条数据是有效的。验证:修改该字段,并不影响程序运行。

⑳DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES](数据目录)

DataDirectory 是一个长度为 16 的数组,表示 PE 文件中可能包含的最多 16 个数据目录。虽然数组有 16 个条目,但并不是所有条目都会被使用。NumberOfRvaAndSizes 字段会指示实际使用了多少个数据目录。DataDirectory 包含指向不同类型数据的相对虚拟地址(RVA)和该数据的大小。这些数据包括导入表、导出表、资源表等,用于在程序加载和执行时进行访问,该字段的类型为_IMAGE_DATA_DIRECTORY,在 PE 文件中,_IMAGE_DATA_DIRECTORY 是一个包含两个字段的结构体,通常定义如下:

typedef struct _IMAGE_DATA_DIRECTORY {DWORD   VirtualAddress;DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
​
#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16

VirtualAddress:指向数据的相对虚拟地址(RVA),即该数据在内存中的位置。通过该地址,加载器可以找到该数据。

Size:该数据的大小(以字节为单位)。如果该字段为 0,表示数据不存在或没有相关内容。

用途:

DataDirectory 包含了 PE 文件中的 16 个数据目录条目,每个条目描述了一段特定的数据,指明了该数据在内存中的位置及其大小。常见的数据目录条目包括:

导入表(Import Table):描述程序使用的外部函数和符号。通过这个表,程序可以访问外部库(DLL)中的函数。
导出表(Export Table):列出程序对外提供的函数和符号,其他程序可以通过这个表调用它们。
资源表(Resource Table):存储程序的各种资源数据,如图标、位图、字符串等。
重定位表(Base Relocation Table):包含程序的地址重定位信息,用于程序加载到非默认地址时调整指针和地址。
异常表(Exception Table):存储异常处理相关信息。
安全表(Security Table):存储安全相关信息,如签名。
调试信息(Debug Data):包含调试信息,如符号、源文件信息等。
TLS 表(TLS Table):线程局部存储(Thread Local Storage)表,指示程序如何访问线程本地数据。
加载配置表(Load Config Table):存储与程序加载配置相关的信息,如安全和调试配置。
Bound Import Table:用于标记程序是否已经绑定到 DLL 中的符号。
IAT(Import Address Table):存储程序导入的符号的地址。

数组中放哪个表,具体看底下的宏定义(Visual Studio):

各个条目在数组中的位置入下:

// Directory Entries
​
#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 TLS表
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // Bound Import Directory in headers 存储程序与 DLL 文件绑定的符号信息。
#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

数据目录(DataDirectory)在 PE 文件中包含了多个关键的数据结构和表(如导入表、导出表、重定位表等),这些数据对于程序的正常加载和运行至关重要。因此,数据目录不能随意修改,否则可能导致程序无法正确加载或运行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/62500.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Oracle EBS FA 如何打开关闭的资产会计期间?

用户“运行折旧”,误勾选为“关闭期间”,还有一部分资产还需要操作报废和调整,希望后台打开关闭的资产会计期 系统环境 RDBMS : 12.1.0.2.0 Oracle Applications : 12.2.9 解决方案 由官方提供SQL脚本代码如下: /*rollback120.sql - for Release 12.X only(based on r…

算法基础学习Day6(动态窗口)

文章目录 1.题目2.题目解答1.最大连续1的个数题目及题目解析算法学习思路一:暴力解法思路二:滑动窗口 代码提交 2.将x减到0的最小操作数题目及题目解析算法学习滑动窗口解决问题 代码提交 1.题目 1004. 最大连续1的个数 III - 力扣(LeetCode)1658. 将 x…

基于springboot+vue的公交线路查询系统(全套)

一、系统架构 前端:vue | element-ui | html 后端:springboot | mybatis-plus 环境:jdk1.8 | mysql | maven | nodejs 二、代码及数据库 三、功能介绍 01. web端-首页1 02. web端-首页2 03. web端-注册 04. web端-登录 …

ASP.NET Core8.0学习笔记(二十五)——EF Core Include导航数据加载之预加载与过滤

一、导航属性数据加载 1.在EF Core中可以使用导航属性来加载相关实体。 2.加载实体的三种方式: (1)预先加载:直接在查询主体时就把对应的依赖实体查出来(作为初始查询的一部分) (2)显式加载:使用代码指示稍后显式的从…

Linux 基础环境的开发工具以及使用(下)

1. make / Makefile 自动化构建的工具 1)引入 在我们进行一些大型的工程的时候,代码量是极其大,当我们代码在进行一系列的编译的时候,难免会出现一些错误,当我们对错误进行一系列的更改之后,难道我们需要…

沃丰科技智能客服在跨境电商独立站中的核心角色

随着全球化进程的加速和互联网技术的不断发展,跨境电商行业蓬勃兴起,为消费者提供了更广阔、更便捷的购物选择。在这样一个竞争激烈的市场环境中,优质的客户服务成为了企业脱颖而出的关键。沃丰科技智能客服凭借其先进的技术和人性化的设计理…

Centos7下搭建Prometheus+Grafana监控

Prometheus 监控 Prometheus 监控系统的架构包括以下组件: Prometheus Server: Prometheus 服务器是监控系统的核心组件,负责收集、存储和处理指标数据。它定期从各种数据源(如 Exporter、Agent 等)拉取指标数据&…

MyBatis-Plus(为简化开发而生)

一、MyBatis-Plus概述 官网: baomidou.com MyBatis-Plus(简称 MP) 在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。 (1)单表操作 不需要编写sql语句,封装方法,…

深入解析 C++11 的 `std::atomic`:误区、性能与实际应用

在现代 C 开发中,std::atomic 是处理多线程同步时的重要工具之一。它通过提供原子操作保证了线程安全,但在实际使用时却隐藏着许多不为人知的陷阱和性能影响。本篇文章将带你深入理解 std::atomic 的使用方式、潜在问题,以及如何正确应用于多…

芋道源码,芋道sql,yudao,yudao-vue-pro拒绝割韭菜

芋道的开发指南实际上只需要小小的操作就可以观看啦 为了避免被割韭菜 我们可以使用插件去进行解锁文档 项目地址 otomayss/free-yd (github.com)[这里是图片002]https://github.com/otomayss/free-yd

Mac软件推荐

Mac软件推荐 截图SnipasteXnipBob 快捷启动Raycast 系统检测Stats 解压缩The UnarchiverKeka(付费) 视频播放IINA 视频下载Downie(付费) 屏幕刘海TopNotchMediaMate(付费)NotchDrop(付费&#x…

车站值班员题库

1. 联系用手信号显示十、五、三车距离信号中的“三车”(约33m)信号时,昼间的显示方式为展开的绿色信号旗单臂平伸下压 ( 一 )次。J442 2. 联系用手信号显示股道号码时,昼间右臂向上直伸&#xff0c…

BI中场战事:国外厂商退,国产厂商进

从沉睡的黄金到经济的新宠,数据要素正上演华丽转身。 近年来,数字经济的长驱向前,离不开数据要素价值释放所带来的持续动力。作为第五大生产要素,数据要素的价值释放需要从数据采集、传输到存储、治理,再到分析和可视…

2024年华中杯数学建模C题基于光纤传感器的平面曲线重建算法建模解题全过程文档及程序

2024年华中杯数学建模 C题 基于光纤传感器的平面曲线重建算法建模 原题再现 光纤传感技术是伴随着光纤及光通信技术发展起来的一种新型传感器技术。它是以光波为传感信号、光纤为传输载体来感知外界环境中的信号,其基本原理是当外界环境参数发生变化时&#xff0c…

【H2O2|全栈】MySQL的基本操作(三)

目录 前言 开篇语 准备工作 案例准备 多表查询 笛卡尔积 等值连接 外连接 内连接 自连接 子查询 存在和所有 含于 分页查询 建表语句 结束语 前言 开篇语 本篇继续讲解MySQL的一些基础的操作——数据字段的查询中的多表查询和分页查询,与单表查询…

从单体到微服务:如何借助 Spring Cloud 实现架构转型

一、Spring Cloud简介 Spring Cloud 是一套基于 Spring 框架的微服务架构解决方案,它提供了一系列的工具和组件,帮助开发者快速构建分布式系统,尤其是微服务架构。 Spring Cloud 提供了诸如服务发现、配置管理、负载均衡、断路器、消息总线…

yarn : 无法加载文件 C:\Users\L\AppData\Roaming\npm\yarn.ps1,因为在此系统上禁

关于执行安装yarn命令后执行yarn -v报错: 先确认执行安装yarn命令是否有误 # 安装yarn npm install yarn -g 终端输入set-ExecutionPolicy RemoteSigned 当然如果yarn -v仍然执行失败,考虑使用管理员方式运行IDEA, 注:如上操作…

java全栈day12-后端Web实战(IOC+DI)

前言:前面的基础知识了解后进入实战篇,从以下四个方面进行准备 一、开发规范 1.1前后端分离开发 前言回顾 二、Restful风格 引言:前端与后端在进行交互的时候,所使用的url风格叫Restful。 2.1概述 小结 2.2环境准备 2.2.1apif…

链式设计模式——装饰模式和职责链模式

一、装饰模式 1、概述 动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。 ConcreteComponent :是定义了一个具体的对象,可以给这个对象添加一些职责;Decorator :装饰抽象…

Java——容器(单例集合)(上)

一 容器介绍 容器,是用来容纳物体、管理物体。生活中,我们会用到各种各样的容器。如锅碗瓢盆、箱子和包等 程序中的“容器”也有类似的功能,用来容纳和管理数据。比如,如下新闻网站的新闻列表、教育网站的课程列表就是用“容器”来管理 视频…