java開發(fā)系統(tǒng)內核:使用LDT保護進程數(shù)據(jù)和代碼

上一節(jié)搅轿,我們開發(fā)了一個流氓程序病涨,當他運行起來后,能夠把自己的數(shù)據(jù)寫入到另一個進程的數(shù)據(jù)內存中璧坟。之所以產生這樣的漏洞既穆,是因為被入侵進程的數(shù)據(jù)段所對應的全局描述符在全局描述符表中。惡意程序通過在全局描述符表中查找沸柔,當找到目標程序的內存描述符后循衰,將對應的描述符加載到自己的ds寄存器里铲敛,于是惡意程序訪問內存時褐澎,就相當于讀寫目標程序的內存。

要防范此類入侵伐蒋,最好的辦法是讓惡意程序無法讀取自己內存段對應的描述符工三,但是如果不把自己的內存描述符放置在全局描述符表中的話,還能放哪里呢先鱼?Intel X86架構還給我們提供了另一種選擇俭正。除了全局描述符表(GDT)外,X86還提供了另一種數(shù)據(jù)結構叫局部描述符表(LDT),局部描述符表的結構跟全局描述符表一模一樣焙畔。不同的是掸读,全局描述符表只能存在一份,而局部描述符表可以是每個進程一份宏多。當進程被內核加載運行時儿惫,它可以讓CPU加載自己的局部描述符表,然后把自己的數(shù)據(jù)段描述符和代碼段描述符存入局部描述符表伸但。局部描述符表只能由相應的進程訪問肾请,其他進程想要訪問本進程的局部描述符表時會被CPU拒絕。

全局描述符表和局部描述符表就構成了一個級聯(lián)層次更胖。CPU先訪問全局描述符表铛铁,全局描述符表中的一個描述符指向局部描述符表的起始地址,內核調用指令lldt 却妨, 指令的參數(shù)是指向局部描述符表起始地址的描述符在全局描述符表中的偏移饵逐,指令執(zhí)行后,局部描述符表就被CPU所加載彪标。當程序被加載時梳毙,CPU會從局部描述符表中獲得程序的代碼段和數(shù)據(jù)段。由于局部描述符表的訪問僅限當前進程捐下,其他進程訪問不了账锹,因此其他進程就無法獲取到本進程數(shù)據(jù)段和代碼段的相關信息萌业。

全局描述符表和局部描述符表的結構如下:

這里寫圖片描述

我們看看如何在代碼中使用上局部描述符表。打開multi_task.h文件奸柬,我們看看TSS數(shù)據(jù)結構的定義:

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;
};

注意代碼中的ldtr,在上圖中生年,有一個描述符指向了局部描述符表的起始位置,ldtr就是該描述符在全局描述符中的下標廓奕。由于局部描述符表是跟各自進程相關的抱婉,所以每個進程都可以為自己分配一個局部描述符表,因此在表示進程的TASK數(shù)據(jù)結構中铃肯,我們增加局部描述符表的定義:

struct TASK {
    int sel, flags;
    int priority;
    int level;
    struct FIFO8 fifo;
    struct TSS32 tss;
    struct CONSOLE console;
    struct Buffer *pTaskBuffer;
    struct SHEET *sht;
    //change here add stack record
    int cons_stack;
    //change here
    struct SEGMENT_DESCRIPTOR ldt[2]; 
};

最末尾的ldt就是進程對應的局部描述符表,顯然它只含有兩個描述符押逼,目前我們的進程只含有數(shù)據(jù)段和代碼段步藕,因此兩個描述符足夠了。進入multi_task.c看看如何將附帶在進程對象上的局部描述符加載到CPU里挑格。

struct TASK  *task_init(struct MEMMAN *memman) {
    int  i;
    struct TASK *task;
    struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *)get_addr_gdt();
    taskctl = (struct TASKCTL *)memman_alloc_4k(memman, SIZE_OF_TASKCTL);
    for (i = 0; i < MAX_TASKS; i++) {
        taskctl->tasks0[i].flags = 0;
        taskctl->tasks0[i].sel = (TASK_GDT0 + i) * 8;
        //change here
        taskctl->tasks0[i].tss.ldtr = (TASK_GDT0 + MAX_TASKS + i) * 8;
        set_segmdesc(gdt + TASK_GDT0 + i, 103, (int)&taskctl->tasks0[i].tss,
        AR_TSS32);
        //change here
        set_segmdesc(gdt + TASK_GDT0 + MAX_TASKS + i, 15, (int)taskctl->tasks0[i].ldt, AR_LDT);
    }
    ....
}

TASK_GDT0 的值是7范删,在全局描述符表中若锁,前7個描述符有專門用途仲器,從第7個往后就用來指向進程對應的任務門描述符(TSS),當前我們的系統(tǒng)內核最多支持同時運行的進程數(shù)是MAX_TASK,因此從第7個描述符往后數(shù)MAX_TASK個描述符煤率,全都是用來指向用戶進程的任務門描述符。接下來的描述符則用來指向用戶進程的局部描述符表乏冀,代碼中我們設置了tasks[i].tss.ldtr的值蝶糯,這個值就是上圖中,全局描述符表里指向局部描述符表的那個描述符對應的下標辆沦。語句:

set_segmdesc(gdt + TASK_GDT0 + MAX_TASKS + i, 15, (int)taskctl->tasks0[i].ldt, AR_LDT);

它的作用是將局部描述符表的起始地址放置到全局描述符表對應的描述符中昼捍,AR_LDT的值是0x0082,用來表示當前描述符是專門指向一個局部描述符表的描述符。在分配任務對象的函數(shù)task_alloc中肢扯,我們要把一條語句注釋掉:

struct TASK *task_alloc(void) {
    int i;
    struct TASK *task;
    for (i = 0; i < MAX_TASKS; i++) {
        if (taskctl->tasks0[i].flags == 0) {
        ....
        //task->tss.ldtr = 0;
        }
    }
}

由于tss里面的ldtr變量指向的是全局描述符表中用來對應局部描述符表的那個描述符下標妒茬,所以此處不再把它初始化為0.接著我們回到write_vga_desktop.c,在函數(shù)cmd_execute_program中蔚晨,做相應修改:

void cmd_execute_program(char* file) {
    ....
    //change here
//    set_segmdesc(gdt + code_seg, 0xfffff, (int) appBuffer->pBuffer, 0x409a + 0x60);
    set_segmdesc(task->ldt + 0, 0xfffff, (int) appBuffer->pBuffer, 0x409a + 0x60);
    //new memory 
    char *q = (char *) memman_alloc_4k(memman, 64*1024);
    appBuffer->pDataSeg = (unsigned char*)q;

    //change here
  //  set_segmdesc(gdt + mem_seg, 64 * 1024 - 1,(int) q ,0x4092 + 0x60);
    set_segmdesc(task->ldt + 1, 64*1204 - 1, (int) q, 0x4092 + 0x60);
....
   //change here
//    start_app(0, code_seg*8,64*1024, mem_seg*8, &(task->tss.esp0));
    start_app(0, 0*8+4,64*1024, 1*8+4, &(task->tss.esp0));
    ....
}

原來我們在加載用戶進程時乍钻,會把用戶進程的代碼段和數(shù)據(jù)段設置到全局描述符表gdt中,現(xiàn)在我們改變了蛛株,我們把它設置到局部描述發(fā)表中团赁,局部描述符表對應的正是task->ldt,它只有兩個描述符育拨,我們把用戶進程的代碼段放入到第一個描述符谨履,把用戶進程的數(shù)據(jù)段放入到第二個描述符。在調用start_app把跳轉到用戶進程的代碼時熬丧,我們傳給該函數(shù)的代碼段編號為 08, 0就是代碼段在局部描述符表中的位置笋粟,這里要注意的是我們還“+4”,加4告訴CPU,當前的段在局部描述符表中析蝴,要到局部描述符表中去查找害捕,后面的參數(shù)18+4,表示數(shù)據(jù)段在表中的下標是1闷畸,加4也是告訴CPU到局部描述符表中去查找相應的段尝盼。

我們總結一下當前進程加載的基本邏輯:

1,每一個控制臺進程都對應著一個數(shù)據(jù)結構叫TSS
2佑菩,在全局描述符表中含有一個表項對應著這個TSS數(shù)據(jù)結構
3盾沫,當啟動控制臺進程時,內核用一個jmp指令殿漠,指令的參數(shù)就是步驟2中表項對在全局描述符表中的下標
4赴精,CPU執(zhí)行jmp指令時,把指令后面對應的表項從全局描述符表中拿到绞幌,讀取表項蕾哟,找到TSS結構在內存中的地址,接著使用指令ltr把tss結構的信息加載到CPU中
5,CPU根據(jù)加載的TSS數(shù)據(jù)結構信息谭确,把用戶進程的代碼和數(shù)據(jù)加載到內存中帘营。同時讀取TSS結構中l(wèi)dtr這個變量的值
6,CPU知道TSS中l(wèi)dtr變量對應的就是是全局描述符表中的一個表項逐哈,這個表項指向的是進程局部描述符表所在的位置
7仪吧,CPU根據(jù)TSS.ldtr指向的表項,獲得局部描述符表的內存地址鞠眉,執(zhí)行指令lldt把局部描述符表加載到CPU里薯鼠。
8,CPU開始執(zhí)行進程的第一條指令
9械蹋,進程運行后出皇,再把自己的代碼段和數(shù)據(jù)段設置到局部描述符表中,就像我們上面cmd_execute_program函數(shù)所做的那樣哗戈。

上面代碼完成后郊艘,我們再次加載內核,運行crack程序看看是什么結果:

這里寫圖片描述

crack程序運行時奔潰掉了唯咬。這是因為我們不再把客戶進程的數(shù)據(jù)段設置在全局描述符表中下標為30的描述符中纱注,于是當crack程序妄圖加載下標為30的描述符時,CPU發(fā)現(xiàn)這個描述符并為被初始化胆胰,于是就產生了錯誤異常狞贱,引發(fā)的異常會使得CPU的控制權交還給內核,內核在異常處理中會強行中的crack程序蜀涨,這樣crack程序就無法入侵客戶進程了瞎嬉。

如果crack進程要想成功入侵客戶進程,那么必須獲得客戶進程的局部描述符表厚柳,但該表只能被對應的進程所訪問氧枣,其他進程是沒有權限也沒有辦法訪問的,這樣客戶進程的代碼和數(shù)據(jù)就能得到完好的保護别垮,惡意進程也無計可施便监。對代碼更詳細的講解和調試演示,請參看視頻:

Linux kernel Hacker, 從零構建自己的內核

更多技術信息碳想,包括操作系統(tǒng)烧董,編譯器,面試算法移袍,機器學習解藻,人工智能,請關照我的公眾號:


這里寫圖片描述
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末葡盗,一起剝皮案震驚了整個濱河市螟左,隨后出現(xiàn)的幾起案子啡浊,更是在濱河造成了極大的恐慌,老刑警劉巖胶背,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件巷嚣,死亡現(xiàn)場離奇詭異,居然都是意外死亡钳吟,警方通過查閱死者的電腦和手機廷粒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來红且,“玉大人坝茎,你說我怎么就攤上這事∠痉” “怎么了嗤放?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長壁酬。 經常有香客問我次酌,道長,這世上最難降的妖魔是什么舆乔? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任岳服,我火速辦了婚禮,結果婚禮上希俩,老公的妹妹穿的比我還像新娘吊宋。我一直安慰自己,他們只是感情好斜纪,可當我...
    茶點故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布贫母。 她就那樣靜靜地躺著文兑,像睡著了一般盒刚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上绿贞,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天因块,我揣著相機與錄音,去河邊找鬼籍铁。 笑死涡上,一個胖子當著我的面吹牛,可吹牛的內容都是我干的拒名。 我是一名探鬼主播吩愧,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼增显!你這毒婦竟也來了雁佳?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎糖权,沒想到半個月后堵腹,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡星澳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年疚顷,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片禁偎。...
    茶點故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡腿堤,死狀恐怖,靈堂內的尸體忽然破棺而出如暖,到底是詐尸還是另有隱情释液,我是刑警寧澤,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布装处,位于F島的核電站误债,受9級特大地震影響,放射性物質發(fā)生泄漏妄迁。R本人自食惡果不足惜寝蹈,卻給世界環(huán)境...
    茶點故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望登淘。 院中可真熱鬧箫老,春花似錦、人聲如沸黔州。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽流妻。三九已至牲蜀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間绅这,已是汗流浹背涣达。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留证薇,地道東北人度苔。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像浑度,于是被迫代替她去往敵國和親寇窑。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內容