Skip to content

Commit b7596ca

Browse files
authored
Merge ee0e687 into 2e3041f
2 parents 2e3041f + ee0e687 commit b7596ca

3 files changed

Lines changed: 82 additions & 9 deletions

File tree

source/UIAHandler/__init__.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -814,21 +814,23 @@ def _isUIAWindowHelper(self,hwnd):
814814
and not config.conf['UIA']['useInMSExcelWhenAvailable']
815815
):
816816
return False
817-
# Unless explicitly allowed, all Chromium implementations (including Edge) should not be UIA,
818-
# As their IA2 implementation is still better at the moment.
819-
elif (
820-
windowClass == "Chrome_RenderWidgetHostHWND"
821-
and (
817+
elif windowClass == "Chrome_RenderWidgetHostHWND":
818+
# Unless explicitly allowed, all Chromium implementations (including Edge) should not be UIA,
819+
# As their IA2 implementation is still better at the moment.
820+
# However, in cases where Chromium is running under another logon session,
821+
# the IAccessible2 implementation is unavailable.
822+
hasAccessToIA2 = not appModule.isRunningUnderDifferentLogonSession
823+
if (
822824
AllowUiaInChromium.getConfig() == AllowUiaInChromium.NO
823825
# Disabling is only useful if we can inject in-process (and use our older code)
824826
or (
825827
canUseOlderInProcessApproach
828+
and hasAccessToIA2
826829
and AllowUiaInChromium.getConfig() != AllowUiaInChromium.YES # Users can prefer to use UIA
827830
)
828-
)
829-
):
830-
return False
831-
if windowClass == "ConsoleWindowClass":
831+
):
832+
return False
833+
elif windowClass == "ConsoleWindowClass":
832834
return utils._shouldUseUIAConsole(hwnd)
833835
return bool(res)
834836

source/appModuleHandler.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import extensionPoints
4545
from fileUtils import getFileVersionInfo
4646
import globalVars
47+
from systemUtils import getCurrentProcessLogonSessionId, getProcessLogonSessionId
4748

4849

4950
_KNOWN_IMPORTERS_T = Union[importlib.machinery.FileFinder, zipimport.zipimporter]
@@ -670,6 +671,20 @@ def _get_isWindowsStoreApp(self):
670671
self.isWindowsStoreApp = False
671672
return self.isWindowsStoreApp
672673

674+
def _get_isRunningUnderDifferentLogonSession(self) -> bool:
675+
"""Returns whether the application for this appModule was started under a different logon session.
676+
This applies to applications started with the Windows runas command
677+
or when choosing "run as a different user" from an application's (shortcut) context menu.
678+
"""
679+
try:
680+
self.isRunningUnderDifferentLogonSession = (
681+
getCurrentProcessLogonSessionId() != getProcessLogonSessionId(self.processHandle)
682+
)
683+
except WindowsError:
684+
log.error(f"Couldn't compare logon session ID for {self}", exc_info=True)
685+
self.isRunningUnderDifferentLogonSession = False
686+
return self.isRunningUnderDifferentLogonSession
687+
673688
def _get_appArchitecture(self):
674689
"""Returns the target architecture for the specified app.
675690
This is useful for detecting X86/X64 apps running on ARM64 releases of Windows 10.

source/systemUtils.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,62 @@ def hasUiAccess():
6262
finally:
6363
ctypes.windll.kernel32.CloseHandle(token)
6464

65+
#: Value from the TOKEN_INFORMATION_CLASS enumeration:
66+
#: https://docs.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-token_information_class
67+
#: When calling The Win32 GetTokenInformation function, the buffer receives a TOKEN_ORIGIN value.
68+
#: If the token resulted from a logon that used explicit credentials, such as passing a name, domain,
69+
#: and password to the LogonUser function, then the TOKEN_ORIGIN structure will contain the ID of
70+
#: the logon session that created it.
71+
#: If the token resulted from network authentication, then this value will be zero.
72+
TOKEN_ORIGIN = 17 # TokenOrigin in winnt.h
73+
74+
75+
class TokenOrigin(ctypes.Structure):
76+
"""TOKEN_ORIGIN structure: https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-token_origin
77+
This structure is used in calls to the Win32 GetTokenInformation function.
78+
"""
79+
_fields_ = [
80+
("originatingLogonSession", ctypes.c_ulonglong) # OriginatingLogonSession in C structure
81+
]
82+
83+
84+
def getProcessLogonSessionId(processHandle: int) -> int:
85+
"""
86+
Retrieves the ID of the logon session that created the process that the given processHandle belongs to.
87+
The function calls several Win32 functions:
88+
* OpenProcessToken: opens the access token associated with a process.
89+
* GetTokenInformation: retrieves a specified type of information about an access token.
90+
The calling process must have appropriate access rights to obtain the information.
91+
GetTokenInformation is called with the TokenOrigin Value from the TOKEN_INFORMATION_CLASS enumeration.
92+
The resulting structure contains the session ID of the logon session that will be returned.
93+
* CloseHandle: To close the token handle.
94+
"""
95+
token = ctypes.wintypes.HANDLE()
96+
if not ctypes.windll.advapi32.OpenProcessToken(
97+
processHandle,
98+
winKernel.MAXIMUM_ALLOWED,
99+
ctypes.byref(token)
100+
):
101+
raise ctypes.WinError()
102+
try:
103+
val = TokenOrigin()
104+
if not ctypes.windll.advapi32.GetTokenInformation(
105+
token,
106+
TOKEN_ORIGIN,
107+
ctypes.byref(val),
108+
ctypes.sizeof(val),
109+
ctypes.byref(ctypes.wintypes.DWORD())
110+
):
111+
raise ctypes.WinError()
112+
return val.originatingLogonSession
113+
finally:
114+
ctypes.windll.kernel32.CloseHandle(token)
115+
116+
117+
@functools.lru_cache(maxsize=1)
118+
def getCurrentProcessLogonSessionId() -> int:
119+
return getProcessLogonSessionId(winKernel.GetCurrentProcess())
120+
65121

66122
def execElevated(path, params=None, wait=False, handleAlreadyElevated=False):
67123
import subprocess

0 commit comments

Comments
 (0)