当前位置:首页 > Windows程序 > 正文

windows的系统调用

2021-03-29 Windows程序

windows系统调用的具体流程在潘爱民老师的《WINDOWS内核原理与实现》中的第8章已经写得很清楚了,先看书中给出的这幅图。

以CreateFile为例,在ring3的CreateFile进行了一些参数检查后最终调用的是Ntdll中的NtCreateFile。同时也有ZwCreateFile,不过它们的地址指向同一区域,所以本质上来说是同一个函数。

可以再ntdll的导出表中看到:

之后通过sysenter或者0x2e中断进入ring0层,并将服务号放入eax中。

ntoskrnl.exe 中的 zwcreatefile:

kd> u nt!zwcreatefile
nt!ZwCreateFile:
80501010 b825000000   mov eax,25h    ;服务号
80501015 8d542404       lea edx,[esp+4]
80501019 9c                  pushfd
8050101a 6a08               push 8
8050101c e830140400    call nt!KiSystemService (80542451)
80501021 c22c00            ret 2Ch

使用int 0x2e进入ring0

使用windbg可以直接查看idt的0x2e号中断:

kd>!idt 2e

Dumping IDT:

2e: 80542451 nt!KiSystemService  ;可以看到指向的是KiSystemService这个例程

当然,也可以通过查看idt表计算出0x2e中断所指向的地址,首先找到idt表中0x2e项的内容:

kd> !pcr
KPCR for Processor 0 at ffdff000:
Major 1 Minor 1
NtTib.ExceptionList: 80551cb0
NtTib.StackBase: 805524f0
NtTib.StackLimit: 8054f700
NtTib.SubSystemTib: 00000000
NtTib.Version: 00000000
NtTib.UserPointer: 00000000
NtTib.SelfTib: 00000000

SelfPcr: ffdff000
Prcb: ffdff120
Irql: 00000000
IRR: 00000000
IDR: ffffffff
InterruptMode: 00000000
IDT: 8003f400          ;IDT表的地址
GDT: 8003f000
TSS: 80042000

CurrentThread: 8055ce60
NextThread: 00000000
IdleThread: 8055ce60

计算一下  8003f400 + 8*0x2e = 8003F570

查看:

kd> db 8003F570
8003f570    51 24 08 00 00 ee 54 80-e0 57 08 00 00 8e 54 80    Q$....T..W....T.
8003f580    10 1b 08 00 00 8e 54 80-1a 1b 08 00 00 8e 54 80    ......T.......T.
8003f590    24 1b 08 00 00 8e 54 80-2e 1b 08 00 00 8e 54 80    $.....T.......T.
8003f5a0    38 1b 08 00 00 8e 54 80-42 1b 08 00 00 8e 54 80    8.....T.B.....T.

根据IDT中断描述符的格式,可以知道该例程偏移为 80542451  ,段选择符为0x8  (IDT结构的内容在《WINDOWS内核原理与实现》中的第5章有讲解)

知道了偏移地址,还需要知道段地址才可以计算出实际的地址。

已经知道了段选择符为0x8,可以查看相应的段描述符:

其中,TI为0表示改索引指向GDT,RPL为0表示当前特权级是0,索引代表它在GDT中的位置是第一项。

(这一部分的知识在书中第4章有介绍,但不是十分详细,具体内容可以查阅一些保护模式的资料)

查看GDT:

kd> r gdtr
gdtr=8003f000   ;也可以使用!pcr指令找到GDT地址

kd> db 8003f000
8003f000    00 00 00 00 00 00 00 00-ff ff 00 00 00 9b cf 00         ................
8003f010    ff   ff  00 00 00 93 cf  00-ff ff 00 00 00 fb  cf 00        ................

由段描述格式获得基地址为 0x0000  

计算出中断例程的地址为 0000 : 80542451  与windbg直接获得的地址一样。

通过sysenter进入ring0

先看sysenter指令的调用位置:

kd> u ntdll!KiFastSystemCall
ntdll!KiFastSystemCall:
770801d0 8bd4    mov edx,esp
770801d2 0f34     sysenter

ntdll!KiFastSystemCallRet:
770801d4 c3            ret
770801d5 8da42400000000    lea esp,[esp]
770801dc 8d642400        lea esp,[esp]

sysenter指令的工作原理是读取MSR寄存器,加载RING0层的CS,EIP,ESP,清楚Eflags中的VM标示.

IA32_SYSENTER_CS   0x174

IA32_SYSENTER_ESP   0x175

IA32_SYSENTER_EIP  0x176

使用windbg查看:

kd> rdmsr 174
msr[174] = 00000000`00000008
kd> rdmsr 175
msr[175] = 00000000`f8ac2000
kd> rdmsr 176
msr[176] = 00000000`80542520

CS段选择符为0x00000008与之前的一样,所以目标地址为  0000:80542520

反汇编此地址:

kd> u 80542520
nt!KiFastCallEntry:
80542520 b923000000    mov ecx,23h
80542525 6a30        push 30h
80542527 0fa1         pop fs
80542529 8ed9        mov ds,cx
8054252b 8ec1        mov es,cx
8054252d 648b0d40000000 mov ecx,dword ptr fs:[40h]
80542534 8b6104       mov esp,dword ptr [ecx+4]

KiFastCallEntry就是进入RING0的调用例程。

其实对KiSystemService的反汇编进行查看会发现,KiSystemService最终还是调用了KiFastCallEntry的例程:

温馨提示: 本文由Jm博客推荐,转载请保留链接: https://www.jmwww.net/file/69893.html