自制操作系统日志——第二十七天

本文记录了自制操作系统的进展,包括实现应用程序在关闭时隐藏命令行窗口,以及通过LDT(局部描述符表)增强应用程序保护。作者详细解释了如何修改代码以关闭命令行窗口,以及如何利用LDT防止应用程序间非法访问。此外,文章还介绍了如何优化程序大小,通过创建库文件减少程序体积,并提供了头文件以方便函数调用。最后,展示了优化前后程序大小的对比和程序全家福。
摘要由CSDN通过智能技术生成

自制操作系统日志——第二十七天

今天主要的内容是LDT与库的制作。加油加油😊



一、应用程序运行时,可以关闭命令行窗口

我们接下来进行修改bootpack.c程序。我们需要修改两个地方,第一个就是当我们按下X按钮后,就先隐藏起命令行的窗口,之所以这样子做是因为由于关闭的一整个命令行窗口的步骤比较多,因此还需要花费一点时间去处理,这样子可能会让用户觉得有点久,所以如果我们直接隐藏起来,这样子就可以让用户觉得我们已经关闭成功了!!

				if(i == 256 + 0x3b && key_shift != 0 && key_win != 0){//shift + F1强制关闭
				    task = key_win->task; //获取当前处于的图层
					if(task != 0 && task->tss.ss0 != 0)
					{
					    cons_putstr0(task->cons, "\nBreak(key):\n");
					    io_cli();//不能再改变寄存器的值时候进行切换
						task->tss.eax = (int) &(task->tss.esp0);
						task->tss.eip = (int) asm_end_app;
						io_sti();
						task_run(task, -1, 0);//为了确实执行结束处理,需要把处于休眠状态的这个任务唤醒
					}
				}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这个程序,以便对接收fifo缓冲区发来的数据进行进一步的处理:
在这个 console_task函数里,主要我们就是将sheet换成cons.sheet。对于平常情况下两者没有太大差别,但是当我们的命令行窗口关闭后cons.sht会被置为0,而sheet则不变!

然后,还要修改键盘里的API:
等待键盘输入的过程中,如果FIFO缓冲区接收到4,则表示关闭命令行窗口的信号,此时取消定时器,并发出清理图层的信息,然后将sht->cons置为0!

主要思路就是,我们利用命令行打开了一个程序后,此时我们对于该命令行的键盘输入都会由该程序进行接收,因此,当该程序接收到鼠标点击的关闭按钮后,系统就会像程序的fifo缓冲区发送4,然后调用API 15 进行接收处理,然后再向主函数发送对应的关闭命令行的fifo缓冲数据。

int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax)
{struct FIFO32 *sys_fifo = (struct FIFO32 *) *((int *) 0xfec);else if (edx == 15){
		for(;;){if(i == 4){//只关闭命令行窗口
				timer_cancel(cons->timer);
				io_cli();
				fifo32_put(sys_fifo, cons->sht - shtctl->sheets0 + 20240);
				cons->sht = 0;
				io_sti();
			}

运行,看看:
在这里插入图片描述

二、应用程序的保护

我们前面已经做了一个操作系统的保护功能了,那么试想一下,如果我们呢利用应用程序访问应用程序段,并向里面写入奇怪的数据的话是不是也会发生异常?(毕竟我们制作了不能越权访问操作系统段的程序而已)
我们制作下面这个程序:

[FORMAT "WCOFF"]
[INSTRSET "i486p"]
[BITS 32]
[FILE "crack7.nas"]

		GLOBAL	_HariMain

[SECTION .text]

_HariMain:
		MOV		AX,1005*8
		MOV		DS,AX
		CMP		DWORD [DS:0x0004],'Hari'
		JNE		fin					; 不是应用程序,因此不执行任何操作

		MOV		ECX,[DS:0x0000]		; 读取该应用程序的段
		MOV		AX,2005*8
		MOV		DS,AX

crackloop:							; 填入数据
		ADD		ECX,-1
		MOV		BYTE [DS:ECX],123
		CMP		ECX,0
		JNE		crackloop

fin:								; 结束
		MOV		EDX,4
		INT		0x40

然后,我们打开系统,先运行一个程序,然后再运行我们的破坏程序,最后移动一下鼠标就会出现下图:

在这里插入图片描述
以下呢,让我们进行分析一下:
首先呢,我们所分配的段是按顺序进行分配的,也就是说:

  • 1003 : task_a
  • 1004 : idel 哨兵任务
  • 1005: 第一个应用程序的应用程序代码段(也就是图中的color2)
  • 1006: 第二个应用程序的代码段(也就是我们的破坏程序)

当然数据也是类似的,从2003 ~ 2006号段这样子。 因此,我们的破坏程序,一开始就先越权读取了color2的代码段,检查是否是程序(即有无hari)。然后检测到以后,就设置将随意的数据写入到color2的数据段里也就是2005中,以此我们就可以进行覆盖了原来的数据内容了。这样子当然就可以搞破坏了!

那么我们想要解决的思路起始就很简单,只要将我们运行的应用程序段进行隔离,不允许其他应用程序越权访问即可。 好在cpu已经为我们提供了一个较好的解决方案,即LDT ( 局部段管理表)。与GDT不同的是,GDT是全局的,其设置的段是所有任务通用的;但是LDT所设置的段只对某个程序有用而已。因此,我们将某个应用程序段设置再LDT中,其他任务就无法使用该LDT段了。

LDT的内存地址是通过我们再GDT中设置的LDT段来告知cpu的。那么以下我们先进行设置LDT的段属性编号:
bootpack.h:

/* dsctbl.c */
#define AR_LDT			0x0082

/* mtask.c */
struct TASK
{
	int sel, flags; //sel用于存放GDT编号
	int level, priority; //设置优先级
	struct FIFO32 fifo;
	struct TSS32 tss;
	struct SEGMENT_DESCRIPTOR ldt[2];
	struct CONSOLE *cons;
	int ds_base, cons_stack;//数据段地址;栈的地址
};

接下来,我们修改mtask.c来设置LDT。我们只需将LDT编号写入tss.ldtr,这样子创建TSS的过程中就会自动再GDT中设置对应的ldt了。

struct TASK *task_init(struct MEMMAN *memman)
{for(i = 0; i < MAX_TASKS; i++)
    {
        taskctl->tasks0[i].flags = 0;
        taskctl->tasks0[i].sel = (TASK_GDT0 + i) * 8;
        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);
        set_segmdesc(gdt + TASK_GDT0 + MAX_TASKS +i, 15, (int) taskctl->tasks0[i].ldt, AR_LDT);
    }}
 struct TASK *task_alloc(void)
{
略
			task->tss.fs = 0;
			task->tss.gs = 0;
			task->tss.iomap = 0x40000000; //删掉原来的ldtr = 0
       task->tss.ss0 = 0 ;
       return task;//找到就返回一个!}

接下来,还需要修改一下console.c函数:


int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline)
{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);start_app(0x1b,0 * 8 +4, esp, 1 * 8 + 4, &(task->tss.esp0));}

首先,在start这里我们+4是为了告诉cpu我们这里不是GDT中段号,而是使用的LDT的段号。其次,由于每个任务的LDT都不一样因此这里不用担心会出现多个程序共用一段内存的情况!

接着,我们make run:
在这里插入图片描述

看吧,这里直接就出现异常了!!因为此时的1005不再是应用程序的段了,使用操作系统默认该段是操作系统内的段,因此出现了访问异常的。

那么,如果我们将段改成我们设置的4 和 12的话会发生什么? 不用想这肯定是自己破坏自己,因为应用程序的ldr仅有自己而已:
在这里插入图片描述
在这里插入图片描述
看,确实没啥影响了吧!

下面我们进行优化程序大小

在前面我们制作的任何程序,都是直接与a_nask进行相连的,但是我们可能程序并不会全部用到这里面所有存储的api,这就会使得整个程序异常的臃肿,于是乎我们接下来打算将所有api都单独进行存储拆分:
在这里插入图片描述
即,一个API单独成一个函数!! 然后由于bim2hrb这个连接器有着筛选功能(也就是说其连接的所有.OBJ文件里如果本应用程序并没有使用到该OBJ文件里的任一函数就会直接丢弃,不进行连接) ,因此我们就可以利用这个极大的简化了我们程序的大小。(makeflie文件里的内容就自行查看了,这里难度不大,就是普通的连接而已)
在这里插入图片描述

现在让我们看看未简化前的程序大小:
在这里插入图片描述

在这里插入图片描述

看到了吧,差距还挺明显的!

这里为了以后方便进行统一的管理以及调用,我们这里将使用库 .lib 的文件将这些.obj文件进行打包成一个文件进行统一的管理。:
makeflie:

GOLIB    = $(TOOLPATH)golib00.exe 

apilib.lib : Makefile $(OBJS_API)
	$(GOLIB) $(OBJS_API) out:apilib.lib

然后,我们就可以吧这些api全部放入库函数中!!程序连接时只需与库函数进行连接即可:
在这里插入图片描述

除此,之外为了方便调用函数,我们还制作了一个头文件:
apilin.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);

这样子,我们的应用程序只需写一个include就足以了:
在这里插入图片描述后续的make整理就参考源文件吧,以及书本上的没啥好说的。

唯一要注意的是,这里我们将操作系统的内核和程序的makfile分开了,因此以后就不用每新增一个应用程序就重新生成以便了。其中app_make.txt 就是控制应用程序的生成的。

最后,我们做完后直接在day27的文件夹下,打开运行的窗口,然后先src_only_full 全部清理一下后,在run_full 全部重新生成一下就好了。那么下面看看我们制作的程序的小全家福吧:
在这里插入图片描述


总结

以上呢,就是今天关于LDT和库的所有内容,明天将进行很难的文字显示部分的内容啊,搞完明天我们的任务也就差不多完成了!!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值