[保护模式]任务段
文章目錄
- TSS
- 什么是TSS
- TSS的作用
- 如何找到TSS
- TR寄存器讀寫
- 將TSS段描述符加載到TR寄存器
- 構(gòu)造TSS段描述符
- 修改TSS
- JMP 訪問代碼段
- JMP 訪問任務(wù)段
- JMP FAR和CALL FAR訪問任務(wù)段的區(qū)別
- TSS切換實(shí)驗(yàn)
- 示例代碼
- 構(gòu)造段描述符
- 填充CR3
TSS
什么是TSS
TSS是一塊內(nèi)存,大小為104字節(jié),結(jié)構(gòu)如圖所示:
結(jié)構(gòu)體如下:
typedef struct TSS {DWORD link; // 保存前一個(gè) TSS 段選擇子,使用 call 指令切換寄存器的時(shí)候由CPU填寫。// 這 6 個(gè)值是固定不變的,用于提權(quán),CPU 切換棧的時(shí)候用DWORD esp0; // 保存 0 環(huán)棧指針DWORD ss0; // 保存 0 環(huán)棧段選擇子DWORD esp1; // 保存 1 環(huán)棧指針DWORD ss1; // 保存 1 環(huán)棧段選擇子DWORD esp2; // 保存 2 環(huán)棧指針DWORD ss2; // 保存 2 環(huán)棧段選擇子// 下面這些都是用來做切換寄存器值用的,切換寄存器的時(shí)候由CPU自動填寫。DWORD cr3;DWORD eip;DWORD eflags;DWORD eax;DWORD ecx;DWORD edx;DWORD ebx;DWORD esp;DWORD ebp;DWORD esi;DWORD edi;DWORD es;DWORD cs;DWORD ss;DWORD ds;DWORD fs;DWORD gs;DWORD ldt;// 這個(gè)暫時(shí)忽略DWORD io_map; } TSS;- Previous Task Link 前一個(gè)TSS的鏈接。通過這個(gè)字段可以找到上一個(gè)TSS
- ESP0:零環(huán)的ESP
- SS0:零環(huán)的SS
- ESP1:1環(huán)的ESP
- SS1:1環(huán)的SS
- ESP2:2環(huán)的ESP
- SS2:2環(huán)的SS
- LDT Segment Seletor:LDT段選擇子,保存了LDT表的基址和長度。 這個(gè)選擇子對應(yīng)的段描述符必須是系統(tǒng)段描述符。LDT段選擇子的數(shù)量有多少個(gè)取決于TSS有多少個(gè)
- IO Map Base Address:IO權(quán)限位圖 與硬件相關(guān) 可忽略
TSS的作用
TSS是CPU設(shè)計(jì)的東西,與操作系統(tǒng)無關(guān)。一個(gè)CPU只有一個(gè)TSS,TSS存在的意義在于讓一個(gè)CPU可以同時(shí)執(zhí)行多個(gè)任務(wù)。但是操作系統(tǒng)并沒有使用TSS來進(jìn)行任務(wù)切換,而是直接將任務(wù)切換所需要保存的寄存器直接存到了堆棧里。
如何找到TSS
那么CPU如果想執(zhí)行任務(wù)切換的話就必須先找到TSS。通過TR段寄存器。
TR寄存器讀寫
將TSS段描述符加載到TR寄存器
指令:LTR
說明:
構(gòu)造TSS段描述符
Base 31:24:00 G:0 0 0 AVL:0 ->0 Limit19:16:0 Type:9(表示未被加載過 加載過后為B) Base 23:16:00 Base Address 15:00:0000 Segment Limit 15:00:0068 XX00E9XX`XXXX0068 # Base Address指的是新的TSS的內(nèi)存地址修改TSS
在Ring3我們可以通過call far或者jmp far指令來修改TSS。
JMP 訪問代碼段
JMP 0x48 0x12345678如果0x48是代碼段,執(zhí)行后CS=0x48 EIP=0x12345678
JMP 訪問任務(wù)段
JMP 0x48 0x12345678如果0x48是TSS段描述符,先修改TR寄存器,再用TR.Base指向的TSS中的值修改當(dāng)前寄存器
JMP FAR和CALL FAR訪問任務(wù)段的區(qū)別
- 當(dāng)使用JMP FAR來實(shí)現(xiàn)任務(wù)切換時(shí),TSS結(jié)構(gòu)體中的Previous Task Link的值在任務(wù)切換完成之后為0,CPU不會為其賦值;如果使用CALL FAR來實(shí)現(xiàn)任務(wù)切換,Previous Task Link的值在任務(wù)切換完成之后會CPU會將其填充為原來的TSS段選擇子
- 當(dāng)使用JMP FAR來實(shí)現(xiàn)任務(wù)切換時(shí),EFLAGS寄存器中的NT位不變;當(dāng)使用CALL FAR來實(shí)現(xiàn)任務(wù)切換時(shí),EFLAGS寄存器中的NT位就會被置1(NT位會對iret指令產(chǎn)生影響 NT位如果為0,iret的值從堆棧中取(中斷返回);如果NT位為1,會找TSS中的Previous Task Link進(jìn)行返回)
TSS切換實(shí)驗(yàn)
示例代碼
首先準(zhǔn)備好需要切換的104個(gè)字節(jié),并且賦上正確的值,示例代碼如下
#include "pch.h" #include<windows.h> #include<stdio.h>typedef struct TSS {DWORD link; // 保存前一個(gè) TSS 段選擇子,使用 call 指令切換寄存器的時(shí)候由CPU填寫。// 這 6 個(gè)值是固定不變的,用于提權(quán),CPU 切換棧的時(shí)候用DWORD esp0; // 保存 0 環(huán)棧指針DWORD ss0; // 保存 0 環(huán)棧段選擇子DWORD esp1; // 保存 1 環(huán)棧指針DWORD ss1; // 保存 1 環(huán)棧段選擇子 DWORD esp2; // 保存 2 環(huán)棧指針DWORD ss2; // 保存 2 環(huán)棧段選擇子// 下面這些都是用來做切換寄存器值用的,切換寄存器的時(shí)候由CPU自動填寫。DWORD cr3;DWORD eip;DWORD eflags;DWORD eax;DWORD ecx;DWORD edx;DWORD ebx;DWORD esp; DWORD ebp;DWORD esi;DWORD edi;DWORD es;DWORD cs;DWORD ss;DWORD ds;DWORD fs;DWORD gs;DWORD ldt;// 這個(gè)暫時(shí)忽略DWORD io_map; } TSS;char st[10] = { 0 }; DWORD g_esp = 0; DWORD g_cs = 0;TSS tss = { 0x00000000,//link(DWORD)st, //esp00x00000010,//ss00x00000000,//esp10x00000000,//ss10x00000000,//esp20x00000000,//ss20x00000000,//cr30x00401090,//eip-----填裸函數(shù)地址0x00000000,//eflags0x00000000,//eax0x00000000,//ecx0x00000000,//edx0x00000000,//ebx(DWORD)st, //esp0x00000000,//ebp0x00000000,//esi0x00000000,//edi0x00000023,//es 0x00000008,//cs 0x00000010,//ss0x00000023,//ds0x00000030,//fs0x00000000,//gs0x00000000,//ldt0x20ac0000 };void __declspec(naked) func() {//00401090__asm {int 3} } int main(int argc, char* argv[]) {printf("%p\n", func);printf("%x\n", &tss);printf("cr3:\n");scanf_s("%x", &(tss.cr3));char buffer[6] = { 0, 0, 0, 0, 0x48, 0 };__asm{call fword ptr[buffer]}printf("g_cs = %08x\ng_esp = %08x\n", g_cs, g_esp);getchar();return 0; }需要注意的是,我們需要將eip設(shè)置為指定的地址,讓TSS切換完成之后,跳轉(zhuǎn)到那個(gè)地址。這里將裸函數(shù)的地址打印出來之后替換掉了EIP。通過主函數(shù)的這句代碼可以打印出要跳轉(zhuǎn)的裸函數(shù)地址
printf("%p\n", func);構(gòu)造段描述符
將我們之前準(zhǔn)備好的任務(wù)段描述符直接拿過來
XX00E9XX`XXXX0068將地址設(shè)置為我們準(zhǔn)備好的TSS結(jié)構(gòu)體,通過下面的代碼打印結(jié)構(gòu)體地址
printf("%x\n", &tss);構(gòu)造好的段描述符如下:
0000E940`30180068接著修改GDT表的段描述符
kd> eq 80b95048 0000E940`30180068填充CR3
接著編譯,放到虛擬機(jī)中執(zhí)行,運(yùn)行以后,在windbg中查看CR3寄存器的值
PROCESS 87065d40 SessionId: 1 Cid: 0cc0 Peb: 7ffd9000 ParentCid: 0534DirBase: 7f0e7560 ObjectTable: c1dcf198 HandleCount: 7.Image: KernelTest.exe將前進(jìn)程的頁目錄基址填充到CR3,接著運(yùn)行程序 即可完成TSS切換
編譯,放到虛擬機(jī)中執(zhí)行,運(yùn)行以后,在windbg中查看CR3寄存器的值
PROCESS 87065d40 SessionId: 1 Cid: 0cc0 Peb: 7ffd9000 ParentCid: 0534DirBase: 7f0e7560 ObjectTable: c1dcf198 HandleCount: 7.Image: KernelTest.exe將前進(jìn)程的頁目錄基址填充到CR3,接著運(yùn)行程序 即可完成TSS切換
總結(jié)
- 上一篇: [保护模式]中断门
- 下一篇: [保护模式]非PAE模式