Windows NT 核心
【原文: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結構體)。通常核心程式碼以以下程式碼取得
此指標:
相關文章
- WINDOWS NTWindows
- Windows NT 是什麼?Windows
- WallPaper Changer 2.5 for Windows 98/NTWindows
- windows NT事件日誌說明 (轉)Windows事件
- Windows NT RAS 精確設定 (轉)Windows
- Cloning An Oracle Database on Windows NT/2000OracleDatabaseWindows
- Linux,Windows NT,OS/2,Mac的比較LinuxWindowsMac
- Windows NT安全性理論與實踐 (轉)Windows
- Windows核心Windows
- WINNT: How to Remove Oracle Fail Safe From a Windows NT Cluster ServerREMOracleAIWindowsServer
- DeviceDriver Windows NT 驅動程式型別 (轉載) (轉)devWindows型別
- Sybase___在Windows NT上手動解除安裝Sybase ServerWindowsServer
- Windows NT 裝置驅動程式開發基礎(1) (轉)Windows
- Windows NT 裝置驅動程式開發基礎(3) (轉)Windows
- Windows NT 裝置驅動程式開發基礎(2) (轉)Windows
- Windows NT 裝置驅動程式開發基礎(4) (轉)Windows
- Windows NT 裝置驅動程式開發基礎(5) (轉)Windows
- Windows NT 裝置驅動程式開發基礎(7) (轉)Windows
- Windows NT 裝置驅動程式開發基礎(6) (轉)Windows
- Windows NT 裝置驅動程式開發基礎(8) (轉)Windows
- NT安全指南
- Windows 核心攻擊Windows
- windows核心程式設計--核心物件Windows程式設計物件
- 在Windows NT/2000下實現"軟"RAID的方法(轉)WindowsAI
- nt高可用部署
- WIndows NT服務和普通程式之間大資料傳輸問題Windows大資料
- windows核心程式設計--程式Windows程式設計
- windows核心程式設計--精華Windows程式設計
- Windows核心程式設計_HookWindows程式設計Hook
- windows核心程式設計--windows程式的執行Windows程式設計
- Windows原理深入學習系列-Windows核心提權Windows
- Linux 核心和 Windows 核心有什麼區別?LinuxWindows
- 以下為Windows NT 下的32 位C++程式,請計算sizeof 的值WindowsC++
- 把NT“趕盡殺絕”攻擊NT的一些技術(轉)
- linux核心和windows核心的一些區別LinuxWindows
- sql server 15404無法獲取有關 Windows NT 組/使用者 處理SQLServerWindows
- Windows NT/2000下不用驅動的Ring0程式碼實現Windows
- ORACLE使用LogMiner分析重做日誌檔案全部步驟(WINDOWS NT CHINESE VERSION) (轉)OracleWindows