《Undocumented Windows 2000 Secrets》翻譯 --- 第四章(2)
第四章 探索 Windows 2000 的內(nèi)存管理機(jī)制
翻譯: Kendiv( fcczj@263.net )
更新: Sunday, February 14, 2005
聲明:轉(zhuǎn)載請(qǐng)注明出處,并保證文章的完整性,本人保留譯文的所有權(quán)利。
數(shù)據(jù)結(jié)構(gòu)
本章隨后的示例代碼的某些部分將涉及底層的內(nèi)存管理機(jī)制,在前面我們已快速瀏覽了該機(jī)制內(nèi)部的大致輪廓。為了方便,我用 C 語(yǔ)言定義了幾個(gè)數(shù)據(jù)結(jié)構(gòu)。這是因?yàn)?i386 CPU 內(nèi)部的很多數(shù)據(jù)項(xiàng)需要使用一個(gè)二進(jìn)制位或一組二進(jìn)制位,而 C 的位域( bit-fIElds )唾手可得。位域可以很有效的訪問(wèn)一個(gè)大的數(shù)據(jù)中的一個(gè)位或從中提取一組連續(xù)的位。微軟的 Visual C/C++ 可以產(chǎn)生非常棒的代碼來(lái)完成位域的操作。 列表 4-2 是一系列 CPU 數(shù)據(jù)類型定義的一部分,該列表包含如下的內(nèi)容:
l X86_REGISTER 這是一個(gè)基本的無(wú)符號(hào) 32 位整數(shù)類型,該類型可描述多個(gè) CPU 寄存器。這包括:通用的、索引、指針、控制、調(diào)試和測(cè)試寄存器。
l X86_SELECTOR 代表一個(gè) 16 位的段選擇器,如 CS 、 DS 、 ES 、 FS 、 GS 和 SS 。在 圖 4-1 和 圖 4-2 中,選擇器可描述 48 位邏輯地址的高 8 位,或作為描述符表的索引。為了計(jì)算的方便, 16 位選擇器的值被擴(kuò)展到 32 位,不過(guò)高 16 位被標(biāo)識(shí)為“保留”。注意, X86_SELECTOR 結(jié)構(gòu)實(shí)際是兩個(gè)結(jié)構(gòu)的聯(lián)合( union )。第一個(gè)指定了選擇器的值,該值占用一個(gè) 16 位的 Word ,其名字為 wValue ,第二個(gè)采用了位域。 RPL 域指定了請(qǐng)求的特權(quán)級(jí),在 Windows 2000 上其值或者為 0 (內(nèi)核模式)或者為 3 (用戶模式)。 TI 位用來(lái)選擇 GDT 或 LDT 。
l X86_DESCRIPTOR 定義了由選擇器指向的頁(yè)表項(xiàng)的格式。這是一個(gè) 64 位的數(shù)值,由于歷史演化,該結(jié)構(gòu)比較讓人費(fèi)解。線性基地址定義了與其相關(guān)的段的起始位置,它們分散在三個(gè)位域中: Base1 、 Base2 和 Base3 , Base1 是作用最小的部分。段的界限指定了段的大小, The segment limit specifying the segment size minus one is divided into the pair Limitl and Limit2, with the former representing the least significant half. 剩余的位域存放不同的段屬性( cf. Intel 1999c, pp.3-11 )。例如, G 位域定義了段的粒度。如果為零,段的限制按字節(jié)指定;否則,限制值為 4KB 的倍數(shù)。像 X86_SELECTOR 一樣, X86_DESCRIPTOR 結(jié)構(gòu)由一個(gè) union 組成,以允許按不同的方式解釋它的值。如果你必須復(fù)制描述符(在忽略其內(nèi)部情況下)那么 dValueLow 和 dValueHigh 成員將會(huì)很有幫助。
l X86_GATE 該結(jié)構(gòu)看起來(lái)有些像 X86_DESCRIPTOR 。事實(shí)上,這兩個(gè)結(jié)構(gòu)是相關(guān)的: X86_DESCRIPTRO 是一個(gè) GDT 項(xiàng),并描述了一個(gè)段的內(nèi)存屬性, X86_GATE 代表中斷描述符表( IDT )中的一項(xiàng),并描述了中斷例程的內(nèi)存屬性。 IDT 可以包含任務(wù)、中斷和陷阱門(mén)(不! Bill Gates 并沒(méi)有存儲(chǔ)在 IDT 中! 哈哈)。 X86_GATE 結(jié)構(gòu)可匹配上述三種類型,并通過(guò) Type 位域來(lái)進(jìn)行區(qū)分。 Type 5 表示這是一個(gè)任務(wù)門(mén); Type 6 和 14 為中斷門(mén); Type 7 和 15 為陷阱門(mén)。 Type 中最重要的位是用來(lái)描述門(mén)的位數(shù)的位:該位若為 0 則表示是 16 位門(mén);其余情況表示 32 位門(mén)。
l X86_TABLE 是一個(gè)巧妙的結(jié)構(gòu),該結(jié)構(gòu)用來(lái)讀取 GDTR 或 IDTR 的當(dāng)前值,分別通過(guò)匯編指令 SGDT (存儲(chǔ) GDT 寄存器)和 SIDT (存儲(chǔ) IDT 寄存器)來(lái)實(shí)現(xiàn)( cf. Intel 1999b, pp.3-636 )。這兩個(gè)指令需要一個(gè) 48 位的內(nèi)存操作數(shù),在該操作數(shù)中存放限制值和基地址值。通過(guò)在結(jié)構(gòu)體中增加一個(gè) DWORD 來(lái)對(duì)齊 32 位的基地址, X86_TABLE 以一個(gè) 16 位的啞元成員 wReserved 開(kāi)始。根據(jù)是否使用了 SGDT 或 SIDT 指令,其基地址將被解釋為一個(gè)描述符指針或一個(gè)門(mén)指針,就像 PX86_DESCRIPTOR 和 PX86_GATE 中的 union 所暗示的那樣。最后的 wLimit 成員在這兩種類型的表中的意義均相同。
譯注:
列表 4-2 中的這些結(jié)構(gòu)定義可以在隨書(shū)光盤(pán)的 srccommonincludew2k_spy.h 中找到。
typedef DWORD X86_REGISTER, *PX86_REGISTER, **PPX86_REGISTER;
// -----------------------------------------------------------------
typedef struct _X86_SELECTOR
{
union
{
struct
{
WORD wValue; // packed value
WORD wReserved;
};
struct
{
unsigned RPL : 2; // requested privilege level
unsigned TI : 1; // table indicator: 0=gdt, 1=ldt
unsigned Index : 13; // index into descriptor table
unsigned Reserved : 16;
};
};
}
X86_SELECTOR, *PX86_SELECTOR, **PPX86_SELECTOR;
#define X86_SELECTOR_ sizeof (X86_SELECTOR)
// -----------------------------------------------------------------
typedef struct _X86_DESCRIPTOR
{
union
{
struct
{
DWORD dValueLow; // packed value
DWORD dValueHigh;
};
struct
{
unsigned Limit1 : 16; // bits 15..00
unsigned Base1 : 16; // bits 15..00
unsigned Base2 : 8; // bits 23..16
unsigned Type : 4; // segment type
unsigned S : 1; // type (0=system, 1=code/data)
unsigned DPL : 2; // descriptor privilege level
unsigned P : 1; // segment present
unsigned Limit2 : 4; // bits 19..16
unsigned AVL : 1; // available to programmer
unsigned Reserved : 1;
unsigned DB : 1; // 0=16-bit, 1=32-bit
unsigned G : 1; // granularity (1=4KB)
unsigned Base3 : 8; // bits 31..24
};
};
}
X86_DESCRIPTOR, *PX86_DESCRIPTOR, **PPX86_DESCRIPTOR;
#define X86_DESCRIPTOR_ sizeof (X86_DESCRIPTOR)
// -----------------------------------------------------------------
typedef struct _X86_GATE
{
union
{
struct
{
DWORD dValueLow; // packed value
DWORD dValueHigh;
};
struct
{
unsigned Offset1 : 16; // bits 15..00
unsigned Selector : 16; // segment selector
unsigned Parameters : 5; // parameters
unsigned Reserved : 3;
unsigned Type : 4; // gate type and size
unsigned S : 1; // always 0
unsigned DPL : 2; // descriptor privilege level
unsigned P : 1; // segment present
unsigned Offset2 : 16; // bits 31..16
};
};
}
X86_GATE, *PX86_GATE, **PPX86_GATE;
#define X86_GATE_ sizeof (X86_GATE)
// -----------------------------------------------------------------
typedef struct _X86_TABLE
{
WORD wReserved; // force 32-bit alignment
WORD wLimit; // table limit
union
{
PX86_DESCRIPTOR pDescriptors; // used by sgdt instruction
PX86_GATE pGates; // used by sidt instruction
};
}
X86_TABLE, *PX86_TABLE, **PPX86_TABLE;
#define X86_TABLE_ sizeof (X86_TABLE)
列表 4-2. i386 的寄存器、選擇器、描述符、門(mén)和表
接下來(lái)的一組與 i386 內(nèi)存管理相關(guān)的結(jié)構(gòu),它們收錄在 列表 4-3 中,這些結(jié)構(gòu)包括:與請(qǐng)求式分頁(yè)相關(guān)的結(jié)構(gòu)和 圖 4-3 和 圖 4-4 給出的幾個(gè)成員。
l X86_PDBR 該結(jié)構(gòu)對(duì)應(yīng) CPU 的 CR3 寄存器,即眾所周知的頁(yè)目錄基地址寄存器( PDBR )。其高 20 位為 PFN ,即 4KB 物理頁(yè)數(shù)組的索引。 PFN=0 對(duì)應(yīng)物理地址 0x00000000 , PFN=1 為 0x00001000 ,依此類推。 20 個(gè)位足夠轉(zhuǎn)換整個(gè) 4GB 地址空間。 PDBR 中的 PFN 是物理頁(yè)的索引,用來(lái)控制整個(gè)頁(yè)目錄。 PFN 中剩余的位大多數(shù)都被保留,但 3 號(hào)位例外,它用來(lái)控制頁(yè)一級(jí)的 write-through ( page-level write-through, PWT ), 4 號(hào)位如果為 1 ,則禁止頁(yè)一級(jí)的高速緩沖。
l X86_PDE_4M 和 X86_PDE_4K 是頁(yè)目錄項(xiàng)( PDE )的兩個(gè)可選方案,用來(lái)選擇 4MB 頁(yè)或者 4KB 的頁(yè)。一個(gè)頁(yè)目錄中最多包含 1024 個(gè) PDE 。 PFN 是頁(yè)幀號(hào),它指向下一級(jí)的頁(yè)。對(duì)于一個(gè) 4MB 的 PDE ,其 PFN 位域僅有 10 個(gè)位的寬度,可尋址一個(gè) 4MB 的數(shù)據(jù)頁(yè)。 4KB 的 PDE 擁有 20 位的 PFN ,可指向一個(gè)頁(yè)表,由頁(yè)表最終選擇一個(gè)數(shù)據(jù)頁(yè)。剩余的位用來(lái)定義多種屬性。這些屬性中最有趣的是“頁(yè)大小”位 PS ,用于控制頁(yè)的大小( 0=4KB , 1=4MB )和“存在”位 P ,標(biāo)識(shí)下屬的數(shù)據(jù)頁(yè)( 4MB 模式)或頁(yè)表( 4KB 模式)是否存在于物理內(nèi)存中。
X86_PTE_4K 定義了頁(yè)表項(xiàng)(屬于一個(gè)頁(yè)表)的內(nèi)部結(jié)構(gòu)。和頁(yè)目錄類似,一個(gè)頁(yè)表可擁有 1024 個(gè)項(xiàng)。 X86_PTE_4K 和 X86_PDE_4K 的不同之處為:前者沒(méi)有 PS 位,這根本不需要,因?yàn)轫?yè)的大小肯定是 4KB 。需要注意的是,沒(méi)有所謂的 4MB 的 PTE ,因?yàn)椴捎?4MB 頁(yè)的內(nèi)存模式不需要頁(yè)表這一中間層。
X86_PNPE 代表一個(gè)“不存在的頁(yè)”項(xiàng)( page-not-present entry, PNPE ),也就是說(shuō),一個(gè) PDE 或 PTE 中的 P 位為 0 。如 Intel 的手冊(cè)所說(shuō)的,保留的第 31 位是“對(duì)操作系統(tǒng)或執(zhí)行體( executive )均可用”( Intel 1999c,pp. 3-28 )。如果一個(gè)線性地址映射到了一個(gè) PNPE ,這意味著這個(gè)地址或者還未使用或者它所指向的頁(yè)已經(jīng)被置換到了頁(yè)面文件中。 Windows 2000 使用 PNPE 保留的第 31 位來(lái)存儲(chǔ)頁(yè)的信息。有關(guān)頁(yè)信息的結(jié)構(gòu)沒(méi)有文檔記載,不過(guò)它類似于名為 PageFile 的第 10 位,如 列表 4-3 所示,如果設(shè)置了該位,則表示頁(yè)已被置換出物理內(nèi)存。在這種情況下, Reserved1 和 Reserved2 位域?qū)到y(tǒng)在頁(yè)面文件中定位該頁(yè)的信息,因此,當(dāng)需要訪問(wèn)該頁(yè)時(shí),可很快的將其換回物理內(nèi)存。
X86_PE 該結(jié)構(gòu)是為了方便使用而加入的。它僅包含一個(gè) union ,該 union 包括頁(yè)項(xiàng)所有可能的狀態(tài),此處的頁(yè)項(xiàng)是指: PDBR 的內(nèi)容、所有 4MB 和 4KB 的 PDE 、 PTE ,以及所有的 PNPE 。
typedef struct _X86_PDBR // page-Directory base register (cr3)
{
union
{
struct
{
DWORD dValue; // packed value
};
struct
{
unsigned Reserved1 : 3;
unsigned PWT : 1; // page-level write-through
unsigned PCD : 1; // page-level cache disabled
unsigned Reserved2 : 7;
unsigned PFN : 20; // page-frame number
};
};
}
X86_PDBR, *PX86_PDBR, **PPX86_PDBR;
#define X86_PDBR_ sizeof (X86_PDBR)
// -----------------------------------------------------------------
typedef struct _X86_PDE_4M // page-directory entry (4-MB page)
{
union
{
struct
{
DWORD dValue; // packed value
};
struct
{
unsigned P : 1; // present (1 = present)
unsigned RW : 1; // read/write
unsigned US : 1; // user/supervisor
unsigned PWT : 1; // page-level write-through
unsigned PCD : 1; // page-level cache disabled
unsigned A : 1; // accessed
unsigned D : 1; // dirty
unsigned PS : 1; // page size (1 = 4-MB page)
unsigned G : 1; // global page
unsigned Available : 3; // available to programmer
unsigned Reserved : 10;
unsigned PFN : 10; // page-frame number
};
};
}
X86_PDE_4M, *PX86_PDE_4M, **PPX86_PDE_4M;
#define X86_PDE_4M_ sizeof (X86_PDE_4M)
// -----------------------------------------------------------------
typedef struct _X86_PDE_4K // page-directory entry (4-KB page)
{
union
{
struct
{
DWORD dValue; // packed value
};
struct
{
unsigned P : 1; // present (1 = present)
unsigned RW : 1; // read/write
unsigned US : 1; // user/supervisor
unsigned PWT : 1; // page-level write-through
unsigned PCD : 1; // page-level cache disabled
unsigned A : 1; // accessed
unsigned Reserved : 1; // dirty
unsigned PS : 1; // page size (0 = 4-KB page)
unsigned G : 1; // global page
unsigned Available : 3; // available to programmer
unsigned PFN : 20; // page-frame number
};
};
}
X86_PDE_4K, *PX86_PDE_4K, **PPX86_PDE_4K;
#define X86_PDE_4K_ sizeof (X86_PDE_4K)
// -----------------------------------------------------------------
typedef struct _X86_PTE_4K // page-table entry (4-KB page)
{
union
{
struct
{
DWORD dValue; // packed value
};
struct
{
unsigned P : 1; // present (1 = present)
unsigned RW : 1; // read/write
unsigned US : 1; // user/supervisor
unsigned PWT : 1; // page-level write-through
unsigned PCD : 1; // page-level cache disabled
unsigned A : 1; // accessed
unsigned D : 1; // dirty
unsigned Reserved : 1;
unsigned G : 1; // global page
unsigned Available : 3; // available to programmer
unsigned PFN : 20; // page-frame number
};
};
}
X86_PTE_4K, *PX86_PTE_4K, **PPX86_PTE_4K;
#define X86_PTE_4K_ sizeof (X86_PTE_4K)
// -----------------------------------------------------------------
typedef struct _X86_PNPE // page not present entry
{
union
{
struct
{
DWORD dValue; // packed value
};
struct
{
unsigned P : 1; // present (0 = not present)
unsigned Reserved1 : 9;
unsigned PageFile : 1; // page swapped to pagefile
unsigned Reserved2 : 21;
};
};
}
X86_PNPE, *PX86_PNPE, **PPX86_PNPE;
#define X86_PNPE_ sizeof (X86_PNPE)
// -----------------------------------------------------------------
typedef struct _X86_PE // general page entry
{
union
{
DWORD dValue; // packed value
X86_PDBR pdbr; // page-directory Base Register
X86_PDE_4M pde4M; // page-directory entry (4-MB page)
X86_PDE_4K pde4K; // page-directory entry (4-KB page)
X86_PTE_4K pte4K; // page-table entry (4-KB page)
X86_PNPE pnpe; // page not present entry
};
}
X86_PE, *PX86_PE, **PPX86_PE;
#define X86_PE_ sizeof (X86_PE)
列表 4-3. i386 的 PDBR 、 PDE 、 PTE 和 PNPE
在 列表 4-4 中,我增加了線性地址的結(jié)構(gòu)化表示。這些結(jié)構(gòu)是 圖 4-3 和 4-4 中的“線性地址”的正式形式。
l X86_LINEAR_4M 該結(jié)構(gòu)是指向 4MB 數(shù)據(jù)頁(yè)的線性地址的正式形式,如 圖 4-4 所示。頁(yè)目錄索引( PDI )是一個(gè)頁(yè)目錄的索引,頁(yè)目錄地址由 PDBR 給出,使用 PDI 可選擇頁(yè)目錄中的一個(gè) PDE 。 22 位的 Offset 成員指向一個(gè)目標(biāo)地址,此目標(biāo)地址對(duì)應(yīng) 4MB 的物理頁(yè)。
l X86_LINEAR_4K 是一個(gè) 4KB 線性地址類型的變量,如 圖 4-3 所示。該結(jié)構(gòu)由三個(gè)位域組成:和 4MB 地址類似,高 10 位為 PDI ,用來(lái)選擇一個(gè) PDE ;頁(yè)表索引 PTI 的任務(wù)與 PDI 相似,指向由 PDE (該 PDE 由前面的 PDI 指定)確定的頁(yè)表中的一個(gè) PTE ;剩余的 12 個(gè)位是在 4KB 物理頁(yè)中的偏移量。
l X86_LINEAR 是另一個(gè)為使用方便而加入的結(jié)構(gòu)。該結(jié)構(gòu)只是簡(jiǎn)單的將 X86_LINEAR_4K 和 X86_LINEAR_4M 聯(lián)合為一個(gè)數(shù)據(jù)類型。詳見(jiàn) 列表 4-4 。
typedef struct _X86_LINEAR_4M // linear address (4-MB page)
{
union
{
struct
{
PVOID pAddress; // packed address
};
struct
{
unsigned Offset : 22; // offset into page
unsigned PDI : 10; // page-directory index
};
};
}
X86_LINEAR_4M, *PX86_LINEAR_4M, **PPX86_LINEAR_4M;
#define X86_LINEAR_4M_ sizeof (X86_LINEAR_4M)
// -----------------------------------------------------------------
typedef struct _X86_LINEAR_4K // linear address (4-KB page)
{
union
{
struct
{
PVOID pAddress; // packed address
};
struct
{
unsigned Offset : 12; // offset into page
unsigned PTI : 10; // page-table index
unsigned PDI : 10; // page-directory index
};
};
}
X86_LINEAR_4K, *PX86_LINEAR_4K, **PPX86_LINEAR_4K;
#define X86_LINEAR_4K_ sizeof (X86_LINEAR_4K)
// -----------------------------------------------------------------
typedef struct _X86_LINEAR // general linear address
{
union
{
PVOID pAddress; // packed address
X86_LINEAR_4M linear4M; // linear address (4-MB page)
X86_LINEAR_4K linear4K; // linear address (4-KB page)
};
}
X86_LINEAR, *PX86_LINEAR, **PPX86_LINEAR;
#define X86_LINEAR_ sizeof (X86_LINEAR)
列表 4-4. i386 的線性地址
宏和常量
列表 4-5 給出的定義是對(duì) 列表 4-2 到 列表 4-4 所示結(jié)構(gòu)的補(bǔ)充,讓我們可以更容易的和 i386 內(nèi)存管理一起工作。 列表 4-5 的定義可以分為三大組。第一組用于控制線性地址:
1. X86_PAGE_MASK 、 X86_PDI_MASK 和 X86_PTI_MASK 都是位掩碼( bit mask ),用來(lái)選擇線性地址中的某一部分。它們都基于常量: PAGE_SHIFT (12) 、 PDI-SHIFT (22) 和 PTI-SHIFT (12) ,這些常量定義于 Windows 2000 DDK 的頭文件 ntddk.h 中。 X86_PAGE_MASK 等價(jià)于 0xFFFFF000 ,可有效的屏蔽 4KB 線性地址( X86_LINEAR_4K )中的偏移量部分。 X86_PDI_MASK 等價(jià)于 0xFFC00000 ,顯然這可從線性地址中提取高 10 位的 PDI 。 X86_PTI_MASK 等價(jià)于 0x003FF0000 ,用于屏蔽線性地址中除 PTI 外的所有位。
2. X86_PAGE() 、 X86_PDI() 和 X86_PTI() 使用上面的常量來(lái)計(jì)算給定線性地址的頁(yè)索引、 PDI 和 PTI 。 X86_PAGE() 一般用來(lái)從 Windows 2000 的 PTE 數(shù)組(該數(shù)組首地址為: 0xC0000000 )中讀取一個(gè) PTE 。 X86_PDI() 和 X86_PTI() 只是針對(duì)給定的指針,簡(jiǎn)單的使用 X86_PDI_MASK 或 X86_PTI_MASK ,并將得到的索引移動(dòng)到最右邊。
3. X86_OFFSET_4M() 和 X86_OFFSET_4K() 分別從 4MB 或 4KB 線性地址中提取偏移量部分。
4. X86_PAGE_4M 和 X86_PAGE_4K 根據(jù) DDK 中的常量 PDI_SHIFT 和 PTI_SHIFT 來(lái)計(jì)算 4MB 和 4KB 頁(yè)的大小。 X86_PAGE_4M=4,194,304 , X86_PAGE_4K=4,096 。注意, X86_PAGE_4K 等價(jià)于 DDK 常量 PAGE_SIZE ,該常量也定義于 ntddk.h 中。
5. X86_PAGES_4M 和 X86_PAGES_4K 分別表示 4GB 地址空間中可容納的 4MB 或 4KB 頁(yè)的總數(shù)。 X86_PAGES_4M 等價(jià)于 1,024 , X86_PAGES_4K 等價(jià)于 1,048,576 。
#define X86_PAGE_MASK (0 - (1 << PAGE_SHIFT))
#define X86_PAGE(_p) (((DWORD) (_p) & X86_PAGE_MASK) >> PAGE_SHIFT)
#define X86_PDI_MASK (0 - (1 << PDI_SHIFT))
#define X86_PDI(_p) (((DWORD) (_p) & X86_PDI_MASK) >> PDI_SHIFT)
#define X86_PTI_MASK ((0 - (1 << PTI_SHIFT)) & ~X86_PDI_MASK)
#define X86_PTI(_p) (((DWORD) (_p) & X86_PTI_MASK) >> PTI_SHIFT)
#define X86_OFFSET(_p,_m) ((DWORD_PTR) (_p) & ~(_m))
#define X86_OFFSET_4M(_p) X86_OFFSET (_p, X86_PDI_MASK)
#define X86_OFFSET_4K(_p) X86_OFFSET (_p, X86_PDI_MASK|X86_PTI_MASK)
#define X86_PAGE_4M (1 << PDI_SHIFT)
#define X86_PAGE_4K (1 << PTI_SHIFT)
#define X86_PAGES_4M (1 << (32 - PDI_SHIFT))
#define X86_PAGES_4K (1 << (32 - PTI_SHIFT))
// -----------------------------------------------------------------
#define X86_PAGES 0xC0000000
#define X86_PTE_ARRAY ((PX86_PE) X86_PAGES)
#define X86_PDE_ARRAY (X86_PTE_ARRAY + (X86_PAGES >> PTI_SHIFT))
// -----------------------------------------------------------------
#define X86_SEGMENT_OTHER 0
#define X86_SEGMENT_CS 1
#define X86_SEGMENT_DS 2
#define X86_SEGMENT_ES 3
#define X86_SEGMENT_FS 4
#define X86_SEGMENT_GS 5
#define X86_SEGMENT_SS 6
#define X86_SEGMENT_TSS 7
// -----------------------------------------------------------------
#define X86_SELECTOR_RPL 0x0003
#define X86_SELECTOR_TI 0x0004
#define X86_SELECTOR_INDEX 0xFFF8
#define X86_SELECTOR_SHIFT 3
#define X86_SELECTOR_LIMIT (X86_SELECTOR_INDEX >>
X86_SELECTOR_SHIFT)
// -----------------------------------------------------------------
#define X86_DESCRIPTOR_SYS_TSS16A 0x1
#define X86_DESCRIPTOR_SYS_LDT 0x2
#define X86_DESCRIPTOR_SYS_TSS16B 0x3
#define X86_DESCRIPTOR_SYS_CALL16 0x4
#define X86_DESCRIPTOR_SYS_TASK 0x5
#define X86_DESCRIPTOR_SYS_INT16 0x6
#define X86_DESCRIPTOR_SYS_TRAP16 0x7
#define X86_DESCRIPTOR_SYS_TSS32A 0x9
#define X86_DESCRIPTOR_SYS_TSS32B 0xB
#define X86_DESCRIPTOR_SYS_CALL32 0xC
#define X86_DESCRIPTOR_SYS_INT32 0xE
#define X86_DESCRIPTOR_SYS_TRAP32 0xF
// -----------------------------------------------------------------
#define X86_DESCRIPTOR_APP_ACCESSED 0x1
#define X86_DESCRIPTOR_APP_READ_WRITE 0x2
#define X86_DESCRIPTOR_APP_EXECUTE_READ 0x2
#define X86_DESCRIPTOR_APP_EXPAND_DOWN 0x4
#define X86_DESCRIPTOR_APP_CONFORMING 0x4
#define X86_DESCRIPTOR_APP_CODE 0x8
列表 4-5. 附加的 i386 內(nèi)存管理相關(guān)定義
第二組宏和常量與 Windows 2000 的 PDE 、 PTE 數(shù)組有關(guān)。和其他幾個(gè)系統(tǒng)地址不同,這些數(shù)組的基地址并沒(méi)有在系統(tǒng)啟動(dòng)時(shí)作為一個(gè)全局變量出現(xiàn),而是被定義成了一個(gè)常量。可以通過(guò)反編譯內(nèi)存管理 API 函數(shù): MmGetPhysicalAddress() 和 MmIsAddressValid() 來(lái)證明,在這些函數(shù)里,這些地址都以“魔術(shù)數(shù)字”的形式出現(xiàn)。這些常量并沒(méi)有包括在 DDK 頭文件中,不過(guò) 列表 4-5 展示了如何定義它們。
l X86_PAGES 是一個(gè)硬編碼的地址和指針(指向 0xC0000000 ), 0xC0000000 是 Windows 2000 的 PTE 數(shù)組開(kāi)始的地方。
X86_PTE_ARRAY 等價(jià)于 X86_PAGES ,但是被轉(zhuǎn)型為 PX86_PE ,也就是說(shuō),指向一個(gè) X86_PE 類型的數(shù)組, X86_PE 定義于列表 4-2 。
X86_PDE_ARRAY 是一個(gè)巧妙的定義,它通過(guò) PTE 數(shù)組的位置來(lái)計(jì)算 PDE 數(shù)組的基地址,這需要用到 PTI_SHIFT 常量。將線性地址映射為 PTE 地址的通用格式為:(( LinearAdress >> 12 ) *4 ) +0xC0000000 ,線性地址 0xC0000000 轉(zhuǎn)換后的地址為頁(yè)目錄的基地址。
列表 4-5 的最后兩部分包括選擇器和特殊類型的描述符,以及對(duì) 列表 4-2 的補(bǔ)充。
l X86_SELECTOR_RPL 、 X86_SELECTOR_TI 和 X86_SELECTOR_INDEX 都是位掩碼,分別對(duì)應(yīng) X86_SELECTOR 結(jié)構(gòu)中的 RPL 、 TI 和 Index 成員。
l X86_SELECTOR_SHIFT 是一個(gè)右移因子,用來(lái)使選擇器的 Index 的數(shù)值向右對(duì)齊。
l X86_SELECTOR_LIMIT 定義了選擇器可使用的最大索引值,該限制為 8,191 。這個(gè)值確定了描述符表的最大尺寸。每個(gè)選擇器索引均指向一個(gè)描述符,每個(gè)描述符包含 64 個(gè)位(即 8 個(gè)字節(jié))。所以,描述符表的最大尺寸為: 8,192*8=64KB 。
l X86_DESCRIPTOR_SYS_* 是一組常量,用于定義系統(tǒng)描述符類型。如果描述符的 S 位被設(shè)為 0 ,那么描述的 Type 成員將采用這一組類型中的某一個(gè)。請(qǐng)參考 列表 4-2 中的 X86_DESCRIPTOR 的定義。系統(tǒng)描述符類型在 Intel 手冊(cè)中有詳細(xì)介紹( Intel 1999c, pp. 3-15f ), 表 4-1 給出了所有可用的系統(tǒng)描述符類型。
列表 4-5 中的 X86_DESCRIPTOR_APP_* 常量也可用于定義描述符的 Type 成員,前提是描述符的 S 位不為 0 。此時(shí),該應(yīng)用程序描述符可能需要引用一個(gè)代碼或數(shù)據(jù)段。因?yàn)閼?yīng)用程序描述符類型的屬性受 Type 域的第四個(gè)位影響,所以 X86_DESCRIPTOR_APP_* 常量被定義為單位掩碼( single-bit mask ),這樣一些位就可針對(duì)數(shù)據(jù)和代碼段有不同的解釋。
l X86_DESCRIPTOR_APP_ACCESSED 如果一個(gè)段可以被訪問(wèn),則采用
l X86_DESCRIPTOR_APP_READ_WRITE 決定一個(gè)數(shù)據(jù)段是否允許只讀或讀 / 寫(xiě)訪問(wèn)。
l X86_DESCRIPTOR_APP_CONFORMATING 說(shuō)明一個(gè)代碼段是否相匹配。也就是說(shuō),它是否可以被以被弱特權(quán)代碼( less privileged code )調(diào)用(參考 Intel 1999c,pp. 4-13ff )。
l X86_DESCRIPTOR_APP_CODE 用來(lái)區(qū)別代碼段和數(shù)據(jù)段。注意,堆棧屬于數(shù)據(jù)段的范疇,而且必須總是可寫(xiě)的。
稍后,當(dāng)下一章中的 Memory Spy 程序開(kāi)始運(yùn)行時(shí),我們將重溫系統(tǒng)描述符。 表 4-1 算是 i386 內(nèi)存管理的一個(gè)簡(jiǎn)短總結(jié)。有關(guān)本話題的更多內(nèi)容,請(qǐng)參考 Intel Pentium 手冊(cè)( Intel 1999a , 1999b , 1999c )。
表 4-1. 系統(tǒng)描述符類型
名 稱
值
描 述
X86_DESCRIPTOR_SYS_TSS16A
0x1
16 位任務(wù)狀態(tài)段(可用)
X86_DESCRIPTOR_SYS_LDT
0x2
本地描述符表( LDT )
X86_DESCRIPTOR_SYS_TSS16B
0x3
16 位任務(wù)狀態(tài)段(繁忙)
X86_DESCRIPTOR_SYS_CALL16
0x4
16 位調(diào)用門(mén)
X86_DESCRIPTOR_SYS_TASK
0x5
任務(wù)門(mén)
X86_DESCRIPTOR_SYS_INT16
0x6
16 位中斷門(mén)
X86_DESCRIPTOR_SYS_TRAP16
0x7
16 位陷阱門(mén)
X86_DESCRIPTOR_SYS_TSS32A
0x9
32 位任務(wù)狀態(tài)段(可用)
X86_DESCRIPTOR_SYS_TSS32B
0xB
32 位任務(wù)狀態(tài)段(繁忙)
X86_DESCRIPTOR_SYS_CALL32
0xC
32 位調(diào)用門(mén)
X86_DESCRIPTOR_SYS_INT32
0xE
32 位中斷門(mén)
X86_DESCRIPTOR_SYS_TRAP32
0XF
32 位陷阱門(mén)
