用C语言编写应用程序
要调用API,需要将参数放到寄存器中然后中断,C语言中没有这样的指令,所以写一个汇编函数:
_api_putchar: ; void api_putchar(int c);
MOV EDX,1
MOV AL,[ESP+4] ; c
INT 0x40
RET
将函数参数放到AL中,然后中断。在C语言中只需要 api_putchar()
就可以了。
但是编译出的.hrb程序不能运行,因为它没有可执行文件的头。将.hrb程序开头6个字节替换成 E8 16 00 00 00 CB
,相当于:
[BITS 32]
CALL 0x1b
RETF
0x1b是程序main函数开头的位置。用RETF是因为程序是从操作系统跳转过来的。
保护操作系统
现在应用程序可以向任意内存地址写入数据,这会破坏操作系统。
之前只为应用程序分配了代码段(CS),数据段DS/SS和操作系统相同。需要为数据段也分配不同的空间。
在start_app()函数中,从参数中获得应用程序的EIP,CS,ESP,DS/SS。EIP是程序起始地址,一般为0,CS是代码段的段号,ESP是栈起始地址,一般等于栈大小值。DS和SS相同,是数据段的段号。将寄存器中的ESP,DS/SS替换成刚才的值,然后将EIP,CS push到栈上,使用 CALL FAR [ESP]
跳转到这个位置。应用程序返回后,恢复操作系统的CS,ESP,DS/SS并返回。
使用API函数时,由于API函数是写在操作系统里的,也需要切换上述寄存器。在asm_hrb_api()中,一方面需要保存寄存器,另一方面需要将寄存器PUSH作为hrb_api()的参数。保存的寄存器在应用程序的栈上,而作为参数的寄存器复制到操作系统的栈上。
同理,中断函数也在操作系统中。当从应用程序中断时,需要切换DS,SS,ESP寄存器。
对异常的支持
当应用程序试图破坏操作系统时(访问操作系统的内存空间),会产生0x0d中断。注册一个中断函数处理这个异常。
在中断函数中,强制结束程序的方法是将ESP恢复到start_app()时的位置,然后RET直接回到cmd_app()。
但是如果应用程序将段修改为操作系统的段,依然可以改写操作系统内存空间的数据。我们可以禁止应用程序向DS存入操作系统段号。
在cmd_app()中,分配应用程序的数据段和代码段时,权限加上0x60,表示应用程序用。
这样的话,操作系统又不能CALL应用程序了。可以使用RETF。把EIP和CS PUSH到栈上,然后使用RETF,效果和CALL FAR 一样。
在应用程序里也是不能用RETF了。
其实这章后面完全没看懂。。