首先他們都是微軟未公開的函數,之所以未公開主要是因為這些函數大部分功能太強大了,把他們公開會讓一些別有用心的人利用。9x下的我不知道,NT(含2000/xp)下你可以參考《Windows NT Native API》,他們中的大部分函數幾乎就從來沒有變過。而幾乎所有從Kenerl.dll中引出的Win32API,都是通過調用的Native API(NTDLL.DLL中匯出)實現系統調用的。舉一個例子:NtQuerySystemInformation 這個函數就強大到可以查詢所有的系統資訊,使用時需要使用者擁有相當高的存取權限。
in ring3:
lkd> ? ntdll!ZwOpenProcess
Evaluate expression_r: 2089999739 = 7c92dd7b
lkd> ? ntdll!NtOpenProcess
Evaluate expression_r: 2089999739 = 7c92dd7b
用dependency也可看到。
可以看到,在ntdll中,ZwOpenProcess和NtOpenProcess其實是同一個函數,只不過擁有兩個名稱而已。
也就是說,在ring3環境中,Zw***系列函數和Nt***系列函數無區別。
in ring0:
lkd> u nt!ZwOpenProcess
nt!ZwOpenProcess:
804de044 b87a000000 mov eax,7Ah
804de049 8d542404 lea edx,[esp+4]
804de04d 9c pushfd
804de04e 6a08 push 8
804de050 e8dc150000 call nt!KiSystemService (804df631)
804de055 c21000 ret 10h
lkd> u nt!NtOpenProcess
nt!NtOpenProcess:
80573d06 68c4000000 push 0C4h
80573d0b 6810b44e80 push offset nt!ObWatchHandles+0x25c (804eb410)
80573d10 e826f7f6ff call nt!_SEH_prolog (804e343b)
80573d15 33f6 xor esi,esi
80573d17 8975d4 mov dword ptr [ebp-2Ch],esi
80573d1a 33c0 xor eax,eax
80573d1c 8d7dd8 lea edi,[ebp-28h]
80573d1f ab stos dword ptr es:[edi]
可以看得出,ZwOpenProcess函數很短,首先把0x7a(NtOpenProcess的服務號)存入eax,然後做一些儲存現場的工作即KiSystemService——這個函數根據eax中的service id在SSDT中尋找相應的系統服務,然後調用之。
而NtOpenProcess函數很長(反組譯碼結果只是一部分),事實上,NtOpenProcess便是真正執行開啟進程操作的函數(在r0中通常稱為服務,或常式),所以,若在驅動中直接調用Nt系列函數,是不會經過SSDT的,也就不會被SSDT HOOK所攔截。
簡單總結如下:
R3下無論如何調用,均無法繞過SSDT HOOK,R0下調用Nt*可以繞過SSDT HOOK。rtl** 函數是windows ddk提供的編寫驅動的函數。
我的總結:
由以上反組譯碼Zw***可知,Zw***代碼內部是通過SSDT中的對應Nt***索引號尋找到真正Nt***執行代碼的入口地址。
大多數win32的api函數都是封裝了這些zwxxx api和ntxxx api實現的。
在使用者模式下,這兩種形式的本機api是相同的,只是ntdll.dll中相同進入點的兩個不同符號。
在核心模式下,代碼依靠ntoskrnl.exe來串連,不再是ntdll.dll。這是,zwxxx的函數進入點含有來自於ntdll.dll的一份拷貝。ntxxx的函數進入點則含有系統服務的實際實現。
【參考資料 感謝作者】
1、Nt**、Zw**和Rtl** 開頭的函數介紹