廣州市品牌網(wǎng)站建設(shè)公司蘇州網(wǎng)絡(luò)推廣seo服務(wù)
節(jié)表(Section Table)是Windows PE/COFF格式的可執(zhí)行文件中一個(gè)非常重要的數(shù)據(jù)結(jié)構(gòu),它記錄了各個(gè)代碼段、數(shù)據(jù)段、資源段、重定向表等在文件中的位置和大小信息,是操作系統(tǒng)加載文件時(shí)根據(jù)節(jié)表來(lái)進(jìn)行各個(gè)段的映射和初始化的重要依據(jù)。節(jié)表中的每個(gè)記錄則被稱為IMAGE_SECTION_HEADER
,它記錄了一個(gè)段的各種屬性信息和在文件中的位置和大小等信息,一個(gè)文件可以由多個(gè)IMAGE_SECTION_HEADER
構(gòu)成。
在執(zhí)行PE文件的時(shí)候,Windows 并不在一開(kāi)始就將整個(gè)文件讀入內(nèi)存,PE裝載器在裝載的時(shí)候僅僅建立好虛擬地址和PE文件之間的映射關(guān)系,只有真正執(zhí)行到某個(gè)內(nèi)存頁(yè)中的指令或者訪問(wèn)頁(yè)中的數(shù)據(jù)時(shí),這個(gè)頁(yè)面才會(huì)被從磁盤提交到內(nèi)存中,這種機(jī)制極大的節(jié)約了內(nèi)存資源,使文件的裝入速度和文件的大小沒(méi)有太多的關(guān)系。
Windows 裝載器在裝載DOS部分PE文件頭部分和節(jié)表部分時(shí)不進(jìn)行任何處理,而在裝載節(jié)區(qū)的時(shí)候會(huì)根據(jù)節(jié)的不同屬性做不同的處理,一般需要處理以下幾個(gè)方面的內(nèi)容:
節(jié)區(qū)的屬性: 節(jié)是相同屬性的數(shù)據(jù)的組合,當(dāng)節(jié)被裝入內(nèi)存的時(shí)候,同一個(gè)節(jié)對(duì)應(yīng)的內(nèi)存頁(yè)面將被賦予相同的頁(yè)屬性,Windows系統(tǒng)對(duì)內(nèi)存屬性的設(shè)置是以頁(yè)為單位進(jìn)行的,所以節(jié)在內(nèi)存中的對(duì)其單位必須至少是一個(gè)頁(yè)的大小,對(duì)于X86來(lái)說(shuō)這個(gè)值是4KB(1000h),而對(duì)于X64來(lái)說(shuō)這個(gè)值是8KB(2000h),磁盤中存儲(chǔ)的程序并不會(huì)對(duì)齊4KB,而只有被PE加載器載入內(nèi)存的時(shí)候,PE裝載器才會(huì)自動(dòng)的補(bǔ)齊4KB對(duì)其的零頭數(shù)據(jù)。
節(jié)區(qū)的偏移: 節(jié)的起始地址在磁盤文件中是按照IMAGE_OPTIONAL_HEADER
結(jié)構(gòu)的FileAhgnment字段的值對(duì)齊的,而被加載到內(nèi)存中時(shí)是按照同一結(jié)構(gòu)中的SectionAlignment字段的值對(duì)齊的,兩者的值可能不同,所以一個(gè)節(jié)被裝入內(nèi)存后相對(duì)于文件頭的偏移和在磁盤文件中的偏移可能是不同的。
節(jié)區(qū)的尺寸: 由于磁盤映像和內(nèi)存映像的對(duì)齊單位不同,磁盤中的映像在裝入內(nèi)存后會(huì)自動(dòng)的進(jìn)行長(zhǎng)度擴(kuò)展,而對(duì)于未初始化的數(shù)據(jù)段(.data?)來(lái)說(shuō),則沒(méi)有必要為它在磁盤文件中預(yù)留空間,只要可執(zhí)行文件裝入內(nèi)存后動(dòng)態(tài)的為其分配空間即可,所以包含未初始化數(shù)據(jù)的節(jié)在磁盤中長(zhǎng)度被定義為0,只有在運(yùn)行后PE加載器才會(huì)動(dòng)態(tài)的為他們開(kāi)辟空間。
不進(jìn)行映射的節(jié): 有些節(jié)中包含的數(shù)據(jù)僅僅是在裝入的時(shí)候用到,當(dāng)文件裝載完畢時(shí),他們不會(huì)被遞交到物理內(nèi)存中,例如重定位節(jié),該節(jié)的數(shù)據(jù)對(duì)于文件的執(zhí)行代碼來(lái)說(shuō)是透明的,他只供Windows裝載器使用,可執(zhí)行代碼根本不會(huì)訪問(wèn)他們,所以這些節(jié)存在于磁盤文件中,不會(huì)被映射到內(nèi)存中。
一般來(lái)說(shuō),當(dāng)一個(gè)PE文件被編譯生成時(shí)則默認(rèn)會(huì)存在.text,.data
等基本節(jié)表,而每一個(gè)節(jié)表都是由一個(gè)IMAGE_SECTION_HEADER
結(jié)構(gòu)排列而成,每個(gè)結(jié)構(gòu)都用來(lái)描述一個(gè)節(jié),節(jié)表總被存放在緊接在PE文件頭的地方,也即是從PE文件頭開(kāi)始偏移為00f8h
的位置,針對(duì)每一個(gè)節(jié)中的定義可查看節(jié)表結(jié)構(gòu)體的定義;
typedef struct _IMAGE_SECTION_HEADER
{BYTE Name[IMAGE_SIZEOF_SHORT_NAME];union {DWORD PhysicalAddress;DWORD VirtualSize; // 節(jié)區(qū)尺寸} Misc;DWORD VirtualAddress; // 節(jié)區(qū)RVADWORD SizeOfRawData; // 在文件中對(duì)齊后的尺寸DWORD PointerToRawData; // 在文件中的偏移DWORD PointerToRelocations; // 在OBJ文件中使用DWORD PointerToLinenumbers;WORD NumberOfRelocations;WORD NumberOfLinenumbers;DWORD Characteristics; // 節(jié)區(qū)屬性字段
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
針對(duì)IMAGE_SECTION_HEADER
中各個(gè)字段的詳細(xì)解析:
-
Name:段名,是一個(gè)8字節(jié)的
ASCII
字符串,不足8字節(jié)用0補(bǔ)齊。 -
VirtualSize:虛擬大小,標(biāo)識(shí)在內(nèi)存中占用的大小,請(qǐng)勿與
PhysicalSize
(物理大小)混淆。 -
VirtualAddress:虛擬地址,標(biāo)識(shí)在內(nèi)存中對(duì)應(yīng)段頭的地址,與實(shí)際加載的位置有關(guān)。
-
SizeOfRawData:物理大小,標(biāo)識(shí)在PE文件中該段的占用大小,不足以文件對(duì)齊單位則會(huì)進(jìn)行填充。
-
PointerToRawData:物理地址,標(biāo)識(shí)該段在文件中的偏移位置。
-
PointerToRelocations:重定向表的偏移位置。
-
PointerToLinenumbers:行號(hào)表的偏移位置。
-
NumberOfRelocations:重定向表數(shù)量。
-
NumberOfLinenumbers:行號(hào)表數(shù)量。
-
Characteristics:標(biāo)識(shí)該段的各種屬性信息,包括下列常用屬性:
- IMAGE_SCN_MEM_READ:可讀;
- IMAGE_SCN_MEM_WRITE:可寫;
- IMAGE_SCN_MEM_EXECUTE:可執(zhí)行;
- IMAGE_SCN_CNT_CODE:代碼段;
- IMAGE_SCN_CNT_INITIALIZED_DATA:已初始化數(shù)據(jù)段;
- IMAGE_SCN_CNT_UNINITIALIZED_DATA:未初始化數(shù)據(jù)段;
- IMAGE_SCN_LNK_INFO:包含附加信息。
與數(shù)據(jù)目錄表的枚舉方式基本一致,數(shù)據(jù)目錄表的枚舉也不會(huì)太難,讀者只需要通過(guò)NtHeader->FileHeader.NumberOfSections
獲取到當(dāng)前有多少個(gè)節(jié),并通過(guò)循環(huán)的方式依次得到這些節(jié)中的指針,并將該指針轉(zhuǎn)換為PIMAGE_SECTION_HEADER
結(jié)構(gòu),依次循環(huán)輸出即可得到;
int main(int argc, char * argv[])
{BOOL PE = IsPeFile(OpenPeFile("c://pe/x86.exe"), 0);if (PE == TRUE){printf("編號(hào)\t 節(jié)區(qū)名稱\t虛擬偏移\t虛擬大小\t實(shí)際偏移\t實(shí)際大小\t節(jié)區(qū)屬性\n");for (DWORD each = 0; each < NtHeader->FileHeader.NumberOfSections; each++, pSection++){printf("%d\t %-9s\t 0x%.8X \t 0x%.8X \t 0x%.8X \t 0x%.8X \t 0x%.8X \n",each + 1, pSection->Name, pSection->VirtualAddress, pSection->Misc.VirtualSize,pSection->PointerToRawData, pSection->SizeOfRawData, pSection->Characteristics);}}else{printf("非標(biāo)準(zhǔn)程序 \n");}system("pause");return 0;
}
運(yùn)行上述程序,即可輸出當(dāng)前程序中存在的節(jié)表信息,輸出效果如下圖所示;