You could use WinEvents to track changes to the foreground window. The event callback could determine if the process owning the foreground window has an integrity level greater than medium. If it does, the assumption would be that your low level mouse hook will not be called until the foreground window changes to a lower integrity level process.
How can I tell in C# or VB.NET if a low-level hook is blocked by a high-integrity process?
Hello,
In a VB.NET application, I'm using a low-level hook to manage mouse movements on the screen.
Windows' built-in security measures prevent the low-level mouse hook from interacting with, for example, the Task Manager.
My program no longer receives mouse coordinates when the Windows Task Manager has focus.
To work around this situation, I don't want to run my application with elevated privileges as an administrator or use a signing certificate.
Is it possible to detect when my application's mouse hook is blocked by a high-integrity process? (API?)
Developer technologies | VB
-
RLWA32 • 49,636 Reputation points
2025-05-04T11:29:31.23+00:00 Proof of concept demo written in C++. The monitoring process is not running with elevated privileges and is not a UIAccess application.
-
jacky Perpète • 146 Reputation points
2025-05-05T07:56:18.0333333+00:00 Thank you for your comment.
What principle did you use to create the program?
Here's a solution (perhaps not the best one) that I just tested.
In my program, I added a dispatcherTimer whose event performs the following steps:
- Retrieves a handle to the window that has focus (GetForegroundWindow API)
- Retrieves the thread ID of this window (GetWindowThreadProcessId API)
- Creates the process associated with the window
- Retrieves the first thread of the process
- Reads the thread's priority level Throws an error if the thread is higher than the application thread
Here's the code in the dispatcherTimer event.
Private Sub dspHook_Tick(ByVal sender As Object, ByVal e As EventArgs) Dim foregroundProcessId As Integer 'Récupère un handle vers la fenêtre qui a le focus Dim foregroundWindowHandle As IntPtr = GetForegroundWindow() 'Récupère l'identifiant du thread de la fenêtre GetWindowThreadProcessId(foregroundWindowHandle, foregroundProcessId) 'Crée le processus associé à la fenêtre Dim procforeground As Process = Process.GetProcessById(foregroundProcessId) 'Récupére le premier thread du processus Dim theThread As ProcessThread = procforeground.Threads.Item(0) 'Lecture du niveau de priorité du thread 'Va en erreur si le thread est plus élévé que le thread de l'application Try Dim priority As ThreadPriorityLevel = theThread.PriorityLevel lblTest.Content = "non-blocking priority" Catch ex As Exception lblTest.Content = "blocking priority" End Try End Sub
-
RLWA32 • 49,636 Reputation points
2025-05-05T08:26:43.82+00:00 What principle did you use to create the program?
I used WinEvents. Refer to SetWinEventHook function to register the callback function with WINEVENT_OUTOFCONTEXT and set the hook to receive only EVENT_SYSTEM_FOREGROUND.
The system invoked the WINEVENTPROC callback function whenever the foreground window changes. No timer was needed.
In the callback function I obtained the process ID of the foreground window from the HWND parameter with GetWindowThreadProcessId, obtained a process handle with OpenProcess specifying PROCESS_QUERY_LIMITED_INFORMATION, obtained the process token with OpenProcessToken specifying only TOKEN_QUERY and then obtained the process integrity level by calling GetTokenInformation specifying TokenIntegrityLevel. The returned structure contains an integrity level SID for the process which was compared to the well-known SID for medium integrity. I chose this method because UIPI is based on process integrity levels.
-
RLWA32 • 49,636 Reputation points
2025-05-09T09:03:00.8066667+00:00 @jacky Perpète, I was wondering if you have decided how to proceed. Any new developments on this issue?
-
jacky Perpète • 146 Reputation points
2025-05-09T15:04:49.6666667+00:00 @RLWA32 , I followed your previous instructions to use the APIs mentioned in my WPF application.
I'm having trouble using the GetTokenInformation API.
I can't extract the integrity level information (Program Error).
All the examples I've tested don't work.
Currently, I'm using WinEvents with the SetWinEventHook function and the part of the program described previously (process thread and error handling).
I can still do some more testing.
Thank you for your follow-up.
-
Viorel • 122.6K Reputation points
2025-05-09T15:31:02.8933333+00:00 Maybe it is possible to assume that any other window, even not in foreground, belongs to a privileged program.
-
RLWA32 • 49,636 Reputation points
2025-05-09T19:29:35.4566667+00:00 I cobbled together a VB console application the compares process integrity levels. I have no doubt that the code can be restructured and modernized but I think it will be helpful to you.
Initial version deleted. See updated code in following comment.
-
RLWA32 • 49,636 Reputation points
2025-05-10T07:43:13.2+00:00 Updated code example -
Module Module1 Public Enum TOKEN_INFORMATION_CLASS TokenUser = 1 TokenGroups TokenPrivileges TokenOwner TokenPrimaryGroup TokenDefaultDacl TokenSource TokenType TokenImpersonationLevel TokenStatistics TokenRestrictedSids TokenSessionId TokenGroupsAndPrivileges TokenSessionReference TokenSandBoxInert TokenAuditPolicy TokenOrigin TokenElevationType TokenLinkedToken TokenElevation TokenHasRestrictions TokenAccessInformation TokenVirtualizationAllowed TokenVirtualizationEnabled TokenIntegrityLevel TokenUIAccess TokenMandatoryPolicy TokenLogonSid TokenIsAppContainer TokenCapabilities TokenAppContainerSid TokenAppContainerNumber TokenUserClaimAttributes TokenDeviceClaimAttributes TokenRestrictedUserClaimAttributes TokenRestrictedDeviceClaimAttributes TokenDeviceGroups TokenRestrictedDeviceGroups TokenSecurityAttributes TokenIsRestricted TokenProcessTrustLevel TokenPrivateNameSpace TokenSingletonAttributes TokenBnoIsolation TokenChildProcessFlags TokenIsLessPrivilegedAppContainer TokenIsSandboxed TokenOriginatingProcessTrustLevel MaxTokenInfoClass 'MaxTokenInfoClass should always be the last Enum End Enum <DllImport("kernel32.dll", CallingConvention:=CallingConvention.StdCall, ExactSpelling:=True, SetLastError:=True)> Public Function OpenProcess(DesiredAccess As UInteger, Inheritable As Boolean, ProcessId As Integer) As SafeProcessHandle End Function <DllImport("advapi32.dll", CallingConvention:=CallingConvention.StdCall, ExactSpelling:=True, SetLastError:=True)> Public Function OpenProcessToken(ProcessHandle As SafeProcessHandle, DesiredAccess As UInteger, <Out> ByRef TokenHandle As SafeAccessTokenHandle) As Boolean End Function <DllImport("advapi32.dll", CallingConvention:=CallingConvention.StdCall, ExactSpelling:=True, SetLastError:=True)> Public Function GetTokenInformation(handle As SafeAccessTokenHandle, TokenInformationClass As TOKEN_INFORMATION_CLASS, TokenInformation As IntPtr, TokenInformationLength As Integer, ByRef ReturnLength As Integer) As Boolean End Function <StructLayout(LayoutKind.Sequential)> Public Structure SID_AND_ATTRIBUTES Public PSID As IntPtr Public Attributes As UInteger End Structure <StructLayout(LayoutKind.Sequential)> Public Class TOKEN_MANDATORY_LABEL Public Label As SID_AND_ATTRIBUTES <MarshalAs(UnmanagedType.ByValArray, SizeConst:=SECURITY_MAX_SID_SIZE)> Public Buffer As Byte() ' Provide buffer to avoid calling GetTokenInformation twice End Class Const SECURITY_MAX_SID_SIZE As Integer = 68 Public Function GetIntegrityOfProcess(ProcessId As Integer) As SecurityIdentifier Dim PROCESS_QUERY_LIMITED_INFORMATION As Integer = &H1000 Dim TOKEN_QUERY As Short = &H8 Dim ti As IntPtr = IntPtr.Zero Try Using ProcessHandle As SafeProcessHandle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, ProcessId) Dim TokenHandle As New SafeAccessTokenHandle(IntPtr.Zero) Try If OpenProcessToken(ProcessHandle, TOKEN_QUERY, TokenHandle) Then Dim len = Marshal.SizeOf(Of TOKEN_MANDATORY_LABEL)() ti = Marshal.AllocHGlobal(len) If GetTokenInformation(TokenHandle, TOKEN_INFORMATION_CLASS.TokenIntegrityLevel, ti, len, len) Then Dim label As TOKEN_MANDATORY_LABEL = New TOKEN_MANDATORY_LABEL() Marshal.PtrToStructure(ti, label) Dim sid As SecurityIdentifier = New SecurityIdentifier(label.Label.PSID) Return sid Else Throw New Win32Exception(Marshal.GetLastWin32Error(), "GetTokenInformation failed") End If Else Throw New Win32Exception(Marshal.GetLastWin32Error(), "OpenProcessToken failed") End If Finally TokenHandle.Dispose() End Try End Using Finally If ti <> IntPtr.Zero Then Marshal.FreeHGlobal(ti) End If End Try End Function Sub Main() Try Dim processIntegrity = GetIntegrityOfProcess(Process.GetCurrentProcess().Id) Dim taskmanagerIntegrity = GetIntegrityOfProcess(Process.GetProcessesByName("TaskMgr")(0).Id) Dim result = processIntegrity.CompareTo(taskmanagerIntegrity) If result < 0 Then Console.WriteLine("Process integrity is less than TaskManger integrity") ElseIf result = 0 Then Console.WriteLine("Process integrity is same as TaskManager integrity") Else Console.WriteLine("Process integrity is greater than TaskManager integrity") End If Catch ex As Exception Console.WriteLine($"Caught exception {ex.Message}") End Try End Sub End Module
I think this is a better version. :)
-
jacky Perpète • 146 Reputation points
2025-05-12T06:53:46.73+00:00 @RLWA32 ,with your example, I was able to correctly use the GetTokenInformation API.Now it works for me.
Thank you.
Here is the result obtained during testing:
GetTokenInformation(tokenHandle, TokenIntegrityLevel, integrityLevelPtr, returnLength, returnLength) Dim label As TOKEN_MANDATORY_LABEL = New TOKEN_MANDATORY_LABEL() Marshal.PtrToStructure(integrityLevelPtr, label) Dim sid As SecurityIdentifier = New SecurityIdentifier(label.Label.PSID) Select Case sid.ToString Case "S-1-16-0" 'Unknown Integrity Level Case "S-1-16-4096" 'Low Integrity Level Case "S-1-16-8192" 'Medium Integrity Level Case "S-1-16-12288" 'High Integrity Level Case "S-1-16-16384" 'System Integrity Level' Case Else 'Custom Integrity Level' End Select
-
RLWA32 • 49,636 Reputation points
2025-05-12T08:12:17.84+00:00 I'm glad you are having success.
I suggest you read How can I check the integrity level of my process?. Pay particular attention to the paragraph that discusses checking for ranges rather than an exact match. Also, From the documentation of Well-Known SID Structures
Sign in to comment
-
RLWA32 • 49,636 Reputation points
2025-05-02T17:07:55.71+00:00