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 I want to achieve as the final goal is to provide a syscall interface in my 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. 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 |
After some debuggung and reading
https://www.crowdstrike.com/blog/state-of-exploit-development-part-1/ and https://www.crowdstrike.com/blog/state-of-exploit-development-part-2/ I found the solution, it was quite trivial, I just had to disable "Control Flow Guard" for the one file doing this calls, LOL. Ofcause a better solution would be to create a hand crafted trampoline instead, but well... some times its efficient to be lazy. |
and here we have the not lazy solution:
do_call.asm Code:
Code:
#include <stdio.h> |
I ran into a really strange issue, for some impossible reason the above code on windows 64 bit, while working fine when the app is 64 bit, fails if its a 32 bit app running under WOW64.
To my understanding this should not be possible as under WOW64 the 32 to 64 translation is done in user mode and the falls to the kernel are all 64 bit so from the kernels viewpoint everything is 64 bit. I have monkeyed around with the code and narrowed the issue down to calls with 4 or less and 5 arguments for 6 and more the code works fine. for 0-4 I crafted a different code that seams to work fine Code:
Sbie_InvokeSyscall4_asm PROC when I use C code to implement a caller for the 5 args case Code:
typedef NTSTATUS (*P_SystemService05)( Code:
Sbie_InvokeSyscall5_asm PROC And yes I have looked in IDA on the driver with the asm version and its byte for byte same as the output of the C compiler, I even tried adding some int 3 before and after to have it aligned the same way with no result. The only apparent difference is that its located in a different plaice of the driver image file. Now the way the 32 bit apps fail is also quiet peculiar, they all fail with an access violation at location 0x0...0 and an empty stack trace, somehow the return from the syscall gets messed up and I have no idea how. |
Sounds like details of calling conventions. Perhaps the stack isn't aligned to 16 bytes in the driver or something like that. I suspect the driver code more than the app code here. The WoW64 is just amplifying a preexisting bug that us perhaps extraordinarily unlikely or impossible on native 64.
App is easy to debug anyway you can check stack and register at time of call and return value immediately after. The driver is much more troublesome to debug. And for that reason it's always where the bugs end up, just to inconvenience us :) Anyway it would be interesting to know the details of this if you figure it out. |
I have created a other test, where with a global variable i can toggle between the c and the asm version thats how it looks in code
Code:
.text:000000014001C66A mov eax, cs:g_test I checked again that Sbie_InvokeSyscall5_asm and Sbie_InvokeSyscall5 are binary same, and they are. Still toggling the variable breaks 32 bit apps. At this point its just wired, I mean the "calling" convention is the same and the functions are the same yet the result is not, WTF :/ |
Can you share the binaries?
|
Sure: [s]https://www10.zippyshare.com...[/s] PDB included
The dispatch function that calls the asm or c versions is called Syscall_Invoke its called from a couple places but most relevant from Syscall_Api_Invoke |
I have found the solution, I needed to add the FRAME, .allocstack and .endprolog
So in the end it was some sort of alignment issue or the compile puts some additional data some ware else that were relevant. Code:
|
Yes the calling convention on 64bit has oddities with 16 byte alignment on calls, shadow spaces needing to be reserved and such. With driver calls it's not surprising these become necessary. The ABI convention is slightly different between Windows and Linux as well.
Quote:
Quote:
|
All times are GMT +8. The time now is 09:00. |
Powered by vBulletin® Version 3.8.8
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Always Your Best Friend: Aaron, JMI, ahmadmansoor, ZeNiX