2 * The PCI Library -- Win32 helper functions
4 * Copyright (c) 2023 Pali Rohár <pali@kernel.org>
6 * Can be freely distributed and used under the terms of the GNU GPL v2+
8 * SPDX-License-Identifier: GPL-2.0-or-later
13 #include <stdio.h> /* for sprintf() */
15 #include "win32-helpers.h"
17 /* Unfortunately i586-mingw32msvc toolchain does not provide this constant. */
18 #ifndef PROCESS_QUERY_LIMITED_INFORMATION
19 #define PROCESS_QUERY_LIMITED_INFORMATION 0x1000
22 /* Unfortunately some toolchains do not provide this constant. */
23 #ifndef SE_IMPERSONATE_NAME
24 #define SE_IMPERSONATE_NAME TEXT("SeImpersonatePrivilege")
27 /* Unfortunately some toolchains do not provide these constants. */
28 #ifndef SE_DACL_AUTO_INHERIT_REQ
29 #define SE_DACL_AUTO_INHERIT_REQ 0x0100
31 #ifndef SE_SACL_AUTO_INHERIT_REQ
32 #define SE_SACL_AUTO_INHERIT_REQ 0x0200
34 #ifndef SE_DACL_AUTO_INHERITED
35 #define SE_DACL_AUTO_INHERITED 0x0400
37 #ifndef SE_SACL_AUTO_INHERITED
38 #define SE_SACL_AUTO_INHERITED 0x0800
42 * These psapi functions are available in kernel32.dll library with K32 prefix
43 * on Windows 7 and higher systems. On older Windows systems these functions are
44 * available in psapi.dll libary without K32 prefix. So resolve pointers to
45 * these functions dynamically at runtime from the available system library.
46 * Function GetProcessImageFileNameW() is not available on Windows 2000 and
49 typedef BOOL (WINAPI *EnumProcessesProt)(DWORD *lpidProcess, DWORD cb, DWORD *cbNeeded);
50 typedef DWORD (WINAPI *GetProcessImageFileNameWProt)(HANDLE hProcess, LPWSTR lpImageFileName, DWORD nSize);
51 typedef DWORD (WINAPI *GetModuleFileNameExWProt)(HANDLE hProcess, HMODULE hModule, LPWSTR lpImageFileName, DWORD nSize);
54 * These aclapi function is available in advapi.dll library on Windows 2000
57 typedef BOOL (WINAPI *SetSecurityDescriptorControlProt)(PSECURITY_DESCRIPTOR pSecurityDescriptor, SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest, SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet);
60 * This errhandlingapi function is available in kernel32.dll library on
61 * Windows 7 and higher systems.
63 typedef BOOL (WINAPI *SetThreadErrorModeProt)(DWORD dwNewMode, LPDWORD lpOldMode);
67 win32_strerror(DWORD win32_error_id)
70 * Use static buffer which is large enough.
71 * Hopefully no Win32 API error message string is longer than 4 kB.
73 static char buffer[4096];
76 len = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, win32_error_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer), NULL);
78 /* FormatMessage() automatically appends ".\r\n" to the error message. */
79 if (len && buffer[len-1] == '\n')
81 if (len && buffer[len-1] == '\r')
83 if (len && buffer[len-1] == '.')
87 sprintf(buffer, "Unknown Win32 error %lu", win32_error_id);
93 win32_is_non_nt_system(void)
95 OSVERSIONINFOA version;
96 version.dwOSVersionInfoSize = sizeof(version);
97 return GetVersionExA(&version) && version.dwPlatformId < VER_PLATFORM_WIN32_NT;
101 win32_is_32bit_on_64bit_system(void)
103 BOOL (WINAPI *MyIsWow64Process)(HANDLE, PBOOL);
108 * Check for 64-bit system via IsWow64Process() function exported
109 * from 32-bit kernel32.dll library available on the 64-bit systems.
110 * Resolve pointer to this function at runtime as this code path is
111 * primary running on 32-bit systems where are not available 64-bit
115 kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
119 MyIsWow64Process = (void *)GetProcAddress(kernel32, "IsWow64Process");
120 if (!MyIsWow64Process)
123 if (!MyIsWow64Process(GetCurrentProcess(), &is_wow64))
130 win32_is_32bit_on_win8_64bit_system(void)
135 OSVERSIONINFOA version;
137 /* Check for Windows 8 (NT 6.2). */
138 version.dwOSVersionInfoSize = sizeof(version);
139 if (!GetVersionExA(&version) ||
140 version.dwPlatformId != VER_PLATFORM_WIN32_NT ||
141 version.dwMajorVersion < 6 ||
142 (version.dwMajorVersion == 6 && version.dwMinorVersion < 2))
145 return win32_is_32bit_on_64bit_system();
150 * Change error mode of the current thread. If it is not possible then change
151 * error mode of the whole process. Always returns previous error mode.
154 win32_change_error_mode(UINT new_mode)
156 SetThreadErrorModeProt MySetThreadErrorMode = NULL;
161 * Function SetThreadErrorMode() was introduced in Windows 7, so use
162 * GetProcAddress() for compatibility with older systems.
164 kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
166 MySetThreadErrorMode = (SetThreadErrorModeProt)(LPVOID)GetProcAddress(kernel32, "SetThreadErrorMode");
168 if (MySetThreadErrorMode &&
169 MySetThreadErrorMode(new_mode, &old_mode))
173 * Fallback to function SetErrorMode() which modifies error mode of the
174 * whole process and returns old mode.
176 return SetErrorMode(new_mode);
180 * Check if the current thread has particular privilege in current active access
181 * token. Case when it not possible to determinate it (e.g. current thread does
182 * not have permission to open its own current active access token) is evaluated
183 * as thread does not have that privilege.
186 win32_have_privilege(LUID luid_privilege)
193 * If the current thread does not have active access token then thread
194 * uses primary process access token for all permission checks.
196 if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token) &&
197 (GetLastError() != ERROR_NO_TOKEN ||
198 !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)))
201 priv.PrivilegeCount = 1;
202 priv.Control = PRIVILEGE_SET_ALL_NECESSARY;
203 priv.Privilege[0].Luid = luid_privilege;
204 priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
206 if (!PrivilegeCheck(token, &priv, &ret))
213 * Enable or disable particular privilege in specified access token.
215 * Note that it is not possible to disable privilege in access token with
216 * SE_PRIVILEGE_ENABLED_BY_DEFAULT attribute. This function does not check
217 * this case and incorrectly returns no error even when disabling failed.
218 * Rationale for this decision: Simplification of this function as WinAPI
219 * call AdjustTokenPrivileges() does not signal error in this case too.
222 set_privilege(HANDLE token, LUID luid_privilege, BOOL enable)
224 TOKEN_PRIVILEGES token_privileges;
226 token_privileges.PrivilegeCount = 1;
227 token_privileges.Privileges[0].Luid = luid_privilege;
228 token_privileges.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
231 * WinAPI function AdjustTokenPrivileges() success also when not all
232 * privileges were enabled. It is always required to check for failure
233 * via GetLastError() call. AdjustTokenPrivileges() always sets error
234 * also when it success, as opposite to other WinAPI functions.
236 if (!AdjustTokenPrivileges(token, FALSE, &token_privileges, sizeof(token_privileges), NULL, NULL) ||
237 GetLastError() != ERROR_SUCCESS)
244 * Change access token for the current thread to new specified access token.
245 * Previously active access token is stored in old_token variable and can be
246 * used for reverting to this access token. It is set to NULL if the current
247 * thread previously used primary process access token.
250 win32_change_token(HANDLE new_token, HANDLE *old_token)
254 if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &token))
256 if (GetLastError() != ERROR_NO_TOKEN)
261 if (!ImpersonateLoggedOnUser(new_token))
273 * Change access token for the current thread to the primary process access
274 * token. This function fails also when the current thread already uses primary
275 * process access token.
278 change_token_to_primary(HANDLE *old_token)
282 if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &token))
292 * Revert to the specified access token for the current thread. When access
293 * token is specified as NULL then revert to the primary process access token.
294 * Use to revert after win32_change_token() or change_token_to_primary() call.
297 win32_revert_to_token(HANDLE token)
300 * If SetThreadToken() call fails then there is no option to revert to
301 * the specified previous thread access token. So in this case revert to
302 * the primary process access token.
304 if (!token || !SetThreadToken(NULL, token))
311 * Enable particular privilege for the current thread. And set method how to
312 * revert this privilege (if to revert whole token or only privilege).
315 win32_enable_privilege(LUID luid_privilege, HANDLE *revert_token, BOOL *revert_only_privilege)
320 if (OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &thread_token))
322 if (set_privilege(thread_token, luid_privilege, TRUE))
325 * Indicate that correct revert method is just to
326 * disable privilege in access token.
328 if (revert_token && revert_only_privilege)
330 *revert_token = thread_token;
331 *revert_only_privilege = TRUE;
335 CloseHandle(thread_token);
339 CloseHandle(thread_token);
341 * If enabling privilege failed then try to enable it via
342 * primary process access token.
347 * If the current thread has already active thread access token then
348 * open it with just impersonate right as it would be used only for
351 if (revert_token && revert_only_privilege)
353 if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &thread_token))
355 if (GetLastError() != ERROR_NO_TOKEN)
361 * If current thread has no access token (and uses primary
362 * process access token) or it does not have permission to
363 * adjust privileges or it does not have specified privilege
364 * then create a copy of the primary process access token,
365 * assign it for the current thread (= impersonate self)
366 * and then try adjusting privilege again.
368 if (!ImpersonateSelf(SecurityImpersonation))
371 CloseHandle(thread_token);
376 if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &new_token))
378 /* thread_token is set only when we were asked for revert method. */
379 if (revert_token && revert_only_privilege)
380 win32_revert_to_token(thread_token);
384 if (!set_privilege(new_token, luid_privilege, TRUE))
386 CloseHandle(new_token);
387 /* thread_token is set only when we were asked for revert method. */
388 if (revert_token && revert_only_privilege)
389 win32_revert_to_token(thread_token);
394 * Indicate that correct revert method is to change to the previous
395 * access token. Either to the primary process access token or to the
396 * previous thread access token.
398 if (revert_token && revert_only_privilege)
400 *revert_token = thread_token;
401 *revert_only_privilege = FALSE;
407 * Revert particular privilege for the current thread was previously enabled by
408 * win32_enable_privilege() call. Either disable privilege in specified access token
409 * or revert to previous access token.
412 win32_revert_privilege(LUID luid_privilege, HANDLE revert_token, BOOL revert_only_privilege)
414 if (revert_only_privilege)
416 set_privilege(revert_token, luid_privilege, FALSE);
417 CloseHandle(revert_token);
421 win32_revert_to_token(revert_token);
426 * Return owner of the access token used by the current thread. Buffer for
427 * returned owner needs to be released by LocalFree() call.
430 get_current_token_owner(VOID)
437 * If the current thread does not have active access token then thread
438 * uses primary process access token for all permission checks.
440 if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token) &&
441 (GetLastError() != ERROR_NO_TOKEN ||
442 !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)))
445 if (!GetTokenInformation(token, TokenOwner, NULL, 0, &length) &&
446 GetLastError() != ERROR_INSUFFICIENT_BUFFER)
453 owner = (TOKEN_OWNER *)LocalAlloc(LPTR, length);
460 if (!GetTokenInformation(token, TokenOwner, owner, length, &length))
463 * Length of token owner (SID) buffer between two get calls may
464 * changes (e.g. by another thread of process), so retry.
466 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
481 * Create a new security descriptor in absolute form from relative form.
482 * Newly created security descriptor in absolute form is stored in linear buffer.
484 static PSECURITY_DESCRIPTOR
485 create_relsd_from_abssd(PSECURITY_DESCRIPTOR rel_security_descriptor)
487 PBYTE abs_security_descriptor_buffer;
488 DWORD abs_security_descriptor_size=0, abs_dacl_size=0, abs_sacl_size=0, abs_owner_size=0, abs_primary_group_size=0;
490 if (!MakeAbsoluteSD(rel_security_descriptor,
491 NULL, &abs_security_descriptor_size,
492 NULL, &abs_dacl_size,
493 NULL, &abs_sacl_size,
494 NULL, &abs_owner_size,
495 NULL, &abs_primary_group_size) && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
498 abs_security_descriptor_buffer = (PBYTE)LocalAlloc(LPTR, abs_security_descriptor_size+abs_dacl_size+abs_sacl_size+abs_owner_size+abs_primary_group_size);
499 if (!abs_security_descriptor_buffer)
502 if (!MakeAbsoluteSD(rel_security_descriptor,
503 (PSECURITY_DESCRIPTOR)abs_security_descriptor_buffer, &abs_security_descriptor_size,
504 (PACL)(abs_security_descriptor_buffer+abs_security_descriptor_size), &abs_dacl_size,
505 (PACL)(abs_security_descriptor_buffer+abs_security_descriptor_size+abs_dacl_size), &abs_sacl_size,
506 (PSID)(abs_security_descriptor_buffer+abs_security_descriptor_size+abs_dacl_size+abs_sacl_size), &abs_owner_size,
507 (PSID)(abs_security_descriptor_buffer+abs_security_descriptor_size+abs_dacl_size+abs_sacl_size+abs_owner_size), &abs_primary_group_size))
510 return (PSECURITY_DESCRIPTOR)abs_security_descriptor_buffer;
514 * Prepare security descriptor obtained by GetKernelObjectSecurity() so it can be
515 * passed to SetKernelObjectSecurity() as identity operation. It modifies control
516 * flags of security descriptor, which is needed for Windows 2000 and new.
519 prepare_security_descriptor_for_set_operation(PSECURITY_DESCRIPTOR security_descriptor)
521 SetSecurityDescriptorControlProt MySetSecurityDescriptorControl;
522 SECURITY_DESCRIPTOR_CONTROL bits_mask;
523 SECURITY_DESCRIPTOR_CONTROL bits_set;
524 SECURITY_DESCRIPTOR_CONTROL control;
525 OSVERSIONINFO version;
530 * SE_DACL_AUTO_INHERITED and SE_SACL_AUTO_INHERITED are flags introduced in
531 * Windows 2000 to control client-side automatic inheritance (client - user
532 * process - is responsible for propagating inherited ACEs to subobjects).
533 * To prevent applications which do not understand client-side automatic
534 * inheritance (applications created prior Windows 2000 or which use low
535 * level API like SetKernelObjectSecurity()) to unintentionally set those
536 * SE_DACL_AUTO_INHERITED and SE_SACL_AUTO_INHERITED control flags when
537 * coping them from other security descriptor.
539 * As we are not modifying existing ACEs, we are compatible with Windows 2000
540 * client-side automatic inheritance model and therefore prepare security
541 * descriptor for SetKernelObjectSecurity() to not clear existing automatic
542 * inheritance control flags.
544 * Control flags SE_DACL_AUTO_INHERITED and SE_SACL_AUTO_INHERITED are set
545 * into security object only when they are set together with set-only flags
546 * SE_DACL_AUTO_INHERIT_REQ and SE_SACL_AUTO_INHERIT_REQ. Those flags are
547 * never received by GetKernelObjectSecurity() and are just commands for
548 * SetKernelObjectSecurity() how to interpret SE_DACL_AUTO_INHERITED and
549 * SE_SACL_AUTO_INHERITED flags.
551 * Function symbol SetSecurityDescriptorControl is not available in the
552 * older versions of advapi32.dll library, so resolve it at runtime.
555 version.dwOSVersionInfoSize = sizeof(version);
556 if (!GetVersionEx(&version) ||
557 version.dwPlatformId != VER_PLATFORM_WIN32_NT ||
558 version.dwMajorVersion < 5)
561 if (!GetSecurityDescriptorControl(security_descriptor, &control, &revision))
567 if (control & SE_DACL_AUTO_INHERITED)
569 bits_mask |= SE_DACL_AUTO_INHERIT_REQ;
570 bits_set |= SE_DACL_AUTO_INHERIT_REQ;
573 if (control & SE_SACL_AUTO_INHERITED)
575 bits_mask |= SE_SACL_AUTO_INHERIT_REQ;
576 bits_set |= SE_SACL_AUTO_INHERIT_REQ;
582 advapi32 = GetModuleHandle(TEXT("advapi32.dll"));
586 MySetSecurityDescriptorControl = (SetSecurityDescriptorControlProt)(LPVOID)GetProcAddress(advapi32, "SetSecurityDescriptorControl");
587 if (!MySetSecurityDescriptorControl)
590 if (!MySetSecurityDescriptorControl(security_descriptor, bits_mask, bits_set))
597 * Grant particular permissions in the primary access token of the specified
598 * process for the owner of current thread token and set old DACL of the
599 * process access token for reverting permissions. Security descriptor is
600 * just memory buffer for old DACL.
603 grant_process_token_dacl_permissions(HANDLE process, DWORD permissions, HANDLE *token, PSECURITY_DESCRIPTOR *old_security_descriptor)
607 BOOL old_dacl_present;
608 BOOL old_dacl_defaulted;
611 PSECURITY_DESCRIPTOR new_security_descriptor;
614 owner = get_current_token_owner();
619 * READ_CONTROL is required for GetSecurityInfo(DACL_SECURITY_INFORMATION)
620 * and WRITE_DAC is required for SetSecurityInfo(DACL_SECURITY_INFORMATION).
622 if (!OpenProcessToken(process, READ_CONTROL | WRITE_DAC, token))
628 if (!GetKernelObjectSecurity(*token, DACL_SECURITY_INFORMATION, NULL, 0, &length) && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
636 *old_security_descriptor = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, length);
637 if (!*old_security_descriptor)
644 if (!GetKernelObjectSecurity(*token, DACL_SECURITY_INFORMATION, *old_security_descriptor, length, &length))
647 * Length of the security descriptor between two get calls
648 * may changes (e.g. by another thread of process), so retry.
650 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
652 LocalFree(*old_security_descriptor);
655 LocalFree(*old_security_descriptor);
661 if (!prepare_security_descriptor_for_set_operation(*old_security_descriptor))
663 LocalFree(*old_security_descriptor);
669 /* Retrieve the current DACL from security descriptor including present and defaulted properties. */
670 if (!GetSecurityDescriptorDacl(*old_security_descriptor, &old_dacl_present, &old_dacl, &old_dacl_defaulted))
672 LocalFree(*old_security_descriptor);
679 * If DACL is not present then system grants full access to everyone. It this
680 * case do not modify DACL as it just adds one ACL allow rule for us, which
681 * automatically disallow access to anybody else which had access before.
683 if (!old_dacl_present || !old_dacl)
685 LocalFree(*old_security_descriptor);
687 *old_security_descriptor = NULL;
691 /* Create new DACL which would be copy of the current old one. */
692 new_dacl_size = old_dacl->AclSize + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(owner->Owner) - sizeof(DWORD);
693 new_dacl = (PACL)LocalAlloc(LPTR, new_dacl_size);
696 LocalFree(*old_security_descriptor);
703 * Initialize new DACL structure to the same format as was the old one.
704 * Set new explicit access for the owner of the current thread access
705 * token with non-inherited granting access to specified permissions.
706 * This permission is added in the first ACE, so has the highest priority.
708 if (!InitializeAcl(new_dacl, new_dacl_size, old_dacl->AclRevision) ||
709 !AddAccessAllowedAce(new_dacl, ACL_REVISION2, permissions, owner->Owner))
712 LocalFree(*old_security_descriptor);
719 * Now (after setting our new permissions) append all ACE entries from the
720 * old DACL to the new DACL, which preserve all other existing permissions.
722 if (old_dacl->AceCount > 0)
727 for (ace_index = 0; ace_index < old_dacl->AceCount; ace_index++)
729 if (!GetAce(old_dacl, ace_index, &ace) ||
730 !AddAce(new_dacl, old_dacl->AclRevision, MAXDWORD, ace, ((PACE_HEADER)ace)->AceSize))
733 LocalFree(*old_security_descriptor);
742 * Create copy of the old security descriptor, so we can modify its DACL.
743 * Function SetSecurityDescriptorDacl() works only with security descriptors
744 * in absolute format. So use our helper function create_relsd_from_abssd()
745 * for converting security descriptor from relative format (which is returned
746 * by GetKernelObjectSecurity() function) to the absolute format.
748 new_security_descriptor = create_relsd_from_abssd(*old_security_descriptor);
749 if (!new_security_descriptor)
752 LocalFree(*old_security_descriptor);
759 * In the new security descriptor replace old DACL by the new DACL (which has
760 * new permissions) and then set this new security descriptor to the token,
761 * so token would have new access permissions.
763 if (!SetSecurityDescriptorDacl(new_security_descriptor, TRUE, new_dacl, FALSE) ||
764 !SetKernelObjectSecurity(*token, DACL_SECURITY_INFORMATION, new_security_descriptor))
766 LocalFree(new_security_descriptor);
768 LocalFree(*old_security_descriptor);
774 LocalFree(new_security_descriptor);
781 * Revert particular granted permissions in specified access token done by
782 * grant_process_token_dacl_permissions() call.
785 revert_token_dacl_permissions(HANDLE token, PSECURITY_DESCRIPTOR old_security_descriptor)
787 SetKernelObjectSecurity(token, DACL_SECURITY_INFORMATION, old_security_descriptor);
788 LocalFree(old_security_descriptor);
793 * Open process handle specified by the process id with the query right and
794 * optionally also with vm read right.
797 open_process_for_query(DWORD pid, BOOL with_vm_read)
799 BOOL revert_only_privilege;
800 LUID luid_debug_privilege;
801 OSVERSIONINFO version;
807 * Some processes on Windows Vista and higher systems can be opened only
808 * with PROCESS_QUERY_LIMITED_INFORMATION right. This right is enough
809 * for accessing primary process token. But this right is not supported
810 * on older pre-Vista systems. When the current thread on these older
811 * systems does not have Debug privilege then OpenProcess() fails with
812 * ERROR_ACCESS_DENIED. If the current thread has Debug privilege then
813 * OpenProcess() success and returns handle to requested process.
814 * Problem is that this handle does not have PROCESS_QUERY_INFORMATION
815 * right and so cannot be used for accessing primary process token
816 * on those older systems. Moreover it has zero rights and therefore
817 * such handle is fully useless. So never try to use open process with
818 * PROCESS_QUERY_LIMITED_INFORMATION right on older systems than
819 * Windows Vista (NT 6.0).
821 version.dwOSVersionInfoSize = sizeof(version);
822 if (GetVersionEx(&version) &&
823 version.dwPlatformId == VER_PLATFORM_WIN32_NT &&
824 version.dwMajorVersion >= 6)
825 process_right = PROCESS_QUERY_LIMITED_INFORMATION;
827 process_right = PROCESS_QUERY_INFORMATION;
830 process_right |= PROCESS_VM_READ;
832 process = OpenProcess(process_right, FALSE, pid);
837 * It is possible to open only processes to which owner of the current
838 * thread access token has permissions. For opening other processing it
839 * is required to have Debug privilege enabled. By default local
840 * administrators have this privilege, but it is disabled. So try to
841 * enable it and then try to open process again.
844 if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid_debug_privilege))
847 if (!win32_enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege))
850 process = OpenProcess(process_right, FALSE, pid);
852 win32_revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
858 * Check if process image path name (wide string) matches exe file name
859 * (7-bit ASCII string). Do case-insensitive string comparison. Process
860 * image path name can be in any namespace format (DOS, Win32, UNC, ...).
863 check_process_name(LPCWSTR path, DWORD path_length, LPCSTR exe_file)
865 DWORD exe_file_length;
871 while (exe_file[exe_file_length] != '\0')
874 /* Path must have backslash before exe file name. */
875 if (exe_file_length >= path_length ||
876 path[path_length-exe_file_length-1] != L'\\')
879 for (i = 0; i < exe_file_length; i++)
881 c1 = path[path_length-exe_file_length+i];
884 * Input string for comparison is 7-bit ASCII and file name part
885 * of path must not contain backslash as it is path separator.
887 if (c1 >= 0x80 || c2 >= 0x80 || c1 == L'\\')
889 if (c1 >= L'a' && c1 <= L'z')
891 if (c2 >= 'a' && c2 <= 'z')
900 /* Open process handle with the query right specified by process exe file. */
902 win32_find_and_open_process_for_query(LPCSTR exe_file)
904 GetProcessImageFileNameWProt MyGetProcessImageFileNameW;
905 GetModuleFileNameExWProt MyGetModuleFileNameExW;
906 EnumProcessesProt MyEnumProcesses;
907 HMODULE kernel32, psapi;
908 UINT prev_error_mode;
920 kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
925 * On Windows 7 and higher systems these functions are available in
926 * kernel32.dll library with K32 prefix.
928 MyGetModuleFileNameExW = NULL;
929 MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(LPVOID)GetProcAddress(kernel32, "K32GetProcessImageFileNameW");
930 MyEnumProcesses = (EnumProcessesProt)(LPVOID)GetProcAddress(kernel32, "K32EnumProcesses");
931 if (!MyGetProcessImageFileNameW || !MyEnumProcesses)
934 * On older NT-based systems these functions are available in
935 * psapi.dll library without K32 prefix.
937 prev_error_mode = win32_change_error_mode(SEM_FAILCRITICALERRORS);
938 psapi = LoadLibrary(TEXT("psapi.dll"));
939 win32_change_error_mode(prev_error_mode);
945 * Function GetProcessImageFileNameW() is available in
946 * Windows XP and higher systems. On older versions is
947 * available function GetModuleFileNameExW().
949 MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(LPVOID)GetProcAddress(psapi, "GetProcessImageFileNameW");
950 MyGetModuleFileNameExW = (GetModuleFileNameExWProt)(LPVOID)GetProcAddress(psapi, "GetModuleFileNameExW");
951 MyEnumProcesses = (EnumProcessesProt)(LPVOID)GetProcAddress(psapi, "EnumProcesses");
952 if ((!MyGetProcessImageFileNameW && !MyGetModuleFileNameExW) || !MyEnumProcesses)
959 /* Make initial buffer size for 1024 processes. */
960 size = 1024 * sizeof(*processes);
963 processes = (DWORD *)LocalAlloc(LPTR, size);
971 if (!MyEnumProcesses(processes, size, &length))
973 LocalFree(processes);
978 else if (size == length)
981 * There is no indication given when the buffer is too small to
982 * store all process identifiers. Therefore if returned length
983 * is same as buffer size there can be more processes. Call
984 * again with larger buffer.
986 LocalFree(processes);
992 count = length / sizeof(*processes);
994 for (i = 0; i < count; i++)
996 /* Skip System Idle Process. */
997 if (processes[i] == 0)
1001 * Function GetModuleFileNameExW() requires additional
1002 * PROCESS_VM_READ right as opposite to function
1003 * GetProcessImageFileNameW() which does not need it.
1005 process = open_process_for_query(processes[i], MyGetProcessImageFileNameW ? FALSE : TRUE);
1010 * Set initial buffer size to 256 (wide) characters.
1011 * Final path length on the modern NT-based systems can be also larger.
1014 found_process = FALSE;
1018 path = (LPWSTR)LocalAlloc(LPTR, size * sizeof(*path));
1022 if (MyGetProcessImageFileNameW)
1023 length = MyGetProcessImageFileNameW(process, path, size);
1025 length = MyGetModuleFileNameExW(process, NULL, path, size);
1027 error = GetLastError();
1030 * GetModuleFileNameEx() returns zero and signal error ERROR_PARTIAL_COPY
1031 * when remote process is in the middle of updating its module table.
1032 * Sleep 10 ms and try again, max 10 attempts.
1034 if (!MyGetProcessImageFileNameW)
1036 if (length == 0 && error == ERROR_PARTIAL_COPY && partial_retry++ < 10)
1045 * When buffer is too small then function GetModuleFileNameEx() returns
1046 * its size argument on older systems (Windows XP) or its size minus
1047 * argument one on new systems (Windows 10) without signalling any error.
1048 * Function GetProcessImageFileNameW() on the other hand returns zero
1049 * value and signals error ERROR_INSUFFICIENT_BUFFER. So in all these
1050 * cases call function again with larger buffer.
1053 if (MyGetProcessImageFileNameW && length == 0 && error != ERROR_INSUFFICIENT_BUFFER)
1056 if ((MyGetProcessImageFileNameW && length == 0) ||
1057 (!MyGetProcessImageFileNameW && (length == size || length == size-1)))
1064 if (length && check_process_name(path, length, exe_file))
1065 found_process = TRUE;
1077 CloseHandle(process);
1081 LocalFree(processes);
1090 * Try to open primary access token of the particular process with specified
1091 * rights. Before opening access token try to adjust DACL permissions of the
1092 * primary process access token, so following open does not fail on error
1093 * related to no open permissions. Revert DACL permissions after open attempt.
1094 * As following steps are not atomic, try to execute them more times in case
1095 * of possible race conditions caused by other threads or processes.
1098 try_grant_permissions_and_open_process_token(HANDLE process, DWORD rights)
1100 PSECURITY_DESCRIPTOR old_security_descriptor;
1107 * This code is not atomic. Between grant and open calls can other
1108 * thread or process change or revert permissions. So try to execute
1111 for (retry = 0; retry < 10; retry++)
1113 if (!grant_process_token_dacl_permissions(process, rights, &grant_token, &old_security_descriptor))
1115 if (!OpenProcessToken(process, rights, &token))
1118 error = GetLastError();
1120 if (old_security_descriptor)
1121 revert_token_dacl_permissions(grant_token, old_security_descriptor);
1124 else if (error != ERROR_ACCESS_DENIED)
1132 * Open primary access token of particular process handle with specified rights.
1133 * If permissions for specified rights are missing then try to grant them.
1136 win32_open_process_token_with_rights(HANDLE process, DWORD rights)
1141 /* First try to open primary access token of process handle directly. */
1142 if (OpenProcessToken(process, rights, &token))
1146 * If opening failed then it means that owner of the current thread
1147 * access token does not have permission for it. Try it again with
1148 * primary process access token.
1150 if (change_token_to_primary(&old_token))
1152 if (!OpenProcessToken(process, rights, &token))
1154 win32_revert_to_token(old_token);
1160 * If opening is still failing then try to grant specified permissions
1161 * for the current thread and try to open it again.
1163 token = try_grant_permissions_and_open_process_token(process, rights);
1168 * And if it is still failing then try it again with granting
1169 * permissions for the primary process token of the current process.
1171 if (change_token_to_primary(&old_token))
1173 token = try_grant_permissions_and_open_process_token(process, rights);
1174 win32_revert_to_token(old_token);
1180 * TODO: Sorry, no other option for now...
1181 * It could be possible to use Take Ownership Name privilege to
1182 * temporary change token owner of specified process to the owner of
1183 * the current thread token, grant permissions for current thread in
1184 * that process token, change ownership back to original one, open
1185 * that process token and revert granted permissions. But this is
1186 * not implemented yet.
1192 * Call supplied function with its argument and if it fails with
1193 * ERROR_PRIVILEGE_NOT_HELD then try to enable Tcb privilege and
1194 * call function with its argument again.
1197 win32_call_func_with_tcb_privilege(BOOL (*function)(LPVOID), LPVOID argument)
1199 LUID luid_tcb_privilege;
1200 LUID luid_impersonate_privilege;
1202 HANDLE revert_token_tcb_privilege;
1203 BOOL revert_only_tcb_privilege;
1205 HANDLE revert_token_impersonate_privilege;
1206 BOOL revert_only_impersonate_privilege;
1208 BOOL impersonate_privilege_enabled;
1210 BOOL revert_to_old_token;
1213 HANDLE lsass_process;
1218 impersonate_privilege_enabled = FALSE;
1219 revert_to_old_token = FALSE;
1223 /* Call supplied function. */
1224 ret = function(argument);
1225 if (ret || GetLastError() != ERROR_PRIVILEGE_NOT_HELD)
1229 * If function call failed with ERROR_PRIVILEGE_NOT_HELD
1230 * error then it means that the current thread token does not have
1231 * Tcb privilege enabled. Try to enable it.
1234 if (!LookupPrivilegeValue(NULL, SE_TCB_NAME, &luid_tcb_privilege))
1235 goto err_privilege_not_held;
1238 * If the current thread has already Tcb privilege enabled then there
1239 * is some additional unhanded restriction.
1241 if (win32_have_privilege(luid_tcb_privilege))
1242 goto err_privilege_not_held;
1244 /* Try to enable Tcb privilege and try function call again. */
1245 if (win32_enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege))
1247 ret = function(argument);
1248 win32_revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege);
1253 * If enabling of Tcb privilege failed then it means that current thread
1254 * does not have this privilege. But current process may have it. So try it
1255 * again with primary process access token.
1259 * If system supports Impersonate privilege (Windows 2000 SP4 or higher) then
1260 * all future actions in this function require this Impersonate privilege.
1261 * So try to enable it in case it is currently disabled.
1263 if (LookupPrivilegeValue(NULL, SE_IMPERSONATE_NAME, &luid_impersonate_privilege) &&
1264 !win32_have_privilege(luid_impersonate_privilege))
1267 * If current thread does not have Impersonate privilege enabled
1268 * then first try to enable it just for the current thread. If
1269 * it is not possible to enable it just for the current thread
1270 * then try it to enable globally for whole process (which
1271 * affects all process threads). Both actions will be reverted
1272 * at the end of this function.
1274 if (win32_enable_privilege(luid_impersonate_privilege, &revert_token_impersonate_privilege, &revert_only_impersonate_privilege))
1276 impersonate_privilege_enabled = TRUE;
1278 else if (win32_enable_privilege(luid_impersonate_privilege, NULL, NULL))
1280 impersonate_privilege_enabled = TRUE;
1281 revert_token_impersonate_privilege = NULL;
1282 revert_only_impersonate_privilege = TRUE;
1286 goto err_privilege_not_held;
1290 * Now when Impersonate privilege is enabled, try to enable Tcb
1291 * privilege again. Enabling other privileges for the current
1292 * thread requires Impersonate privilege, so enabling Tcb again
1295 if (win32_enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege))
1297 ret = function(argument);
1298 win32_revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege);
1304 * If enabling Tcb privilege failed then it means that the current
1305 * thread access token does not have this privilege or does not
1306 * have permission to adjust privileges.
1308 * Try to use more privileged token from Local Security Authority
1309 * Subsystem Service process (lsass.exe) which has Tcb privilege.
1310 * Retrieving this more privileged token is possible for local
1311 * administrators (unless it was disabled by local administrators).
1314 lsass_process = win32_find_and_open_process_for_query("lsass.exe");
1316 goto err_privilege_not_held;
1319 * Open primary lsass.exe process access token with query and duplicate
1320 * rights. Just these two rights are required for impersonating other
1321 * primary process token (impersonate right is really not required!).
1323 lsass_token = win32_open_process_token_with_rights(lsass_process, TOKEN_QUERY | TOKEN_DUPLICATE);
1325 CloseHandle(lsass_process);
1328 goto err_privilege_not_held;
1331 * After successful open of the primary lsass.exe process access token,
1332 * assign its copy for the current thread.
1334 if (!win32_change_token(lsass_token, &old_token))
1335 goto err_privilege_not_held;
1337 revert_to_old_token = TRUE;
1339 ret = function(argument);
1340 if (ret || GetLastError() != ERROR_PRIVILEGE_NOT_HELD)
1344 * Now current thread is not using primary process token anymore
1345 * but is using custom access token. There is no need to revert
1346 * enabled Tcb privilege as the whole custom access token would
1347 * be reverted. So there is no need to setup revert method for
1348 * enabling privilege.
1350 if (win32_have_privilege(luid_tcb_privilege) ||
1351 !win32_enable_privilege(luid_tcb_privilege, NULL, NULL))
1352 goto err_privilege_not_held;
1354 ret = function(argument);
1357 err_privilege_not_held:
1358 SetLastError(ERROR_PRIVILEGE_NOT_HELD);
1363 if (revert_to_old_token)
1364 win32_revert_to_token(old_token);
1366 if (impersonate_privilege_enabled)
1367 win32_revert_privilege(luid_impersonate_privilege, revert_token_impersonate_privilege, revert_only_impersonate_privilege);
1370 CloseHandle(lsass_token);