今天在这里主要想了解一下Windows如何从ring3下的Win32 API转到ring0下的Kernel Routine。
以NtReadFile为例:
kd> u ntdll!NtReadFile (Win 2003 SP1)
ntdll!ZwReadFile:
7c821b78 b8bf000000 mov eax,0BFh;(系统调用号)
7c821b7d ba0003fe7f mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
7c821b82 ff12 call dword ptr [edx]
可以看到NtReadFile被WinDBG自动转成了ZwReadFile,其实这两个函数都在ntdll中导出,实际指向的是同一个地址(7c821b78)。在kernel模式下对应的Routine也叫NtReadFile
kd> u nt!NtReadFile
nt!NtReadFile [e:/windowsresearchkernel-wrk/wrk-v1.2/base/ntos/io/iomgr/read.c @ 90]:
808e8552 6a58 push 58h
808e8554 68b8318080 push offset nt!GUID_DOCK_INTERFACE+0x34c (808031b8)
808e8559 e852c9f8ff call nt!__SEH_prolog (80874eb0)
808e855e 33f6 xor esi,esi
808e8560 8975e0 mov dword ptr [ebp-20h],esi
808e8563 8975d0 mov dword ptr [ebp-30h],esi
…………
但是kernel中也有一个叫ZwReadFile的函数
kd> u nt!ZwReadFile
nt!ZwReadFile [E:/WindowsResearchKernel-WRK/WRK-v1.2/base/ntos/ke/i386/sysstubs.asm @ 1644]:
8082cbd4 b8bf000000 mov eax,0BFh;(系统调用号)
8082cbd9 8d542404 lea edx,[esp+4]
8082cbdd 9c pushfd
8082cbde 6a08 push 8
8082cbe0 e8a55b0500 call nt!_KiSystemService (8088278a)
8082cbe5 c22400 ret 24h
这个函数其实是个stub,不做真正的事情,最终仍然要调用NtReadFile,所以内核中的NtReadFile才是真正干活的有用的函数!!
回到ntdll下的NtReadFile(ZwReadFile),0BFh是win 2003下的系统调用号(系统调用号其实就是一个索引,后面会讲到)。SharedUserData 是操作系统为每个进程提供的个共享数据结构,里面存放有很多重要的系统信息,如TickCount、系统时间、SystemRoot等……
其在DDK定义为:
#define KI