2 * The PCI Library -- Access to i386 I/O ports on Windows
4 * Copyright (c) 2004 Alexander Stock <stock.alexander@gmx.de>
5 * Copyright (c) 2006 Martin Mares <mj@ucw.cz>
6 * Copyright (c) 2021 Pali Rohár <pali@kernel.org>
8 * Can be freely distributed and used under the terms of the GNU GPL.
16 /* MSVC compiler provides I/O port intrinsics for both 32 and 64-bit modes. */
17 #pragma intrinsic(_outp)
18 #pragma intrinsic(_outpw)
19 #pragma intrinsic(_outpd)
20 #pragma intrinsic(_inp)
21 #pragma intrinsic(_inpw)
22 #pragma intrinsic(_inpd)
23 #elif defined(_WIN64) || defined(_UCRT)
25 * For other compilers I/O port intrinsics are available in <intrin.h> header
26 * file either as inline/external functions or macros. Beware that <intrin.h>
27 * names are different than MSVC intrinsics names and glibc function names.
28 * Usage of <intrin.h> is also the prefered way for 64-bit mode or when using
32 #define _outp(x,y) __outbyte(x,y)
33 #define _outpw(x,y) __outword(x,y)
34 #define _outpd(x,y) __outdword(x,y)
35 #define _inp(x) __inbyte(x)
36 #define _inpw(x) __inword(x)
37 #define _inpd(x) __indword(x)
38 #elif defined(__CRTDLL__)
40 * Old CRTDLL library does not provide I/O port functions. Even it is the oldest
41 * CRT library it exists also in 64-bit variant. Implement I/O port functions
42 * via inline assembly just for 32-bit mode as 64-bit mode uses above <intrin.h>
45 static inline int _outp(unsigned short port, int databyte)
47 asm volatile ("outb %b0, %w1" : : "a" (databyte), "Nd" (port));
50 static inline unsigned short _outpw(unsigned short port, unsigned short dataword)
52 asm volatile ("outw %w0, %w1" : : "a" (dataword), "Nd" (port));
55 static inline unsigned long _outpd(unsigned short port, unsigned long dataword)
57 asm volatile ("outl %0, %w1" : : "a" (dataword), "Nd" (port));
60 static inline int _inp(unsigned short port)
63 asm volatile ("inb %w1, %0" : "=a" (ret) : "Nd" (port));
66 static inline unsigned short _inpw(unsigned short port)
69 asm volatile ("inw %w1, %0" : "=a" (ret) : "Nd" (port));
72 static inline unsigned long _inpd(unsigned short port)
75 asm volatile ("inl %w1, %0" : "=a" (ret) : "Nd" (port));
78 #elif !defined(__GNUC__)
80 * Old 32-bit MSVCRT (non-UCRT) library provides I/O port functions. Function
81 * prototypes are defined in <conio.h> header file but they are missing in
82 * some MinGW toolchains. So for GCC compiler define them manually.
86 int _outp(unsigned short port, int databyte);
87 unsigned short _outpw(unsigned short port, unsigned short dataword);
88 unsigned long _outpd(unsigned short port, unsigned long dataword);
89 int _inp(unsigned short port);
90 unsigned short _inpw(unsigned short port);
91 unsigned long _inpd(unsigned short port);
94 #define outb(x,y) _outp(y,x)
95 #define outw(x,y) _outpw(y,x)
96 #define outl(x,y) _outpd(y,x)
98 #define inb(x) _inp(x)
99 #define inw(x) _inpw(x)
100 #define inl(x) _inpd(x)
103 * Define __readeflags() for MSVC and GCC compilers.
104 * MSVC since version 14.00 included in WDK 6001 and since version 15.00
105 * included in VS 2008 provides __readeflags() intrinsic for both 32 and 64-bit
106 * modes. WDK 6001 defines macro __BUILDMACHINE__ to value WinDDK. VS 2008 does
107 * not define this macro at all. MSVC throws error if name of user defined
108 * function conflicts with some MSVC intrinsic.
109 * MSVC supports inline assembly via __asm keyword in 32-bit mode only.
110 * GCC version 4.9.0 and higher provides __builtin_ia32_readeflags_uXX()
111 * builtin for XX-mode.
113 #if defined(_MSC_VER) && (_MSC_VER >= 1500 || (_MSC_VER >= 1400 && defined(__BUILDMACHINE__)))
114 #pragma intrinsic(__readeflags)
115 #elif defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 9) || (__GNUC__ > 4))
117 #define __readeflags() __builtin_ia32_readeflags_u64()
119 #define __readeflags() __builtin_ia32_readeflags_u32()
121 #elif defined(_MSC_VER) && defined(_M_IX86)
122 static inline unsigned int
128 #elif defined(__GNUC__)
129 static inline unsigned
141 asm volatile ("pushf\n\tpop %0\n" : "=r" (eflags));
145 #error "Unsupported compiler"
148 /* Read IOPL of the current process, IOPL is stored in eflag bits [13:12]. */
149 #define read_iopl() ((__readeflags() >> 12) & 0x3)
151 /* Unfortunately i586-mingw32msvc toolchain does not provide this constant. */
152 #ifndef PROCESS_QUERY_LIMITED_INFORMATION
153 #define PROCESS_QUERY_LIMITED_INFORMATION 0x1000
156 /* Unfortunately some toolchains do not provide this constant. */
157 #ifndef SE_IMPERSONATE_NAME
158 #define SE_IMPERSONATE_NAME TEXT("SeImpersonatePrivilege")
162 * These psapi functions are available in kernel32.dll library with K32 prefix
163 * on Windows 7 and higher systems. On older Windows systems these functions are
164 * available in psapi.dll libary without K32 prefix. So resolve pointers to
165 * these functions dynamically at runtime from the available system library.
166 * Function GetProcessImageFileNameW() is not available on Windows 2000 and
169 typedef BOOL (WINAPI *EnumProcessesProt)(DWORD *lpidProcess, DWORD cb, DWORD *cbNeeded);
170 typedef DWORD (WINAPI *GetProcessImageFileNameWProt)(HANDLE hProcess, LPWSTR lpImageFileName, DWORD nSize);
171 typedef DWORD (WINAPI *GetModuleFileNameExWProt)(HANDLE hProcess, HMODULE hModule, LPWSTR lpImageFileName, DWORD nSize);
174 * These aclapi functions are available in advapi.dll library on Windows NT 4.0
175 * and higher systems.
177 typedef DWORD (WINAPI *GetSecurityInfoProt)(HANDLE handle, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, PSID *ppsidOwner, PSID *ppsidGroup, PACL *ppDacl, PACL *ppSacl, PSECURITY_DESCRIPTOR *ppSecurityDescriptor);
178 typedef DWORD (WINAPI *SetSecurityInfoProt)(HANDLE handle, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, PSID psidOwner, PSID psidGroup, PACL pDacl, PACL pSacl);
179 typedef DWORD (WINAPI *SetEntriesInAclProt)(ULONG cCountOfExplicitEntries, PEXPLICIT_ACCESS pListOfExplicitEntries, PACL OldAcl, PACL *NewAcl);
182 * This errhandlingapi function is available in kernel32.dll library on
183 * Windows 7 and higher systems.
185 typedef BOOL (WINAPI *SetThreadErrorModeProt)(DWORD dwNewMode, LPDWORD lpOldMode);
188 * Unfortunately NtSetInformationProcess() function, ProcessUserModeIOPL
189 * constant and all other helpers for its usage are not specified in any
190 * standard WinAPI header file. So define all of required constants and types.
191 * Function NtSetInformationProcess() is available in ntdll.dll library on all
192 * Windows systems but marked as it can be removed in some future version.
195 #define NTSTATUS LONG
197 #ifndef STATUS_NOT_IMPLEMENTED
198 #define STATUS_NOT_IMPLEMENTED (NTSTATUS)0xC0000002
200 #ifndef STATUS_PRIVILEGE_NOT_HELD
201 #define STATUS_PRIVILEGE_NOT_HELD (NTSTATUS)0xC0000061
203 #ifndef PROCESSINFOCLASS
204 #define PROCESSINFOCLASS DWORD
206 #ifndef ProcessUserModeIOPL
207 #define ProcessUserModeIOPL 16
209 typedef NTSTATUS (NTAPI *NtSetInformationProcessProt)(HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength);
212 * Check if the current thread has particular privilege in current active access
213 * token. Case when it not possible to determinate it (e.g. current thread does
214 * not have permission to open its own current active access token) is evaluated
215 * as thread does not have that privilege.
218 have_privilege(LUID luid_privilege)
225 * If the current thread does not have active access token then thread
226 * uses primary process access token for all permission checks.
228 if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token) &&
229 (GetLastError() != ERROR_NO_TOKEN ||
230 !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)))
233 priv.PrivilegeCount = 1;
234 priv.Control = PRIVILEGE_SET_ALL_NECESSARY;
235 priv.Privilege[0].Luid = luid_privilege;
236 priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
238 if (!PrivilegeCheck(token, &priv, &ret))
245 * Enable or disable particular privilege in specified access token.
247 * Note that it is not possible to disable privilege in access token with
248 * SE_PRIVILEGE_ENABLED_BY_DEFAULT attribute. This function does not check
249 * this case and incorrectly returns no error even when disabling failed.
250 * Rationale for this decision: Simplification of this function as WinAPI
251 * call AdjustTokenPrivileges() does not signal error in this case too.
254 set_privilege(HANDLE token, LUID luid_privilege, BOOL enable)
256 TOKEN_PRIVILEGES token_privileges;
258 token_privileges.PrivilegeCount = 1;
259 token_privileges.Privileges[0].Luid = luid_privilege;
260 token_privileges.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
263 * WinAPI function AdjustTokenPrivileges() success also when not all
264 * privileges were enabled. It is always required to check for failure
265 * via GetLastError() call. AdjustTokenPrivileges() always sets error
266 * also when it success, as opposite to other WinAPI functions.
268 if (!AdjustTokenPrivileges(token, FALSE, &token_privileges, sizeof(token_privileges), NULL, NULL) ||
269 GetLastError() != ERROR_SUCCESS)
276 * Change access token for the current thread to new specified access token.
277 * Previously active access token is stored in old_token variable and can be
278 * used for reverting to this access token. It is set to NULL if the current
279 * thread previously used primary process access token.
282 change_token(HANDLE new_token, HANDLE *old_token)
286 if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &token))
288 if (GetLastError() != ERROR_NO_TOKEN)
293 if (!ImpersonateLoggedOnUser(new_token))
305 * Change access token for the current thread to the primary process access
306 * token. This function fails also when the current thread already uses primary
307 * process access token.
310 change_token_to_primary(HANDLE *old_token)
314 if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &token))
324 * Revert to the specified access token for the current thread. When access
325 * token is specified as NULL then revert to the primary process access token.
326 * Use to revert after change_token() or change_token_to_primary() call.
329 revert_to_token(HANDLE token)
332 * If SetThreadToken() call fails then there is no option to revert to
333 * the specified previous thread access token. So in this case revert to
334 * the primary process access token.
336 if (!token || !SetThreadToken(NULL, token))
343 * Enable particular privilege for the current thread. And set method how to
344 * revert this privilege (if to revert whole token or only privilege).
347 enable_privilege(LUID luid_privilege, HANDLE *revert_token, BOOL *revert_only_privilege)
352 if (OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &thread_token))
354 if (set_privilege(thread_token, luid_privilege, TRUE))
357 * Indicate that correct revert method is just to
358 * disable privilege in access token.
360 if (revert_token && revert_only_privilege)
362 *revert_token = thread_token;
363 *revert_only_privilege = TRUE;
367 CloseHandle(thread_token);
371 CloseHandle(thread_token);
373 * If enabling privilege failed then try to enable it via
374 * primary process access token.
379 * If the current thread has already active thread access token then
380 * open it with just impersonate right as it would be used only for
383 if (revert_token && revert_only_privilege)
385 if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &thread_token))
387 if (GetLastError() != ERROR_NO_TOKEN)
393 * If current thread has no access token (and uses primary
394 * process access token) or it does not have permission to
395 * adjust privileges or it does not have specified privilege
396 * then create a copy of the primary process access token,
397 * assign it for the current thread (= impersonate self)
398 * and then try adjusting privilege again.
400 if (!ImpersonateSelf(SecurityImpersonation))
403 CloseHandle(thread_token);
408 if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &new_token))
410 /* thread_token is set only when we were asked for revert method. */
411 if (revert_token && revert_only_privilege)
412 revert_to_token(thread_token);
416 if (!set_privilege(new_token, luid_privilege, TRUE))
418 CloseHandle(new_token);
419 /* thread_token is set only when we were asked for revert method. */
420 if (revert_token && revert_only_privilege)
421 revert_to_token(thread_token);
426 * Indicate that correct revert method is to change to the previous
427 * access token. Either to the primary process access token or to the
428 * previous thread access token.
430 if (revert_token && revert_only_privilege)
432 *revert_token = thread_token;
433 *revert_only_privilege = FALSE;
439 * Revert particular privilege for the current thread was previously enabled by
440 * enable_privilege() call. Either disable privilege in specified access token
441 * or revert to previous access token.
444 revert_privilege(LUID luid_privilege, HANDLE revert_token, BOOL revert_only_privilege)
446 if (revert_only_privilege)
448 set_privilege(revert_token, luid_privilege, FALSE);
449 CloseHandle(revert_token);
453 revert_to_token(revert_token);
458 * Return owner of the access token used by the current thread. Buffer for
459 * returned owner needs to be released by LocalFree() call.
462 get_current_token_owner(VOID)
469 * If the current thread does not have active access token then thread
470 * uses primary process access token for all permission checks.
472 if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token) &&
473 (GetLastError() != ERROR_NO_TOKEN ||
474 !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)))
477 if (!GetTokenInformation(token, TokenOwner, NULL, 0, &length) &&
478 GetLastError() != ERROR_INSUFFICIENT_BUFFER)
485 owner = (TOKEN_OWNER *)LocalAlloc(LPTR, length);
492 if (!GetTokenInformation(token, TokenOwner, owner, length, &length))
495 * Length of token owner (SID) buffer between two get calls may
496 * changes (e.g. by another thread of process), so retry.
498 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
513 * Grant particular permissions in the primary access token of the specified
514 * process for the owner of current thread token and set old DACL of the
515 * process access token for reverting permissions. Security descriptor is
516 * just memory buffer for old DACL.
519 grant_process_token_dacl_permissions(HANDLE process, DWORD permissions, HANDLE *token, PACL *old_dacl, PSECURITY_DESCRIPTOR *security_descriptor)
521 GetSecurityInfoProt MyGetSecurityInfo;
522 SetSecurityInfoProt MySetSecurityInfo;
523 SetEntriesInAclProt MySetEntriesInAcl;
524 EXPLICIT_ACCESS explicit_access;
530 * This source file already uses advapi32.dll library, so it is
531 * linked to executable and automatically loaded when starting
532 * current running process.
534 advapi32 = GetModuleHandle(TEXT("advapi32.dll"));
539 * It does not matter if SetEntriesInAclA() or SetEntriesInAclW() is
540 * called as no string is passed to SetEntriesInAcl function.
542 MyGetSecurityInfo = (GetSecurityInfoProt)(LPVOID)GetProcAddress(advapi32, "GetSecurityInfo");
543 MySetSecurityInfo = (SetSecurityInfoProt)(LPVOID)GetProcAddress(advapi32, "SetSecurityInfo");
544 MySetEntriesInAcl = (SetEntriesInAclProt)(LPVOID)GetProcAddress(advapi32, "SetEntriesInAclA");
545 if (!MyGetSecurityInfo || !MySetSecurityInfo || !MySetEntriesInAcl)
548 owner = get_current_token_owner();
553 * READ_CONTROL is required for GetSecurityInfo(DACL_SECURITY_INFORMATION)
554 * and WRITE_DAC is required for SetSecurityInfo(DACL_SECURITY_INFORMATION).
556 if (!OpenProcessToken(process, READ_CONTROL | WRITE_DAC, token))
562 if (MyGetSecurityInfo(*token, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, old_dacl, NULL, security_descriptor) != ERROR_SUCCESS)
570 * Set new explicit access for the owner of the current thread access
571 * token with non-inherited granting access to specified permissions.
573 explicit_access.grfAccessPermissions = permissions;
574 explicit_access.grfAccessMode = GRANT_ACCESS;
575 explicit_access.grfInheritance = NO_PROPAGATE_INHERIT_ACE;
576 explicit_access.Trustee.pMultipleTrustee = NULL;
577 explicit_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
578 explicit_access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
579 explicit_access.Trustee.TrusteeType = TRUSTEE_IS_USER;
581 * Unfortunately i586-mingw32msvc toolchain does not have pSid pointer
582 * member in Trustee union. So assign owner SID to ptstrName pointer
583 * member which aliases with pSid pointer member in the same union.
585 explicit_access.Trustee.ptstrName = (PVOID)owner->Owner;
587 if (MySetEntriesInAcl(1, &explicit_access, *old_dacl, &new_dacl) != ERROR_SUCCESS)
589 LocalFree(*security_descriptor);
595 if (MySetSecurityInfo(*token, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, new_dacl, NULL) != ERROR_SUCCESS)
597 LocalFree(*security_descriptor);
608 * Revert particular granted permissions in specified access token done by
609 * grant_process_token_dacl_permissions() call.
612 revert_token_dacl_permissions(HANDLE token, PACL old_dacl, PSECURITY_DESCRIPTOR security_descriptor)
614 SetSecurityInfoProt MySetSecurityInfo;
618 * This source file already uses advapi32.dll library, so it is
619 * linked to executable and automatically loaded when starting
620 * current running process.
622 advapi32 = GetModuleHandle(TEXT("advapi32.dll"));
625 MySetSecurityInfo = (SetSecurityInfoProt)(LPVOID)GetProcAddress(advapi32, "SetSecurityInfo");
626 MySetSecurityInfo(token, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, old_dacl, NULL);
629 LocalFree(security_descriptor);
634 * Change error mode of the current thread. If it is not possible then change
635 * error mode of the whole process. Always returns previous error mode.
638 change_error_mode(UINT new_mode)
640 SetThreadErrorModeProt MySetThreadErrorMode = NULL;
645 * Function SetThreadErrorMode() was introduced in Windows 7, so use
646 * GetProcAddress() for compatibility with older systems.
648 kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
650 MySetThreadErrorMode = (SetThreadErrorModeProt)(LPVOID)GetProcAddress(kernel32, "SetThreadErrorMode");
652 if (MySetThreadErrorMode &&
653 MySetThreadErrorMode(new_mode, &old_mode))
657 * Fallback to function SetErrorMode() which modifies error mode of the
658 * whole process and returns old mode.
660 return SetErrorMode(new_mode);
664 * Open process handle specified by the process id with the query right and
665 * optionally also with vm read right.
668 open_process_for_query(DWORD pid, BOOL with_vm_read)
670 BOOL revert_only_privilege;
671 LUID luid_debug_privilege;
672 OSVERSIONINFO version;
678 * Some processes on Windows Vista and higher systems can be opened only
679 * with PROCESS_QUERY_LIMITED_INFORMATION right. This right is enough
680 * for accessing primary process token. But this right is not supported
681 * on older pre-Vista systems. When the current thread on these older
682 * systems does not have Debug privilege then OpenProcess() fails with
683 * ERROR_ACCESS_DENIED. If the current thread has Debug privilege then
684 * OpenProcess() success and returns handle to requested process.
685 * Problem is that this handle does not have PROCESS_QUERY_INFORMATION
686 * right and so cannot be used for accessing primary process token
687 * on those older systems. Moreover it has zero rights and therefore
688 * such handle is fully useless. So never try to use open process with
689 * PROCESS_QUERY_LIMITED_INFORMATION right on older systems than
690 * Windows Vista (NT 6.0).
692 version.dwOSVersionInfoSize = sizeof(version);
693 if (GetVersionEx(&version) &&
694 version.dwPlatformId == VER_PLATFORM_WIN32_NT &&
695 version.dwMajorVersion >= 6)
696 process_right = PROCESS_QUERY_LIMITED_INFORMATION;
698 process_right = PROCESS_QUERY_INFORMATION;
701 process_right |= PROCESS_VM_READ;
703 process = OpenProcess(process_right, FALSE, pid);
708 * It is possible to open only processes to which owner of the current
709 * thread access token has permissions. For opening other processing it
710 * is required to have Debug privilege enabled. By default local
711 * administrators have this privilege, but it is disabled. So try to
712 * enable it and then try to open process again.
715 if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid_debug_privilege))
718 if (!enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege))
721 process = OpenProcess(process_right, FALSE, pid);
723 revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
729 * Check if process image path name (wide string) matches exe file name
730 * (7-bit ASCII string). Do case-insensitive string comparison. Process
731 * image path name can be in any namespace format (DOS, Win32, UNC, ...).
734 check_process_name(LPCWSTR path, DWORD path_length, LPCSTR exe_file)
736 DWORD exe_file_length;
742 while (exe_file[exe_file_length] != '\0')
745 /* Path must have backslash before exe file name. */
746 if (exe_file_length >= path_length ||
747 path[path_length-exe_file_length-1] != L'\\')
750 for (i = 0; i < exe_file_length; i++)
752 c1 = path[path_length-exe_file_length+i];
755 * Input string for comparison is 7-bit ASCII and file name part
756 * of path must not contain backslash as it is path separator.
758 if (c1 >= 0x80 || c2 >= 0x80 || c1 == L'\\')
760 if (c1 >= L'a' && c1 <= L'z')
762 if (c2 >= 'a' && c2 <= 'z')
771 /* Open process handle with the query right specified by process exe file. */
773 find_and_open_process_for_query(LPCSTR exe_file)
775 GetProcessImageFileNameWProt MyGetProcessImageFileNameW;
776 GetModuleFileNameExWProt MyGetModuleFileNameExW;
777 EnumProcessesProt MyEnumProcesses;
778 HMODULE kernel32, psapi;
779 UINT prev_error_mode;
780 WCHAR path[MAX_PATH];
788 kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
793 * On Windows 7 and higher systems these functions are available in
794 * kernel32.dll library with K32 prefix.
796 MyGetModuleFileNameExW = NULL;
797 MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(LPVOID)GetProcAddress(kernel32, "K32GetProcessImageFileNameW");
798 MyEnumProcesses = (EnumProcessesProt)(LPVOID)GetProcAddress(kernel32, "K32EnumProcesses");
799 if (!MyGetProcessImageFileNameW || !MyEnumProcesses)
802 * On older NT-based systems these functions are available in
803 * psapi.dll library without K32 prefix.
805 prev_error_mode = change_error_mode(SEM_FAILCRITICALERRORS);
806 psapi = LoadLibrary(TEXT("psapi.dll"));
807 change_error_mode(prev_error_mode);
813 * Function GetProcessImageFileNameW() is available in
814 * Windows XP and higher systems. On older versions is
815 * available function GetModuleFileNameExW().
817 MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(LPVOID)GetProcAddress(psapi, "GetProcessImageFileNameW");
818 MyGetModuleFileNameExW = (GetModuleFileNameExWProt)(LPVOID)GetProcAddress(psapi, "GetModuleFileNameExW");
819 MyEnumProcesses = (EnumProcessesProt)(LPVOID)GetProcAddress(psapi, "EnumProcesses");
820 if ((!MyGetProcessImageFileNameW && !MyGetModuleFileNameExW) || !MyEnumProcesses)
827 /* Make initial buffer size for 1024 processes. */
828 size = 1024 * sizeof(*processes);
831 processes = (DWORD *)LocalAlloc(LPTR, size);
839 if (!MyEnumProcesses(processes, size, &length))
841 LocalFree(processes);
846 else if (size == length)
849 * There is no indication given when the buffer is too small to
850 * store all process identifiers. Therefore if returned length
851 * is same as buffer size there can be more processes. Call
852 * again with larger buffer.
854 LocalFree(processes);
860 count = length / sizeof(*processes);
862 for (i = 0; i < count; i++)
864 /* Skip System Idle Process. */
865 if (processes[i] == 0)
869 * Function GetModuleFileNameExW() requires additional
870 * PROCESS_VM_READ right as opposite to function
871 * GetProcessImageFileNameW() which does not need it.
873 process = open_process_for_query(processes[i], MyGetProcessImageFileNameW ? FALSE : TRUE);
877 if (MyGetProcessImageFileNameW)
878 length = MyGetProcessImageFileNameW(process, path, sizeof(path)/sizeof(*path));
880 length = MyGetModuleFileNameExW(process, NULL, path, sizeof(path)/sizeof(*path));
881 if (length && check_process_name(path, length, exe_file))
884 CloseHandle(process);
888 LocalFree(processes);
897 * Try to open primary access token of the particular process with specified
898 * rights. Before opening access token try to adjust DACL permissions of the
899 * primary process access token, so following open does not fail on error
900 * related to no open permissions. Revert DACL permissions after open attempt.
901 * As following steps are not atomic, try to execute them more times in case
902 * of possible race conditions caused by other threads or processes.
905 try_grant_permissions_and_open_process_token(HANDLE process, DWORD rights)
907 PSECURITY_DESCRIPTOR security_descriptor;
915 * This code is not atomic. Between grant and open calls can other
916 * thread or process change or revert permissions. So try to execute
919 for (retry = 0; retry < 10; retry++)
921 if (!grant_process_token_dacl_permissions(process, rights, &grant_token, &old_dacl, &security_descriptor))
923 if (!OpenProcessToken(process, rights, &token))
926 error = GetLastError();
928 revert_token_dacl_permissions(grant_token, old_dacl, security_descriptor);
931 else if (error != ERROR_ACCESS_DENIED)
939 * Open primary access token of particular process handle with specified rights.
940 * If permissions for specified rights are missing then try to grant them.
943 open_process_token_with_rights(HANDLE process, DWORD rights)
948 /* First try to open primary access token of process handle directly. */
949 if (OpenProcessToken(process, rights, &token))
953 * If opening failed then it means that owner of the current thread
954 * access token does not have permission for it. Try it again with
955 * primary process access token.
957 if (change_token_to_primary(&old_token))
959 if (!OpenProcessToken(process, rights, &token))
961 revert_to_token(old_token);
967 * If opening is still failing then try to grant specified permissions
968 * for the current thread and try to open it again.
970 token = try_grant_permissions_and_open_process_token(process, rights);
975 * And if it is still failing then try it again with granting
976 * permissions for the primary process token of the current process.
978 if (change_token_to_primary(&old_token))
980 token = try_grant_permissions_and_open_process_token(process, rights);
981 revert_to_token(old_token);
987 * TODO: Sorry, no other option for now...
988 * It could be possible to use Take Ownership Name privilege to
989 * temporary change token owner of specified process to the owner of
990 * the current thread token, grant permissions for current thread in
991 * that process token, change ownership back to original one, open
992 * that process token and revert granted permissions. But this is
993 * not implemented yet.
999 * Set x86 I/O Privilege Level to 3 for the whole current NT process. Do it via
1000 * NtSetInformationProcess() call with ProcessUserModeIOPL information class,
1001 * which is supported by 32-bit Windows NT kernel versions and requires Tcb
1005 SetProcessUserModeIOPL(VOID)
1007 NtSetInformationProcessProt MyNtSetInformationProcess;
1009 LUID luid_tcb_privilege;
1010 LUID luid_impersonate_privilege;
1012 HANDLE revert_token_tcb_privilege;
1013 BOOL revert_only_tcb_privilege;
1015 HANDLE revert_token_impersonate_privilege;
1016 BOOL revert_only_impersonate_privilege;
1018 BOOL impersonate_privilege_enabled;
1020 BOOL revert_to_old_token;
1023 HANDLE lsass_process;
1026 UINT prev_error_mode;
1031 impersonate_privilege_enabled = FALSE;
1032 revert_to_old_token = FALSE;
1035 /* Fast path when ProcessUserModeIOPL was already called. */
1036 if (read_iopl() == 3)
1040 * Load ntdll.dll library with disabled critical-error-handler message box.
1041 * It means that NT kernel does not show unwanted GUI message box to user
1042 * when LoadLibrary() function fails.
1044 prev_error_mode = change_error_mode(SEM_FAILCRITICALERRORS);
1045 ntdll = LoadLibrary(TEXT("ntdll.dll"));
1046 change_error_mode(prev_error_mode);
1048 goto err_not_implemented;
1050 /* Retrieve pointer to NtSetInformationProcess() function. */
1051 MyNtSetInformationProcess = (NtSetInformationProcessProt)(LPVOID)GetProcAddress(ntdll, "NtSetInformationProcess");
1052 if (!MyNtSetInformationProcess)
1053 goto err_not_implemented;
1056 * ProcessUserModeIOPL is syscall for NT kernel to change x86 IOPL
1057 * of the current running process to 3.
1059 * Process handle argument for ProcessUserModeIOPL is ignored and
1060 * IOPL is always changed for the current running process. So pass
1061 * GetCurrentProcess() handle for documentation purpose. Process
1062 * information buffer and length are unused for ProcessUserModeIOPL.
1064 * ProcessUserModeIOPL may success (return value >= 0) or may fail
1065 * because it is not implemented or because of missing privilege.
1066 * Other errors are not defined, so handle them as unknown.
1068 nt_status = MyNtSetInformationProcess(GetCurrentProcess(), ProcessUserModeIOPL, NULL, 0);
1071 else if (nt_status == STATUS_NOT_IMPLEMENTED)
1072 goto err_not_implemented;
1073 else if (nt_status != STATUS_PRIVILEGE_NOT_HELD)
1077 * If ProcessUserModeIOPL call failed with STATUS_PRIVILEGE_NOT_HELD
1078 * error then it means that the current thread token does not have
1079 * Tcb privilege enabled. Try to enable it.
1082 if (!LookupPrivilegeValue(NULL, SE_TCB_NAME, &luid_tcb_privilege))
1083 goto err_not_implemented;
1086 * If the current thread has already Tcb privilege enabled then there
1087 * is some additional unhanded restriction.
1089 if (have_privilege(luid_tcb_privilege))
1090 goto err_privilege_not_held;
1092 /* Try to enable Tcb privilege and try ProcessUserModeIOPL call again. */
1093 if (enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege))
1095 nt_status = MyNtSetInformationProcess(GetCurrentProcess(), ProcessUserModeIOPL, NULL, 0);
1096 revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege);
1099 else if (nt_status == STATUS_NOT_IMPLEMENTED)
1100 goto err_not_implemented;
1101 else if (nt_status == STATUS_PRIVILEGE_NOT_HELD)
1102 goto err_privilege_not_held;
1108 * If enabling of Tcb privilege failed then it means that current thread
1109 * does not this privilege. But current process may have it. So try it
1110 * again with primary process access token.
1114 * If system supports Impersonate privilege (Windows 2000 SP4 or higher) then
1115 * all future actions in this function require this Impersonate privilege.
1116 * So try to enable it in case it is currently disabled.
1118 if (LookupPrivilegeValue(NULL, SE_IMPERSONATE_NAME, &luid_impersonate_privilege) &&
1119 !have_privilege(luid_impersonate_privilege))
1122 * If current thread does not have Impersonate privilege enabled
1123 * then first try to enable it just for the current thread. If
1124 * it is not possible to enable it just for the current thread
1125 * then try it to enable globally for whole process (which
1126 * affects all process threads). Both actions will be reverted
1127 * at the end of this function.
1129 if (enable_privilege(luid_impersonate_privilege, &revert_token_impersonate_privilege, &revert_only_impersonate_privilege))
1131 impersonate_privilege_enabled = TRUE;
1133 else if (enable_privilege(luid_impersonate_privilege, NULL, NULL))
1135 impersonate_privilege_enabled = TRUE;
1136 revert_token_impersonate_privilege = NULL;
1137 revert_only_impersonate_privilege = TRUE;
1141 goto err_privilege_not_held;
1145 * Now when Impersonate privilege is enabled, try to enable Tcb
1146 * privilege again. Enabling other privileges for the current
1147 * thread requires Impersonate privilege, so enabling Tcb again
1150 if (enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege))
1152 nt_status = MyNtSetInformationProcess(GetCurrentProcess(), ProcessUserModeIOPL, NULL, 0);
1153 revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege);
1156 else if (nt_status == STATUS_NOT_IMPLEMENTED)
1157 goto err_not_implemented;
1158 else if (nt_status == STATUS_PRIVILEGE_NOT_HELD)
1159 goto err_privilege_not_held;
1166 * If enabling Tcb privilege failed then it means that the current
1167 * thread access token does not have this privilege or does not
1168 * have permission to adjust privileges.
1170 * Try to use more privileged token from Local Security Authority
1171 * Subsystem Service process (lsass.exe) which has Tcb privilege.
1172 * Retrieving this more privileged token is possible for local
1173 * administrators (unless it was disabled by local administrators).
1176 lsass_process = find_and_open_process_for_query("lsass.exe");
1178 goto err_privilege_not_held;
1181 * Open primary lsass.exe process access token with query and duplicate
1182 * rights. Just these two rights are required for impersonating other
1183 * primary process token (impersonate right is really not required!).
1185 lsass_token = open_process_token_with_rights(lsass_process, TOKEN_QUERY | TOKEN_DUPLICATE);
1187 CloseHandle(lsass_process);
1190 goto err_privilege_not_held;
1193 * After successful open of the primary lsass.exe process access token,
1194 * assign its copy for the current thread.
1196 if (!change_token(lsass_token, &old_token))
1197 goto err_privilege_not_held;
1199 revert_to_old_token = TRUE;
1201 nt_status = MyNtSetInformationProcess(GetCurrentProcess(), ProcessUserModeIOPL, NULL, 0);
1202 if (nt_status == STATUS_PRIVILEGE_NOT_HELD)
1205 * Now current thread is not using primary process token anymore
1206 * but is using custom access token. There is no need to revert
1207 * enabled Tcb privilege as the whole custom access token would
1208 * be reverted. So there is no need to setup revert method for
1209 * enabling privilege.
1211 if (have_privilege(luid_tcb_privilege) ||
1212 !enable_privilege(luid_tcb_privilege, NULL, NULL))
1213 goto err_privilege_not_held;
1214 nt_status = MyNtSetInformationProcess(GetCurrentProcess(), ProcessUserModeIOPL, NULL, 0);
1218 else if (nt_status == STATUS_NOT_IMPLEMENTED)
1219 goto err_not_implemented;
1220 else if (nt_status == STATUS_PRIVILEGE_NOT_HELD)
1221 goto err_privilege_not_held;
1227 * Some Windows NT kernel versions (e.g. Windows 2003 x64) do not
1228 * implement ProcessUserModeIOPL syscall at all but incorrectly
1229 * returns success when it is called by user process. So always
1230 * after this call verify that IOPL is set to 3.
1232 if (read_iopl() != 3)
1233 goto err_not_implemented;
1237 err_not_implemented:
1238 SetLastError(ERROR_INVALID_FUNCTION);
1242 err_privilege_not_held:
1243 SetLastError(ERROR_PRIVILEGE_NOT_HELD);
1248 SetLastError(ERROR_GEN_FAILURE);
1253 if (revert_to_old_token)
1254 revert_to_token(old_token);
1256 if (impersonate_privilege_enabled)
1257 revert_privilege(luid_impersonate_privilege, revert_token_impersonate_privilege, revert_only_impersonate_privilege);
1260 CloseHandle(lsass_token);
1269 intel_setup_io(struct pci_access *a)
1272 /* 16/32-bit non-NT systems allow applications to access PCI I/O ports without any special setup. */
1273 OSVERSIONINFOA version;
1274 version.dwOSVersionInfoSize = sizeof(version);
1275 if (GetVersionExA(&version) && version.dwPlatformId < VER_PLATFORM_WIN32_NT)
1277 a->debug("Detected 16/32-bit non-NT system, skipping NT setup...");
1282 /* On NT-based systems issue ProcessUserModeIOPL syscall which changes IOPL to 3. */
1283 if (!SetProcessUserModeIOPL())
1285 a->warning("NT ProcessUserModeIOPL call failed with error: %lu.", (unsigned long int)GetLastError());
1289 a->debug("NT ProcessUserModeIOPL call succeeded...");
1294 intel_cleanup_io(struct pci_access *a UNUSED)
1297 * 16/32-bit non-NT systems do not use any special setup and on NT-based
1298 * systems ProcessUserModeIOPL permanently changes IOPL to 3 for the current
1299 * NT process, no revert for current process is possible.
1303 static inline void intel_io_lock(void)
1307 static inline void intel_io_unlock(void)