自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java開(kāi)發(fā)操作系統(tǒng)內(nèi)核:實(shí)現(xiàn)進(jìn)程的優(yōu)先級(jí)切換

移動(dòng)開(kāi)發(fā) 開(kāi)發(fā)
為了保護(hù)系統(tǒng)內(nèi)核不受惡意程序的破壞,我們?cè)瓉?lái)的做法是專(zhuān)門(mén)為應(yīng)用程序分配單獨(dú)使用的內(nèi)存,使得應(yīng)用程序?qū)?shù)據(jù)的讀寫(xiě)都限制在內(nèi)核給他分配的內(nèi)存段內(nèi)。

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

在X86架構(gòu)下,程序可以分為4個(gè)等級(jí),分別是0,1,2,3.級(jí)別數(shù)越低,它的權(quán)限就越高,系統(tǒng)內(nèi)核是權(quán)限最高的,因此它運(yùn)行的優(yōu)先級(jí)為0,為了防止應(yīng)用程序作亂,我們?cè)趩?dòng)它之前,必須把它的優(yōu)先級(jí)設(shè)定為最低級(jí),也就是3.

為了讓?xiě)?yīng)用程序運(yùn)行在低特權(quán)級(jí)上,內(nèi)核在啟動(dòng)應(yīng)用程序前,必須把應(yīng)用程序代碼所在的內(nèi)存段的級(jí)別設(shè)置為3,在一個(gè)級(jí)別為3的代碼段上運(yùn)行指令時(shí),如果指令的優(yōu)先級(jí)高于3,例如讀寫(xiě)段寄存器等,那么就會(huì)觸發(fā)CPU錯(cuò)誤,根據(jù)上篇文章講過(guò)的內(nèi)核異常處理機(jī)制,內(nèi)核就會(huì)把應(yīng)用程序的殺掉。于是我們修改內(nèi)核啟動(dòng)應(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)存段屬性時(shí),我們多加了0x60,加上0x60的目的是,把該描述符所指向的內(nèi)存其優(yōu)先級(jí)設(shè)置為3.這樣一來(lái),應(yīng)用程序一旦指向高優(yōu)先級(jí)的CPU指令,例如move ds, ax這種讀寫(xiě)段寄存器的指令時(shí),就會(huì)引發(fā)CPU異常。

同時(shí)我們通過(guò)調(diào)用task_now()獲得當(dāng)前正在運(yùn)行的進(jìn)程對(duì)象,每個(gè)進(jìn)程對(duì)象都含有一個(gè)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)我們?cè)谠缜爸v到進(jìn)程切換的章節(jié)里有過(guò)對(duì)它的詳細(xì)解讀,這里我們需要注意它的幾個(gè)變量:esp0, ss0, esp1, ss1, esp2, ss2。一個(gè)進(jìn)程在運(yùn)行時(shí),它可以運(yùn)行在不同優(yōu)先級(jí)下,在不同優(yōu)先級(jí)下運(yùn)行時(shí),它必須使用不同的堆棧,這些變量就是用于存儲(chǔ)不同優(yōu)先級(jí)下對(duì)應(yīng)的堆棧段和堆棧指針的。如果進(jìn)程要切換到優(yōu)先級(jí)0,那么CPU會(huì)自動(dòng)從esp0和ss0中讀取堆棧指針和堆棧內(nèi)存段的全局描述符,如果進(jìn)程要從優(yōu)先級(jí)0切換到優(yōu)先級(jí)1,那么我們內(nèi)核需要自己把對(duì)應(yīng)優(yōu)先級(jí)0的堆棧指針和堆棧段描述符的值存入到esp0和ss0。也就是說(shuō)如果進(jìn)程從低優(yōu)先級(jí)切換到高優(yōu)先級(jí)時(shí),CPU會(huì)自動(dòng)幫我們從TSS中讀取對(duì)應(yīng)的堆棧段全局描述符和堆棧指針,實(shí)現(xiàn)相應(yīng)的堆棧切換。如果進(jìn)程從高優(yōu)先級(jí)切換到低優(yōu)先級(jí)時(shí),需要進(jìn)程自己把高優(yōu)先級(jí)的堆棧段描述符和堆棧指針存儲(chǔ)到TSS中的相應(yīng)位置。

這也是為何我們調(diào)用start_app時(shí),要把TSS對(duì)應(yīng)的esp0變量的地址傳入,因?yàn)閟tart_app要啟動(dòng)一個(gè)優(yōu)先級(jí)為3的應(yīng)用程序,一旦應(yīng)用程序運(yùn)行起來(lái)時(shí),進(jìn)程的優(yōu)先級(jí)會(huì)從0變?yōu)?,因此我們需要把切換前的堆棧指針和堆棧段存儲(chǔ)到TSS結(jié)構(gòu)的esp0和ss0中。我們?cè)倏纯磗tart_app的實(shí)現(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
    retf

esp+52對(duì)應(yīng)的正好是start_app的最后一個(gè)參數(shù),也就是&(task->tss.esp0),也就是當(dāng)前進(jìn)程含有的TSS結(jié)構(gòu)中,esp0變量的地址。我們把它的地址賦值給寄存器eax, 指令mov [eax], esp 它的作用就是把當(dāng)前堆棧指針存入TSS結(jié)構(gòu)里的esp0變量,指令mov [eax+4], ss, 它的作用是把當(dāng)前堆棧段描述符存儲(chǔ)到TSS結(jié)構(gòu)的ss0變量。

上面代碼中,有兩條指令特別值得我們注意,他們是:

or ecx, 3
or ebx, 3

ecx寄存器存儲(chǔ)的是應(yīng)用程序的代碼段,ebx寄存器存儲(chǔ)的是應(yīng)有程序的內(nèi)存段。我們以前講過(guò),在把全局描述符賦值給段寄存器時(shí),需要把該描述符對(duì)應(yīng)在全局描述符表中的下標(biāo)乘以8后再傳給段寄存器,為何要乘以8呢?假設(shè)某個(gè)全局描述符它的下標(biāo)是1,乘以8相當(dāng)于左移三位:

00000001  ->  00001000

左移三位后會(huì)在右邊空出3個(gè)0,這三個(gè)0是有專(zhuān)門(mén)作用的,前兩個(gè)0用于表示對(duì)應(yīng)內(nèi)存段的優(yōu)先級(jí),也叫請(qǐng)求優(yōu)先級(jí),當(dāng)內(nèi)核要運(yùn)行應(yīng)用程序的代碼時(shí),我們需要把應(yīng)用程序代碼段賦值給寄存器cs,把應(yīng)用程序的內(nèi)存段賦值給ds,如果要把優(yōu)先級(jí)從0切換成3時(shí),我們需要把請(qǐng)求優(yōu)先級(jí)也設(shè)置為3,這就是前面兩條指令的作用:

or ecx, 3
or ebx, 3

上面兩條指令運(yùn)行后,最右邊的兩個(gè)0都會(huì)變成1,也就是把請(qǐng)求優(yōu)先級(jí)設(shè)置成了3。還值得注意的是,以前我們把CPU控制器交給應(yīng)用程序時(shí),使用的指令是call far,但如果跳轉(zhuǎn)時(shí)帶有優(yōu)先級(jí)切換,那么CPU就不允許使用call far 或者是jmp far 這兩種指令,我也不知道英特爾為何這么設(shè)計(jì),要實(shí)現(xiàn)從優(yōu)先級(jí)0跳轉(zhuǎn)到優(yōu)先級(jí)3,必須先把優(yōu)先級(jí)3對(duì)應(yīng)的堆棧和堆棧指針壓入當(dāng)前堆棧,然后把優(yōu)先級(jí)3的代碼段描述符和IP指針壓入堆棧,然后再執(zhí)行retf命令,這幾個(gè)步驟對(duì)應(yīng)的正好是最后幾條指令:

    push ebx
    push edx
    push ecx
    push eax
    retf

執(zhí)行完上面幾條指令后,應(yīng)用程序就可以運(yùn)行起來(lái)了,并且應(yīng)用程序是運(yùn)行在優(yōu)先級(jí)為3的條件下,此時(shí)應(yīng)用程序不運(yùn)行執(zhí)行任何權(quán)限超過(guò)3的指令,例如存儲(chǔ)段寄存器相關(guān)的指令,如果應(yīng)用程序執(zhí)行類(lèi)似指令:move ds, ax時(shí),CPU會(huì)產(chǎn)生OD異常,于是根據(jù)上一節(jié)內(nèi)容,應(yīng)用程序會(huì)被殺掉。

在應(yīng)用程序運(yùn)行過(guò)程中,如果它需要調(diào)用內(nèi)核API,也就是需要運(yùn)行內(nèi)核代碼時(shí),CPU會(huì)自動(dòng)從TSS中讀取esp0和ss0兩個(gè)變量的信息,然后自動(dòng)把堆棧段和堆棧指針切換到內(nèi)核原來(lái)的堆棧段和堆棧指針,這樣可以省卻我們大量的麻煩,于是相關(guān)代碼便可以得到極大的精簡(jiǎn),例如實(shí)現(xiàn)API調(diào)用的02Dh中斷的實(shí)現(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

相比于上個(gè)版本,代碼精簡(jiǎn)了很多,那是因?yàn)槲覀儾挥迷倏紤]應(yīng)用程序切換到內(nèi)核時(shí)堆棧如何切換,因?yàn)镃PU已經(jīng)幫我們處理了。這里我們?cè)倏纯磌ernel_api的實(shí)現(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;
}

這里我們?cè)黾恿艘粋€(gè)標(biāo)號(hào)為4的API調(diào)用,它只是簡(jiǎn)單的返回內(nèi)核存儲(chǔ)在TSS結(jié)構(gòu)里的堆棧指針,當(dāng)調(diào)用完kernel_api后,它的返回值會(huì)被存儲(chǔ)在寄存器eax中,于是API中斷發(fā)現(xiàn)eax不是0,那意味著eax存儲(chǔ)的是內(nèi)核在切換到應(yīng)用程序前的堆棧指針,于是它把這個(gè)指針的值賦值給堆棧指針寄存器esp,于是語(yǔ)句popad是把堆棧上寄存的所有通用寄存器的數(shù)值返回給對(duì)應(yīng)通用寄存器,這條指令對(duì)應(yīng)的指令是start_app調(diào)用中的pushad指令,執(zhí)行完popad指令后,堆棧上存儲(chǔ)的是從start_app返回后的下一條指令的地址,因此接下來(lái)執(zhí)行ret指令時(shí),CPU控制權(quán)會(huì)重現(xiàn)返還給內(nèi)核。

最后我們?cè)倏纯从袀€(gè)應(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"

當(dāng)代碼調(diào)用完main函數(shù)后,也就是應(yīng)用程序執(zhí)行完畢后,代碼把4賦值給edx寄存器,然后調(diào)用api中斷,根據(jù)前面的分析,中斷執(zhí)行后CPU的控制權(quán)就交還給了內(nèi)核。另外由于應(yīng)用程序運(yùn)行在優(yōu)先級(jí)3,它要調(diào)用內(nèi)核中斷時(shí),需要使用指令int 02Dh來(lái)觸發(fā)中斷,我們必須把02Dh號(hào)中斷的優(yōu)先級(jí)也設(shè)置成3,要不然應(yīng)用程序就沒(méi)有資格調(diào)用02Dh號(hào)中斷,于是在kernel.asm中做如下修改:

.2DH:
    Gate SelectorCode32, AsmConsPutCharHandler,0, DA_386IGate+0x60

像前面說(shuō)過(guò)的一樣,加上0x60就是把該中斷的優(yōu)先級(jí)設(shè)置為3.

完成所有代碼修改后,內(nèi)核運(yùn)行情況如下:

雖然運(yùn)行結(jié)果與往常一樣

 

但應(yīng)用程序運(yùn)行時(shí)的優(yōu)先級(jí)已經(jīng)轉(zhuǎn)變?yōu)?,因此應(yīng)用程序沒(méi)有了執(zhí)行高級(jí)指令的權(quán)限,因此內(nèi)核得到了進(jìn)一步的保護(hù)。
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。

責(zé)任編輯:張子龍 來(lái)源: 簡(jiǎn)書(shū)
相關(guān)推薦

2023-11-03 08:22:09

Android系統(tǒng)算法

2010-04-14 09:40:05

2020-06-04 08:36:55

Linux內(nèi)核線程

2010-03-18 14:09:20

Java線程同步

2024-12-27 09:46:10

2012-08-14 09:38:29

WAN優(yōu)化

2011-01-14 16:23:46

Linux內(nèi)核

2010-09-01 14:10:36

CSS優(yōu)先級(jí)

2022-12-23 09:41:14

優(yōu)先級(jí)反轉(zhuǎn)

2010-04-20 12:40:23

Unix操作系統(tǒng)

2024-03-11 07:46:40

React優(yōu)先級(jí)隊(duì)列二叉堆

2009-12-17 18:19:12

Linux操作系統(tǒng)

2009-07-21 08:41:05

Scala操作符優(yōu)先級(jí)

2020-09-30 09:07:37

DevOps

2025-02-03 08:23:33

2010-04-14 13:59:45

Unix操作系統(tǒng)

2010-04-16 18:19:32

Unix操作系統(tǒng)

2021-04-06 10:45:18

React前端優(yōu)先級(jí)

2010-09-13 17:30:07

CSS優(yōu)先級(jí)

2012-05-04 09:49:34

進(jìn)程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)