新聞中心
為了保護系統(tǒng)內(nèi)核不受惡意程序的破壞,我們原來的做法是專門為應(yīng)用程序分配單獨使用的內(nèi)存,使得應(yīng)用程序?qū)?shù)據(jù)的讀寫都限制在內(nèi)核給他分配的內(nèi)存段內(nèi)。程序?qū)?nèi)存段的讀寫,完全是由DS寄存器指向的全局描述符決定的,如果惡意程序通過修改DS寄存器的值,使得它在運行時,讓DS寄存器指向內(nèi)核數(shù)據(jù)段的全局描述符,那么惡意程序就可以讀寫內(nèi)核的數(shù)據(jù)了,為了防范出現(xiàn)這種情況,我們要做的是讓應(yīng)用程序沒有讀寫段寄存器的權(quán)力,因此我們就必須設(shè)定應(yīng)有程序的優(yōu)先級。

奎文ssl適用于網(wǎng)站、小程序/APP、API接口等需要進行數(shù)據(jù)傳輸應(yīng)用場景,ssl證書未來市場廣闊!成為創(chuàng)新互聯(lián)建站的ssl證書銷售渠道,可以享受市場價格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18980820575(備注:SSL證書合作)期待與您的合作!
在X86架構(gòu)下,程序可以分為4個等級,分別是0,1,2,3.級別數(shù)越低,它的權(quán)限就越高,系統(tǒng)內(nèi)核是權(quán)限最高的,因此它運行的優(yōu)先級為0,為了防止應(yīng)用程序作亂,我們在啟動它之前,必須把它的優(yōu)先級設(shè)定為最低級,也就是3.
為了讓應(yīng)用程序運行在低特權(quán)級上,內(nèi)核在啟動應(yīng)用程序前,必須把應(yīng)用程序代碼所在的內(nèi)存段的級別設(shè)置為3,在一個級別為3的代碼段上運行指令時,如果指令的優(yōu)先級高于3,例如讀寫段寄存器等,那么就會觸發(fā)CPU錯誤,根據(jù)上篇文章講過的內(nèi)核異常處理機制,內(nèi)核就會把應(yīng)用程序的殺掉。于是我們修改內(nèi)核啟動應(yīng)用程序的相關(guān)代碼,在write_vga_desktop.c中:
void cmd_hlt() {
file_loadfile("abc.exe", &buffer);
struct SEGMENT_DESCRIPTOR *gdt =(struct SEGMENT_DESCRIPTOR *)get_addr_gdt();
set_segmdesc(gdt+11, 0xfffff, buffer.pBuffer, 0x409a + 0x60);
//new memory
char *q = memman_alloc_4k(memman, 64*1024);
set_segmdesc(gdt+12, 64 * 1024 - 1, q ,0x4092 + 0x60);
struct TASK *task = task_now();
start_app(0, 11*8,64*1024, 12*8, &(task->tss.esp0));
memman_free_4k(memman, buffer.pBuffer, buffer.length);
memman_free_4k(memman, q, 64 * 1024);
}上面代碼跟以前相比,差別在于set_segmdesc調(diào)用中,設(shè)置內(nèi)存段屬性時,我們多加了0x60,加上0x60的目的是,把該描述符所指向的內(nèi)存其優(yōu)先級設(shè)置為3.這樣一來,應(yīng)用程序一旦指向高優(yōu)先級的CPU指令,例如move ds, ax這種讀寫段寄存器的指令時,就會引發(fā)CPU異常。
同時我們通過調(diào)用task_now()獲得當前正在運行的進程對象,每個進程對象都含有一個TSS數(shù)據(jù)結(jié)構(gòu),其內(nèi)容如下(在multi_task.h中):
struct TSS32 {
int backlink, esp0, ss0, esp1, ss1, esp2, ss2, cr3;
int eip, eflags, eax, ecx, edx, ebx, esp, ebp, esi, edi;
int es, cs, ss, ds, fs, gs;
int ldtr, iomap;
};TSS結(jié)構(gòu)我們在早前講到進程切換的章節(jié)里有過對它的詳細解讀,這里我們需要注意它的幾個變量:esp0, ss0, esp1, ss1, esp2, ss2。一個進程在運行時,它可以運行在不同優(yōu)先級下,在不同優(yōu)先級下運行時,它必須使用不同的堆棧,這些變量就是用于存儲不同優(yōu)先級下對應(yīng)的堆棧段和堆棧指針的。如果進程要切換到優(yōu)先級0,那么CPU會自動從esp0和ss0中讀取堆棧指針和堆棧內(nèi)存段的全局描述符,如果進程要從優(yōu)先級0切換到優(yōu)先級1,那么我們內(nèi)核需要自己把對應(yīng)優(yōu)先級0的堆棧指針和堆棧段描述符的值存入到esp0和ss0。也就是說如果進程從低優(yōu)先級切換到高優(yōu)先級時,CPU會自動幫我們從TSS中讀取對應(yīng)的堆棧段全局描述符和堆棧指針,實現(xiàn)相應(yīng)的堆棧切換。如果進程從高優(yōu)先級切換到低優(yōu)先級時,需要進程自己把高優(yōu)先級的堆棧段描述符和堆棧指針存儲到TSS中的相應(yīng)位置。
這也是為何我們調(diào)用start_app時,要把TSS對應(yīng)的esp0變量的地址傳入,因為start_app要啟動一個優(yōu)先級為3的應(yīng)用程序,一旦應(yīng)用程序運行起來時,進程的優(yōu)先級會從0變?yōu)?,因此我們需要把切換前的堆棧指針和堆棧段存儲到TSS結(jié)構(gòu)的esp0和ss0中。我們再看看start_app的實現(xiàn):
start_app: ;void start_app(int eip, int cs,int esp, int ds, &(task->tss.esp0))
pushad
mov eax, [esp+52]
mov [eax], esp
mov [eax+4], ss
mov eax, [esp+36] ;eip
mov ecx, [esp+40] ;cs
mov edx, [esp+44] ;esp
mov ebx, [esp+48] ;ds
mov ds, bx
mov es, bx
or ecx,3
or ebx, 3
push ebx
push edx
push ecx
push eax
retfesp+52對應(yīng)的正好是start_app的最后一個參數(shù),也就是&(task->tss.esp0),也就是當前進程含有的TSS結(jié)構(gòu)中,esp0變量的地址。我們把它的地址賦值給寄存器eax, 指令mov [eax], esp 它的作用就是把當前堆棧指針存入TSS結(jié)構(gòu)里的esp0變量,指令mov [eax+4], ss, 它的作用是把當前堆棧段描述符存儲到TSS結(jié)構(gòu)的ss0變量。
上面代碼中,有兩條指令特別值得我們注意,他們是:
or ecx, 3 or ebx, 3
ecx寄存器存儲的是應(yīng)用程序的代碼段,ebx寄存器存儲的是應(yīng)有程序的內(nèi)存段。我們以前講過,在把全局描述符賦值給段寄存器時,需要把該描述符對應(yīng)在全局描述符表中的下標乘以8后再傳給段寄存器,為何要乘以8呢?假設(shè)某個全局描述符它的下標是1,乘以8相當于左移三位:
00000001 -> 00001000
左移三位后會在右邊空出3個0,這三個0是有專門作用的,前兩個0用于表示對應(yīng)內(nèi)存段的優(yōu)先級,也叫請求優(yōu)先級,當內(nèi)核要運行應(yīng)用程序的代碼時,我們需要把應(yīng)用程序代碼段賦值給寄存器cs,把應(yīng)用程序的內(nèi)存段賦值給ds,如果要把優(yōu)先級從0切換成3時,我們需要把請求優(yōu)先級也設(shè)置為3,這就是前面兩條指令的作用:
or ecx, 3 or ebx, 3
上面兩條指令運行后,最右邊的兩個0都會變成1,也就是把請求優(yōu)先級設(shè)置成了3。還值得注意的是,以前我們把CPU控制器交給應(yīng)用程序時,使用的指令是call far,但如果跳轉(zhuǎn)時帶有優(yōu)先級切換,那么CPU就不允許使用call far 或者是jmp far 這兩種指令,我也不知道英特爾為何這么設(shè)計,要實現(xiàn)從優(yōu)先級0跳轉(zhuǎn)到優(yōu)先級3,必須先把優(yōu)先級3對應(yīng)的堆棧和堆棧指針壓入當前堆棧,然后把優(yōu)先級3的代碼段描述符和IP指針壓入堆棧,然后再執(zhí)行retf命令,這幾個步驟對應(yīng)的正好是最后幾條指令:
push ebx
push edx
push ecx
push eax
retf執(zhí)行完上面幾條指令后,應(yīng)用程序就可以運行起來了,并且應(yīng)用程序是運行在優(yōu)先級為3的條件下,此時應(yīng)用程序不運行執(zhí)行任何權(quán)限超過3的指令,例如存儲段寄存器相關(guān)的指令,如果應(yīng)用程序執(zhí)行類似指令:move ds, ax時,CPU會產(chǎn)生OD異常,于是根據(jù)上一節(jié)內(nèi)容,應(yīng)用程序會被殺掉。
在應(yīng)用程序運行過程中,如果它需要調(diào)用內(nèi)核API,也就是需要運行內(nèi)核代碼時,CPU會自動從TSS中讀取esp0和ss0兩個變量的信息,然后自動把堆棧段和堆棧指針切換到內(nèi)核原來的堆棧段和堆棧指針,這樣可以省卻我們大量的麻煩,于是相關(guān)代碼便可以得到極大的精簡,例如實現(xiàn)API調(diào)用的02Dh中斷的實現(xiàn)如下:
asm_cons_putchar:
AsmConsPutCharHandler equ asm_cons_putchar - $$
push ds
push es
pushad
pusdad
;把內(nèi)存段切換到內(nèi)核
mov ax, SelectorVram
mov ds, ax
mov es, ax
call kernel_api
cmp eax, 0
jne end_app
popad
pop es
pop ds
iretd
end_app:
mov esp, [eax]
popad
ret相比于上個版本,代碼精簡了很多,那是因為我們不用再考慮應(yīng)用程序切換到內(nèi)核時堆棧如何切換,因為CPU已經(jīng)幫我們處理了。這里我們再看看kernel_api的實現(xiàn):
int* kernel_api(int edi, int esi, int ebp, int esp,
int ebx, int edx, int ecx, int eax) {
struct TASK *task = task_now();
if (edx == 1) {
cons_putchar(eax & 0xff, 1);
}else if (edx == 2) {
cons_putstr((char*)(buffer.pBuffer + ebx));
}else if (edx == 4) {
return &(task->tss.esp0);
}
return 0;
}這里我們增加了一個標號為4的API調(diào)用,它只是簡單的返回內(nèi)核存儲在TSS結(jié)構(gòu)里的堆棧指針,當調(diào)用完kernel_api后,它的返回值會被存儲在寄存器eax中,于是API中斷發(fā)現(xiàn)eax不是0,那意味著eax存儲的是內(nèi)核在切換到應(yīng)用程序前的堆棧指針,于是它把這個指針的值賦值給堆棧指針寄存器esp,于是語句popad是把堆棧上寄存的所有通用寄存器的數(shù)值返回給對應(yīng)通用寄存器,這條指令對應(yīng)的指令是start_app調(diào)用中的pushad指令,執(zhí)行完popad指令后,堆棧上存儲的是從start_app返回后的下一條指令的地址,因此接下來執(zhí)行ret指令時,CPU控制權(quán)會重現(xiàn)返還給內(nèi)核。
最后我們再看看有個應(yīng)用程序代碼的修改,在api_call.asm中:
[SECTION .s32] BITS 32 call main mov edx, 4 ;返回內(nèi)核 int 02Dh api_putchar: mov edx, 1 mov al, [esp + 4] int 02Dh ret %include "app.asm"
當代碼調(diào)用完main函數(shù)后,也就是應(yīng)用程序執(zhí)行完畢后,代碼把4賦值給edx寄存器,然后調(diào)用api中斷,根據(jù)前面的分析,中斷執(zhí)行后CPU的控制權(quán)就交還給了內(nèi)核。另外由于應(yīng)用程序運行在優(yōu)先級3,它要調(diào)用內(nèi)核中斷時,需要使用指令int 02Dh來觸發(fā)中斷,我們必須把02Dh號中斷的優(yōu)先級也設(shè)置成3,要不然應(yīng)用程序就沒有資格調(diào)用02Dh號中斷,于是在kernel.asm中做如下修改:
.2DH:
Gate SelectorCode32, AsmConsPutCharHandler,0, DA_386IGate+0x60像前面說過的一樣,加上0x60就是把該中斷的優(yōu)先級設(shè)置為3.
完成所有代碼修改后,內(nèi)核運行情況如下:
雖然運行結(jié)果與往常一樣
但應(yīng)用程序運行時的優(yōu)先級已經(jīng)轉(zhuǎn)變?yōu)?,因此應(yīng)用程序沒有了執(zhí)行高級指令的權(quán)限,因此內(nèi)核得到了進一步的保護。
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。
網(wǎng)站題目:Java開發(fā)操作系統(tǒng)內(nèi)核:實現(xiàn)進程的優(yōu)先級切換
路徑分享:http://www.fisionsoft.com.cn/article/dpoospg.html


咨詢
建站咨詢
