以ReadFile为例
ntdll.dll导出了ZwReadFile和NtReadFile
在用户态
不管你调用ZwReadFile还是NtReadFile都是一样的
因为他们是同一个函数的两个不同名称而已....
而且他们最终都会调用到ntoskrnl中的NtReadFile中去
在内核态
ntoskrnl.exe导出了ZwReadFile和NtReadFile
Ntoskrnl导出的NtReadFile是真正的执行函数,ZwReadFile是一个stub函数
内核态调用ZwReadFile,会将Previous Mode设置为Kernel Mode,然后再调用NtReadFile
而在内核态直接调用NtReadFile,不会改变Previous Mode。
NtReadFile中会检测当前调用来自用户态还是内核态
如果是来自内核态,不会检测参数;而如果是来自用户态,就会做一系列的参数检测。
我们知道,内核组件可能运行在任意进程的上下文中,当它调用NtReadFile时,因为Previous Mode很可能是User Mode,
而我们的参数请求的内核态的地址,这时通常就会产 STATUS_ACCESS_VIOLATION 错误
所以内核态一般用Zw*系列的函数
关于Nt*与Zw*更为详细的介绍,请参考 Nt vs. Zw - Clearing Confusion On The Native API