【操作系统】30天自制操作系统--(26)LDT与库
????????本章主要描述了一種基于LDT(本地段描述符表)的程序保護機制。另外引入了庫,并基于此整理了一下當前的操作系統源碼以及應用程序的目錄。
一 解決BUG
????????上一章最后留有一個“使用 ncst 啟動的程序無論是用 shift + F1 還是鼠標點 x 都無法結束”的BUG。
????????問題在于在shift + F1 還是鼠標點 x 處需要喚醒任務,以便結束處理能夠得到執行:
task_run(task, -1, 0); //功能是將休眠的任務喚醒????????之前也沒有這個語句,但是退出沒有問題,又是什么原因呢?原因在于,命令行窗口會觸發用來控制光標閃爍的定時器中斷,當產生定時器中斷時,定時器超時會向FIFO寫數據,于是任務就被自動喚醒了(沒有這個語句的情況下,最大會產生大約0.5s的延遲)。
二 應用程序運行時關閉命令行窗口
????????用普通的方法運行應用程序的時候,在應用程序退出之前,我們是無法關閉用來啟動這個程序的命令行窗口的。
????????首先在 bootpack.c 中加入點擊 x 后的隱藏窗口和接收到 console.c 發送的關閉窗口請求數據時所進行的處理:
? ? ? ? 在 console.c 中將變量 sheet 改用變量 cons.sht 來代替,cons.sht 在命令行窗口關閉之后有會被置為 0,而 sheet 則不會變。再來修改 API 中鍵盤輸入的過程。
????????如果FIFO中接收到4這個數據,則表示收到了關閉命令行窗口的信號,此時取消定時器,并發出清理圖層的消息,然后 將cons—>sht置為0。
三 基于LDT的程序保護
? ? ? ? 前面介紹過幾種針對操作系統的攻擊行為,并分別作了保護措施(【操作系統】30天自制操作系統--(20)保護操作系統),這邊補充一種攻擊方式。這種攻擊不是針對操作系統,而是針對操作系統中運行的其他應用程序。惡意程序會覆蓋應用程序段原有的內容:
_HariMain:MOV AX, 1005 * 8MOV DS, AXCMP DWORD [DS:0x0004], 'Hari'JNE fin ; 不是應用程序,不做操作MOV ECX, [DS:0x0000] ; 讀取應用程序數據段的大小MOV AX, 2005 * 8MOV DS, AXcrackloop: ; 循環用123填充應用程序段,直到全部填滿ADD ECX, -1MOV BYTE [DS:ECX], 123CMP ECX, 0JNE crackloopfin:MOV EDX, 4INT 0x40? ? ? ? 要防御這樣的攻擊,我們只要禁止應用程序隨意訪問其他任務所擁有的內存段就可以了。這樣一來,搗亂的程序就只能攻擊自己,自取滅亡了。
? ? ? ? 所幸,CPU已經提供了這樣的機制,即LDT(local (segment) descriptor table)。區別于之前用的GDT(global (segment) descriptor table),GDT中的段設置時供所有任務使用的,而LDT的段設置只是針對某個應用程序有效。將應用程序段設置在LDT就不用擔心上述的這種攻擊了。
????????和GDT一樣,LDT的容量也是64KB(1024 * 64 / 8 段),但是在此處我們只需要設置兩個段(一段 == 8字節)。我們將這 16字節的的信息放在 struct TASK 中:
? ? ? ? (1)我們可以通過GDTR這個寄存器將GDT的內存地址告知CPU;
????????(2)LDT的內存地址則是通過在GDT中創建LDT段來告知CPU;
????????(3)在 GDT 中我們設置多個 LDT,但是不能同時使用超過 2 個以上的 LDT;
????????我們在bootpack.h中添加用于設置LDT的段屬性編號:
【1】首先在 bootpack.h 中添加用于設置LDT的段屬性編號:
/* dsctbl.c */ // ... #define AR_LDT 0x0082 /*這里!*/ // .../* mtask.c */ // ... struct TASK {// ...struct SEGMENT_DESCRIPTOR ldt[2]; /*這里!*/// ... };【2】修改 mtask.c 以便設置LDT,我們可以將LDT編號寫入 tss.ldtr,這樣在創建TSS時就順便在GDT中設置了LDT,CPU也就知道這個任務應該使用哪個LDT了:
/* mtask.c */struct TASK *task_init(struct MEMMAN *memman) {//...taskctl->tasks0[i].tss.ldtr = (TASK_GDT0 + MAX_TASKS + i) * 8;// ...set_segmdesc(gdt + TASK_GDT0 + MAX_TASKS + i, 15, (int) taskctl->tasks0[i].ldt, AR_LDT);// ... }struct TASK *task_alloc(void) {// ...for (i = 0; i < MAX_TASKS; i++) {if (taskctl->tasks0[i].flags == 0) {// ...task->tss.fs = 0;task->tss.gs = 0;/*刪掉原來的task->tss.ldtr = 0;*/task->tss.iomap = 0x40000000;task->tss.ss0 = 0;return task;}}return 0; /*已經全部正在使用*/ }【3】最后修改 console.c ,使得應用程序段創建在LDT中:
int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline) {// ...if (finfo !=s 0) {/*找到文件的情況*/// ...if (finfo->size >= 36 && strncmp(p + 4, "Hari", 4) == 0 && *p == 0x00) {// ...set_segmdesc(task->ldt + 0, finfo->size - 1, (int) p, AR_CODE32_ER + 0x60); /*這里!*/set_segmdesc(task->ldt + 1, segsiz - 1, (int) q, AR_DATA32_RW + 0x60); /*這里!*/for (i = 0; i < datsiz; i++) {q[esp + i] = p[dathrb + i];}start_app(0x1b, 0 * 8 + 4, esp, 1 * 8 + 4, &(task->tss.esp0)); /*這里!*/// ...} else {cons_putstr0(cons, ".hrb file format error.\n");}// ...}// ... }? ? ? ? 這邊需要注意的是,在start_app的地方,我們指定的段號是4(=0×8+4)和12(=1×8+ 4),這里乘以8的部分和GDT是一樣的,但不一樣的是還加上了4,這 是代表該段號不是GDT中的段號,而是LDT內的段號的意思。由于這里我們使用的是LDT的段號,而每個任務都有自己專用的LDT,因此這樣寫完全沒有問題。
四 基于庫的程序目錄整理
? ? ? ? 基于庫的程序目錄整理,可以總結為:先分(拆分a_nask.nas),再合(整合obj為lib),最后整理。
【1】拆分 a_nask.nas :
? ? ? ? 現在,每寫一個應用程序,大小都很大,原因在于,創建應用程序.hrb時所引用的a_nask.nas變大了(持續添加API),這實在是對空間的浪費,我們只需要將實際用到的部分包含在可執行文件中即可。
? ? ? ? 解決辦法是將這些API做成不同的 .obj 文件。連接器的功能只是決定是否將 .obj 文件連接上去,而不會在一個包含多個函數的 .obj 文件中挑出需要使用的部分,并舍去不需要使用的部分。
? ? ? ? 作者將?a_nask.nas 拆成了 api001.nas----api020.nas 。對于應用程序 a.hrb,我們只需要用到api001.nas 和?api004.nas ,所以Makefile這么改即可,以此類推,其他的應用程序也根據需要作出改動:
a.bim : a.obj api001.obj api004.obj Makefile$(OBJ2BIM) @$(RULEFILE) out:a.bim map:a.map a.obj api001.obj api004.obj? ? ? ? 這樣每個程序改還是很麻煩,所以基于 obj2bim 連接器的一個特性(如果所指定的.obj文件中的函數并沒有被程序所使用,那么這個.obj文件是不會被連接的,所以我們把用不到的.obj文件寫進去也沒有問題),作者提出了一個更簡單的改法:
OBJS_API = api001.obj api002.obj api003.obj api004.obj api005.obj api006.obj \api007.obj api008.obj api009.obj api010.obj api011.obj api012.obj \api013.obj api014.obj api015.obj api016.obj api017.obj api018.obj \api019.obj api020.obja.bim : a.obj $(OBJS_API) Makefile$(OBJ2BIM) @$(RULEFILE) out:a.bim map:a.map a.obj $(OBJS_API)? ? ? ? 其他的應用程序直接用 $(OBJS_API) 即可方便擴展。
【2】整合 obj 為庫文件 lib :
? ? ? ? 引入庫來管理所有的 obj 文件,達到精簡系統結構的作用。要創建一個庫,需要 .obj 文件作為原材料,也需要一個叫做 庫管理器 的東西,這個東西作者寫好了包含在 tolset 中:
GOLIB = $(TOOLPATH)golib00.exe apilib.lib : Makefile $(OBJS_API)$(GOLIB) $(OBJS_API) out:apilib.lib????????所以說在下面使用到 $(OBJS_API) 的地方都可以使用 apilib.lib 來替換。借此機會,順便寫一個 apilib.h:
/* apilib.h */void api_putchar(int c); void api_putstr0(char *s); void api_putstr1(char *s, int l); void api_end(void); int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title); void api_putstrwin(int win, int x, int y, int col, int len, char *str); void api_boxfilwin(int win, int x0, int y0, int x1, int y1, int col); void api_initmalloc(void); char *api_malloc(int size); void api_free(char *addr, int size); void api_point(int win, int x, int y, int col); void api_refreshwin(int win, int x0, int y0, int x1, int y1); void api_linewin(int win, int x0, int y0, int x1, int y1, int col); void api_closewin(int win); int api_getkey(int mode); int api_alloctimer(void); void api_inittimer(int timer, int data); void api_settimer(int timer, int time); void api_freetimer(int timer); void api_beep(int tone);????????后面應用程序中使用API函數,可以包含這個頭文件,然后直接引用指定函數即可。例如beepdown.c 就可以簡化成下面這樣:
#include "apilib.h" /* 這里! */void HariMain(void) {/* 中略 */ }? ? ? ? 庫是一種結構化編程的概念。,便于移植和擴展。我們可以自己編寫庫,也可以直接使用別人編寫的庫。例如之前用到的 sprintf 和 rand 等函數,就是包含在 golibc.lib 庫中的函數。
【3】整理 make 環境:
? ? ? ? 現在操作系統、應用程序和庫的源文件都堆在一起,沒有層次,這邊各就各位:
? ? ? ? ?(1)haribote : 只包含操作系統核心部分,用于生成操作系統相關的 .hrb 文件,包含 make | make clean | make src_only。包含的重要的文件有 : 引導文件ipl10.nas、做 C語言做不了的事情的 naskfunc.c、使用匯編語言經行一系列初始化的 asmhead.nas。
? ? ? ? (2)apilib:只是make | make clean | make src_only用來生成.lib文件。
? ? ? ? (3)應用程序:所有的應用程序的 Makefile 都比較短,將其中的公共部分提取出來放在 app_make.txt 中:
? ? ? ? app_make.txt 篇幅比較長,就不詳細列出了,內容就是文件生成規則和編譯命令的定義等。
? ? ? ? (4)總的Makefile:篇幅也比較長,不詳細列出了,內容就是對haribote.img的編譯、文件生成規則和編譯命令的定義,可以支持很多命令:
? ? ? ? ?綜上所述,文件整理結束。
總結
以上是生活随笔為你收集整理的【操作系统】30天自制操作系统--(26)LDT与库的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python椭圆拟合_椭圆拟合(pyth
- 下一篇: NLP算法-词性标注