Windows内核实验002 中断现场
文章目錄
- 如何獲取中斷現(xiàn)場(chǎng)環(huán)境
- 中段現(xiàn)場(chǎng)環(huán)境
- 觀察中斷現(xiàn)場(chǎng)堆棧環(huán)境
- 觀察中斷現(xiàn)場(chǎng)的寄存器環(huán)境
- 段選擇子
- 段寄存器結(jié)構(gòu)
- 變化的段寄存器的具體含義
- 遺留問(wèn)題:SS段寄存器和棧頂指針來(lái)自于哪?
- 什么是TSS
- TSS的工作細(xì)節(jié)
- 中斷提權(quán)的任務(wù)切換過(guò)程
- 實(shí)驗(yàn)代碼
上一課我們已經(jīng)實(shí)現(xiàn)了利用中斷提權(quán)的方式讓自己寫的函數(shù)擁有了零環(huán)的權(quán)限,但是為了更方便的寫零環(huán)代碼。我們還需要搞清楚中斷現(xiàn)場(chǎng)的上下文環(huán)境,什么資源可以被使用,什么資源不能被使用。如果不清楚現(xiàn)場(chǎng)的環(huán)境,就很容易寫出有問(wèn)題的代碼,從而導(dǎo)致系統(tǒng)藍(lán)屏。
如何獲取中斷現(xiàn)場(chǎng)環(huán)境
比較方便的一個(gè)方式是在函數(shù)內(nèi)部將當(dāng)前的寄存器包括通用寄存器 段寄存器甚至是控制寄存器等等全部打印出來(lái)。
中段現(xiàn)場(chǎng)環(huán)境
觀察中斷現(xiàn)場(chǎng)堆棧環(huán)境
我們加上下面幾行代碼,來(lái)觀察一下中斷之前和之后的堆棧環(huán)境,看一下int 20這條指令對(duì)堆棧的影響
DWORD g_esp[2];void __declspec(naked) IdtEntry() {__asm{mov [g_esp+4], esp;iretd;} }void go() {__asm mov[g_esp], esp;__asm int 0x20; }運(yùn)行程序,可以看到在中斷提權(quán)之前,地址是一個(gè)三環(huán)的esp,而中斷之后esp的地址則變成了零環(huán)的地址。
接下來(lái)我們?cè)诖a中加入一條int3指令,在調(diào)試器中觀察一下堆棧內(nèi)的數(shù)據(jù)情況
kd> dps esp a6e63c9c 0040114f a6e63ca0 0000001b a6e63ca4 00000246 a6e63ca8 0012ff44 a6e63cac 00000023 ......可以看到棧頂?shù)臄?shù)據(jù)是0x0040114f,接著用ub命令觀察這個(gè)地方的反匯編
kd> ub 0040114f 00401131 668cc0 mov ax,es 00401134 66a380334000 mov word ptr ds:[00403380h],ax 0040113a 668ce0 mov ax,fs 0040113d 66a37c334000 mov word ptr ds:[0040337Ch],ax 00401143 668ce8 mov ax,gs 00401146 66a378334000 mov word ptr ds:[00403378h],ax 0040114c 58 pop eax 0040114d cd20 int 20h可以發(fā)現(xiàn),這個(gè)地方就是我之前寫的代碼,也就是三環(huán)的返回地址。堆棧中的其他數(shù)據(jù)含義如下:
- 0000001b是三環(huán)的cs
- 00000246是三環(huán)的eflags
- 0012ff44是三環(huán)的esp
- 00000023是三環(huán)的ss
總結(jié):通過(guò)中斷門進(jìn)入零環(huán)之前需要先保存三環(huán)的寄存器環(huán)境來(lái)達(dá)到再次返回三環(huán)的目的
觀察中斷現(xiàn)場(chǎng)的寄存器環(huán)境
接下來(lái)我們修改一下代碼,把所有的寄存器盡可能全部打印出來(lái)。接著運(yùn)行我們寫的程序
可以看到中斷現(xiàn)場(chǎng)前后的寄存器環(huán)境中,ESP從三環(huán)棧切換到了零環(huán)棧,CS從0x1B變成了0x8,代碼提權(quán)也是從這里體現(xiàn)的,另外ss也發(fā)生了變化。
那么問(wèn)題也就來(lái)了,這些寄存器改變的背后是一種什么樣的機(jī)制,另外這些變化的寄存器都是從哪來(lái)的。要想知道這個(gè)問(wèn)題的答案,需要了解一個(gè)東西叫做段選擇子
段選擇子
段寄存器結(jié)構(gòu)
段寄存器結(jié)構(gòu)可以抽象成以下的結(jié)構(gòu)
struct SegMent {WORD selector; //段選擇子WORD attribute; //屬性DWORD base; //基址DWORD limit; //段限長(zhǎng) }- selector: 首先可見(jiàn)部分16位對(duì)應(yīng)上面SegMent的selector成員。在 OD 中,可以看到段寄存器后面就跟著一個(gè)數(shù)字,比如 ds 后面的 0023。而 0023 后面的部分就是,剩余部分不可見(jiàn)部分,不過(guò) OD 也給我們展示出來(lái)了
- attribute: attribute 屬性記錄了該段是否有效,是否可讀寫等權(quán)限。如果往一個(gè)不可寫的段執(zhí)行寫數(shù)據(jù),會(huì)報(bào)異常。
- base 表示基地址
- limit 表示段界限,如果在超出了段界限進(jìn)行讀寫,會(huì)報(bào)錯(cuò)。
在段寄存器中,16位的段選擇子是可見(jiàn)的,其余80位的不可見(jiàn)的。我們主要關(guān)注段選擇子
例如上圖中CS的段選擇子位0x23,SS的段選擇子為0x2B。段選擇子就是一個(gè)數(shù)字,一共有16位,結(jié)構(gòu)如下:
| 1 | 0 | 字節(jié) |7654321076543 2 10| 比特 |-------------|-|--| 占位 | INDEX |T|R | 含義 | |I|P | | | |L |- INDEX:高13位表示的是在GDT數(shù)組或LDT數(shù)組的索引號(hào)
- TI:Table Indicator,這個(gè)值為0表示查找GDT,1則查找LDT
- RPL:請(qǐng)求特權(quán)級(jí)。以什么樣的權(quán)限去訪問(wèn)段。
變化的段寄存器的具體含義
明白了段選擇子的結(jié)構(gòu),接下來(lái)就來(lái)拆解其中的具體含義,以CS從0x1B變成了0x8為例:
0x1B=?00011011?
- 索引號(hào):00011=3,代表查找GTB表下標(biāo)為3的描述符
- TI:0代表查GDT表
- RPL:11=3 代表特權(quán)等級(jí)為3 也就是三環(huán)權(quán)限
0x8=?00001000
- ?索引號(hào):00001,代表查找GTB表下標(biāo)為1的描述符
- TI:0代表查GDT表
- RPL:0 代表特權(quán)等級(jí)為0,也就是零環(huán)權(quán)限
總結(jié):CS段寄存器從0x1B變成0x8,主要是代表特權(quán)等級(jí)從三環(huán)提升到了零環(huán)。
權(quán)限切換了,esp堆棧和相應(yīng)的段寄存器ss自然也會(huì)跟著切換。因?yàn)闂?shù)據(jù)是線程的核心資源,特權(quán)切換必須伴隨棧切換
遺留問(wèn)題:SS段寄存器和棧頂指針來(lái)自于哪?
我們已經(jīng)知道代碼提權(quán)的時(shí)候是需要切換棧的。那么,還剩下一個(gè)問(wèn)題:就是SS段寄存器和棧頂指針是來(lái)自于哪?
答案是來(lái)自于TSS 任務(wù)狀態(tài)段
什么是TSS
TSS 全稱task state segment,是指在操作系統(tǒng)進(jìn)程管理的過(guò)程中,任務(wù)(進(jìn)程)切換時(shí)的任務(wù)現(xiàn)場(chǎng)信息。注意不要把TSS和任務(wù)切換關(guān)聯(lián)起來(lái),它只是一塊用于保存任務(wù)現(xiàn)場(chǎng)的一些數(shù)據(jù)
Inter白皮書(shū)給出的TSS在內(nèi)存中的圖是這樣的:
看的一臉懵逼對(duì)吧?我也看的一臉懵逼。抽象成結(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)棧段選擇子// 下面這些都是用來(lái)做切換寄存器值用的,切換寄存器的時(shí)候由CPU自動(dòng)填寫。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;TSS的工作細(xì)節(jié)
TSS在任務(wù)切換過(guò)程中起著重要作用。在任務(wù)切換的過(guò)程中,首先,處理器中的各寄存器的當(dāng)前值被自動(dòng)保存到TR(任務(wù)寄存器)所指定的TSS中;然后下一任務(wù)的TSS選擇子被裝入TR;最后,從TR所指定的TSS中取出各寄存器的值送到處理器的各寄存器中,從而實(shí)現(xiàn)任務(wù)切換。
中斷提權(quán)的任務(wù)切換過(guò)程
三環(huán)程序通過(guò)中斷門進(jìn)入到零環(huán)時(shí),首先會(huì)保存當(dāng)前的寄存器環(huán)境到TR所指定的TSS中,這一點(diǎn)我們已經(jīng)在中斷現(xiàn)場(chǎng)的堆棧環(huán)境中見(jiàn)過(guò)了。
然后將零環(huán)所需要的選擇子裝入TR。
最后從TSS中取出各寄存器送到當(dāng)前環(huán)境下。這其中就包括了ss段寄存器和棧頂指針。我們也可以從TSS的結(jié)構(gòu)體中看到0環(huán)的ss段寄存器和esp棧頂指針。
總結(jié):ss段寄存器和esp棧頂指針來(lái)自于TSS
實(shí)驗(yàn)代碼
最后 附上本次實(shí)驗(yàn)所用到的代碼
#include "pch.h" #include <iostream> #include <stdio.h> #include <stdlib.h> #include <windows.h>DWORD g_eax[2], g_ecx[2], g_edx[2], g_ebx[2]; DWORD g_esp[2], g_ebp[2], g_esi[2], g_edi[2]; WORD g_cs[2], g_ds[2], g_ss[2], g_es[2], g_fs[2], g_gs[2]; WORD g_tr;void __declspec(naked) IdtEntry() {__asm{mov [g_eax + 4], eax;mov [g_ecx + 4], ecx;mov [g_edx + 4], edx;mov [g_ebx + 4], ebx;mov [g_esp + 4], esp;mov [g_ebp + 4], ebp;mov [g_esi + 4], esi;mov [g_edi + 4], edi;push eax;mov ax, cs;mov [g_cs + 2], ax;mov ax, ds;mov[g_ds + 2], ax;mov ax, ss;mov[g_ss + 2], ax;mov ax, es;mov[g_es + 2], ax;mov ax, fs;mov[g_fs + 2], ax;mov ax, gs;mov[g_gs + 2], ax;str ax;mov g_tr, ax;pop eax;int 3;iretd;} }void go() {__asm{mov[g_eax], eax;mov[g_ecx], ecx;mov[g_edx], edx;mov[g_ebx], ebx;mov[g_esp], esp;mov[g_ebp], ebp;mov[g_esi], esi;mov[g_edi], edi;push eax;mov ax, cs;mov[g_cs], ax;mov ax, ds;mov[g_ds], ax;mov ax, ss;mov[g_ss], ax;mov ax, es;mov[g_es], ax;mov ax, fs;mov[g_fs], ax;mov ax, gs;mov[g_gs], ax;pop eax;}__asm int 0x20; }//eq 80b95500 0040ee00`00081040 int main() {if ((DWORD)IdtEntry != 0x401040){printf("wrong addr:%p", IdtEntry);exit(-1);}go();printf("eax:%p,\tecx:%p\tedx:%p\tebx:%p\n", g_eax[0], g_ecx[0], g_edx[0], g_ebx[0]);printf("esp:%p,\tebp:%p\tesi:%p\tedi:%p\n", g_esp[0], g_ebp[0], g_esi[0], g_edi[0]);printf("cs:%p,\tds:%p\tss:%p\tes:%p\tfs:%p\tgs:%p\n", g_cs[0], g_ds[0], g_ss[0], g_es[0], g_fs[0], g_gs[0]);printf("eax:%p,\tecx:%p\tedx:%p\tebx:%p\n", g_eax[1], g_ecx[1], g_edx[1], g_ebx[1]);printf("esp:%p,\tebp:%p\tesi:%p\tedi:%p\n", g_esp[1], g_ebp[1], g_esi[1], g_edi[1]);printf("cs:%p,\tds:%p\tss:%p\tes:%p\tfs:%p\tgs:%p\n", g_cs[1], g_ds[1], g_ss[1], g_es[1], g_fs[1], g_gs[1]);printf("tr:%p\n", g_tr);system("pause"); }總結(jié)
以上是生活随笔為你收集整理的Windows内核实验002 中断现场的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Windows内核实验001 中断提权
- 下一篇: Windows内核实验003 再次回到中