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.pswp.cn/web/62500.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/62500.shtml
英文地址,請注明出處:http://en.pswp.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)引入 在我們進行一些大型的工程的時候,代碼量是極其大,當我們代碼在進行一系列的編譯的時候,難免會出現一些錯誤,當我們對錯誤進行一系列的更改之后,難道我們需要…

沃豐科技智能客服在跨境電商獨立站中的核心角色

隨著全球化進程的加速和互聯網技術的不斷發展,跨境電商行業蓬勃興起,為消費者提供了更廣闊、更便捷的購物選擇。在這樣一個競爭激烈的市場環境中,優質的客戶服務成為了企業脫穎而出的關鍵。沃豐科技智能客服憑借其先進的技術和人性化的設計理…

uniapp 彈出軟鍵盤后打開二級頁面,解決其UI布局變動

軟鍵盤彈出,此時點擊某按鈕打開二級頁面,position:fixed 位于底部的按鈕不見了(通過加高其區域,發現被下移動了),什么原因不清楚? 但是發現是軟鍵盤彈出導致,問題解決通過隱藏鍵盤再打開二級頁…

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…

【ETCD】【源碼閱讀】 深入解析 raftNode.start`函數:Raft 核心啟動邏輯剖析

raftNode.start方法 是 etcd 中 Raft 模塊的核心啟動點,其職責是管理 Raft 狀態機的狀態變遷、日志處理及集群通信等邏輯。通過對源碼的逐行分析,我們將全面揭示其運行機制,探討其設計背后的分布式系統理念。 函數核心結構 raftNode.start 方…

車站值班員題庫

1. 聯系用手信號顯示十、五、三車距離信號中的“三車”(約33m)信號時,晝間的顯示方式為展開的綠色信號旗單臂平伸下壓 ( 一 )次。J442 2. 聯系用手信號顯示股道號碼時,晝間右臂向上直伸&#xff0c…

BI中場戰事:國外廠商退,國產廠商進

從沉睡的黃金到經濟的新寵,數據要素正上演華麗轉身。 近年來,數字經濟的長驅向前,離不開數據要素價值釋放所帶來的持續動力。作為第五大生產要素,數據要素的價值釋放需要從數據采集、傳輸到存儲、治理,再到分析和可視…

2024年華中杯數學建模C題基于光纖傳感器的平面曲線重建算法建模解題全過程文檔及程序

2024年華中杯數學建模 C題 基于光纖傳感器的平面曲線重建算法建模 原題再現 光纖傳感技術是伴隨著光纖及光通信技術發展起來的一種新型傳感器技術。它是以光波為傳感信號、光纖為傳輸載體來感知外界環境中的信號,其基本原理是當外界環境參數發生變化時&#xff0c…

【LeetCode每日一題】LeetCode 209.長度最小的子數組

LeetCode 209.長度最小的子數組 題目描述 給定一個正整數數組 nums 和一個正整數 target,找出連續子數組的最小長度,使得子數組的和大于或等于 target。如果不存在符合條件的子數組,返回 0。 Java 實現代碼 public class Solution {publi…

【openwrt】openwrt-21.02 基于IP地址使用ipset實現策略路由操作說明

openwrt版本信息 DISTRIB_ID=OpenWrt DISTRIB_RELEASE=21.02-SNAPSHOT DISTRIB_REVISION=r0-6bf6af1d5 DISTRIB_TARGET=mediatek/mt7981 DISTRIB_ARCH=aarch64_cortex-a53 DISTRIB_DESCRIPTION=OpenWrt 21.02-SNAPSHOT r0-6bf6af1d5 DISTRIB_TAINTS=no-all busybox override …

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

目錄 前言 開篇語 準備工作 案例準備 多表查詢 笛卡爾積 等值連接 外連接 內連接 自連接 子查詢 存在和所有 含于 分頁查詢 建表語句 結束語 前言 開篇語 本篇繼續講解MySQL的一些基礎的操作——數據字段的查詢中的多表查詢和分頁查詢,與單表查詢…

從單體到微服務:如何借助 Spring Cloud 實現架構轉型

一、Spring Cloud簡介 Spring Cloud 是一套基于 Spring 框架的微服務架構解決方案,它提供了一系列的工具和組件,幫助開發者快速構建分布式系統,尤其是微服務架構。 Spring Cloud 提供了諸如服務發現、配置管理、負載均衡、斷路器、消息總線…