Windows本地操作系統服務API由一系列以Nt或Zw為前綴的函數實現的,這些函數以內核模式運行,內核驅動可以直接調用這些函數,而用戶層程序只能通過系統進行調用。通常情況下用戶層應用程序不會直接調用Nt和Zw系函數,更多的是通過直接調用Win32函數,這些Win32函數內部會調用Nt和Zw系函數,但也僅限於通常情況下,當Win32函數不支持一些操作時,用戶層也會直接調用這些本地系統服務函數。
Nt前綴是Windows NT的縮寫,但Zw前綴並沒有任何意義,使用Zw只是避免跟其他已存在和未來可能出現的API有命名沖突而已。很多Windows驅動支持函數都以兩到三個特定的簡稱字母為前綴進行命名,以此來表示這些例程都是由哪些內核系統組件實現的,比如CmRegisterCallbackEx中的Cm就表示配置管理器(Configuration manager)
每個本地系統服務例程都有兩個有着不同前綴的相似名稱的函數版本,比如NtCreateFile和ZwCreateFile,兩者執行相同的操作,並且事實上兩者也都服務於相同的內核模式系統例程。對於用戶層的系統調用,Nt和Zw系函數是沒有什么區別的,但對於來自於內核驅動的調用,Nt和Zw系函數對傳入參數的處理方式有些不一樣。
如果傳入參數是來自於可信任的內核層,那么內核模式驅動則調用Zw版本的本地系統服務例程來通知其他例程,在這種情況下,例程都是不經過驗證就直接使用這些參數。反而,如果這些參數可能來自用戶層或者內核層,那么驅動則調用Nt版本的例程,這取決於調用線程的歷程——這些參數是從用戶層還是內核層發起的,線程對象中有個PreviousMode的屬性可用於判斷參數是否從用戶層過來的,關於例程如何判斷參數是來自用戶層還是內核層,詳細內容請參見預先模式
當一個用戶層應用程序調用Nt或Zw系函數,這些本地系統服務函數始終會認為它接收到的參數來自於不可信任的用戶層,在使用前必先驗證參數的有效性。特別是對於由調用者提供的緩存區,這些函數將會探測其內存地址是否有效並且是否正常對齊。
本地系統服務例程對於接收到參數值還會做額外的設定。如果一個例程接收到一個由指向由內核驅動分配的緩存區指針,它會認為這緩存區是從系統內存而不是從用戶層內存分配的,如果例程接收到一個由用戶層應用程序打開的句柄類型參數,那么例程就會從用戶層句柄表中查找句柄而不是從內核層。
在一些情況下,從用戶層調用還是從內核層調用對傳入參數的意義和后續的使用影響重要。比如說ZwNotifyChangeKey(或說NtNotifyChangeKey)這個函數,其中有兩個輸入參數ApcRoutine和APCContext,從用戶層和從內核層傳過來分別代表不同的意義。如果其從用戶層被調用,ApcRoutine指向一個APC例程,ApcContext則指向一個由操作系統在調用APC例程時分配的上下文;如果其從內核層被調用,ApcRoutine指向一個WORK_QUEUE_ITEM結構,而ApcContext則表示WORK_QUEUE_ITEM隊列項的類型。
用戶層不支持調用Zw系函數,而在內核層調用Zw系函數時,上面也稍微提到過,系統不檢測調用者的訪問權限,調用之前必須檢測從用戶模式下傳來的參數的有效性
大多數Zw 系函數的聲明在Wdm.h中可以找得到,少部分散落在其他頭文件里如Ntddk.h和Ntifs.h
用戶層可通過引用Ntdll.lib靜態庫(在WDK中可以找到)來調用這些本地系統服務例程,大多數文檔化的Nt系函數聲明在Windows SDK的Winternl.h頭文件中,對於未文檔化的Nt系函數,微軟一直不建議開發者進行調用,因為在未來的Windows版本中這些函數接口可能會有所改動或者直接被廢除,這對使用了這些未文檔化函數的應用程序的穩定運行造成一定的影響,但往往是這些未文檔化的函數和結構體能夠獲取更多的系統權限,這也是眾多的Windows應用開發者不聽勸告反而樂此不疲地去挖掘的原因。
內核驅動可通過調用Nt和Zw在Ntoskrnl.exe的動態鏈接庫的入口點(entry points)來使用這些本地系統服務例程的,該DLL(動態鏈接庫)包含這些服務例程的具體實現,要訪問這些入口點,驅動程序需要靜態鏈接到Ntoskrnl.lib(在WDK中也可以找到)
對於Nt*Xxx* and Zw*Xxx* 的具體函數列表可查看此處
