天天看點

Windows保護模式學習筆記(五)—— 任務段&任務門

Windows保護模式學習筆記(五)—— 任務段&任務門

    • 要點回顧
    • 任務段
      • TSS (Task-state segment )
      • TR段寄存器
        • TR段寄存器的讀寫
      • TSS段描述符
      • 實驗:加載自定義TSS
        • 第一步:擷取必要參數
        • 第二步:構造TSS段描述符
        • 第三步:将TSS段描述符寫入GDT表
        • 第四步:解除中斷,繼續執行代碼
    • 任務門
      • 任務門執行過程
      • 實驗:通過任務門切換TSS
        • 第一步:構造任務門描述符
        • 第二步:将任務門描述符寫入IDT表
        • 第三步:構造TSS段描述符
        • 第四步:運作代碼

要點回顧

  1. 在調用門、中斷門與陷阱門中,一旦出現權限切換,那麼就會有堆棧的切換;而且,由于CS的CPL發生改變,也導緻了SS也必須要切換
  2. 思考:切換時,會有新的ESP和SS(CS是由中斷門或者調用門指定)這2個值從哪裡來的呢?

    答案:TSS (Task-state segment ):任務狀态段

任務段

TSS (Task-state segment )

描述:

TSS是一塊記憶體

大小:104位元組

TSS存儲了一堆寄存器的值

TSS結構圖:

Windows保護模式學習筆記(五)—— 任務段&任務門

TSS的作用:

  1. Intel的設計思想:通過使用TSS以達到任務(線程)的切換
  2. 作業系統的設計思想:與Intel設計思想不同的是,Windows并沒有根據Intel的設計思想來做,甚至Linux也沒有這樣做

TSS的本質:

  1. 不要把TSS與"任務切換"聯系到一起
  2. TSS的意義就在于可以同時換掉"一堆"寄存器

CPU通過TR段寄存器尋找TSS

TR段寄存器

描述:

TR寄存器的值是當作業系統啟動時,從TSS段描述符中加載出來的,TSS段描述符在GDT表中

TR.Base = TSS起始位址

TR.Limit = TSS大小

TR段寄存器的讀寫

一、将TSS段描述符加載到TR寄存器

指令:

LTR

說明:

  1. 用LTR指令去裝載的話 僅僅是改變TR寄存器的值(96位)
  2. 并沒有真正改變TSS
  3. LTR指令隻能在系統層使用
  4. 加載後TSS段描述符的狀态位會發生改變

二、讀TR寄存器

指令:

STR

說明:如果用STR去讀的話,隻讀了TR的16位,也就是段選擇子

TSS段描述符

描述:

TSS段描述符是系統段描述符中的一種

結構圖:

Windows保護模式學習筆記(五)—— 任務段&任務門

Type = 二進制1001:說明該TSS段描述符未被加載到TR段寄存器中

Type = 二進制1011:說明該TSS段描述符已被加載到TR段寄存器中

TSS、TSS段描述符、TR段寄存器關系示意圖:

Windows保護模式學習筆記(五)—— 任務段&任務門

實驗:加載自定義TSS

第一步:擷取必要參數

在VC6中運作如下代碼并在main函數頭部設定中斷:

#include <windows.h>

DWORD dwOK;
DWORD dwESP;
DWORD dwCS;

void __declspec(naked) func()
{
	dwOK = 1;
	__asm
	{
		int 3
		
		mov eax,esp
		mov dwESP,eax
		mov ax,cs
		mov word ptr [dwCS],ax

		//回去的代碼沒寫。。。
	}
}

int main(int argc, char* argv[])
{
	char bu[0x10];  //12ff70
	int iCr3;

	printf("input CR3:\n");
	scanf("%x", &iCr3);

	DWORD iTss[0x68] = {
		0x00000000,		//link
		0x00000000,		//esp0		//(DWORD)bu
		0x00000000,		//ss0
		0x00000000,		//esp1
		0x00000000,		//ss1
		0x00000000,		//esp2
		0x00000000,		//ss2
		(DWORD)iCr3,	//cr3
		0x0040DE50,		//eip
		0x00000000,		//eflags
		0x00000000,		//eax
		0x00000000,		//ecx
		0x00000000,		//edx
		0x00000000,		//ebx
		(DWORD)bu,		//esp
		0x00000000,		//ebp
		0x00000000,		//esi
		0x00000000,		//edi
		0x00000023,		//es
		0x00000008,		//cs		0x0000001B
		0x00000010,		//ss		0x00000023
		0x00000023,		//ds
		0x00000030,		//fs
		0x00000000,		//gs
		0x00000000,		//dit
		0x20ac0000};

		char buff[6];

		*(DWORD*)&buff[0] = 0x12345678;
		*(WORD*)&buff[4] = 0xC0;

		__asm
		{
			call fword ptr[buff]
		}

		printf("ok=%d \t ESP=%x \t CS=%x \n", dwOK, dwESP, dwCS);

		return 0;
}
           

進入反彙編視窗檢視 func 函數起始位址,我這裡是0x401020

Windows保護模式學習筆記(五)—— 任務段&amp;任務門

将位址填入iTss數組注釋為eip的地方,表示TSS切換後EIP的值

Windows保護模式學習筆記(五)—— 任務段&amp;任務門

再通過 memory 視窗檢視iTss數組所在位址,記下來備用

Windows保護模式學習筆記(五)—— 任務段&amp;任務門

注意:代碼中如有地方發生修改,需要先停止程式,重新編譯

第二步:構造TSS段描述符

Offset in Segment 31:16 = 0x0000		// 暫定
					  G = 0
					AVL = 0
				  Limit = 二進制:0000
				  	  P = 1
				  	DPL = 二進制:11
	   			   Type = 二進制:1001
	   	  Segment Limit = 0068H			// Intel規定TSS段描述符G=0時Limit必須大于或等于67H
Offset in Segment 15:00 = 0x0000		// 暫定
           

由上述參數構造出的門描述符為:0000E900`00000068

在第一步中,我們已經知道iTss數組所在位址為0x12FDCC

是以,TSS段描述符最終确定為:0000E9

12`FDCC

0068

第三步:将TSS段描述符寫入GDT表

我寫入的位址是8003f0c0,若寫入其他位址,則需要修改buff數組的後兩個位元組

Windows保護模式學習筆記(五)—— 任務段&amp;任務門

這時候先不要急着繼續運作代碼,先在WinDbg中輸入指令:

!process 0 0

獲得目前程序的Cr3(我這裡程序名叫TestDoor.exe,之前是用來做調用門的實驗,TSS沒有建立項目)

Windows保護模式學習筆記(五)—— 任務段&amp;任務門

DirBase的值就是Cr3

第四步:解除中斷,繼續執行代碼

輸入上一步得到的Cr3,回車

Windows保護模式學習筆記(五)—— 任務段&amp;任務門

WinDbg成功獲得了中斷信号

Windows保護模式學習筆記(五)—— 任務段&amp;任務門

這時候看一下反彙編代碼

Windows保護模式學習筆記(五)—— 任務段&amp;任務門

可以确定正在執行func函數的代碼

再看一下寄存器

Windows保護模式學習筆記(五)—— 任務段&amp;任務門

eip、cs、ss都符合我們想要的結果

至此,TSS切換成功!

思考:TSS切換完成後,如何回到切換前的下一行繼續執行?

任務門

描述:

任務門存在于IDT表

任務門中包含TSS段選擇子

可以通過通路任務門達到切換TSS的目的

結構圖:

Windows保護模式學習筆記(五)—— 任務段&amp;任務門

任務門執行過程

  1. INT N(N為IDT表索引号)
  2. 系統通過使用者指定的索引查找IDT表,找到對應的門描述符
  3. 門描述符若為任務門描述符,則根據任務門描述符中TSS段選擇子查找GDT表,找到TSS段描述符
  4. 将TSS段描述符中的内容加載到TR段寄存器
  5. TR段寄存器通過Base和Limit找到TSS
  6. 使用TSS中的值修改寄存器
  7. IRETD傳回

實驗:通過任務門切換TSS

第一步:構造任務門描述符

任務門描述符結構圖灰色部分預設填充為0

P = 1
					DPL = 二進制:11
   TSS Segment Selector = 0x00C3		// TSS段描述符選擇子
           

由上述參數及預設參數構造出的門描述符為:

0000e500`00C30000

第二步:将任務門描述符寫入IDT表

Windows保護模式學習筆記(五)—— 任務段&amp;任務門

第三步:構造TSS段描述符

TSS段描述符構造具體過程參照任務段實驗部分,這裡不再詳解,隻給出測試代碼

代碼如下:

#include <windows.h>

DWORD dwOK;
DWORD dwESP;
DWORD dwCS;

void __declspec(naked) func()
{
	dwOK = 1;
	__asm
	{
		mov eax,esp
		mov dwESP,eax
		mov ax,cs
		mov word ptr [dwCS],ax
		iretd
	}
}

int main(int argc, char* argv[])
{
	char bu[0x10];  //12ff70
	int iCr3;

	printf("input CR3:\n");
	scanf("%x", &iCr3);

	DWORD iTss[0x68] = {
		0x00000000,		//link
		0x00000000,		//esp0		//(DWORD)bu
		0x00000000,		//ss0
		0x00000000,		//esp1
		0x00000000,		//ss1
		0x00000000,		//esp2
		0x00000000,		//ss2
		(DWORD)iCr3,	//cr3
		0x00401020,		//eip
		0x00000000,		//eflags
		0x00000000,		//eax
		0x00000000,		//ecx
		0x00000000,		//edx
		0x00000000,		//ebx
		(DWORD)bu,		//esp
		0x00000000,		//ebp
		0x00000000,		//esi
		0x00000000,		//edi
		0x00000023,		//es
		0x00000008,		//cs		0x0000001B
		0x00000010,		//ss		0x00000023
		0x00000023,		//ds
		0x00000030,		//fs
		0x00000000,		//gs
		0x00000000,		//dit
		0x20ac0000};

		__asm
		{
			int 0x20
		}

		printf("ok=%d \t ESP=%x \t CS=%x \n", dwOK, dwESP, dwCS);
		getchar();
		return 0;
}
           

第四步:運作代碼

需要輸入Cr3,Cr3擷取流程參照任務段實驗部分,這裡不再詳解

運作結果:

Windows保護模式學習筆記(五)—— 任務段&amp;任務門

TSS切換成功!

至此,我們已經學會通過CALL/JMP以及任務門來切換TSS

思考:既然已經可以直接通路任務段了,為什麼還要有任務門?

繼續閱讀