Windows NT 核心

查志強發表於2015-01-07

【原文:http://blog.csdn.net/lincyang/article/details/5520493

由於我在看到這篇文章時已經沒有了作者與譯者的資訊,抱歉一下,這的確是一篇好文,值得轉:

     00.系統元件

     01.Windows NT作業系統的記憶體格局

     02.Windows NT與FLAT模型

     03.執行緒資訊塊(THREAD INFORMATION BLOCK)

     04.程式控制域(PROCESSOR CONTROL REGION)

 

00.系統元件

=====================

實際上,所有的Windows NT元件其本身都是DLL、PE格式的.EXE檔案、匯入匯出函式。Windo

ws NT的主要元件有:

*Ntoskrnl.exe

系統核心。系統執行函式都集中於此,這些函式又會呼叫其它元件。核心元件有:物件管理

器、記憶體管理器、進執行緒建立、進執行緒控制、LPC、安全管理、異常處理、檔案系統、輸入輸

出、VDM、和т.д.一般都位於大於80100000h的地址上。

*Hal.dll

硬體抽象層(Hardware  Abstraction  Layer)- 硬體相關的模組。該隔離層將作業系統中

硬體相關的部分隔離出來以增強系統的可移植性。主要的模組實現了非常底層的函式:程式

控制中斷、硬體輸入輸出等等。一般位於大於80100000h的地址上。

 

*Ntdll.dll

實現某些Win32 API函式。提供了核心模式與使用者模式之間的介面。位於使用者空間。換句話說

,系統函式呼叫主要由這裡匯出。

*Kernel32.dll

實現了一些函式,類似於Win9x的核心。其中有很多的函式封裝在ntdll.dll中。

*Csrss.exe

程式伺服器子系統。其是一個單獨的程式,因此受到保護以免受其它程式(位於其它程式的

地址空間中)影響。對服務的請求要藉助於LPC產生。

*Win32k.sys

驅動程式。用以減少呼叫Csrss服務開銷損失。在此程式中實現了GDI和USER函式。不用LPC而

用系統呼叫-這很明顯提高了Windows NT4.0和2K的圖形處理效能。

 

01.Windows NT Windows NT作業系統的記憶體格局

=============================

在Windows NT中高2G的32位線性地址空間儲存了供系統使用的程式。這種格局,地址空間80

000000   -   ffffffff為系統元件:驅動程式、系統表、系統資料結構等等。系統記憶體的精

確格局是不可能得到的,但是通過其功能用途和大致位置可以區分出各個區域。

*80000000-9FFFFFFF

系統程式碼。在這裡駐留的是Hal和ntoskrnl的程式碼和資料,還有驅動程式(boot驅動和載入n

tosldr的驅動)。GDT、IDT和TSS結構體同樣駐留在這些區域中。

*C0000000-C0FFFFFF

系統表的區域。這個線性地址空間區域儲存著程式的頁表,頁目錄和其它與程式結構體有關

的東西。這個區域不是全域性性的,不像其它的系統空間區域,而且對於於每一個程式來說會

對映到不同的物理空間,其儲存著當前程式的資料結構。

*E1000000-E57FFFFF

分頁池。這個區域可以換出到磁碟上。作業系統中的大多數物件都在這個區域中產生。實際

上一些記憶體池位於這個區域中。

*FB000000-FFDFEFFF

不可換出頁的區域,即非分頁區(Non Paged Poll)。這個區域中的資料永遠不能換出到磁

盤上。這個區域中的資料總是系統必需的資料。例如,這裡有程式與執行緒的資訊塊(Thread

 environment block, Process Environment block)。

*FFDFF000-FFFFFFFF

PCR  - Processor Control Region (程式控制域) 用於每一個程式。這個區域中儲存著PCR

結構體。在此結構體中儲存著系統狀態的資訊。例如,關於IRQL、當前執行緒、IDT等的資訊。

低2G線性地址空間(00000000-0FFFFFFFF)為程式使用者模式的地址空間(每個程式自己的空

間)。Win32地址空間看上去一般是下面這個樣子:

*00000000-0000FFFF

保護區域。訪問此區域會引發異常。被用於檢測NULL指標。

*00xx0000

通常,應用程式載入在這樣的地址上。

*70000000-78000000

Win32子系統的庫通常對映到這裡。

*7FFB0000-7FFD3FFF

內碼表。

*7FFDE000-7FFDEFFF

使用者模式的Thread Environment Block。

*7FFDF000-7FFDFFFF

使用者模式的Process Environment Block。

*7FFE0000-7FFE0FFF

共享資料區。

*7FFFF000-7FFFFFFF

保護區域。

 

02.Windows NT與FLAT模型

===========================

自i286開始,在Intel的處理器裡實現了四級保護機制,相應的就有四個特權級。程式碼與資料

能夠擁有某級別的特權。這樣,應用程式、系統程式、核心等等都執行在自己的特權級上,

而且不能隨意訪問比自己特權級高的程式碼和資料。實際上沒有一個基於Intel處理器的操作系

統能用到所有的四個特權級(不為人知的那些不算在內)。Windows NT作業系統也不例外,

只用到了兩個特權級(ring)。0級(最高特權級)下執行核心,3級(最低特權級)為使用者

級。Intel處理器提供了強大的記憶體分段機制,其與特權級一起實現了直到段級的保護(例如

,程式的每一個邏輯段都可以由一個描述符表來描述)。但是Windows NT實現的是FLAT模型

。這就將選擇子的使用降到了最低限度。處理器的全域性描述符表GDT(Global Descriptor T

able),由Windows NT作業系統管理,其包含以下描述符(由SoftIce'a得到):

Sel.  Type      Base      Limit     DPL  Attributes

GDTbase=80036000  Limit=03FF

0008  Code32    00000000  FFFFFFFF  0    P   RE

0010  Data32    00000000  FFFFFFFF  0    P   RW

001B  Code32    00000000  FFFFFFFF  3    P   RE

0023  Data32    00000000  FFFFFFFF  3    P   RW

0028  TSS32     8024D000  000020AB  0    P   B

0030  Data32    FFDFF000  00001FFF  0    P   RW

003B  Data32    7FFD9000  00000FFF  3    P   RW

0043  Data16    00000400  0000FFFF  3    P   RW

0048  LDT       E1190000  000001FF  0    P

0050  TSS32     80149F60  00000068  0    P

0058  TSS32     80149FC8  00000068  0    P

0060  Data16    00022940  0000FFFF  0    P   RW

0068  Data16    000B8000  00003FFF  0    P   RW

0070  Data16    FFFF7000  000003FF  0    P   RW

0078  Code16    80400000  0000FFFF  0    P   RE

0080  Data16    80400000  0000FFFF  0    P   RW

0088  Data16    00000000  00000000  0    P   RW

0090  Reserved  00000000  00000000  0    NP

...

00E0  Reserved  00008003  00006100  0    NP

00E8  Data16    00000000  0000FFFF  0    P   RW

00F0  Code16    80117DB0  0000028D  0    P   EO

00F8  Data16    00000000  0000FFFF  0    P   RW

0100  Reserved  00008003  00006108  0    NP

...

03F8  Reserved  00000000  00000000  0    NP

前四個選擇子全都位於線性地址空間。而且前兩個選擇子的描述符特權級DPL(Descriptor 

Privilege Level)等於0,而後面兩個的都是3。選擇子8和10由使用者應用程式使用。在FLAT

模型下,應用程式本身並不關心段暫存器的內容。在ring3工作時,CS、DS、SS暫存器總是分

別為值8、10、10。這樣,系統程式碼就可以監視段暫存器的值。選擇子1b和23用於核心(驅動

程式、系統程式碼)工作時的定址。選擇子30和3b分別指向Kernel Process Region和Thread 

Information Block。當程式碼執行在ring0時,FS暫存器的值為30,如過執行在ring3,則FS的

值為3b。選擇子30總是指向基址為FFDFF000的描述符。選擇子3b指示的基址則依賴於使用者線

程。選擇子48定義了區域性描述符表LDT(Local Descriptor Table)。LDT只在Virtual DOS 

 machine(VDM)應用程式下使用。當執行該程式時,在處理器的LDTR暫存器中載入著相應的

指標,否則,LDTR的值為0。LDT主要用在Windows 3.x應用程式下。Windows 3.x應用程式運

行在WOW(Windows On Windows)下,而WOW則實現在VDM程式裡。VDM程式的LDT使用上和Win

3.x裡的一樣。在GDT表裡總會有兩個TSS型別的選擇子。這是因為執行在Intel處理器上的Wi

ndows NT作業系統沒有使用基於任務門的任務切換機制。IDT包含以下描述符(由SoftIce'a

得到):

Int   Type     Sel:Offset     Attributes Symbol/Owner

IDTbase=F8500FC8  Limit=07FF

0000  IntG32   0008:8013EC54  DPL=0  P   _KiTrap00

...

0007  IntG32   0008:8013F968  DPL=0  P   _KiTrap07

0008  TaskG    0050:00001338  DPL=0  P

0009  IntG32   0008:8013FCA8  DPL=0  P   _KiTrap09

...

0012  IntG32   0008:80141148  DPL=0  P   _KiTrap0F

...

001F  IntG32   0008:80141148  DPL=0  P   _KiTrap0F

0020  Reserved 0008:00000000  DPL=0  NP

...

0029  Reserved 0008:00000000  DPL=0  NP

002A  IntG32   0008:8013E1A6  DPL=3  P   _KiGetTickCount

002B  IntG32   0008:8013E290  DPL=3  P   _KiCallbackReturn

002C  IntG32   0008:8013E3A0  DPL=3  P   _KiSetLowWaitHighThread

002D  IntG32   0008:8013EF5C  DPL=3  P   _KiDebugService

002E  IntG32   0008:8013DD20  DPL=3  P   _KiSystemService

002F  IntG32   0008:80141148  DPL=0  P   _KiTrap0F

0030  IntG32   0008:80014FFC  DPL=0  P   _HalpClockInterrupt

0031  IntG32   0008:807E4224  DPL=0  P

0032  IntG32   0008:8013D464  DPL=0  P   _KiUnexpectedInterrupt2

0033  IntG32   0008:80708864  DPL=0  P

0034  IntG32   0008:807CEDC4  DPL=0  P

0035  IntG32   0008:807E3464  DPL=0  P

0036  IntG32   0008:8013D48C  DPL=0  P   _KiUnexpectedInterrupt6

0037  IntG32   0008:8013D496  DPL=0  P   _KiUnexpectedInterrupt7

0038  IntG32   0008:80010A58  DPL=0  P   _HalpProfileInterrupt

0039  IntG32   0008:8013D4AA  DPL=0  P   _KiUnexpectedInterrupt9

...

00FF  IntG32   0008:8013DC66  DPL=0  P   _KiUnexpectedInterrupt207

表中主要的門型別為中斷門。中斷任務只是用在關鍵的時刻,保證特殊情況下能正確完成應

急處理,比如說,雙重異常(8號中斷)。中斷2e是系統呼叫。中斷30到3f對應於irq0  -  

irq15。於是,總是有兩個TSS選擇子。其中一個(50)用於雙重異常。TSS中除了其自己必需

部分所佔空間外,還在104個位元組中儲存了一個輸入輸出點陣圖。在執行Win32應用程式的時候

,TSS中的指標儲存著不正確的值,這樣任何對埠的操作都會引發異常。在VDM工作的時候

,點陣圖用來選擇是否禁止對埠的訪問。我們來看一下在FLAT模型下是如何進行保護的。要

研究這個問題需要將注意力轉向下面這個條件——保護的原則應該是:保護核心不受使用者進

程的干擾、保護一個程式不受另一個程式的干擾、保護子系統的程式碼和資料不受使用者程式的

干擾。Windows NT的線性地址空間可以分成使用者空間(通常為0-7fffffff)和系統與核心空

間(通常為80000000-ffffffff)。切換上下文時,核心空間對於所有程式幾乎都是一樣的。

在Windows NT中使用了分頁保護的記憶體。這樣,實現核心同使用者程式隔離方法就是將核心空

間地址頁的頁表項指標的U/S位置零。這樣就不能在ring3下隨意訪問ring0下的程式碼和資料了

。同樣頁的定址也使得程式彼此間的地址空間得到隔離。Windows NT保留了4KB的頁作為區域

c0300000中的頁目錄,該頁目錄對映了所有的4GB實體地址空間。在頁目錄中有1024個頁目錄

項。每個頁目錄項都應是4B大小(4KB/1024)。1024個頁目錄項指向1024個頁表,每個頁表

指向一個4KB的頁。頁目錄的基地址在上下文切換時會相應發生變化並指向將被使用的頁目錄

,當執行新執行緒時這個頁目錄被用於線性地址向實體地址的轉換(在Intel處理器中這個物理

基地址位於CR3暫存器中)。

結果,不同的上下文中裡的地址空間(00000000-7fffffff)中一樣的線性地址能夠對映到不

同實體地址上,實現了程式地址空間的彼此隔離。核心空間實際上對所有的上下文都是一樣

的,其被保護起來不能由使用者程式碼訪問,核心程式碼的頁表項的U/S位為零。下面我們將不討論

核心模式而是將注意力轉到使用者模式上來。的確,ring0和ring3定址使用的描述符有相同的

基址和界限,這可以作為使用者在核心或核心驅動中出錯的理由。發生錯誤時可能會破壞當前

程式地址空間中的程式碼和資料。例如,使用者程式指標校驗錯誤時呼叫核心服務可能破壞核心

的程式碼和資料。

作業系統的子系統通過匯出呼叫的DLL模組來提供自己的服務。在使用者程式向子系統轉換時,

比如呼叫WIn32 API函式,開始總是執行DLL模組的程式碼。隨後,DLL模組的程式碼可能通過LPC

進行系統呼叫。所以,DLL模組被對映到使用者空間中,共享程式上下文使用庫函式必須保護D

LL模組中的程式碼和資料不會受到可能發生的直接訪問。事實上,這種並不能保護資料。所有

的保護都是通過頁表項的R/W和U/S位來實現的。DLL模組的程式碼和資料通常只允許被讀取。用

戶程式能夠不經允許而向資料中寫入,當然,如果告知它們大致的線性地址也可以允許寫入

。但是,如果需要,每一個程式能產生自己的資料副本(copy-on-write機制)。例如,會有

以下的情況出現:分析PE檔案kernel32.dll的檔案頭可以確定data section的虛擬地址,隨

後寫程式向這個區域寫入錯誤資料,之後校對髒資料。在新程式啟動時,資料將是“恢復了

的”。因此,如果有程式和子系統破壞系統的正常執行,則其只會在自己的上下文中發揮作

用。除此之外,所有關鍵的資料(直接訪問可能會損害子系統的整體性的資料或是重要的數

據)都位於單獨的程式地址空間中——子系統伺服器。使用這些資料的操作都是通過從子系

統DLL模組轉向服務來進行的(通過LPC)。我們注意到,這個操作開銷較大,所以在Window

s NT 4.0中開始進行一系列的嘗試來使之減少。特別重要的是,現在GDI和USER函式都實現在

核心裡(更準確的說是在驅動程式Win32k.sys裡)。事實上,產生了一個結論,在安全性上

子系統大量依賴於對其的周密考慮。頁保護機制本身提供的保護是不夠的。

 

03.執行緒資訊塊(THREAD INFORMATION BLOCK)

======================================================

線上程執行的時候,在使用者模式下,FS暫存器的值為3b。這個選擇子用於定址一個結構體,

這個結構體位於NTDDK.H檔案中,叫做THREAD ENVIRONMENT BLOCK。每一個執行緒都有其自己的

結構體,並在上下文切換時,選擇子中的基址會改變,以指向當前執行緒執行緒的這個結構體。

在NTDDK.H中對這個結構體只描述了很少的一部分,如_NT_TIB(Thread Information Block

)。其實_NT_TIB是結構體NT_TEB和NT_TIB的結合。NT_TEB是使用者模式執行緒訪問的結構體。N

T_TIB通過選擇子30從核心模式下訪問。TEB結構體的幾個域在NTDDK.H(Windows NT 4.0)中

沒有描述:

typedef struct _EXCEPTION_REGISTRATION_RECORD

{

    struct _EXCEPTION_REGISTRATION_RECORD * pNext;

    FARPROC                                 pfnHandler;

} EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD;

 

typedef struct _NT_TIB {

    struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList; // 00h Head of excepti

on

      // record list

    PVOID StackBase;                         // 04h

    PVOID StackLimit;                        // 08h

    PVOID SubSystemTib;                 // 0Ch

    union {                             // 10h

        PVOID FiberData;                // for TIB

        ULONG Version;                       // for TEB

    };

    PVOID ArbitraryUserPointer;              // 14h Available

// for application use

    struct _NT_TIB *Self;                    // 18h Linear address

// of TEB structure

} NT_TIB;

typedef NT_TIB *PNT_TIB;

 

typedef struct _TEB {              // Size: 0xF88

/*000*/  NT_TIB NtTib;

/*01C*/  VOID *EnvironmentPointer;

/*020*/  CLIENT_ID ClientId;       // PROCESS id, THREAD id

/*028*/  HANDLE ActiveRpcHandle;

/*02C*/  VOID *ThreadLocalStoragePointer;

/*030*/  PEB *ProcessEnvironmentBlock;  // PEB

/*034*/  ULONG LastErrorValue;

/*038*/  ULONG CountOfOwnedCriticalSections;

/*03C*/  ULONG CsrClientThread;

/*040*/  ULONG Win32ThreadInfo;

/*044*/  UCHAR Win32ClientInfo[0x7C];

/*0C0*/  ULONG WOW32Reserved;

/*0C4*/  ULONG CurrentLocale;

/*0C8*/  ULONG FpSoftwareStatusRegister;

/*0CC*/  UCHAR SystemReserved1[0xD8];   // ExitStack ???

/*1A4*/  ULONG Spare1;

/*1A8*/  ULONG ExceptionCode;

/*1AC*/  UCHAR SpareBytes1[0x28];

/*1D4*/  UCHAR SystemReserved2[0x28];

/*1FC*/  UCHAR GdiTebBatch[0x4E0];

/*6DC*/  ULONG gdiRgn;

/*6E0*/  ULONG gdiPen;

/*6E4*/  ULONG gdiBrush;

/*6E8*/  CLIENT_ID RealClientId;

/*6F0*/  ULONG GdiCachedProcessHandle;

/*6F4*/  ULONG GdiClientPID;

/*6F8*/  ULONG GdiClientTID;

/*6FC*/  ULONG GdiThreadLocalInfo;

/*700*/  UCHAR UserReserved[0x14];

/*714*/  UCHAR glDispatchTable[0x460];

/*B74*/  UCHAR glReserved1[0x68];

/*BDC*/  ULONG glReserved2;

/*BE0*/  ULONG glSectionInfo;

/*BE4*/  ULONG glSection;

/*BE8*/  ULONG glTable;

/*BEC*/  ULONG glCurrentRC;

/*BF0*/  ULONG glContext;

/*BF4*/  ULONG LastStatusValue;

/*BF8*/  LARGE_INTEGER StaticUnicodeString;

/*C00*/  UCHAR StaticUnicodeBuffer[0x20C];

/*E0C*/  ULONG DeallocationStack;

/*E10*/  UCHAR TlsSlots[0x100];

/*F10*/  LARGE_INTEGER TlsLinks;

/*F18*/  ULONG Vdm;

/*F1C*/  ULONG ReservedForNtRpc;

/*F20*/  LARGE_INTEGER DbgSsReserved;

/*F28*/  ULONG HardErrorsAreDisabled;

/*F2C*/  UCHAR Instrumentation[0x40];

/*F6C*/  ULONG WinSockData;

/*F70*/  ULONG GdiBatchCount;

/*F74*/  ULONG Spare2;

/*F78*/  ULONG Spare3;

/*F7C*/  ULONG Spare4;

/*F80*/  ULONG ReservedForOle;

/*F84*/  ULONG WaitingOnLoaderLock;

} TEB, *PTEB;

在Windows 95下,位於TIB中的偏移0x30的是指向擁有該執行緒的程式的基址資料指標。在Win

dows NT 4.0中,這個偏移儲存的是指向結構體的指標,該結構體實現於kernel32.dll。遺憾

的是,到現在為止,除了幾個域之外我還不清楚這個結構體的格式。除此之外,類似的,在

Win 2K中

PEB結構體也發生了變化。

typedef struct _PROCESS_PARAMETERS {

/*000*/   ULONG AllocationSize;

/*004*/   ULONG ActualSize;

/*008*/   ULONG Flags;//PPFLAG_xxx

/*00c*/   ULONG Unknown1;

/*010*/   ULONG Unknown2;

/*014*/   ULONG Unknown3;

/*018*/   HANDLE InputHandle;

/*01c*/   HANDLE OutputHandle;

/*020*/   HANDLE ErrorHandle;

/*024*/   UNICODE_STRING CurrentDirectory;

/*028*/   HANDLE CurrentDir;

/*02c*/   UNICODE_STRING SearchPaths;

/*030*/   UNICODE_STRING ApplicationName;

/*034*/   UNICODE_STRING CommandLine;

/*038*/   PVOID EnvironmentBlock;

/*03c*/   ULONG Unknown[9];

     UNICODE_STRING Unknown4;

     UNICODE_STRING Unknown5;

     UNICODE_STRING Unknown6;

     UNICODE_STRING Unknown7;

} PROCESS_PARAMETERS, *PPROCESS_PARAMETERS;

typedef struct _PEB {                   // Size: 0x1D8

/*000*/ UCHAR InheritedAddressSpace;

/*001*/ UCHAR ReadImageFileExecOptions;

/*002*/ UCHAR BeingDebugged;

/*003*/ UCHAR SpareBool;                     // Allocation size

/*004*/ HANDLE Mutant;

/*008*/ HINSTANCE ImageBaseAddress;          // Instance

/*00C*/ VOID *DllList;

/*010*/ PPROCESS_PARAMETERS *ProcessParameters;

/*014*/ ULONG SubSystemData;

/*018*/ HANDLE DefaultHeap;

/*01C*/ KSPIN_LOCK FastPebLock;

/*020*/ ULONG FastPebLockRoutine;

/*024*/ ULONG FastPebUnlockRoutine;

/*028*/ ULONG EnvironmentUpdateCount;

/*02C*/ ULONG KernelCallbackTable;

/*030*/ LARGE_INTEGER SystemReserved;

/*038*/ ULONG FreeList;

/*03C*/ ULONG TlsExpansionCounter;

/*040*/ ULONG TlsBitmap;

/*044*/ LARGE_INTEGER TlsBitmapBits;

/*04C*/ ULONG ReadOnlySharedMemoryBase;

/*050*/ ULONG ReadOnlySharedMemoryHeap;

/*054*/ ULONG ReadOnlyStaticServerData;

/*058*/ ULONG AnsiCodePageData;

/*05C*/ ULONG OemCodePageData;

/*060*/ ULONG UnicodeCaseTableData;

/*064*/ ULONG NumberOfProcessors;

/*068*/ LARGE_INTEGER NtGlobalFlag;          // Address of a local copy

/*070*/ LARGE_INTEGER CriticalSectionTimeout;

/*078*/ ULONG HeapSegmentReserve;

/*07C*/ ULONG HeapSegmentCommit;

/*080*/ ULONG HeapDeCommitTotalFreeThreshold;

/*084*/ ULONG HeapDeCommitFreeBlockThreshold;

/*088*/ ULONG NumberOfHeaps;

/*08C*/ ULONG MaximumNumberOfHeaps;

/*090*/ ULONG ProcessHeaps;

/*094*/ ULONG GdiSharedHandleTable;

/*098*/ ULONG ProcessStarterHelper;

/*09C*/ ULONG GdiDCAttributeList;

/*0A0*/ KSPIN_LOCK LoaderLock;

/*0A4*/ ULONG OSMajorVersion;

/*0A8*/ ULONG OSMinorVersion;

/*0AC*/ USHORT OSBuildNumber;

/*0AE*/ USHORT OSCSDVersion;

/*0B0*/ ULONG OSPlatformId;

/*0B4*/ ULONG ImageSubsystem;

/*0B8*/ ULONG ImageSubsystemMajorVersion;

/*0BC*/ ULONG ImageSubsystemMinorVersion;

/*0C0*/ ULONG ImageProcessAffinityMask;

/*0C4*/ ULONG GdiHandleBuffer[0x22];

/*14C*/ ULONG PostProcessInitRoutine;

/*150*/ ULONG TlsExpansionBitmap;

/*154*/ UCHAR TlsExpansionBitmapBits[0x80];

/*1D4*/ ULONG SessionId;

} PEB, *PPEB;

在TEB的開頭是NT_TIB結構體(TEB和TIB的結合)。這個結構體中的大部分名字都很易懂,最

有意思的是指向異常處理連結串列的指標peExcept(Fs:[0])。這個域經常被引用。如果在隨便

某個Win32應用程式下看一下實際的情況,可以看到類似下面這樣的程式碼:

.01B45480: 64A100000000                 mov       eax,fs:[000000000]

.01B45486: 55                                        push      ebp

.01B45487: 8BEC                                  mov       ebp,esp

.01B45489: 6AFF                                   push      0FF

.01B4548B: 68F868B401                    push      001B468F8

.01B45490: 687256B401                    push      001B45672

.01B45495: 50                                       push      eax

.01B45496: 64892500000000           mov       fs:[000000000],esp

.01B4549D: 83EC78                            sub       esp,078

這段有代表性的程式碼是由編譯器生成的,用於在堆疊中生成_EXCEPTION_REGISTRATION_RECO

RD。這個堆疊中的結構體用於實現稱作“structured exception handling”的機制,這就是

結構化異常處理。接著,我們來看Windows NT下的結構化異常處理。這個機制可真是十分著

名,而且實現在編譯器的細節之中。在MSDN中可以找到Matt Petriek寫得非常詳細的文章,

題為“A Crash Course  on  the  Depths  of Win32 Structured Exception Handling”,

此文介紹的就是這項機制。

FS:[0]中的指標是指向_EXCEPTION_REGISTRATION_RECORD首部的指標。對應地,每個結構體

在pNext域中包含著指向下一個結構體的指標和指向回撥函式pfnHandler的指標。不難猜到,

這就是異常處理的處理程式。函式的原型如下:

EXCEPTION_DISPOSITION    __cdecl _except_handler(

struct _EXCEPTION_RECORD *ExceptionRecord,

void * EstablisherFrame,

struct _CONTEXT *ContextRecord,

void * DispatcherContext

);

我們來分析函式的引數。第一個引數是指向下面結構體的指標。

typedef struct _EXCEPTION_RECORD {

DWORD ExceptionCode;

DWORD ExceptionFlags;

struct _EXCEPTION_RECORD *ExceptionRecord;

PVOID ExceptionAddress;

DWORD NumberParameters;

DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];

} EXCEPTION_RECORD;

 

ExceptionCode是Windows NT的異常代號。異常在NTSTATUS.H檔案中被描述為STATUX_xxxxxx

:  ExceptionAddres - 發生異常的地址。

第三個引數是指向CONTEXT結構體的指標。

typedef struct _CONTEXT

{

DWORD ContextFlags;

DWORD Dr0;

DWORD Dr1;

DWORD Dr2;

:.

DWORD Esp;

DWORD SegSs;

} CONTEXT;

這個結構體定義於WINNT.H檔案。其意義是不言而喻的,這裡就不全寫了。函式返回下面列舉

型別值中的一個:

typedef enum _EXCEPTION_DISPOSITION {

    ExceptionContinueExecution,

    ExceptionContinueSearch,

    ExceptionNestedException,

    ExceptionCollidedUnwind

} EXCEPTION_DISPOSITION;

ExceptionFlags定義了下面的位標誌:

#define EH_NONCONTINUABLE   1

#define EH_UNWINDING        2

#define EH_EXIT_UNWIND      4

#define EH_STACK_INVALID    8

#define EH_NESTED_CALL   0x10

在發生異常時,控制傳遞到ntoskrnl.exe中相應的處理程式。例如,如果試圖下面這段程式碼

mov eax,80100000h

mov dword ptr [eax],0

這會引發異常0e,控制傳遞向處理程式KiTrap0E,堆疊中錯誤號為07(試圖在使用者模式下向

核心寫入。該頁位於記憶體中,因為線性地址80100000h是核心載入的起始地址)。之後,控制

傳遞到ntdll.dll,在這裡解析執行緒的TEB並順次執行連結串列中所有的處理程式,直到某個函式

的返回代號不是ExceptionContinueSearch。在此之後,再次呼叫連結串列中的所有處理函式,直

到找到某個函式,但這次用的是另外一個代號ExceptionCode(STATUS_UNWIND)並在Except

ionFlags設定位EH_UNWINDINGS。這個標誌用於異常處理,進行堆疊的清除和其它必要的工作

。如果沒有一個處理程式能處理,則就來到連結串列中最後一個處理程式,由Win32子系統建立的

處理程式。這個處理程式是如何被建立的以及建立在哪裡在以後研究CreateProcessW函式時

會講到。關於異常處理的完整介紹可以從MSDN獲得,因為在反彙編原始碼中所得到的是擴大

化了的異常處理機制(更高層次的機制)。

 

04. 程式控制域(PROCESSOR CONTROL REGION)

===========================================================

當執行緒在核心模式下執行時,在FS暫存器中載入的是選擇子30,用於定址PCR結構體(基址0

xFFDFF000,界限0x00001FFF)。在NTDDK.H中遠沒有描述結構體所有的成員,只是其中不多

的部分。為了說明PCR中的成分資訊,這裡列出用於i386的結構體(Windows NT 4.0)

typedef struct _KPCR {             // Size: 0xB10

/*000*/ NT_TIB NtTib;

/*01C*/ struct _KPCR* SelfPcr;

/*020*/ struct _KPRCB* Prcb;       // Current PCB

/*024*/ KIRQL Irql;           // Current IRQL

/*028*/ ULONG IRR;

/*02C*/ ULONG IrrActive;

/*030*/ ULONG IDR;

/*034*/ ULONG Reserved2;

/*038*/ struct _KIDTENTRY* ULONG IDT;

/*03C*/ struct _KGDTENTRY* GDT;

/*040*/ struct _KTSS* TSS;

/*044*/ USHORT MajorVersion;       // 1

/*046*/ USHORT MinorVersion;       // 1

/*048*/ KAFFINITY SetMember;

/*04C*/ ULONG StallScaleFactor;

/*050*/ UCHAR DebugActive;

/*051*/ UCHAR Number;

// End of official portion of KPCR

/*052*/ BOOLEAN VdmAlert;

/*053*/ UCHAR Reserved;

/*054*/ UCHAR KernelReserved[0x90-0x54];

/*090*/ ULONG SecondLevelCacheSize;

/*094*/ UCHAR HalReserved[0xD4-0x94];

/*0D4*/ ULONG InterruptMode;

/*0D8*/ ULONG Spare1;

/*0DC*/ UCHAR KernelReserved2[0x120-0xDC];

// Note that current thread is at offset 0x124 (pointer to KTHREAD)

// This essentially means that the 1st PRCB must match the active CPU

/*120*/ UCHAR PrcbData[0xB10-0x120];    // PCBs for all CPUs supported

} KPCR, *PKPCR;

PCR包含著指向PCRB(Processor Control Region)的指標,但實際上,PCRB位於PCR的偏移

0x120處,並且所有的向PCRB域的轉換都是相對於PCR的起點的。在WINNT.H中描述了PCRB結構

體。這裡給出整個結構體(Windows NT 4.0):

// 0x120 from KPCR

typedef struct _KPRCB {

/*000*/    USHORT MinorVersion;

/*002*/    USHORT MajorVersion;

/*004*/    struct _KTHREAD *CurrentThread;

/*008*/    struct _KTHREAD *NextThread;

/*00c*/    struct _KTHREAD *IdleThread;

/*010*/    CCHAR Number;

/*011*/    CCHAR Reserved;

/*012*/    USHORT BuildType;

/*014*/    KAFFINITY SetMember;

/*015*/    struct _RESTART_BLOCK *RestartBlock;

/*018*/    CCHAR CpuType;

/*019*/    CCHAR CpuID;

/*01A*/    CCHAR CpuStep;

/*01C*/    KPROCESSOR_STATE ProcessorState;

/*13C*/    CCHAR KernelReserved[0x40];

/*17C*/    CCHAR HalReserved[0x40];

/*1BC*/    ULONG NpxThread;

/*1C0*/    ULONG InterruptCount;

/*1C4*/    ULONG KernelTime;

/*1C8*/    ULONG UserTime;

/*1CC*/    ULONG DpcTime;

/*1D0*/    ULONG InterruptTime;

/*1D4*/    ULONG ApcBypassCount;

/*1D8*/    ULONG DpcBypassCount;

/*1DC*/    ULONG AdjustDpcThreshold;

/*1E0*/    UCHAR Spare2[0x14];

/*1F4*/    ULONG64 ThreadStartCount;

/*1FC*/    SINGLE_LIST_ENTRY FsRtlFreeSharedLockList;

/*200*/    SINGLE_LIST_ENTRY FsRtlFreeExclusiveLockList;

/*204*/    ULONG CcFastReadNoWait;

/*208*/    ULONG CcFastReadWait;

/*20C*/    ULONG CcFastReadNotPossible;

/*210*/    ULONG CcCopyReadNoWait;

/*214*/    ULONG CcCopyReadWait;

/*218*/    ULONG CcCopyReadNoWaitMiss;

/*21C*/    ULONG KeAlignmentFixupCount;

/*220*/    ULONG KeContextSwitches;

/*224*/    ULONG KeDcacheFlushCount;

/*228*/    ULONG KeExceptionDispatchCount;

/*22C*/    ULONG KeFirstLevelTbFills;

/*230*/    ULONG KeFloatingEmulationCount;

/*234*/    ULONG KeIcacheFlushCount;

/*238*/    ULONG KeSecondLevelTbFills;

/*23C*/    ULONG KeSystemCalls;

/*240*/    SINGLE_LIST_ENTRY FsRtlFreeWaitingLockList;

/*244*/    SINGLE_LIST_ENTRY FsRtlFreeLockTreeNodeList

/*248*/    CCHAR ReservedCounter[0x18];

/*260*/    PVOID SmallIrpFreeEntry;

/*264*/    PVOID LargeIrpFreeEntry;

/*268*/    PVOID MdlFreeEntry

/*26C*/    PVOID  CreateInfoFreeEntry;

/*270*/    PVOID NameBufferFreeEntry

/*274*/    PVOID SharedCacheMapEntry

/*278*/    CCHAR CachePad0[8];

/*280*/    CCHAR ReservedPad[0x200];

/*480*/    CCHAR  CurrentPacket[0xc];

/*48C*/    ULONG TargetSet;

/*490*/    PVOID WorkerRoutine;

/*494*/    ULONG IpiFrozen;

/*498*/    CCHAR  CachePad1[0x8];

/*4A0*/    ULONG RequestSummary;

/*4A4*/    ULONG SignalDone;

/*4A8*/    ULONG ReverseStall;

/*4AC*/    ULONG IpiFrame;

/*4B0*/    CCHAR CachePad2[0x10];

/*4C0*/    ULONG DpcInterruptRequested;

/*4C4*/    CCHAR CachePad3 [0xc];

/*4D0*/    ULONG MaximumDpcQueueDepth;

/*4D4*/    ULONG MinimumDpcRate;

/*4D8*/    CCHAR CachePad4[0x8];

/*4E0*/    LIST_ENTRY DpcListHead;

/*4E8*/    ULONG DpcQueueDepth;

/*4EC*/    ULONG DpcRoutineActive;

/*4F0*/    ULONG DpcCount;

/*4F4*/    ULONG DpcLastCount;

/*4F8*/    ULONG DpcRequestRate;

/*4FC*/    CCHAR KernelReserved2[0x2c];

/*528*/    ULONG DpcLock;

/*52C*/    CCHAR SkipTick;

/*52D*/    CCHAR VendorString[0xf];

/*53C*/    ULONG MHz;

/*540*/    ULONG64 FeatureBits;

/*548*/    ULONG64 UpdateSignature;

/*550*/    ULONG QuantumEnd;

} KPRCB, *PKPRCB, *RESTRICTED_POINTER PRKPRCB;

PCRB中最有用的就是指向當前執行緒的指標(KTHREAD結構體)。通常核心程式碼以以下程式碼取得

此指標:


相關文章