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


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

    今天我们再接再励,主要完成保护操作系统的这个功能!



    一、用c语言编写应用程序

    在这之前,我们都是使用的汇编语言进行编写的。但是为了方便吧,我们今天尝试用c语言来编写一个应用程序看看。

    编写一个程序名为:
    a.c:

    void api_putchar(int c);
    
    void HariMain(void)
    {
        api_putchar('A');
        return;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这里的harimain与操作系统的能够bootpack虽然都是指主函数,但是这可和前面的不一样。我们的a.c是一个应用程序,是单独独立出来的。

    此外,我们还要创建一个nas文件,里面写入api_putchar这个函数,以供我们程序的调用:
    a_nask.nas:

    [FORMAT "WCOFF"]				; 生成对象文件的模式	
    [INSTRSET "i486p"]				; 使用486兼容模式
    [BITS 32]						; 使用32位模式机器语言
    [FILE "a_nask.nas"]				; 源文件信息名
    
    		GLOBAL	_api_putchar
    
    [SECTION .text]
    ;调用cons_putchar这个模式!!
    _api_putchar:	; void api_putchar(int c);
    		MOV		EDX,1
    		MOV		AL,[ESP+4]		; c
    		INT		0x40
    		RET
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    然后继续修改一下makefile文件:

    a.bim : a.obj a_nask.obj Makefile
    	$(OBJ2BIM) @$(RULEFILE) out:a.bim map:a.map a.obj a_nask.obj
    
    a.hrb : a.bim Makefile
    	$(BIM2HRB) a.bim a.hrb 0
    
    haribote.sys : asmhead.bin bootpack.hrb Makefile
    	copy /B asmhead.bin+bootpack.hrb haribote.sys
    
    haribote.img : ipl10.bin haribote.sys hello.hrb hello2.hrb \
                   a.hrb  Makefile
    	$(EDIMG)   imgin:../z_tools/fdimg0at.tek \
    		wbinimg src:ipl10.bin len:512 from:0 to:0 \
    		copy from:haribote.sys to:@: \
    		copy from:ipl10.nas to:@: \
    		copy from:make.bat to:@: \
    		copy from:hello.hrb to:@: \
    		copy from:hello2.hrb to:@: \
    		copy from:a.hrb to:@: \
    		imgout:haribote.img
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    然后,运行看看?
    哎!!我们输入a貌似一bug了。那么我们先试着修改一个地方看看吧:
    打开a.hrb,我们将前6个字节修改以下:修改位E8 16 00 00 00 CB在这里插入图片描述

    然后保存,在运行看看:
    在这里插入图片描述

    喔!成功了。下面呢我来解释解释。我们输入的那6个字节用汇编语言表示就是:
    [BITS 32]
    CALL 0x1b
    RETF
    那么这里的0x1b是指啥呢?让我们来看看之前的asmhead:
    在这里插入图片描述

    在asmhead中也有这一句。实际上,0x1b大致指向的就是.hrb文件中的HariMain函数。在asmhead中就是利用这个调用的bootpack.hrb的主函数的。因此这里我们也需要利用这一个进行调用!!

    当然了,这里我们总不可能有一个程序就改一次吧!于是,我们直接再操作系统中进行对应的设置:

    cmd_app{if (finfo != 0) {
    		/*找到文件的情况*/
    		p = (char *) memman_alloc_4k(memman, finfo->size);
    		*((int *) 0xfe8) = (int) p;
    		file_loadfile(finfo->clustno, finfo->size, p, fat, (char *) (ADR_DISKIMG + 0x003e00));
    		set_segmdesc(gdt + 1003, finfo->size - 1, (int) p, AR_CODE32_ER);
    		if (finfo->size >= 8 && strncmp(p + 4, "Hari", 4) == 0) {
    			p[0] = 0xe8;
    			p[1] = 0x16;
    			p[2] = 0x00;
    			p[3] = 0x00;
    			p[4] = 0x00;
    			p[5] = 0xcb;
    		}
    		farcall(0, 1003 * 8);
    		memman_free_4k(memman, (int) p, finfo->size);
    		cons_newline(cons);
    		return 1;
    	}
    	return}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    凡是通过bim2hrb生成的hrb文件,其第4~7字节一定位“Hari” , 因此,这里我们就可以利用这个特性进行检查,然后进行对应的修改即可!
    hello3:

    void api_putchar(int c);
    
    void HariMain(void)
    {
    	api_putchar('h');
    	api_putchar('e');
    	api_putchar('l');
    	api_putchar('l');
    	api_putchar('o');
    	return;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    耶耶,完美运行了!

    二、保护操作系统

    1.保护系统第一部分

    我们之所以要讲这一部分,主要原因在于我们是想要防止某些应用程序编程导致的系统bug或者说某些应用程序中含有严重的病毒导致整个系统的异常运行和崩溃的出现!

    我们使用的x86架构cpu已经为我们提供了良好的保护操作系统的功能,我们以下就要利用此来有效的进行保护我们的系统。

    首先,编写一个捣蛋的程序:
    crack1.c:

    void HariMain(void)
    {
        *((char *) 0x00102600) = 0;
        return;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    然后运行,就会发现bug了:
    在这里插入图片描述

    那么,我们接下来自制一个保护操作系统的程序吧。前面的crack1这个程序之所以能够产生破坏,其主要原因在于私自访问了本该由操作系统进行管理的内存。因此,想要防范,我们可以规划出一片应用程序专用的地址空间,并且禁止应用程序访问其他的段:
    操作系统用的数据段: 1 * 8
    操作系统用的代码段: 2 * 8
    TSS专用段 : 3 * 8~1002 * 8
    应用程序代码段:1003 * 8
    应用程序的数据段:1004 * 8

    然后以下我们将利用x86架构的cpu系统保护功能;来进行实现。

    • 在段定义的地方,我们加上访问权限0x60,此时就意味着我们已经将该段设置为了应用程序用的。即,当CS中段地址为应用程序段时,cpu就会认为我们正在正常的运行应用程序;而当CS的段地址为操作系统用的段地址时就会抛出异常:
      console.c:
    int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline)
    {
    略:
    	char name[18], *p, *q;
    	struct TASK *task = task_now();if (finfo != 0) {
    		/*找到文件的情况*/
    		p = (char *) memman_alloc_4k(memman, finfo->size);
    		q = (char *) memman_alloc_4k(memman, 64 * 1024);//分配64KB给应用程序
    		*((int *) 0xfe8) = (int) p;
    		file_loadfile(finfo->clustno, finfo->size, p, fat, (char *) (ADR_DISKIMG + 0x003e00));
    		set_segmdesc(gdt + 1003, finfo->size - 1, (int) p, AR_CODE32_ER + 0x60);
    		set_segmdesc(gdt + 1004,    64 * 1024 -1, (int) q, AR_DATA32_RW + 0x60);
    		if (finfo->size >= 8 && strncmp(p + 4, "Hari", 4) == 0) {
    			p[0] = 0xe8;
    			p[1] = 0x16;
    			p[2] = 0x00;
    			p[3] = 0x00;
    			p[4] = 0x00;
    			p[5] = 0xcb;
    		}
    		start_app(0, 1003 * 8, 64 * 1024, 1004 * 8, &(task->tss.esp0));
    		memman_free_4k(memman, (int) p, finfo->size);
    		memman_free_4k(memman, (int) q, 64 * 1024);
    		cons_newline(cons);
    		return 1;
    	}
    	/*没有找到文件的情况*/
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    这里我们多建立了一个函数start_app,用于定位到对应的应用程序的段地址空间中。除此之外,我们还设置了一个task->tss.esp0 用于将操作系统用的段地址和ESP传入。

    • 根据x86的规则,不允许操作系统使用call或者far-jmp,因此我们我们在start_app中不能直接使用这两个命令,我们需要利用reft,先将cs和ip压入栈中,然后再弹出来间接的指向应用程序段:
      naskfun.c:
    ;此时还在操作系统内部
    _start_app:		; void start_app(int eip, int cs, int esp, int ds, int *tss_esp0);
    		PUSHAD						; 将操作系统内部此时的32位寄存器值全部保存起来
    		MOV			EAX,[ESP+36]	; 导入应用程序用EIP
    		MOV			ECX,[ESP+40]	; 应用程序用CS
    		MOV			EDX,[ESP+44]	; 应用程序用ESP
    		MOV			EBX,[ESP+48]	; 应用程序用DS/SS
    		MOV			EBP,[ESP+52]	; tss.esp0的地址,可参考task的定义
    		MOV			[EBP ],ESP		; 保存操作系统用ESP,[ebp] 指向的是task->tss.esp
    		MOV			[EBP+4],SS		; 保存操作系统用SS,[ebp+4] 指向的是task->tss.ss
    		MOV	     	ES,BX			;将DS/SS进行复制,且以防万一,也给其他寄存器值进行赋值
    		MOV	     	DS,BX
    		MOV		    FS,BX
    		MOV		    GS,BX
    ; 下面调整栈,以免用RETF跳转到应用程序
    		OR			ECX,3 				; 将应用程序用段号和3进行OR运算,以便后面跳转到应用程序段
    		OR          EBX,3 				; 将应用程序用段号和3进行OR运算
    		PUSH		EBX					; 应用程序的SS
    		PUSH		EDX					; 应用程序的ESP
    		PUSH		ECX					; 应用程序的CS
    		PUSH		EAX					; 应用程序的EIP
    		RETF							;取得CS和EIP,然后跳转到应用程序的地址
    ; 应用程序结束后不会回到这里,此时还剩与esp、ss、pushad的各个值
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    然后,我们还需要修改接收API调用的asm_hrb_api这个函数:

    _asm_hrb_api:
    		STI
    		PUSH	DS
    		PUSH	ES
    		PUSHAD			; 用于保存的PUSH
    		PUSHAD			; 用于向hrb_api传值的PUSH
    		MOV		AX,SS
    		MOV		DS,AX ; 将操作系统用段地址存入DS和ES
    		MOV		ES,AX
    		CALL	_hrb_api
    		CMP		EAX,0 ; 当EAX不为0时程序结束
    		JNE		end_app
    		ADD		ESP,32
    		POPAD
    		POP		ES
    		POP		DS
    		IRETD
    end_app:
    ;	EAX为tss.esp0的地址
    		MOV		ESP,[EAX]
    		POPAD
    		RET			; 返回cmd_app
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    当hrb_api返回的值是0时继续运行应用程序,若非零值时当作tss.esp0,此时cpu就会识别到这是操作系统的段,就会说哦!应用程序你越界了,我会马上终止你的!

    • 程序若想结束的话会直接分配给edx = 4:
    //用edx来指向是哪一号的子功能
    int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax)
    {
    	int cs_base = *((int *) 0xfe8);
    	struct TASK *task = task_now();
    	struct CONSOLE *cons = (struct CONSOLE *) *((int *) 0x0fec);
    	if (edx == 1) {
    		cons_putchar(cons, eax & 0xff, 1);
    	} else if (edx == 2) {
    		cons_putstr0(cons, (char *) ebx + cs_base);
    	} else if (edx == 3) {
    		cons_putstr1(cons, (char *) ebx + cs_base, ecx);
    	} else if (edx == 4) {
    		return &(task->tss.esp0);
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 此外,若想强制中断程序,需要我们一个0x0d的中断。在x86架构中,0x0d就表示当应用程序试图破坏操作系统或者违背操作系统设置时,自动产生一个0x0d的中断出来,称之为异常:
      naskfun.c:
    _asm_inthandler0d:
    		STI
    		PUSH	ES
    		PUSH	DS
    		PUSHAD
    		MOV		EAX,ESP
    		PUSH	EAX
    		MOV		AX,SS
    		MOV		DS,AX
    		MOV		ES,AX
    		CALL	_inthandler0d
    		CMP		EAX,0		
    		JNE		end_app		
    		POP		EAX
    		POPAD
    		POP		DS
    		POP		ES
    		ADD		ESP,4			; INT 0x0d需要这句
    		IRETD
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    console.c:

    int *inthandler0d(int *esp)
    {
    	struct CONSOLE *cons = (struct CONSOLE *) *((int *) 0x0fec);
    	struct TASK *task = task_now();
    	cons_putstr0(cons, "\nINT 0D :\n General Protected Exception.\n");
    	return &(task->tss.esp0);	/*强制结束程序*/
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    dsctbl.c:

    	set_gatedesc(idt + 0x40, (int) asm_hrb_api, 2 * 8, AR_INTGATE32 + 0x60); 
    
    • 1

    然后为了验证破坏性,我们尝试再写一个破坏程序:

    [INSTRSET "i486p"]
    [BITS 32]
    		MOV		EAX,1*8			; OS用的段号
    		MOV		DS,AX				; 将其存入DS
    		MOV		BYTE [0x102600],0
    		MOV		EDX,4
    		INT		0x40
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    注意,上面我们已经不能使用irted之类的进行返回了,因为cpu已经禁止了。所以我们使用最后两行这种进行返回!!

    让我们试试看吧:
    在这里插入图片描述
    在这里插入图片描述

    喔喔,很棒。成功拦截了


    总结

    以上就是今天主要写的对于操作系统保护的内容,明天还要继续加油加油呀!

  • 相关阅读:
    SQL Server如何获取GUID号
    python的前缀树(字典树)
    FreeRTOS 中如何定位 HardFault?
    从0开始学杂项 第三期:隐写分析(2) PNG图片隐写
    SCT52240STDR,SCT52240MTER,SCT52240QSTDR,SCT52240QMTER,栅极驱动器
    二叉树题目:路径总和 II
    专为小白打造—Kafka一篇文章从入门到入土
    【IPC 通信】信号处理接口 Signal API(7)
    在线网络代理转发NPClient
    SpringBoot 刷新上下文7--执行BeanFactoryPostProcessor
  • 原文地址:https://blog.csdn.net/qq_43696276/article/details/126166603