Windows内核分析之一 —— 内核入口函数

来源:互联网 发布:电脑网络不好怎么办 编辑:程序博客网 时间:2024/05/17 01:29
 前段时间和yuewang和一块三毛钱商量着写写Windows分析的文章,我来开个头吧,哈哈。既然是开头,所以就选择了内核入口点开始,我向来不怎么会写文章,也就当流水账记记吧,看能不能引出他们更好的分析出来J

Ntoskrnl的入口点函数名是KiSystemStartup,这是bootloader执行了一些基本的初始化之后跳转到的内核入口函数,用汇编语言实现。

一、KiSystemStartup功能介绍

KiSystemStartup第一次运行于processor 0,主要是初始化一些系统硬件状态,调用一些系统初始化过程,然后就进入调度程序,开始系统调度过程。而对于其他processor,初始化的时候也是进入KiSystemStartup,但是做的工作有所区别而已。

 

二、Processor 0(以后简称P0)开始执行KiSystemStartup时的系统环境

这个运行环境是由bootloader准备好的:

1、 一个精简版的IDT环境,从00x1F号中断已经被准备好

2、 一个完整的GDT被初始化出来并且Load

3、 完整的TSS被初始化并且Load

4、 页面映射经过了基本的初始化,并且设置好了初始化所需的最少的页面。虚拟内存的最低4M被直接映射到物理内存中。

5、 ntoskrnl.exe被装载到它内存描述符中的地址。也即编译时确定的基地址。

6、 DS=ES=SSESP指向一个可用的栈中。

7、 中断被关闭。

 

三、其他Processor开始执行KiSystemStartup的环境

IDT, GDT, TSS, stack, selectors, PCR全部初始化完成并可用,页表设置为当前运行的页表(这一点偶也不太明白,可能还需要看看以后的代码才能理解),具备一个LoaderBlock,作为在该处理器上执行KiSystemStartup的参数。

 

四、大致流程

1、 KiSystemStartup将参数KissLoaderBlock放到全局变量_KeLoaderBlock

2、 取出_KeNumberProcessors,并判断是否是0_KeNumberProcessors保存了系统中的处理器数目,这个变量被初始化为0,所以当Ntoskrnl开始执行时,这个变量还没有被填充。因此判断_KeNumberProcessors是否是0,就可以知道当前是不是第一次执行KiSystemStartup

3、 如果是P0,会将_KiInitialThreadP0BootStack的地址分别保存 _KeLoaderBlock中的对应字段中。_KiInitialThread是系统启动之后的初始线程,而P0BootStack应该是初始化时临时使用的内核堆栈,定义为db      KERNEL_STACK_SIZE dup (?)KERNEL_STACK_SIZEi386中是0x3000,在AMD64中是0x6000。然后会设置fs0x30,这是内核_KPCR结构的在GDT中序号。最后,会将处理器序号,也就是0,保存到_KPCR中对应位置,这个位置在i386AMD64中也是不同的。

4、 下面又是所有处理器都会执行的代码了,设置初始线程的_ETHREAD:: Tcb:: ApcState:: ApcListHead[0],将_LIST_ENTRYFlinkBlink都设置为自身。

5、 调用_KiInitializeMachineType过程,会设置一下机器类型。不过这里做得很简单,这个函数可能在未来也会有较大更改。主要的机器类型信息可能包含了总线类型、CPU大致的系列等简单信息。

6、 然后又判断是否是P0,如果不是,会跳过一大段初始化代码。

7、 P0的情况下,调用GetMachineBootPointers函数,获取由bootloader初始化过的一些信息。从这个函数返回后,edi中保存gdt基地址,esi中保存pcr基地址,edx保存tss基地址,eax保存idt基地址。KiSystemStartup接下来会将这些值保存到自己的局部变量中使用。

8、 Bootloader初始化的TSS16位的,KiSystemStartup在这里会将它的标志改为32位,然后连续调用_KiInitializeTSS2_KiInitializeTSS初始化TSSKiInitializeTSS2初始化了内核TSS结构在GDT中描述符的界限大小,以及初始化IOPM的相关结构。_KiInitializeTSSTSS中首先设置不使用IOPM,然后设置Tss->Flags = 0,将EFLAGS清空。最后将LDTss0都设置为0。设置完成后,重新装载TR寄存器。

9、 接下来设置了double fault task gate。这里会设置IDT中的08号中断,设置成了一个任务门,并填充相应的TSS结构,是用于#DF异常时的。

10、             设置用于NMI fault task gate,设置IDT02号中断。这是用于不可屏蔽中断的中断号。同样也调用_KiInitializeTSS填充了另外一个TSS结构。上面两条详细的原理参考Intel手册关于IDTtask gate的描述。

11、             调用_KiInitializePcr初始化了当前的pcr

12、             将初始进程的_EPROCESS地址,即_KiInitialProcess的地址设置到了初始线程的_ETHREAD:: Tcb:: ApcState:: Process中。

13、             设置PCR->Teb = 0

14、             设置PCR->PrcbData.ProcessorState.SpecialRegisters.KernelDr6PCR->PrcbData.ProcessorState.SpecialRegisters.KernelDr70。这里是为了初始化内核调试器相关的东西,具体作用可能只有分析到相关代码才能知道了。

15、             调用_KiSwapIDT转换IDT描述符的格式。IDTENTRY定义如下:

typedef struct tagIDTENTRY

{

        unsigned short OffsetLow;

        unsigned short Selector;

        unsigned char Reserved;

        unsigned char Type:4;

        unsigned char Always0:1;

        unsigned char Dpl:2;

        unsigned char Present:1;

        unsigned short OffsetHigh;

} IDTENTRY, *PIDTENTRY;

这个函数将ntoskrnl定义的IDT表项数组中,选择子的SelectorOffsetHigh字段对换。这里估计是在初始化这些表项的时候,为了方便直接将处理代码的地址填到了&OffsetLow中,所以Selector保存了高位的地址,然后到后面来统一替换。详细的原理参见Intel手册。

16、             dses的值设置为0x23,也就是Ring3下的dses值。

17、             ntoskrnl_IDT数组的内容复制到当前的IDT表中。前面设置的double fault nmi fault的表项不会被覆盖掉,而是使用新设置的内容。

18、             接下来又是所有处理器都会执行的操作了。调用_KiProcessorStart初始化处理器。这个函数会根据KiProcessorStartControl的不同值进行不同的操作,例如获取一些处理器信息、启动或者停止处理器等等。由于P0已经不需要初始化了,所以在P0阶段这个函数直接返回。

19、             获取_KiFreezeExecutionLock这个锁,用于修改一些和处理器相关的全局资源。主要是_KPCR里面的处理器相关的信息。然后调用了_HalInitializeProcessor函数,初始化该处理器的IDT。估计这个函数会继续为每个处理器调用KiSystemStartup函数。不过没能确认。

20、             IRQL的信息保存下来。这是hal由参数传过来的。

21、             _KeActiveProcessors中设置初始化完成的处理器MASK

22、             调用_KiInitializeAbios初始化ABIOS结构。这里的详细原理就不太清楚了,因为没能分析过相关部分。

23、             _KeNumberProcessors1,增加已初始化完成的处理器数量。然后就会释放掉_KiFreezeExecutionLock锁了。

24、             接下来调用_KdInitSystem函数。这里应该会初始化内核调试器。只在P0上调用。

25、             后面将会初始化内核了,首先会将IRQL提升到HIGH_LEVEL,并初始化调用内核初始化函数使用的寄存器,包括传递参数的eax,ebx,edx,以及用于堆栈访问的espebp。然后就调用_KiInitializeKernel进行内核初始化。这个函数相当复杂,也够一篇文章,这里就不写了。呵呵

26、             出来之后设置idle thread的优先级为0,开中断,降低IRQLDISPATCH_LEVEL。然后检查并等待_KiBarrierWait这个锁。对P0来说,由于_KiBarrierWait初始化为0,所以直接就跳到idle线程了,其他处理器会一直等待_KiBarrierWait,直到允许他们运行。

27、             最后通过一个长跳转到KiIdleLoop函数,开始系统的处理和调度,整个系统初始化过程就完成了。

 

五、后记

唉,真的写起来才发现文章不好写啊。自己再看的时候都感觉不清不楚的,呵呵。不过暂时就这样吧J

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 眼镜度数配高了怎么办 孩子近视800度可怎么办 儿童眼睛近视怎么办才能恢复正常 小孩眼睛近视怎么办才能恢复正常 3岁宝宝近视怎么办啊 6个月婴儿近视怎么办 近视眼的人老了怎么办 一千多度的近视怎么办 近视镜片磨花了怎么办 眼镜镜片磨花了怎么办 戴眼镜鼻梁有印怎么办 狗狗发烧怎么办最有效 狗狗感冒怎么办最有效 狗狗发烧去医院怎么办 眼睛里长了虫子怎么办 吃了没熟的猪肉怎么办 没熟的鸡肉吃了怎么办 狗狗大便有绦虫怎么办 吃了有虫的猪肉怎么办 米猪肉吃了会怎么办 吃了鱼的寄生虫怎么办 鱼身体里有线虫怎么办 幼猫半夜一直叫怎么办 猫半夜4点叫不停怎么办 眼睛里长了黄斑怎么办 眼睛周围长小疙瘩怎么办 眼睛被手机砸了怎么办 眼睛被东西砸了怎么办 眼睛被打了一下怎么办 眼睛被树枝伤了怎么办 眼球被打了一下怎么办 眼睛黄有血丝是怎么办 上眼皮有异物感怎么办 20岁眼周围皱纹怎么办 才22岁眼睛皱纹怎么办 眼周围发干皱纹怎么办 眼周围淤血肿了怎么办 每到秋季就咳嗽怎么办 左肾泥沙样结石怎么办 双肾泥沙样结石怎么办 温州市民卡丢了怎么办