windows系统服务调用过程

windows系统服务原理

系统服务原理

Windows使用两种模式Ring0和Ring3,分别称为内核模式和用户模式。Windows使用终端门来实现模式切换,通过int 2e指令切换到内核中,IDT(中断描述符表)中的0x2e表项指定了内核系统服务的入口点;当内核模式代码完成了指定的系统服务的时候,通过iret执行返回到用户模式代码

int 2e的指令跳转过程:通过int 2e指令得到中断描述符表中的处理函数进行跳转。在Windows中,IDT2e表项中的段选择符为0x0008,终端例程偏移为_KiSystemService例程的地址。
在这里插入图片描述

当一个线程通过int 2e指令从用户模式跳转到内核模式中的时候,必然会有栈的切换。处理器将控制权交给目标例程之前,首先将原来的SS,esp,eflags,cs和eip寄存器的值存放到内核栈中,并让新的eip指向内核栈顶位置。用户程序通过任务寄存器指向的TSS段可以找到内核栈的位置,Ring0层的esp位于TSS+4的位置。如图所示:
在这里插入图片描述

快速系统调用

为了避免所次访问内存,引入了sysenter和sysexit指令。sysenter指令要做的事情与int2e指令相似。但是他会尽可能的减少内存访问和权限检查的开销。

sysenter通过使用三个MSR寄存器来访问指定跳转目标地址和栈的位置。在内核模式中可以使用rdmsr/wrmsr指令来设置这三个寄存区。
在这里插入图片描述

sysenter的思想是:将IA32_SYSENTER_CS和IA32_SYSENTER_EIP分别装载到cs和eip寄存器中。将IA32_SYSENTER_ESP+8和IA32_SYSENTER_ESP分别装载在ss和esp寄存器中。具体的操作如图:
在这里插入图片描述

内核模式与用户模式切换

Windows应用程序调用的都是系统DLL中的函数,这些函数一般情况下会进一步调用Ntdll模块中的系统函数。ntdll中通过上述内容切换到内核层,调用内核层的系统服务来完成应用程序的请求。

举例说明:CreateFile函数

我们在用户层调用CreateFile函数实际上该函数是从Kernel32.dll模块中导出的函数。createfile函数内部进一步调用Ntdll.dll中的NtCreateFile函数。该函数只是简单的处理。并将处理完后的函数交给内核层NtCreateFile函数来处理。在ntdll中的函数中通过KiFastSystemCall函数执行int2e或者sysenter俩进入内核层。然后进入内核层的KisystemService来调用NtCreateFile函数。

在这里插入图片描述

在用户层中和内核层中都存在两套函数分别为ZwCreateFile和NtCreateFile函数。但是在内核层与用户层中函数模式不同。具体情况如下:

在用户层中:ZwCreatefile函数与NtCreateFile函数完全相同。

0:035> u ZwCreateFile
ntdll!ZwCreateFile:
77415608 b842000000      mov     eax,42h
7741560d ba0003fe7f      mov     edx,offset SharedUserData!SystemCallStub (7ffe0300)
77415612 ff12            call    dword ptr [edx]
77415614 c22c00          ret     2Ch
77415617 90              nop
/************************************************************/ /************************************************************/
0:035> u NtCreateFile
ntdll!ZwCreateFile:
77415608 b842000000      mov     eax,42h
7741560d ba0003fe7f      mov     edx,offset SharedUserData!SystemCallStub (7ffe0300)
77415612 ff12            call    dword ptr [edx]
77415614 c22c00          ret     2Ch
77415617 90              nop 

但是在内核层中两套函数并不一样,实际上是zw函数只是简单的进行参数的校验和处理,然后底层调用NtCreateFile函数进行处理。

0: kd> u ZwCreateFile
nt!ZwCreateFile:
84849328 b842000000      mov     eax,42h
8484932d 8d542404        lea     edx,[esp+4]
84849331 9c              pushfd
84849332 6a08            push    8
84849334 e885230000      call    nt!KiSystemService (8484b6be)
84849339 c22c00          ret     2Ch
nt!ZwCreateIoCompletion:
8484933c b843000000      mov     eax,43h
84849341 8d542404        lea     edx,[esp+4]
/************************************************************/
/************************************************************/
0: kd> u NtCreateFile
nt!NtCreateFile:
84a5e470 8bff            mov     edi,edi
84a5e472 55              push    ebp
84a5e473 8bec            mov     ebp,esp
84a5e475 51              push    ecx
84a5e476 33c0            xor     eax,eax
84a5e478 50              push    eax
84a5e479 6a20            push    20h
84a5e47b 50              push    eax

在Windows的虚拟地址空间中,系统有一个特殊的页面会被用户空间和内核空间地址共享,在内核模式中该位置为0xffdf0000,即KI_USER_SHARED_DATA,在用户模式中该地址位0x7ffe0000,即宏定义MM_SHARED_USER_DATA_VA。此页面共享了当前系统重的一些关键信息。

Windows中的系统服务分发

在ntdll中系统服务存根函数指定了一个系统服务号,在内核模式下的_KiSystemServiceRepeat根据此系统服务号得知该调用系统中的哪个服务例程以及拷贝多少数据到内核栈。这一过程被称为系统服务分发。

在Windows内部有一个系统服务表为SSDT表(System Service Descriptor Table),该表项中保存了系统服务,在x86系统中全局变量KeServiceDescriptorTable导出变量指向了该表的位置。其中的每一个元素指向一个SSDT结构。结构如下:

typedef struct _SYSTEM_SERVICE_DESCRIPTOR_TABLE
{
    PULONG_PTR ServiceTableBase;   //指针数组
    PULONG_PTR ServiceCounterTableBase;
    ULONG_PTR NumberOfServices;    //多少个函数
    PULONG_PTR ParamterTableBase;
} SYSTEM_SERVICE_DESCRIPTOR_TABLE, * PSYSTEM_SERVICE_DESCRIPTOR_TABLE;
  • ServiceTableBase指向了一个由熊服务例程构成的32位地址数组。
  • ServiceCounterTableBase是一个指针成员,指向了一个计数器数组,数组中的每个元素记录了相应的系统服务被调用的次数。
  • NumberOfServices记录了SSDT表中服务的数量。
  • ParamterTableBase记录了每个服务所需传递参数的长度,长度!!!不是个数

用户层的代码最终调用到这些系统调用中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值