Invoking Win32k syscalls from kernel space
I have noticed that while calling ntoskrnl.exe/ntdll.dll syscalls directly from the kernel works just fine, doing the same for win32k.sys/win32u.dll syscalls however fails when HVCI is enabled with the bug check 139, with the additional hint: Arg1: 0 - A stack-based buffer has been overrun.
Which is strange, as why would it work with HVCI enabled for the kernel itself but not for the win32 stuff? That would be problem 1.
Another issue is that before calling the first win32k sys call KiConvertToGuiThread must be triggered, unfortunately neither this nor PsConvertToGuiThread is exported, by the kernel. And this would be problem 2.
What we want to achieve as the final goal is to provide a syscall interface in our driver that an application could call, the driver would than do something in the kernel space, invoke the actual syscall, then do something else, before finally returning control to the user mode application.
“What is this good for?” - you may ask.
This mechanism is used by Sandboxie (https://github.com/sandboxie-plus/Sandboxie) for almost all ntoskrnl.exe syscalls to allow the driver to inspect the operations before they get executed. This was in older versions of windows only needed for ntdll.dll but unfortunately some win32k.sys syscalls also require this treatment. For sandboxed applications to keep working correctly, for example GdiDdDDI* are used by chromium/msedge and only operate correctly for sandboxed processes when redirected through this syscall interface. Without those functions working the HW acceleration is not available and at least on win 11 this causes UI glitches.
The syscalls redirection is done in user space by hooking ntdll.dll and now win32u.dll by processes that start in the sandbox. The driver only needs to provide an interface to invoke the original syscall.
So, the application now instead of doing a syscall directly calls a routine provided by the injected dll specifying the syscall number this routine than calls using a clean copy of NtDeviceIOControlFile the driver passing it the sys call number and a pointer to that stack containing the arguments. The driver then inspects the request and if found permissible, makes the thread impersonate a less restrictive token, calls the address of the syscall routine (obtained during initialization from the service table) the those executing the syscall, once that’s finished it de-impersonates the thread, back to its highly restricted primary token, and returns.
So as one can see a process running under sandboxie supervision with its highly restricted primary token, couldn’t do much when it would bypass the syscall interface for example by loading a unmodified copy of ntdll.dll it would just run into access denied almost every ware.
In the past this wasn’t an issue for win32k syscalls once the helper service helped the restricted process to connect to the desktop object. But as mentioned some new win32k functions don’t want to operate as desired when restricted.
So just to clarify it we are not messing with the kernel the SSDT or any unsandboxed process.
Problem 2 could be successfully ignored as other un redirected win32k syscalls would have been executed at this point already.
But problem 1 is in urgent need of fixing and I’m out of ideas why this would not work with HVCI enabled for the new set of syscalls. The redirection is implemented in the same exact way as the old working one and without HVCI it works just fine.
Anyone here knowing whats going on and how to fix it?
imho the best would be a way to actually invoke the original syscall from the kernel such that it does everything including KiConvertToGuiThread , but I’m not sure if that is even possible.
Cheers
David