Yet another look at Zw* and Nt* functions
While reading the new book “Professional Rootkits” by Ric Vieler I encountered the following macro definition to get function index in system service table:
#define HOOK_INDEX(function2hook) *(PULONG)((PUCHAR)function2hook+1)
Couldn’t understand the code until looked at disassembly of a typical ntdll!Zw and nt!Zw function (x86 W2K3):
lkd> u ntdll!ZwCreateProcess
ntdll!NtCreateProcess:
7c821298 b831000000 mov eax,31h
7c82129d ba0003fe7f mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
7c8212a2 ff12 call dword ptr [edx]
7c8212a4 c22000 ret 20h
7c8212a7 90 nop
ntdll!ZwCreateProcessEx:
7c8212a8 b832000000 mov eax,32h
7c8212ad ba0003fe7f mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
7c8212b2 ff12 call dword ptr [edx]
lkd> u nt!ZwCreateProcess
nt!ZwCreateProcess:
8083c2a3 b831000000 mov eax,31h
8083c2a8 8d542404 lea edx,[esp+4]
8083c2ac 9c pushfd
8083c2ad 6a08 push 8
8083c2af e8c688ffff call nt!KiSystemService (80834b7a)
8083c2b4 c22000 ret 20h
nt!ZwCreateProcessEx:
8083c2b7 b832000000 mov eax,32h
8083c2bc 8d542404 lea edx,[esp+4]
You can notice that user space ntdll!Nt and ntdll!Zw variants are the same. This is not the case in kernel space:
lkd> u nt!NtCreateProcess
nt!NtCreateProcess:
808f80ea 8bff mov edi,edi
808f80ec 55 push ebp
808f80ed 8bec mov ebp,esp
808f80ef 33c0 xor eax,eax
808f80f1 f6451c01 test byte ptr [ebp+1Ch],1
808f80f5 0f8549d10600 jne nt!NtCreateProcess+0xd (80965244)
808f80fb f6452001 test byte ptr [ebp+20h],1
808f80ff 0f8545d10600 jne nt!NtCreateProcess+0×14 (8096524a)
nt!Zw functions are dispatched through service table. nt!Nt functions are actual code.
For completeness let’s look at AMD x64 W2K3. User space x64 call:
0:001> u ntdll!ZwCreateProcess
ntdll!NtCreateProcess:
00000000`78ef1ab0 4c8bd1 mov r10,rcx
00000000`78ef1ab3 b882000000 mov eax,82h
00000000`78ef1ab8 0f05 syscall
00000000`78ef1aba c3 ret
00000000`78ef1abb 666690 xchg ax,ax
00000000`78ef1abe 6690 xchg ax,ax
ntdll!NtCreateProfile:
00000000`78ef1ac0 4c8bd1 mov r10,rcx
00000000`78ef1ac3 b883000000 mov eax,83h
User space x86 call in x64 W2K3:
0:001> u ntdll!ZwCreateProcess
ntdll!ZwCreateProcess:
7d61d428 b882000000 mov eax,82h
7d61d42d 33c9 xor ecx,ecx
7d61d42f 8d542404 lea edx,[esp+4]
7d61d433 64ff15c0000000 call dword ptr fs:[0C0h]
7d61d43a c22000 ret 20h
7d61d43d 8d4900 lea ecx,[ecx]
ntdll!ZwCreateProfile:
7d61d440 b883000000 mov eax,83h
7d61d445 33c9 xor ecx,ecx
Kernel space call in x64 W2K3:
kd> u nt!ZwCreateProcess nt!ZwCreateProcess+20
nt!ZwCreateProcess:
fffff800`0103dd70 488bc4 mov rax,rsp
fffff800`0103dd73 fa cli
fffff800`0103dd74 4883ec10 sub rsp,10h
fffff800`0103dd78 50 push rax
fffff800`0103dd79 9c pushfq
fffff800`0103dd7a 6a10 push 10h
fffff800`0103dd7c 488d057d380000 lea rax,[nt!KiServiceLinkage (fffff800`01041600)]
fffff800`0103dd83 50 push rax
fffff800`0103dd84 b882000000 mov eax,82h
fffff800`0103dd89 e972310000 jmp nt!KiServiceInternal (fffff800`01040f00)
fffff800`0103dd8e 6690 xchg ax,ax
kd> u nt!NtCreateProcess
nt!NtCreateProcess:
fffff800`01245832 53 push rbx
fffff800`01245833 4883ec50 sub rsp,50h
fffff800`01245837 4c8b9c2488000000 mov r11,qword ptr [rsp+88h]
fffff800`0124583f b801000000 mov eax,1
fffff800`01245844 488bd9 mov rbx,rcx
fffff800`01245847 488b8c2490000000 mov rcx,qword ptr [rsp+90h]
fffff800`0124584f 41f6c301 test r11b,1
fffff800`01245853 41ba00000000 mov r10d,0
Here is the same as in kernel x86: Zw functions are dispatched but Nt functions are actual code. If you want to remember which function variant is dispatched and which is actual code I propose the mnemonic “Z-dispatch”.
- Dmitry Vostokov -
June 21st, 2007 at 10:55 pm
Dmitry:
Great analysis of HOOK_INDEX!
I just wanted to let you know that this type of kernel hooking is now detectable by most anti-rootkit software. Newer rootkits should follow the call table to the actual kernel function and replace the first few instructions with a jump to a trampoline function that can call hooks before and after the original function. As an additional benifit, trampoline kernel hooking works with the Microsoft Vista operating system.
Ric Vieler
June 21st, 2007 at 11:16 pm
Thanks! Do you have any plans to write a second edition of your book and update it for x64 and Vista?
That would be great!
Dmitry
July 28th, 2009 at 5:47 pm
[…] Zw (no parameter validation) vs. Nt (p. 73) - here is another look at their difference: http://www.dumpanalysis.org/blog/index.php/2007/04/10/yet-another-look-at-zw-and-nt-functions/ […]
August 12th, 2009 at 5:08 pm
[…] Zw… as fake interrupts (p. 129) - here is another view (remember that there are ntdll!Nt… and nt!Nt… functions): http://www.dumpanalysis.org/blog/index.php/2007/04/10/yet-another-look-at-zw-and-nt-functions/ […]