A Deep Dive into the GetProcessHandleFromHwnd API
In my previous blog post I mentioned the GetProcessHandleFromHwnd API. This was an API I didn’t know existed until I found a publicly disclosed UAC bypass using the Quick Assist UI Access application. This API looked interesting so I thought I should take a closer look. I typically start by reading the documentation for an API I don’t know about, assuming it’s documented at all. It can give you an idea of how long the API has existed as well as its security properties. The documentation’s remarks contain the following three statements that I thought were interesting: If the caller has UIAccess, however, they can use a windows hook to inject code into the target process, and from within the target process, send a handle back to the caller. GetProcessHandleFromHwnd is a convenience function that uses this technique to obtain the handle of the process that owns the specified HWND. Note that it only succeeds in cases where the caller and target process are running as the same user. The interesting thing about these statements is none of them are completely true. Firstly as the previous blog post outlined it’s not sufficient to have UI Access enabled to use windows hooks, you need to have the same or greater integrity level as the target process. Secondly, if you go and look at how GetProcessHandleFromHwnd is implemented in Windows 11 it’s a Win32k kernel function which opens the process directly, not using windows hooks. And finally, the fact that the Quick Assist bypass which uses the API still works with Administrator Protection means the processes can be running as different users. Of course some of the factual inaccuracies might be changes made to UAC and UI Access over the years since Vista was released. Therefore I thought it’d be interesting to do a quick bit of code archaeology to see how this API has changed over the years and perhaps find some interesting behaviors. The First Version The first version of the API exists in Vista, implemented in the oleacc.dll library. The documentation claims it was supported back in Windows XP, but that makes little sense for what the API was designed for. Checking a copy of the library from XP SP3 doesn’t show the API, so we can assume the documentation is incorrect. The API first tries to open the process directly, but if that fails it’ll use a windows hook exactly as the documentation described. The oleacc.dll library with the hook will be loaded into the process associated with the window using the SetWindowsHookEx API and specifying the thread ID parameter. However it still won’t do anything until a custom window message, WM_OLEACC_HOOK is sent to the window. The hook function is roughly as follows (I’ve removed error checking): void HandleHookMessage ( CWPSTRUCT * cwp ) { UINT msg = RegisterWindowMessage ( L"WM_OLEACC_HOOK" ); if ( cwp - message != msg ) return ; WCHAR name [ 64 ]; wParam = cwp - wParam ; StringCchPrintf ( name , _countof ( name ), L"OLEACC_HOOK_SHMEM_%d_%d" , wParam , cwp - lParam );
Sign in to read the full article
Create a free account to access all news, downloads, and community features
Originally published by Google Project Zero
Source: https://projectzero.google/2026/02/gphfh-deep-dive.html
This article is shared for informational purposes. All rights belong to the original author and publisher. If you are the copyright holder and would like this content removed, please contact us.