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 v2+
10 * SPDX-License-Identifier: GPL-2.0-or-later
16 #include "i386-io-access.h"
19 * Define __readeflags() for MSVC and GCC compilers.
20 * MSVC since version 14.00 included in WDK 6001 and since version 15.00
21 * included in VS 2008 provides __readeflags() intrinsic for both 32 and 64-bit
22 * modes. WDK 6001 defines macro __BUILDMACHINE__ to value WinDDK. VS 2008 does
23 * not define this macro at all. MSVC throws error if name of user defined
24 * function conflicts with some MSVC intrinsic.
25 * MSVC supports inline assembly via __asm keyword in 32-bit mode only.
26 * GCC version 4.9.0 and higher provides __builtin_ia32_readeflags_uXX()
27 * builtin for XX-mode. This builtin is also available as __readeflags()
28 * function indirectly via <x86intrin.h> header file.
30 * CAVEAT: Semicolon in MSVC __asm block means start of the comment, and not
31 * end of the __asm statement, like it is for all other C statements. Also
32 * function which uses MSVC inline assembly cannot be inlined to another function
33 * (compiler reports a warning about it, not a fatal error). So we add explicit
34 * curly brackets for __asm blocks, remove misleading semicolons and do not
35 * declare functions as inline.
37 #if defined(_MSC_VER) && (_MSC_VER >= 1500 || (_MSC_VER >= 1400 && defined(__BUILDMACHINE__)))
38 #pragma intrinsic(__readeflags)
39 #elif defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 9) || (__GNUC__ > 4))
40 #include <x86intrin.h>
41 #elif defined(_MSC_VER) && defined(_M_IX86)
50 #elif defined(__GNUC__)
51 static inline unsigned
63 asm volatile ("pushf\n\tpop %0\n" : "=r" (eflags));
67 #error "Unsupported compiler"
70 /* Read IOPL of the current process, IOPL is stored in eflag bits [13:12]. */
71 #define read_iopl() ((__readeflags() >> 12) & 0x3)
73 /* Unfortunately i586-mingw32msvc toolchain does not provide this constant. */
74 #ifndef PROCESS_QUERY_LIMITED_INFORMATION
75 #define PROCESS_QUERY_LIMITED_INFORMATION 0x1000
78 /* Unfortunately some toolchains do not provide this constant. */
79 #ifndef SE_IMPERSONATE_NAME
80 #define SE_IMPERSONATE_NAME TEXT("SeImpersonatePrivilege")
84 * These psapi functions are available in kernel32.dll library with K32 prefix
85 * on Windows 7 and higher systems. On older Windows systems these functions are
86 * available in psapi.dll libary without K32 prefix. So resolve pointers to
87 * these functions dynamically at runtime from the available system library.
88 * Function GetProcessImageFileNameW() is not available on Windows 2000 and
91 typedef BOOL (WINAPI *EnumProcessesProt)(DWORD *lpidProcess, DWORD cb, DWORD *cbNeeded);
92 typedef DWORD (WINAPI *GetProcessImageFileNameWProt)(HANDLE hProcess, LPWSTR lpImageFileName, DWORD nSize);
93 typedef DWORD (WINAPI *GetModuleFileNameExWProt)(HANDLE hProcess, HMODULE hModule, LPWSTR lpImageFileName, DWORD nSize);
96 * These aclapi functions are available in advapi.dll library on Windows NT 4.0
99 typedef DWORD (WINAPI *GetSecurityInfoProt)(HANDLE handle, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, PSID *ppsidOwner, PSID *ppsidGroup, PACL *ppDacl, PACL *ppSacl, PSECURITY_DESCRIPTOR *ppSecurityDescriptor);
100 typedef DWORD (WINAPI *SetSecurityInfoProt)(HANDLE handle, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, PSID psidOwner, PSID psidGroup, PACL pDacl, PACL pSacl);
101 typedef DWORD (WINAPI *SetEntriesInAclProt)(ULONG cCountOfExplicitEntries, PEXPLICIT_ACCESS pListOfExplicitEntries, PACL OldAcl, PACL *NewAcl);
104 * This errhandlingapi function is available in kernel32.dll library on
105 * Windows 7 and higher systems.
107 typedef BOOL (WINAPI *SetThreadErrorModeProt)(DWORD dwNewMode, LPDWORD lpOldMode);
110 * Unfortunately NtSetInformationProcess() function, ProcessUserModeIOPL
111 * constant and all other helpers for its usage are not specified in any
112 * standard WinAPI header file. So define all of required constants and types.
113 * Function NtSetInformationProcess() is available in ntdll.dll library on all
114 * Windows systems but marked as it can be removed in some future version.
117 #define NTSTATUS LONG
119 #ifndef STATUS_NOT_IMPLEMENTED
120 #define STATUS_NOT_IMPLEMENTED (NTSTATUS)0xC0000002
122 #ifndef STATUS_PRIVILEGE_NOT_HELD
123 #define STATUS_PRIVILEGE_NOT_HELD (NTSTATUS)0xC0000061
125 #ifndef PROCESSINFOCLASS
126 #define PROCESSINFOCLASS DWORD
128 #ifndef ProcessUserModeIOPL
129 #define ProcessUserModeIOPL 16
131 typedef NTSTATUS (NTAPI *NtSetInformationProcessProt)(HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength);
134 * Check if the current thread has particular privilege in current active access
135 * token. Case when it not possible to determinate it (e.g. current thread does
136 * not have permission to open its own current active access token) is evaluated
137 * as thread does not have that privilege.
140 have_privilege(LUID luid_privilege)
147 * If the current thread does not have active access token then thread
148 * uses primary process access token for all permission checks.
150 if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token) &&
151 (GetLastError() != ERROR_NO_TOKEN ||
152 !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)))
155 priv.PrivilegeCount = 1;
156 priv.Control = PRIVILEGE_SET_ALL_NECESSARY;
157 priv.Privilege[0].Luid = luid_privilege;
158 priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
160 if (!PrivilegeCheck(token, &priv, &ret))
167 * Enable or disable particular privilege in specified access token.
169 * Note that it is not possible to disable privilege in access token with
170 * SE_PRIVILEGE_ENABLED_BY_DEFAULT attribute. This function does not check
171 * this case and incorrectly returns no error even when disabling failed.
172 * Rationale for this decision: Simplification of this function as WinAPI
173 * call AdjustTokenPrivileges() does not signal error in this case too.
176 set_privilege(HANDLE token, LUID luid_privilege, BOOL enable)
178 TOKEN_PRIVILEGES token_privileges;
180 token_privileges.PrivilegeCount = 1;
181 token_privileges.Privileges[0].Luid = luid_privilege;
182 token_privileges.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
185 * WinAPI function AdjustTokenPrivileges() success also when not all
186 * privileges were enabled. It is always required to check for failure
187 * via GetLastError() call. AdjustTokenPrivileges() always sets error
188 * also when it success, as opposite to other WinAPI functions.
190 if (!AdjustTokenPrivileges(token, FALSE, &token_privileges, sizeof(token_privileges), NULL, NULL) ||
191 GetLastError() != ERROR_SUCCESS)
198 * Change access token for the current thread to new specified access token.
199 * Previously active access token is stored in old_token variable and can be
200 * used for reverting to this access token. It is set to NULL if the current
201 * thread previously used primary process access token.
204 change_token(HANDLE new_token, HANDLE *old_token)
208 if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &token))
210 if (GetLastError() != ERROR_NO_TOKEN)
215 if (!ImpersonateLoggedOnUser(new_token))
227 * Change access token for the current thread to the primary process access
228 * token. This function fails also when the current thread already uses primary
229 * process access token.
232 change_token_to_primary(HANDLE *old_token)
236 if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &token))
246 * Revert to the specified access token for the current thread. When access
247 * token is specified as NULL then revert to the primary process access token.
248 * Use to revert after change_token() or change_token_to_primary() call.
251 revert_to_token(HANDLE token)
254 * If SetThreadToken() call fails then there is no option to revert to
255 * the specified previous thread access token. So in this case revert to
256 * the primary process access token.
258 if (!token || !SetThreadToken(NULL, token))
265 * Enable particular privilege for the current thread. And set method how to
266 * revert this privilege (if to revert whole token or only privilege).
269 enable_privilege(LUID luid_privilege, HANDLE *revert_token, BOOL *revert_only_privilege)
274 if (OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &thread_token))
276 if (set_privilege(thread_token, luid_privilege, TRUE))
279 * Indicate that correct revert method is just to
280 * disable privilege in access token.
282 if (revert_token && revert_only_privilege)
284 *revert_token = thread_token;
285 *revert_only_privilege = TRUE;
289 CloseHandle(thread_token);
293 CloseHandle(thread_token);
295 * If enabling privilege failed then try to enable it via
296 * primary process access token.
301 * If the current thread has already active thread access token then
302 * open it with just impersonate right as it would be used only for
305 if (revert_token && revert_only_privilege)
307 if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &thread_token))
309 if (GetLastError() != ERROR_NO_TOKEN)
315 * If current thread has no access token (and uses primary
316 * process access token) or it does not have permission to
317 * adjust privileges or it does not have specified privilege
318 * then create a copy of the primary process access token,
319 * assign it for the current thread (= impersonate self)
320 * and then try adjusting privilege again.
322 if (!ImpersonateSelf(SecurityImpersonation))
325 CloseHandle(thread_token);
330 if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &new_token))
332 /* thread_token is set only when we were asked for revert method. */
333 if (revert_token && revert_only_privilege)
334 revert_to_token(thread_token);
338 if (!set_privilege(new_token, luid_privilege, TRUE))
340 CloseHandle(new_token);
341 /* thread_token is set only when we were asked for revert method. */
342 if (revert_token && revert_only_privilege)
343 revert_to_token(thread_token);
348 * Indicate that correct revert method is to change to the previous
349 * access token. Either to the primary process access token or to the
350 * previous thread access token.
352 if (revert_token && revert_only_privilege)
354 *revert_token = thread_token;
355 *revert_only_privilege = FALSE;
361 * Revert particular privilege for the current thread was previously enabled by
362 * enable_privilege() call. Either disable privilege in specified access token
363 * or revert to previous access token.
366 revert_privilege(LUID luid_privilege, HANDLE revert_token, BOOL revert_only_privilege)
368 if (revert_only_privilege)
370 set_privilege(revert_token, luid_privilege, FALSE);
371 CloseHandle(revert_token);
375 revert_to_token(revert_token);
380 * Return owner of the access token used by the current thread. Buffer for
381 * returned owner needs to be released by LocalFree() call.
384 get_current_token_owner(VOID)
391 * If the current thread does not have active access token then thread
392 * uses primary process access token for all permission checks.
394 if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token) &&
395 (GetLastError() != ERROR_NO_TOKEN ||
396 !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)))
399 if (!GetTokenInformation(token, TokenOwner, NULL, 0, &length) &&
400 GetLastError() != ERROR_INSUFFICIENT_BUFFER)
407 owner = (TOKEN_OWNER *)LocalAlloc(LPTR, length);
414 if (!GetTokenInformation(token, TokenOwner, owner, length, &length))
417 * Length of token owner (SID) buffer between two get calls may
418 * changes (e.g. by another thread of process), so retry.
420 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
435 * Grant particular permissions in the primary access token of the specified
436 * process for the owner of current thread token and set old DACL of the
437 * process access token for reverting permissions. Security descriptor is
438 * just memory buffer for old DACL.
441 grant_process_token_dacl_permissions(HANDLE process, DWORD permissions, HANDLE *token, PACL *old_dacl, PSECURITY_DESCRIPTOR *security_descriptor)
443 GetSecurityInfoProt MyGetSecurityInfo;
444 SetSecurityInfoProt MySetSecurityInfo;
445 SetEntriesInAclProt MySetEntriesInAcl;
446 EXPLICIT_ACCESS explicit_access;
452 * This source file already uses advapi32.dll library, so it is
453 * linked to executable and automatically loaded when starting
454 * current running process.
456 advapi32 = GetModuleHandle(TEXT("advapi32.dll"));
461 * It does not matter if SetEntriesInAclA() or SetEntriesInAclW() is
462 * called as no string is passed to SetEntriesInAcl function.
464 MyGetSecurityInfo = (GetSecurityInfoProt)(LPVOID)GetProcAddress(advapi32, "GetSecurityInfo");
465 MySetSecurityInfo = (SetSecurityInfoProt)(LPVOID)GetProcAddress(advapi32, "SetSecurityInfo");
466 MySetEntriesInAcl = (SetEntriesInAclProt)(LPVOID)GetProcAddress(advapi32, "SetEntriesInAclA");
467 if (!MyGetSecurityInfo || !MySetSecurityInfo || !MySetEntriesInAcl)
470 owner = get_current_token_owner();
475 * READ_CONTROL is required for GetSecurityInfo(DACL_SECURITY_INFORMATION)
476 * and WRITE_DAC is required for SetSecurityInfo(DACL_SECURITY_INFORMATION).
478 if (!OpenProcessToken(process, READ_CONTROL | WRITE_DAC, token))
484 if (MyGetSecurityInfo(*token, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, old_dacl, NULL, security_descriptor) != ERROR_SUCCESS)
492 * Set new explicit access for the owner of the current thread access
493 * token with non-inherited granting access to specified permissions.
495 explicit_access.grfAccessPermissions = permissions;
496 explicit_access.grfAccessMode = GRANT_ACCESS;
497 explicit_access.grfInheritance = NO_PROPAGATE_INHERIT_ACE;
498 explicit_access.Trustee.pMultipleTrustee = NULL;
499 explicit_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
500 explicit_access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
501 explicit_access.Trustee.TrusteeType = TRUSTEE_IS_USER;
503 * Unfortunately i586-mingw32msvc toolchain does not have pSid pointer
504 * member in Trustee union. So assign owner SID to ptstrName pointer
505 * member which aliases with pSid pointer member in the same union.
507 explicit_access.Trustee.ptstrName = (PVOID)owner->Owner;
509 if (MySetEntriesInAcl(1, &explicit_access, *old_dacl, &new_dacl) != ERROR_SUCCESS)
511 LocalFree(*security_descriptor);
517 if (MySetSecurityInfo(*token, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, new_dacl, NULL) != ERROR_SUCCESS)
520 LocalFree(*security_descriptor);
532 * Revert particular granted permissions in specified access token done by
533 * grant_process_token_dacl_permissions() call.
536 revert_token_dacl_permissions(HANDLE token, PACL old_dacl, PSECURITY_DESCRIPTOR security_descriptor)
538 SetSecurityInfoProt MySetSecurityInfo;
542 * This source file already uses advapi32.dll library, so it is
543 * linked to executable and automatically loaded when starting
544 * current running process.
546 advapi32 = GetModuleHandle(TEXT("advapi32.dll"));
549 MySetSecurityInfo = (SetSecurityInfoProt)(LPVOID)GetProcAddress(advapi32, "SetSecurityInfo");
550 MySetSecurityInfo(token, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, old_dacl, NULL);
553 LocalFree(security_descriptor);
558 * Change error mode of the current thread. If it is not possible then change
559 * error mode of the whole process. Always returns previous error mode.
562 change_error_mode(UINT new_mode)
564 SetThreadErrorModeProt MySetThreadErrorMode = NULL;
569 * Function SetThreadErrorMode() was introduced in Windows 7, so use
570 * GetProcAddress() for compatibility with older systems.
572 kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
574 MySetThreadErrorMode = (SetThreadErrorModeProt)(LPVOID)GetProcAddress(kernel32, "SetThreadErrorMode");
576 if (MySetThreadErrorMode &&
577 MySetThreadErrorMode(new_mode, &old_mode))
581 * Fallback to function SetErrorMode() which modifies error mode of the
582 * whole process and returns old mode.
584 return SetErrorMode(new_mode);
588 * Open process handle specified by the process id with the query right and
589 * optionally also with vm read right.
592 open_process_for_query(DWORD pid, BOOL with_vm_read)
594 BOOL revert_only_privilege;
595 LUID luid_debug_privilege;
596 OSVERSIONINFO version;
602 * Some processes on Windows Vista and higher systems can be opened only
603 * with PROCESS_QUERY_LIMITED_INFORMATION right. This right is enough
604 * for accessing primary process token. But this right is not supported
605 * on older pre-Vista systems. When the current thread on these older
606 * systems does not have Debug privilege then OpenProcess() fails with
607 * ERROR_ACCESS_DENIED. If the current thread has Debug privilege then
608 * OpenProcess() success and returns handle to requested process.
609 * Problem is that this handle does not have PROCESS_QUERY_INFORMATION
610 * right and so cannot be used for accessing primary process token
611 * on those older systems. Moreover it has zero rights and therefore
612 * such handle is fully useless. So never try to use open process with
613 * PROCESS_QUERY_LIMITED_INFORMATION right on older systems than
614 * Windows Vista (NT 6.0).
616 version.dwOSVersionInfoSize = sizeof(version);
617 if (GetVersionEx(&version) &&
618 version.dwPlatformId == VER_PLATFORM_WIN32_NT &&
619 version.dwMajorVersion >= 6)
620 process_right = PROCESS_QUERY_LIMITED_INFORMATION;
622 process_right = PROCESS_QUERY_INFORMATION;
625 process_right |= PROCESS_VM_READ;
627 process = OpenProcess(process_right, FALSE, pid);
632 * It is possible to open only processes to which owner of the current
633 * thread access token has permissions. For opening other processing it
634 * is required to have Debug privilege enabled. By default local
635 * administrators have this privilege, but it is disabled. So try to
636 * enable it and then try to open process again.
639 if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid_debug_privilege))
642 if (!enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege))
645 process = OpenProcess(process_right, FALSE, pid);
647 revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
653 * Check if process image path name (wide string) matches exe file name
654 * (7-bit ASCII string). Do case-insensitive string comparison. Process
655 * image path name can be in any namespace format (DOS, Win32, UNC, ...).
658 check_process_name(LPCWSTR path, DWORD path_length, LPCSTR exe_file)
660 DWORD exe_file_length;
666 while (exe_file[exe_file_length] != '\0')
669 /* Path must have backslash before exe file name. */
670 if (exe_file_length >= path_length ||
671 path[path_length-exe_file_length-1] != L'\\')
674 for (i = 0; i < exe_file_length; i++)
676 c1 = path[path_length-exe_file_length+i];
679 * Input string for comparison is 7-bit ASCII and file name part
680 * of path must not contain backslash as it is path separator.
682 if (c1 >= 0x80 || c2 >= 0x80 || c1 == L'\\')
684 if (c1 >= L'a' && c1 <= L'z')
686 if (c2 >= 'a' && c2 <= 'z')
695 /* Open process handle with the query right specified by process exe file. */
697 find_and_open_process_for_query(LPCSTR exe_file)
699 GetProcessImageFileNameWProt MyGetProcessImageFileNameW;
700 GetModuleFileNameExWProt MyGetModuleFileNameExW;
701 EnumProcessesProt MyEnumProcesses;
702 HMODULE kernel32, psapi;
703 UINT prev_error_mode;
715 kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
720 * On Windows 7 and higher systems these functions are available in
721 * kernel32.dll library with K32 prefix.
723 MyGetModuleFileNameExW = NULL;
724 MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(LPVOID)GetProcAddress(kernel32, "K32GetProcessImageFileNameW");
725 MyEnumProcesses = (EnumProcessesProt)(LPVOID)GetProcAddress(kernel32, "K32EnumProcesses");
726 if (!MyGetProcessImageFileNameW || !MyEnumProcesses)
729 * On older NT-based systems these functions are available in
730 * psapi.dll library without K32 prefix.
732 prev_error_mode = change_error_mode(SEM_FAILCRITICALERRORS);
733 psapi = LoadLibrary(TEXT("psapi.dll"));
734 change_error_mode(prev_error_mode);
740 * Function GetProcessImageFileNameW() is available in
741 * Windows XP and higher systems. On older versions is
742 * available function GetModuleFileNameExW().
744 MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(LPVOID)GetProcAddress(psapi, "GetProcessImageFileNameW");
745 MyGetModuleFileNameExW = (GetModuleFileNameExWProt)(LPVOID)GetProcAddress(psapi, "GetModuleFileNameExW");
746 MyEnumProcesses = (EnumProcessesProt)(LPVOID)GetProcAddress(psapi, "EnumProcesses");
747 if ((!MyGetProcessImageFileNameW && !MyGetModuleFileNameExW) || !MyEnumProcesses)
754 /* Make initial buffer size for 1024 processes. */
755 size = 1024 * sizeof(*processes);
758 processes = (DWORD *)LocalAlloc(LPTR, size);
766 if (!MyEnumProcesses(processes, size, &length))
768 LocalFree(processes);
773 else if (size == length)
776 * There is no indication given when the buffer is too small to
777 * store all process identifiers. Therefore if returned length
778 * is same as buffer size there can be more processes. Call
779 * again with larger buffer.
781 LocalFree(processes);
787 count = length / sizeof(*processes);
789 for (i = 0; i < count; i++)
791 /* Skip System Idle Process. */
792 if (processes[i] == 0)
796 * Function GetModuleFileNameExW() requires additional
797 * PROCESS_VM_READ right as opposite to function
798 * GetProcessImageFileNameW() which does not need it.
800 process = open_process_for_query(processes[i], MyGetProcessImageFileNameW ? FALSE : TRUE);
805 * Set initial buffer size to 256 (wide) characters.
806 * Final path length on the modern NT-based systems can be also larger.
809 found_process = FALSE;
813 path = (LPWSTR)LocalAlloc(LPTR, size * sizeof(*path));
817 if (MyGetProcessImageFileNameW)
818 length = MyGetProcessImageFileNameW(process, path, size);
820 length = MyGetModuleFileNameExW(process, NULL, path, size);
822 error = GetLastError();
825 * GetModuleFileNameEx() returns zero and signal error ERROR_PARTIAL_COPY
826 * when remote process is in the middle of updating its module table.
827 * Sleep 10 ms and try again, max 10 attempts.
829 if (!MyGetProcessImageFileNameW)
831 if (length == 0 && error == ERROR_PARTIAL_COPY && partial_retry++ < 10)
840 * When buffer is too small then function GetModuleFileNameEx() returns
841 * its size argument on older systems (Windows XP) or its size minus
842 * argument one on new systems (Windows 10) without signalling any error.
843 * Function GetProcessImageFileNameW() on the other hand returns zero
844 * value and signals error ERROR_INSUFFICIENT_BUFFER. So in all these
845 * cases call function again with larger buffer.
848 if (MyGetProcessImageFileNameW && length == 0 && error != ERROR_INSUFFICIENT_BUFFER)
851 if ((MyGetProcessImageFileNameW && length == 0) ||
852 (!MyGetProcessImageFileNameW && (length == size || length == size-1)))
859 if (length && check_process_name(path, length, exe_file))
860 found_process = TRUE;
872 CloseHandle(process);
876 LocalFree(processes);
885 * Try to open primary access token of the particular process with specified
886 * rights. Before opening access token try to adjust DACL permissions of the
887 * primary process access token, so following open does not fail on error
888 * related to no open permissions. Revert DACL permissions after open attempt.
889 * As following steps are not atomic, try to execute them more times in case
890 * of possible race conditions caused by other threads or processes.
893 try_grant_permissions_and_open_process_token(HANDLE process, DWORD rights)
895 PSECURITY_DESCRIPTOR security_descriptor;
903 * This code is not atomic. Between grant and open calls can other
904 * thread or process change or revert permissions. So try to execute
907 for (retry = 0; retry < 10; retry++)
909 if (!grant_process_token_dacl_permissions(process, rights, &grant_token, &old_dacl, &security_descriptor))
911 if (!OpenProcessToken(process, rights, &token))
914 error = GetLastError();
916 revert_token_dacl_permissions(grant_token, old_dacl, security_descriptor);
919 else if (error != ERROR_ACCESS_DENIED)
927 * Open primary access token of particular process handle with specified rights.
928 * If permissions for specified rights are missing then try to grant them.
931 open_process_token_with_rights(HANDLE process, DWORD rights)
936 /* First try to open primary access token of process handle directly. */
937 if (OpenProcessToken(process, rights, &token))
941 * If opening failed then it means that owner of the current thread
942 * access token does not have permission for it. Try it again with
943 * primary process access token.
945 if (change_token_to_primary(&old_token))
947 if (!OpenProcessToken(process, rights, &token))
949 revert_to_token(old_token);
955 * If opening is still failing then try to grant specified permissions
956 * for the current thread and try to open it again.
958 token = try_grant_permissions_and_open_process_token(process, rights);
963 * And if it is still failing then try it again with granting
964 * permissions for the primary process token of the current process.
966 if (change_token_to_primary(&old_token))
968 token = try_grant_permissions_and_open_process_token(process, rights);
969 revert_to_token(old_token);
975 * TODO: Sorry, no other option for now...
976 * It could be possible to use Take Ownership Name privilege to
977 * temporary change token owner of specified process to the owner of
978 * the current thread token, grant permissions for current thread in
979 * that process token, change ownership back to original one, open
980 * that process token and revert granted permissions. But this is
981 * not implemented yet.
987 * Call supplied function Func with its Arg and if it fails with
988 * ERROR_PRIVILEGE_NOT_HELD then try to enable Tcb privilege and
989 * call Func with its Arg again.
992 CallFuncWithTcbPrivilege(BOOL (*Func)(LPVOID), LPVOID Arg)
994 LUID luid_tcb_privilege;
995 LUID luid_impersonate_privilege;
997 HANDLE revert_token_tcb_privilege;
998 BOOL revert_only_tcb_privilege;
1000 HANDLE revert_token_impersonate_privilege;
1001 BOOL revert_only_impersonate_privilege;
1003 BOOL impersonate_privilege_enabled;
1005 BOOL revert_to_old_token;
1008 HANDLE lsass_process;
1013 impersonate_privilege_enabled = FALSE;
1014 revert_to_old_token = FALSE;
1018 /* Call supplied function. */
1020 if (ret || GetLastError() != ERROR_PRIVILEGE_NOT_HELD)
1024 * If function call failed with ERROR_PRIVILEGE_NOT_HELD
1025 * error then it means that the current thread token does not have
1026 * Tcb privilege enabled. Try to enable it.
1029 if (!LookupPrivilegeValue(NULL, SE_TCB_NAME, &luid_tcb_privilege))
1030 goto err_privilege_not_held;
1033 * If the current thread has already Tcb privilege enabled then there
1034 * is some additional unhanded restriction.
1036 if (have_privilege(luid_tcb_privilege))
1037 goto err_privilege_not_held;
1039 /* Try to enable Tcb privilege and try function call again. */
1040 if (enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege))
1043 revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege);
1048 * If enabling of Tcb privilege failed then it means that current thread
1049 * does not have this privilege. But current process may have it. So try it
1050 * again with primary process access token.
1054 * If system supports Impersonate privilege (Windows 2000 SP4 or higher) then
1055 * all future actions in this function require this Impersonate privilege.
1056 * So try to enable it in case it is currently disabled.
1058 if (LookupPrivilegeValue(NULL, SE_IMPERSONATE_NAME, &luid_impersonate_privilege) &&
1059 !have_privilege(luid_impersonate_privilege))
1062 * If current thread does not have Impersonate privilege enabled
1063 * then first try to enable it just for the current thread. If
1064 * it is not possible to enable it just for the current thread
1065 * then try it to enable globally for whole process (which
1066 * affects all process threads). Both actions will be reverted
1067 * at the end of this function.
1069 if (enable_privilege(luid_impersonate_privilege, &revert_token_impersonate_privilege, &revert_only_impersonate_privilege))
1071 impersonate_privilege_enabled = TRUE;
1073 else if (enable_privilege(luid_impersonate_privilege, NULL, NULL))
1075 impersonate_privilege_enabled = TRUE;
1076 revert_token_impersonate_privilege = NULL;
1077 revert_only_impersonate_privilege = TRUE;
1081 goto err_privilege_not_held;
1085 * Now when Impersonate privilege is enabled, try to enable Tcb
1086 * privilege again. Enabling other privileges for the current
1087 * thread requires Impersonate privilege, so enabling Tcb again
1090 if (enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege))
1093 revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege);
1099 * If enabling Tcb privilege failed then it means that the current
1100 * thread access token does not have this privilege or does not
1101 * have permission to adjust privileges.
1103 * Try to use more privileged token from Local Security Authority
1104 * Subsystem Service process (lsass.exe) which has Tcb privilege.
1105 * Retrieving this more privileged token is possible for local
1106 * administrators (unless it was disabled by local administrators).
1109 lsass_process = find_and_open_process_for_query("lsass.exe");
1111 goto err_privilege_not_held;
1114 * Open primary lsass.exe process access token with query and duplicate
1115 * rights. Just these two rights are required for impersonating other
1116 * primary process token (impersonate right is really not required!).
1118 lsass_token = open_process_token_with_rights(lsass_process, TOKEN_QUERY | TOKEN_DUPLICATE);
1120 CloseHandle(lsass_process);
1123 goto err_privilege_not_held;
1126 * After successful open of the primary lsass.exe process access token,
1127 * assign its copy for the current thread.
1129 if (!change_token(lsass_token, &old_token))
1130 goto err_privilege_not_held;
1132 revert_to_old_token = TRUE;
1135 if (ret || GetLastError() != ERROR_PRIVILEGE_NOT_HELD)
1139 * Now current thread is not using primary process token anymore
1140 * but is using custom access token. There is no need to revert
1141 * enabled Tcb privilege as the whole custom access token would
1142 * be reverted. So there is no need to setup revert method for
1143 * enabling privilege.
1145 if (have_privilege(luid_tcb_privilege) ||
1146 !enable_privilege(luid_tcb_privilege, NULL, NULL))
1147 goto err_privilege_not_held;
1152 err_privilege_not_held:
1153 SetLastError(ERROR_PRIVILEGE_NOT_HELD);
1158 if (revert_to_old_token)
1159 revert_to_token(old_token);
1161 if (impersonate_privilege_enabled)
1162 revert_privilege(luid_impersonate_privilege, revert_token_impersonate_privilege, revert_only_impersonate_privilege);
1165 CloseHandle(lsass_token);
1171 * ProcessUserModeIOPL is syscall for NT kernel to change x86 IOPL
1172 * of the current running process to 3.
1174 * Process handle argument for ProcessUserModeIOPL is ignored and
1175 * IOPL is always changed for the current running process. So pass
1176 * GetCurrentProcess() handle for documentation purpose. Process
1177 * information buffer and length are unused for ProcessUserModeIOPL.
1179 * ProcessUserModeIOPL may success (return value >= 0) or may fail
1180 * because it is not implemented or because of missing privilege.
1181 * Other errors are not defined, so handle them as unknown.
1184 SetProcessUserModeIOPLFunc(LPVOID Arg)
1186 NtSetInformationProcessProt NtSetInformationProcessPtr = (NtSetInformationProcessProt)Arg;
1187 NTSTATUS nt_status = NtSetInformationProcessPtr(GetCurrentProcess(), ProcessUserModeIOPL, NULL, 0);
1191 if (nt_status == STATUS_NOT_IMPLEMENTED)
1192 SetLastError(ERROR_INVALID_FUNCTION);
1193 else if (nt_status == STATUS_PRIVILEGE_NOT_HELD)
1194 SetLastError(ERROR_PRIVILEGE_NOT_HELD);
1195 else /* TODO: convert NT STATUS to WIN32 ERROR */
1196 SetLastError(ERROR_GEN_FAILURE);
1202 * Set x86 I/O Privilege Level to 3 for the whole current NT process. Do it via
1203 * NtSetInformationProcess() call with ProcessUserModeIOPL information class,
1204 * which is supported by 32-bit Windows NT kernel versions and requires Tcb
1208 SetProcessUserModeIOPL(VOID)
1210 NtSetInformationProcessProt NtSetInformationProcessPtr;
1211 UINT prev_error_mode;
1216 * Load ntdll.dll library with disabled critical-error-handler message box.
1217 * It means that NT kernel does not show unwanted GUI message box to user
1218 * when LoadLibrary() function fails.
1220 prev_error_mode = change_error_mode(SEM_FAILCRITICALERRORS);
1221 ntdll = LoadLibrary(TEXT("ntdll.dll"));
1222 change_error_mode(prev_error_mode);
1225 SetLastError(ERROR_INVALID_FUNCTION);
1229 /* Retrieve pointer to NtSetInformationProcess() function. */
1230 NtSetInformationProcessPtr = (NtSetInformationProcessProt)(LPVOID)GetProcAddress(ntdll, "NtSetInformationProcess");
1231 if (!NtSetInformationProcessPtr)
1234 SetLastError(ERROR_INVALID_FUNCTION);
1238 /* Call ProcessUserModeIOPL with Tcb privilege. */
1239 ret = CallFuncWithTcbPrivilege(SetProcessUserModeIOPLFunc, (LPVOID)NtSetInformationProcessPtr);
1247 * Some Windows NT kernel versions (e.g. Windows 2003 x64) do not
1248 * implement ProcessUserModeIOPL syscall at all but incorrectly
1249 * returns success when it is called by user process. So always
1250 * after this call verify that IOPL is set to 3.
1252 if (read_iopl() != 3)
1254 SetLastError(ERROR_INVALID_FUNCTION);
1262 intel_setup_io(struct pci_access *a)
1265 /* 16/32-bit non-NT systems allow applications to access PCI I/O ports without any special setup. */
1266 OSVERSIONINFOA version;
1267 version.dwOSVersionInfoSize = sizeof(version);
1268 if (GetVersionExA(&version) && version.dwPlatformId < VER_PLATFORM_WIN32_NT)
1270 a->debug("Detected 16/32-bit non-NT system, skipping NT setup...");
1275 /* Check if we have I/O permission */
1276 if (read_iopl() == 3)
1278 a->debug("IOPL is already set to 3, skipping NT setup...");
1282 /* On NT-based systems issue ProcessUserModeIOPL syscall which changes IOPL to 3. */
1283 if (!SetProcessUserModeIOPL())
1285 DWORD error = GetLastError();
1286 a->debug("NT ProcessUserModeIOPL call failed: %s.", error == ERROR_INVALID_FUNCTION ? "Not Implemented" : error == ERROR_PRIVILEGE_NOT_HELD ? "Access Denied" : "Operation Failed");
1290 a->debug("NT ProcessUserModeIOPL call succeeded...");
1295 intel_cleanup_io(struct pci_access *a UNUSED)
1298 * 16/32-bit non-NT systems do not use any special setup and on NT-based
1299 * systems ProcessUserModeIOPL permanently changes IOPL to 3 for the current
1300 * NT process, no revert for current process is possible.
1304 static inline void intel_io_lock(void)
1308 static inline void intel_io_unlock(void)