CTI Swarm
Zurück zu allen Deep Dives
PROJECT ZERO

A Deep Dive into the GetProcessHandleFromHwnd API

Strategische Zusammenfassung

Detaillierte Analyse von Privilege-Escalation-Techniken über Windows UI Access flags und Token-Stealing; zeigt, dass CVE-2023-41772 in älteren Windows-Versionen (Server 2022/2019) noch relevant sein kann, aber in Windows 11 24H2 behoben wurde.

Volltext

Title: A Deep Dive into the GetProcessHandleFromHwnd API - Project Zero

URL Source: https://projectzero.google/2026/02/gphfh-deep-dive.html

Markdown Content: # A Deep Dive into the GetProcessHandleFromHwnd API - Project Zero

[Project Zero](https://projectzero.google/)

* * *

- [x] * [blog archive](https://projectzero.google/archive.html) * [bug reports](https://project-zero.issues.chromium.org/savedsearches/7162405) * [about](https://projectzero.google/about-pz.html) * [Working at PZ](https://projectzero.google/working-at-project-zero.html) * [0day: spreadsheet](https://projectzero.google/0day.html) * [0day: Root Cause Analyses](https://googleprojectzero.github.io/0days-in-the-wild/rca.html) * [vulnerability disclosure policy](https://projectzero.google/vulnerability-disclosure-policy.html) * [reporting transparency](https://projectzero.google/reporting-transparency.html) * search

# A Deep Dive into the GetProcessHandleFromHwnd API

[2026-Feb-26](https://projectzero.google/2026/02/gphfh-deep-dive.html "Permalink to this post")James Forshaw

In my previous blog post I mentioned the [```plaintext GetProcessHandleFromHwnd ```](https://learn.microsoft.com/en-us/windows/win32/winauto/getprocesshandlefromhwnd) API. This was an API I didn’t know existed until I found a publicly disclosed [UAC bypass](https://github.com/R41N3RZUF477/QuickAssist_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 ```plaintext 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 ```plaintext 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.

[… 13,610 Zeichen — nächste Zone: keyword-dense paragraphs …]

However, even if the caller doesn’t have a suitable integrity level it’s sufficient to just have the UI Access flag enabled. This means that tricks such as my [token stealing attack](https://www.tiraniddo.dev/2019/02/accessing-access-tokens-for-uiaccess.html) would be sufficient to open any other process on the same desktop which created a window. This issue was reported to MSRC and fixed as [CVE-2023-41772](https://msrc.microsoft.com/update-guide/vulnerability/CVE-2023-41772). The reporter was the same researcher [Sascha Mayer](https://github.com/R41N3RZUF477) who found the Quick Assist UI Access bypass that I mentioned earlier.

It’s important to note that the protection check is ignored if UIPI is disabled at a system level. Therefore if you’re willing to reboot the system and have administrator access you can disable UIPI by setting an ```plaintext EnforceUIPI ``` DWORD registry value with the value of 0 inside the key ```plaintext HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System ``` . You might also need to disable the ```plaintext UIPIAlwaysOn ``` feature flag, you can do that using a tool like [ViVe](https://github.com/thebookisclosed/ViVe) and running the command ```plaintext ViveTool.exe /disable /id:56625134 ``` as an administrator and rebooting the machine.

The second version of the API doesn’t appear until well into Windows 10’s lifetime, in version 1803. This version is where the API was moved into a Win32k kernel function. The kernel API is exposed as ```plaintext NtUserGetWindowProcessHandle ``` from ```plaintext win32kfull.sys ``` . It’s roughly implemented as follows:

This is a special problem for two process types, first is restricted token sandbox processes. While you might assume this wouldn’t be a big deal if two restricted token sandboxed processes running at the same integrity could access each other, that isn’t always the case. For example Chromium doesn’t allow renderers to open each other, and some renderers have more privilege that others for example if they’re rendering WebUI content. Fortunately at least in this case renderers run under win32k lockdown meaning they can’t create a window even if they wanted to.

This version’s goal was to fix CVE-2023-41772 and there are two major changes. First and most importantly, if the UIPI check fails, the function will still check for the UI Access flag being enabled.

Next […]

[… 13,150 Zeichen — nächste Zone: tail …]

The final step is to call ```plaintext GetProcessHandleFromHwnd ``` with the found window handle and you should get a process handle back with ```plaintext PROCESS_DUP_HANDLE, PROCESS_VM_OPERATION, PROCESS_VM_READ, PROCESS_VM_WRITE, PROCESS_QUERY_LIMITED_INFORMATION ``` access. Typically with this access I’d duplicate a copy of the current process pseudo handle to get a full access handle. However due to the way protected processes work this will fail, as the protection checks cover both opening the process directly and duplicating the handle.

Therefore, this is all the access you’re going to get. While you can’t just create a new thread in the process, it gives you sufficient access to the process to allocate and modify executable memory so a simple attack would be to write some shell code into the process and modify an existing jump to execute the code. I’ll leave the final exploitation as an exercise for the reader. Alternatively Sascha Mayer has [published a PoC](https://github.com/R41N3RZUF477/PPLwindow) after I had [posted a screenshot](https://infosec.exchange/@tiraniddo/115539156769921108) of my version’s console output that you can play with instead.

## Conclusions

In conclusion the ```plaintext GetProcessHandleFromHwnd ``` function is quite interesting in how it’s evolved over the years. The first version using windows hooks was actually secure against accessing protected processes as you can’t duplicate a process handle with access rights such as ```plaintext PROCESS_VM_READ ``` from a protected process to a non-protected process. However it was decided it’d be better to do it all in kernel mode, but the check for protected processes was forgotten.

Finally in Windows 11 24H2, along with a general shake up of UIPI this seems to be fixed and the function is also no longer quite so dangerous. Time will tell if at least some of the changes, like making UIPI permanent, come to pass.

Make zeroday hard.