本章主要描述了一种基于LDT(本地段描述符表)的程序保护机制。另外引入了库,并基于此整理了一下当前的操作系统源码以及应用程序的目录。

一 解决BUG

上一章最后留有一个“使用 ncst 启动的程序无论是用 shift + F1 还是鼠标点 x 都无法结束”的BUG。

问题在于在shift + F1 还是鼠标点 x 处需要唤醒任务,以便结束处理能够得到执行:

task_run(task, -1, 0);  //功能是将休眠的任务唤醒

之前也没有这个语句,但是退出没有问题,又是什么原因呢?原因在于,命令行窗口会触发用来控制光标闪烁的定时器中断,当产生定时器中断时,定时器超时会向FIFO写数据,于是任务就被自动唤醒了(没有这个语句的情况下,最大会产生大约0.5s的延迟)。

二 应用程序运行时关闭命令行窗口

用普通的方法运行应用程序的时候,在应用程序退出之前,我们是无法关闭用来启动这个程序的命令行窗口的。
        首先在 bootpack.c 中加入点击 x 后的隐藏窗口和接收到 console.c 发送的关闭窗口请求数据时所进行的处理:

else { /*命令行窗口*/task = sht->task;sheet_updown(sht, -1); /*暂且隐藏该图层*/keywin_off(key_win);key_win = shtctl->sheets[shtctl->top - 1];keywin_on(key_win);io_cli();fifo32_put(&task->fifo, 4);io_sti();
}
else if (2024 <= i && i <= 2279) { /*只关闭命令行窗口*/sht2 = shtctl->sheets0 + (i - 2024);memman_free_4k(memman, (int) sht2->buf, 256 * 165);sheet_free(sht2);
}

在 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      = color
STACK    = 1k
MALLOC   = 56kinclude ../app_make.txt

app_make.txt 篇幅比较长,就不详细列出了,内容就是文件生成规则和编译命令的定义等。

(4)总的Makefile:篇幅也比较长,不详细列出了,内容就是对haribote.img的编译、文件生成规则和编译命令的定义,可以支持很多命令:

综上所述,文件整理结束。

【操作系统】30天自制操作系统--(26)LDT与库相关推荐

  1. 索骥馆-DIY操作系统之《30天自制操作系统》扫描版[PDF]

    内容简介: <30天自制操作系统>是一本兼具趣味性.实用性与学习性的操作系统图书.作者从计算机的构造.汇编语言.C语言开始解说,让读者在实践中掌握算法.在这本书的指导下,从零编写所有代码, ...

  2. 《30天自制操作系统》笔记(01)——hello bitzhuwei’s OS!

    <30天自制操作系统>笔记(01)--hello bitzhuwei's OS! 最初的OS代码 1 ; hello-os 2 ; TAB=4 3 4 ORG 0x7c00 ; 指明程序的 ...

  3. 《30天自制操作系统》笔记(04)——显示器256色

    <30天自制操作系统>笔记(04)--显示器256色 进度回顾 从最开始的(01)篇到上一篇为止,已经解决了开发环境问题和OS项目的顶层设计问题. 本篇做一个小练习:设置显卡显示256色. ...

  4. 【操作系统】30天自制操作系统--(27)文件操作

    本章主要介绍了对 _alloca 函数的兼容,日文的显示,以及着重介绍了文件系统操作. 一 对_alloca的支持 首先作者写了一个小应用程序,功能是找出并打印1000以内的质数: #include ...

  5. 【操作系统】30天自制操作系统--(14)多任务1

    本章开始多任务的设计. 一 多任务的说明 多任务(multitask),指的是操作系统中,多个应用程序同时运行的状态.然而,对于单核CPU来说,同一个瞬间只能处理一个事情,不能做到左右互搏.一心二用的 ...

  6. 《30天自制操作系统》U盘启动,真机运行(16天)

    首先说一下到目前为止U盘启动遇到的问题,首先的一个问题是"system volume information",目前尚未解决,这个问题可能导致U盘启动失败,我猜测可能是由于每一次重 ...

  7. 《30天自制操作系统》笔记(09)——绘制窗口

    <30天自制操作系统>笔记(09)--绘制窗口 进度回顾 上一篇中介绍了图层式窗口管理的思路和算法.在此基础上,本篇就解决绘制窗口及其简单的优化问题. 这里稍微吐槽一下<30天自制操 ...

  8. 为什么《30天自制操作系统》封面中的猫是两只尾巴

    刚刚在一社区,发了一贴,被指出一问题,询一高人,得一答案.这便是我没有关注到的封面上的那只猫,我想这也是很多读者没有关注到的.因为在我微博的200转发贴中,并没有人提到封面中的猫为何有两只尾巴.于是咨 ...

  9. 发布在《30天自制操作系统》之前的帮助阅读贴

    说明:这是8月15日即将上市的一本新书,本文的摘选也可以命名为<30天自制操作系统>上市之前必读.本书幽默,有趣,可以说是技术书里的幽默书,让您读起来绝对不会感到乏味.在本书上市之前,您一 ...

  10. 写在《30天自制操作系统》上市之前

       这本<30天自制操作系统>马上就要在各大书店和网上商城全面上架了,作为本书的4位译者之一,我负责翻译了本书约三分之二的内容.这是我参与翻译的第一本译著,我感到很激动也很紧张,因为我知 ...

最新文章

  1. 互联网1分钟 |1212
  2. DHTML4(select与checkbox应用)
  3. u盘装系统学计算机好,教你如何使用u盘做系统
  4. C:打印菱形(自己的方法)
  5. android baidupush
  6. JavaScript 严格模式(use strict)
  7. JCP执行委员会新成员选举结果揭晓:Hologic未获通过
  8. 生日排序(洛谷P1104题题解,Java语言描述)
  9. iOS解决表格中TextField,TextView编辑时,输入框被键盘遮挡的问题
  10. usercontroller.java,springboot controller 参数绑定
  11. 【lvgl 学习】怎么让LVGL支持GBK编码
  12. 无法连接GitHub完整解决方案
  13. eclipse常用搜索快捷键
  14. RSA生成密钥对的过程
  15. Multisim基础 示波器 波形沿Y轴正方向上移一格
  16. 树莓派Ubuntu18.04下无线鼠标延迟问题解决
  17. Android 集成友盟统计
  18. Vue<一>互动教程
  19. docker-Login 报错 Error response from daemon
  20. ‘冰封’合约背后的老牌劲敌 | 链安团队漏洞分析连载第二期 —— 拒绝服务漏洞

热门文章

  1. 三行代码做一辆Q弹物理自行车,骑上它去海边兜风吧!
  2. Proxmox VE桌面虚拟化
  3. android mediastore指定前置摄像头,Intent方式打开前置摄像头
  4. php 开源系统(cms),30个很棒的PHP开源CMS内容管理系统
  5. mysql中怎么防止数据丢失
  6. 【WIN】超简单的Excel密码破解
  7. 为前端工作者准备的几个炫彩的js动画库
  8. 入侵检测系统的原理与应用
  9. DxO FilmPack 5 for Mac(胶片模拟效果滤镜软件)
  10. PHP利用QQ邮箱发送邮件