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

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

今天主要的内容是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
    评论
操作系统的定义:是一组控制和管理计算机软硬件资源,为用户提供便捷使用计算机的程序的集合。 基本功能:1.进程管理 2.存储管理 3.文件管理 4.设备管理 5.作业管理 基本组成: 1、驱动程序 最底层bai的、直接控制和监视各类硬件的部分,它du们的职zhi责是隐藏硬件的具体细节,并向dao其他部分提供一个抽象的、通用的接口。 2、内核 操作系统之最内核部分,通常运行在最高特权级,负责提供基础性、结构性的功能。 3、支承库(亦作“接口库”) 是一系列特殊的程序库,它们职责在于把系统所提供的基本服务包装成应用程序所能够使用的编程接口(API),是最靠近应用程序的部分。例如,GNU C运行期库就属于此类,它把各种操作系统的内部编程接口包装成ANSI C和POSIX编程接口的形式。 4、外围 所谓外围,是指操作系统中除以上三类以外的所有其他部分,通常是用于提供特定高级服务的部件。例如,在微内核结构中,大部分系统服务,以及UNIX/Linux中各种守护进程都通常被划归此列。 操作系统的分类:1.批处理系统 2.分时操作系统 3.实时操作系统 4.分布式操作系统 5.网络操作系统 6.嵌入式操作系统 操作系统的特点: 1、并发性: 是在计算机bai系统中同时存在多个程序,宏观上看,du这些程序是同时向前推进的。 在单CPU上,这些并发执行的程序是交替在CPU上运行的。 程序并发性体现在两个方面: 用户程序与用户程序之间的并发执行。 用户程序与操作系统程序之间的并发。 2、共享性: 资源共享是操作系统程序和多个用户程序共用系统中的资源。 3、 随机性: 随机性指:操作系统的运行是在一个随机的环境中,一个设备可能在任何时间向处理机发出中断请求,系统无法知道运行着的程序会在什么时候做什么事情。 4、虚拟 (virtual)是指通过技术将一个物理实体变成若干个逻辑上的对应物。在操作系统中虚拟的实现主要是通过分时的使用方法。显然,如果n是某一个物理设备所对应的虚拟逻辑设备数,则虚拟设备的速度必然是物理设备速度的1/n。 5、异步性:不确定性。同一程序和数据的多次运行可能得到不同的结果;程序的运行时间、运行顺序也具有不确定性;外部输入的请求、运行故障发生的时间难以预测。这些都是不确定性的表现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值