想了解进入startup_32之前,CPU执行了哪些操作,但是找不到跳转到它的call或jmp指令:
为避免介绍内容过于杂乱,本文尽量紧密围绕最主体、最关键的过程,不深究过于细节、相对独立、不影响整体理解的环节和疑问。
硬件设计层面会保证,CPU刚加电时处于实模式、仅开启段式内存管理,并且CS寄存器=0xffff、IP寄存器=0,从而执行0xffff0处的BIOS程序(主板出厂时烧入代码,断电不挥发),将bootsect(内核加载程序,位于主引导扇区),加载到0x7c00内存地址处并执行,bootsect先将自己移动到0x90000处,然后进一步将setup和内核文件,分别加载到0x90200和0x10000处,并执行setup(内核启动程序)。
setup程序最主要的目标,是开启CPU的保护模式,并跳转到内核的入口startup_32处执行。
内核最初的入口是startup_32,它最主要的目标,是开启CPU的页式内存管理,并调用start_kernel()函数。
以上实际上是一个边搭桥、边过桥的过程:一边加载,一边执行;一边准备,一边逐步开启CPU保护模式和页式内存管理。
bootsect+setup可正常执行(过桥):
(1) BIOS为实模式提供了中断向量表,所以int指令可以正常执行;
(2) CPU按照”CS/SS << 4 + IP/SP”或”DS << 4 + 包含在指令中的偏移值”计算地址,仅依赖寄存器,不依赖内存上的管理数据,所以指令的执行和寻址也正常。
setup切换保护模式(搭桥):
setup事先构造全局段描述符表(gdt),并执行lgdt指令,设置GDTR寄存器=gdt,然后开启保护模式。
startup_32可正常执行(过桥):
切换到保护模式后,CPU硬件层对段寄存器的使用逻辑,会发生改变,段基址不再从中直接获取,而是从它指向的段描述符,间接获取,这在setup执行时,已经准备好了。
startup_32开启页式内存管理(搭桥):
startup_32事先构造目录表swapper_pg_dir,并准备充足的目录表项、页表项,然后设置cr3寄存器=swapper_pg_dir,最后开启页式内存管理。
start_kernel()可正常执行(过桥):
开启页式内存管理后,CPU硬件层的寻址逻辑,又会发生改变,对于i386 CPU,指令中的地址,不光要经过段式映射,还要经过页式映射,才能得到最终的物理地址,页式映射依赖的目录表和页表(包括表项),这些都由startup_32准备好了。
CPU的内核态和用户态切换:
保护模式下,CPU硬件层,开始区分ring0~ring3权限。由于内核先于用户程序执行,也就是先抢到ring0权限,它在跳转到用户程序执行之前,就有权将CPU权限改为ring3,而用户程序只能穿过门,回到内核代码的同时,才能将CPU权限重新提升到ring0,因此所有依赖ring0权限的操作,只能请求内核帮忙完成。这样,内核就利用CPU提供的硬件特性,将ring0权限掌握在自己手里,用户程序永远只能在ring3权限下执行。另外,从用户程序跳入内核代码执行,仍然是在推进这个进程的目标,并没有导致进程切换(和跳入.so动态库执行类似),只是执行进程的CPU状态改变了而已,这其实也是内核的本质。
有些操作并不用关注,主要的有三个:
(1) 将自己从0x7c00迁移到0x90000,并跳转到新区域,继续执行一下条指令;
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!
最后于 1天前
被jmpcall编辑
,原因: