Why do Zw calls go through KiSystemService?

As far as I understand, the purpose of Zw calls is to perform the following:

  • set the previous mode to KernelMode
  • invoke the system service
  • restore the previous mode

This question can be divided to multiple sub-questions:

  • Why do Zw calls go through the SSDT? The address can be hardcoded into the Zw stub
  • Why do Zw calls create a new trap frame?

My best guess to this is: Because the Zw stub needs to run code after the system service (the code that restores the previous mode) it needs to copy the arguments of the caller. KiSystemService already has this functionality (via KiArgumentTable)… This still does not explain why it allocates a new trap frame though. I know specific services like NtRaiseException require a trap frame, maybe that’s the reason…?

I’m asking this out of curiosity and for my understanding of Zw calls, Thanks for answering!

Except… that’s not really how Zw calls work.

Zw calls work from user mode OR kernel mode. They actually set the previous mode to… whatever the previous mode actually WAS. This is different to the Nt calls that ARE direct calls that leave the previous mode unchanged.

Peter

Zw calls work from user mode OR kernel mode. They actually set the previous mode to… > whatever the previous mode actually WAS.

Maybe my question was unclear… I’m talking only about kernel mode here. Undoubtedly if ZwCreateFile is called and CurrentThread->PreviousMode == UserMode, the PreviousMode will be changed to ‘KernelMode’ during the NtCreateFile call as can be seen in the following experiment:

kd> bp nt!ZwCreateFile
kd> g
Breakpoint 0 hit
nt!ZwCreateFile:
fffff801`0edb2670 488bc4          mov     rax,rsp
kd> k
 # Child-SP          RetAddr           Call Site
00 ffffa404`9411e0e8 fffff801`0f196c8d nt!ZwCreateFile
01 ffffa404`9411e0f0 fffff801`0f1f2f9f nt!CmpOpenHiveFile+0x151
02 ffffa404`9411e270 fffff801`0f193839 nt!CmLoadAppKey+0x17f
03 ffffa404`9411e6c0 fffff801`0f1931ad nt!CmLoadDifferencingKey+0x681
04 ffffa404`9411ea20 fffff801`0edc6cbe nt!NtLoadKeyEx+0x4d
05 ffffa404`9411ea90 00007ffb`b1ff16c4 nt!KiSystemServiceExitPico+0x2b9
06 00000092`342fe588 00007ffb`aedeb665 0x00007ffb`b1ff16c4
07 00000092`342fe590 006f0043`002e0073 0x00007ffb`aedeb665
08 00000092`342fe598 00000000`00000000 0x006f0043`002e0073
kd> dx @$curthread.KernelObject.Tcb.PreviousMode
@$curthread.KernelObject.Tcb.PreviousMode : 1 [Type: char]
kd> dx (void*)@$curthread.KernelObject.Tcb.TrapFrame
(void*)@$curthread.KernelObject.Tcb.TrapFrame : 0xffffa4049411eb00 [Type: void *]
kd> .thread
Implicit thread is now ffffbc08`78821080
kd> bp /t ffffbc08`78821080 nt!NtCreateFile
kd> g
Breakpoint 1 hit
nt!NtCreateFile:
fffff801`0f1f04e0 4881ec88000000  sub     rsp,88h
kd> dx (void*)@$curthread.KernelObject.Tcb.TrapFrame
(void*)@$curthread.KernelObject.Tcb.TrapFrame : 0xffffa4049411df50 [Type: void *]
kd> dx @$curthread.KernelObject.Tcb.PreviousMode
@$curthread.KernelObject.Tcb.PreviousMode : 0 [Type: char]

I’m aware that the previous mode is restored after NtCreateFile finishes running to the value is was before the ZwCreateFile call, which in this case is UserMode.

The question was: why do we need a new trap frame and also why does ZwCreateFile go through the SSDT?