aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPali Rohár <pali@kernel.org>2023-05-08 14:04:40 +0200
committerMartin Mares <mj@ucw.cz>2024-02-18 15:48:29 +0100
commit899a7ac5d34e25a62ac231333250ec887cbc8fae (patch)
tree823acac5c9ed8ad39daa6852f5cebefc0c9a1548
parentdb5f48e779d7e3599cc3c7595cdf9c37eb71a9d5 (diff)
downloadpciutils-899a7ac5d34e25a62ac231333250ec887cbc8fae.tar.gz
windows: Move common non-I/O port code from i386-io-windows.h to win32-helpers.c
-rw-r--r--lib/i386-io-windows.h919
-rw-r--r--lib/win32-helpers.c886
-rw-r--r--lib/win32-helpers.h8
-rw-r--r--lib/win32-kldbg.c7
-rw-r--r--lib/win32-sysdbg.c11
5 files changed, 919 insertions, 912 deletions
diff --git a/lib/i386-io-windows.h b/lib/i386-io-windows.h
index 69ed821..eff1901 100644
--- a/lib/i386-io-windows.h
+++ b/lib/i386-io-windows.h
@@ -11,7 +11,6 @@
*/
#include <windows.h>
-#include <aclapi.h>
#include "win32-helpers.h"
#include "i386-io-access.h"
@@ -71,43 +70,12 @@ __readeflags(void)
/* Read IOPL of the current process, IOPL is stored in eflag bits [13:12]. */
#define read_iopl() ((__readeflags() >> 12) & 0x3)
-/* Unfortunately i586-mingw32msvc toolchain does not provide this constant. */
-#ifndef PROCESS_QUERY_LIMITED_INFORMATION
-#define PROCESS_QUERY_LIMITED_INFORMATION 0x1000
-#endif
-
/* Unfortunately some toolchains do not provide this constant. */
#ifndef SE_IMPERSONATE_NAME
#define SE_IMPERSONATE_NAME TEXT("SeImpersonatePrivilege")
#endif
/*
- * These psapi functions are available in kernel32.dll library with K32 prefix
- * on Windows 7 and higher systems. On older Windows systems these functions are
- * available in psapi.dll libary without K32 prefix. So resolve pointers to
- * these functions dynamically at runtime from the available system library.
- * Function GetProcessImageFileNameW() is not available on Windows 2000 and
- * older systems.
- */
-typedef BOOL (WINAPI *EnumProcessesProt)(DWORD *lpidProcess, DWORD cb, DWORD *cbNeeded);
-typedef DWORD (WINAPI *GetProcessImageFileNameWProt)(HANDLE hProcess, LPWSTR lpImageFileName, DWORD nSize);
-typedef DWORD (WINAPI *GetModuleFileNameExWProt)(HANDLE hProcess, HMODULE hModule, LPWSTR lpImageFileName, DWORD nSize);
-
-/*
- * These aclapi functions are available in advapi.dll library on Windows NT 4.0
- * and higher systems.
- */
-typedef DWORD (WINAPI *GetSecurityInfoProt)(HANDLE handle, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, PSID *ppsidOwner, PSID *ppsidGroup, PACL *ppDacl, PACL *ppSacl, PSECURITY_DESCRIPTOR *ppSecurityDescriptor);
-typedef DWORD (WINAPI *SetSecurityInfoProt)(HANDLE handle, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, PSID psidOwner, PSID psidGroup, PACL pDacl, PACL pSacl);
-typedef DWORD (WINAPI *SetEntriesInAclProt)(ULONG cCountOfExplicitEntries, PEXPLICIT_ACCESS pListOfExplicitEntries, PACL OldAcl, PACL *NewAcl);
-
-/*
- * This errhandlingapi function is available in kernel32.dll library on
- * Windows 7 and higher systems.
- */
-typedef BOOL (WINAPI *SetThreadErrorModeProt)(DWORD dwNewMode, LPDWORD lpOldMode);
-
-/*
* Unfortunately NtSetInformationProcess() function, ProcessUserModeIOPL
* constant and all other helpers for its usage are not specified in any
* standard WinAPI header file. So define all of required constants and types.
@@ -133,859 +101,6 @@ typedef NTSTATUS (NTAPI *NtSetInformationProcessProt)(HANDLE ProcessHandle, PROC
typedef ULONG (NTAPI *RtlNtStatusToDosErrorProt)(NTSTATUS Status);
/*
- * Check if the current thread has particular privilege in current active access
- * token. Case when it not possible to determinate it (e.g. current thread does
- * not have permission to open its own current active access token) is evaluated
- * as thread does not have that privilege.
- */
-static BOOL
-have_privilege(LUID luid_privilege)
-{
- PRIVILEGE_SET priv;
- HANDLE token;
- BOOL ret;
-
- /*
- * If the current thread does not have active access token then thread
- * uses primary process access token for all permission checks.
- */
- if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token) &&
- (GetLastError() != ERROR_NO_TOKEN ||
- !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)))
- return FALSE;
-
- priv.PrivilegeCount = 1;
- priv.Control = PRIVILEGE_SET_ALL_NECESSARY;
- priv.Privilege[0].Luid = luid_privilege;
- priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
-
- if (!PrivilegeCheck(token, &priv, &ret))
- return FALSE;
-
- return ret;
-}
-
-/*
- * Enable or disable particular privilege in specified access token.
- *
- * Note that it is not possible to disable privilege in access token with
- * SE_PRIVILEGE_ENABLED_BY_DEFAULT attribute. This function does not check
- * this case and incorrectly returns no error even when disabling failed.
- * Rationale for this decision: Simplification of this function as WinAPI
- * call AdjustTokenPrivileges() does not signal error in this case too.
- */
-static BOOL
-set_privilege(HANDLE token, LUID luid_privilege, BOOL enable)
-{
- TOKEN_PRIVILEGES token_privileges;
-
- token_privileges.PrivilegeCount = 1;
- token_privileges.Privileges[0].Luid = luid_privilege;
- token_privileges.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
-
- /*
- * WinAPI function AdjustTokenPrivileges() success also when not all
- * privileges were enabled. It is always required to check for failure
- * via GetLastError() call. AdjustTokenPrivileges() always sets error
- * also when it success, as opposite to other WinAPI functions.
- */
- if (!AdjustTokenPrivileges(token, FALSE, &token_privileges, sizeof(token_privileges), NULL, NULL) ||
- GetLastError() != ERROR_SUCCESS)
- return FALSE;
-
- return TRUE;
-}
-
-/*
- * Change access token for the current thread to new specified access token.
- * Previously active access token is stored in old_token variable and can be
- * used for reverting to this access token. It is set to NULL if the current
- * thread previously used primary process access token.
- */
-static BOOL
-change_token(HANDLE new_token, HANDLE *old_token)
-{
- HANDLE token;
-
- if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &token))
- {
- if (GetLastError() != ERROR_NO_TOKEN)
- return FALSE;
- token = NULL;
- }
-
- if (!ImpersonateLoggedOnUser(new_token))
- {
- if (token)
- CloseHandle(token);
- return FALSE;
- }
-
- *old_token = token;
- return TRUE;
-}
-
-/*
- * Change access token for the current thread to the primary process access
- * token. This function fails also when the current thread already uses primary
- * process access token.
- */
-static BOOL
-change_token_to_primary(HANDLE *old_token)
-{
- HANDLE token;
-
- if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &token))
- return FALSE;
-
- RevertToSelf();
-
- *old_token = token;
- return TRUE;
-}
-
-/*
- * Revert to the specified access token for the current thread. When access
- * token is specified as NULL then revert to the primary process access token.
- * Use to revert after change_token() or change_token_to_primary() call.
- */
-static VOID
-revert_to_token(HANDLE token)
-{
- /*
- * If SetThreadToken() call fails then there is no option to revert to
- * the specified previous thread access token. So in this case revert to
- * the primary process access token.
- */
- if (!token || !SetThreadToken(NULL, token))
- RevertToSelf();
- if (token)
- CloseHandle(token);
-}
-
-/*
- * Enable particular privilege for the current thread. And set method how to
- * revert this privilege (if to revert whole token or only privilege).
- */
-static BOOL
-enable_privilege(LUID luid_privilege, HANDLE *revert_token, BOOL *revert_only_privilege)
-{
- HANDLE thread_token;
- HANDLE new_token;
-
- if (OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &thread_token))
- {
- if (set_privilege(thread_token, luid_privilege, TRUE))
- {
- /*
- * Indicate that correct revert method is just to
- * disable privilege in access token.
- */
- if (revert_token && revert_only_privilege)
- {
- *revert_token = thread_token;
- *revert_only_privilege = TRUE;
- }
- else
- {
- CloseHandle(thread_token);
- }
- return TRUE;
- }
- CloseHandle(thread_token);
- /*
- * If enabling privilege failed then try to enable it via
- * primary process access token.
- */
- }
-
- /*
- * If the current thread has already active thread access token then
- * open it with just impersonate right as it would be used only for
- * future revert.
- */
- if (revert_token && revert_only_privilege)
- {
- if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &thread_token))
- {
- if (GetLastError() != ERROR_NO_TOKEN)
- return FALSE;
- thread_token = NULL;
- }
-
- /*
- * If current thread has no access token (and uses primary
- * process access token) or it does not have permission to
- * adjust privileges or it does not have specified privilege
- * then create a copy of the primary process access token,
- * assign it for the current thread (= impersonate self)
- * and then try adjusting privilege again.
- */
- if (!ImpersonateSelf(SecurityImpersonation))
- {
- if (thread_token)
- CloseHandle(thread_token);
- return FALSE;
- }
- }
-
- if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &new_token))
- {
- /* thread_token is set only when we were asked for revert method. */
- if (revert_token && revert_only_privilege)
- revert_to_token(thread_token);
- return FALSE;
- }
-
- if (!set_privilege(new_token, luid_privilege, TRUE))
- {
- CloseHandle(new_token);
- /* thread_token is set only when we were asked for revert method. */
- if (revert_token && revert_only_privilege)
- revert_to_token(thread_token);
- return FALSE;
- }
-
- /*
- * Indicate that correct revert method is to change to the previous
- * access token. Either to the primary process access token or to the
- * previous thread access token.
- */
- if (revert_token && revert_only_privilege)
- {
- *revert_token = thread_token;
- *revert_only_privilege = FALSE;
- }
- return TRUE;
-}
-
-/*
- * Revert particular privilege for the current thread was previously enabled by
- * enable_privilege() call. Either disable privilege in specified access token
- * or revert to previous access token.
- */
-static VOID
-revert_privilege(LUID luid_privilege, HANDLE revert_token, BOOL revert_only_privilege)
-{
- if (revert_only_privilege)
- {
- set_privilege(revert_token, luid_privilege, FALSE);
- CloseHandle(revert_token);
- }
- else
- {
- revert_to_token(revert_token);
- }
-}
-
-/*
- * Return owner of the access token used by the current thread. Buffer for
- * returned owner needs to be released by LocalFree() call.
- */
-static TOKEN_OWNER *
-get_current_token_owner(VOID)
-{
- HANDLE token;
- DWORD length;
- TOKEN_OWNER *owner;
-
- /*
- * If the current thread does not have active access token then thread
- * uses primary process access token for all permission checks.
- */
- if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token) &&
- (GetLastError() != ERROR_NO_TOKEN ||
- !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)))
- return NULL;
-
- if (!GetTokenInformation(token, TokenOwner, NULL, 0, &length) &&
- GetLastError() != ERROR_INSUFFICIENT_BUFFER)
- {
- CloseHandle(token);
- return NULL;
- }
-
-retry:
- owner = (TOKEN_OWNER *)LocalAlloc(LPTR, length);
- if (!owner)
- {
- CloseHandle(token);
- return NULL;
- }
-
- if (!GetTokenInformation(token, TokenOwner, owner, length, &length))
- {
- /*
- * Length of token owner (SID) buffer between two get calls may
- * changes (e.g. by another thread of process), so retry.
- */
- if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
- {
- LocalFree(owner);
- goto retry;
- }
- LocalFree(owner);
- CloseHandle(token);
- return NULL;
- }
-
- CloseHandle(token);
- return owner;
-}
-
-/*
- * Grant particular permissions in the primary access token of the specified
- * process for the owner of current thread token and set old DACL of the
- * process access token for reverting permissions. Security descriptor is
- * just memory buffer for old DACL.
- */
-static BOOL
-grant_process_token_dacl_permissions(HANDLE process, DWORD permissions, HANDLE *token, PACL *old_dacl, PSECURITY_DESCRIPTOR *security_descriptor)
-{
- GetSecurityInfoProt MyGetSecurityInfo;
- SetSecurityInfoProt MySetSecurityInfo;
- SetEntriesInAclProt MySetEntriesInAcl;
- EXPLICIT_ACCESS explicit_access;
- TOKEN_OWNER *owner;
- HMODULE advapi32;
- PACL new_dacl;
-
- /*
- * This source file already uses advapi32.dll library, so it is
- * linked to executable and automatically loaded when starting
- * current running process.
- */
- advapi32 = GetModuleHandle(TEXT("advapi32.dll"));
- if (!advapi32)
- return FALSE;
-
- /*
- * It does not matter if SetEntriesInAclA() or SetEntriesInAclW() is
- * called as no string is passed to SetEntriesInAcl function.
- */
- MyGetSecurityInfo = (GetSecurityInfoProt)(LPVOID)GetProcAddress(advapi32, "GetSecurityInfo");
- MySetSecurityInfo = (SetSecurityInfoProt)(LPVOID)GetProcAddress(advapi32, "SetSecurityInfo");
- MySetEntriesInAcl = (SetEntriesInAclProt)(LPVOID)GetProcAddress(advapi32, "SetEntriesInAclA");
- if (!MyGetSecurityInfo || !MySetSecurityInfo || !MySetEntriesInAcl)
- return FALSE;
-
- owner = get_current_token_owner();
- if (!owner)
- return FALSE;
-
- /*
- * READ_CONTROL is required for GetSecurityInfo(DACL_SECURITY_INFORMATION)
- * and WRITE_DAC is required for SetSecurityInfo(DACL_SECURITY_INFORMATION).
- */
- if (!OpenProcessToken(process, READ_CONTROL | WRITE_DAC, token))
- {
- LocalFree(owner);
- return FALSE;
- }
-
- if (MyGetSecurityInfo(*token, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, old_dacl, NULL, security_descriptor) != ERROR_SUCCESS)
- {
- LocalFree(owner);
- CloseHandle(*token);
- return FALSE;
- }
-
- /*
- * Set new explicit access for the owner of the current thread access
- * token with non-inherited granting access to specified permissions.
- */
- explicit_access.grfAccessPermissions = permissions;
- explicit_access.grfAccessMode = GRANT_ACCESS;
- explicit_access.grfInheritance = NO_PROPAGATE_INHERIT_ACE;
- explicit_access.Trustee.pMultipleTrustee = NULL;
- explicit_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
- explicit_access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
- explicit_access.Trustee.TrusteeType = TRUSTEE_IS_USER;
- /*
- * Unfortunately i586-mingw32msvc toolchain does not have pSid pointer
- * member in Trustee union. So assign owner SID to ptstrName pointer
- * member which aliases with pSid pointer member in the same union.
- */
- explicit_access.Trustee.ptstrName = (PVOID)owner->Owner;
-
- if (MySetEntriesInAcl(1, &explicit_access, *old_dacl, &new_dacl) != ERROR_SUCCESS)
- {
- LocalFree(*security_descriptor);
- LocalFree(owner);
- CloseHandle(*token);
- return FALSE;
- }
-
- if (MySetSecurityInfo(*token, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, new_dacl, NULL) != ERROR_SUCCESS)
- {
- LocalFree(new_dacl);
- LocalFree(*security_descriptor);
- LocalFree(owner);
- CloseHandle(*token);
- return FALSE;
- }
-
- LocalFree(new_dacl);
- LocalFree(owner);
- return TRUE;
-}
-
-/*
- * Revert particular granted permissions in specified access token done by
- * grant_process_token_dacl_permissions() call.
- */
-static VOID
-revert_token_dacl_permissions(HANDLE token, PACL old_dacl, PSECURITY_DESCRIPTOR security_descriptor)
-{
- SetSecurityInfoProt MySetSecurityInfo;
- HMODULE advapi32;
-
- /*
- * This source file already uses advapi32.dll library, so it is
- * linked to executable and automatically loaded when starting
- * current running process.
- */
- advapi32 = GetModuleHandle(TEXT("advapi32.dll"));
- if (advapi32)
- {
- MySetSecurityInfo = (SetSecurityInfoProt)(LPVOID)GetProcAddress(advapi32, "SetSecurityInfo");
- MySetSecurityInfo(token, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, old_dacl, NULL);
- }
-
- LocalFree(security_descriptor);
- CloseHandle(token);
-}
-
-/*
- * Change error mode of the current thread. If it is not possible then change
- * error mode of the whole process. Always returns previous error mode.
- */
-static UINT
-change_error_mode(UINT new_mode)
-{
- SetThreadErrorModeProt MySetThreadErrorMode = NULL;
- HMODULE kernel32;
- DWORD old_mode;
-
- /*
- * Function SetThreadErrorMode() was introduced in Windows 7, so use
- * GetProcAddress() for compatibility with older systems.
- */
- kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
- if (kernel32)
- MySetThreadErrorMode = (SetThreadErrorModeProt)(LPVOID)GetProcAddress(kernel32, "SetThreadErrorMode");
-
- if (MySetThreadErrorMode &&
- MySetThreadErrorMode(new_mode, &old_mode))
- return old_mode;
-
- /*
- * Fallback to function SetErrorMode() which modifies error mode of the
- * whole process and returns old mode.
- */
- return SetErrorMode(new_mode);
-}
-
-/*
- * Open process handle specified by the process id with the query right and
- * optionally also with vm read right.
- */
-static HANDLE
-open_process_for_query(DWORD pid, BOOL with_vm_read)
-{
- BOOL revert_only_privilege;
- LUID luid_debug_privilege;
- OSVERSIONINFO version;
- DWORD process_right;
- HANDLE revert_token;
- HANDLE process;
-
- /*
- * Some processes on Windows Vista and higher systems can be opened only
- * with PROCESS_QUERY_LIMITED_INFORMATION right. This right is enough
- * for accessing primary process token. But this right is not supported
- * on older pre-Vista systems. When the current thread on these older
- * systems does not have Debug privilege then OpenProcess() fails with
- * ERROR_ACCESS_DENIED. If the current thread has Debug privilege then
- * OpenProcess() success and returns handle to requested process.
- * Problem is that this handle does not have PROCESS_QUERY_INFORMATION
- * right and so cannot be used for accessing primary process token
- * on those older systems. Moreover it has zero rights and therefore
- * such handle is fully useless. So never try to use open process with
- * PROCESS_QUERY_LIMITED_INFORMATION right on older systems than
- * Windows Vista (NT 6.0).
- */
- version.dwOSVersionInfoSize = sizeof(version);
- if (GetVersionEx(&version) &&
- version.dwPlatformId == VER_PLATFORM_WIN32_NT &&
- version.dwMajorVersion >= 6)
- process_right = PROCESS_QUERY_LIMITED_INFORMATION;
- else
- process_right = PROCESS_QUERY_INFORMATION;
-
- if (with_vm_read)
- process_right |= PROCESS_VM_READ;
-
- process = OpenProcess(process_right, FALSE, pid);
- if (process)
- return process;
-
- /*
- * It is possible to open only processes to which owner of the current
- * thread access token has permissions. For opening other processing it
- * is required to have Debug privilege enabled. By default local
- * administrators have this privilege, but it is disabled. So try to
- * enable it and then try to open process again.
- */
-
- if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid_debug_privilege))
- return NULL;
-
- if (!enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege))
- return NULL;
-
- process = OpenProcess(process_right, FALSE, pid);
-
- revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
-
- return process;
-}
-
-/*
- * Check if process image path name (wide string) matches exe file name
- * (7-bit ASCII string). Do case-insensitive string comparison. Process
- * image path name can be in any namespace format (DOS, Win32, UNC, ...).
- */
-static BOOL
-check_process_name(LPCWSTR path, DWORD path_length, LPCSTR exe_file)
-{
- DWORD exe_file_length;
- WCHAR c1;
- UCHAR c2;
- DWORD i;
-
- exe_file_length = 0;
- while (exe_file[exe_file_length] != '\0')
- exe_file_length++;
-
- /* Path must have backslash before exe file name. */
- if (exe_file_length >= path_length ||
- path[path_length-exe_file_length-1] != L'\\')
- return FALSE;
-
- for (i = 0; i < exe_file_length; i++)
- {
- c1 = path[path_length-exe_file_length+i];
- c2 = exe_file[i];
- /*
- * Input string for comparison is 7-bit ASCII and file name part
- * of path must not contain backslash as it is path separator.
- */
- if (c1 >= 0x80 || c2 >= 0x80 || c1 == L'\\')
- return FALSE;
- if (c1 >= L'a' && c1 <= L'z')
- c1 -= L'a' - L'A';
- if (c2 >= 'a' && c2 <= 'z')
- c2 -= 'a' - 'A';
- if (c1 != c2)
- return FALSE;
- }
-
- return TRUE;
-}
-
-/* Open process handle with the query right specified by process exe file. */
-static HANDLE
-find_and_open_process_for_query(LPCSTR exe_file)
-{
- GetProcessImageFileNameWProt MyGetProcessImageFileNameW;
- GetModuleFileNameExWProt MyGetModuleFileNameExW;
- EnumProcessesProt MyEnumProcesses;
- HMODULE kernel32, psapi;
- UINT prev_error_mode;
- DWORD partial_retry;
- BOOL found_process;
- DWORD size, length;
- DWORD *processes;
- HANDLE process;
- LPWSTR path;
- DWORD error;
- DWORD count;
- DWORD i;
-
- psapi = NULL;
- kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
- if (!kernel32)
- return NULL;
-
- /*
- * On Windows 7 and higher systems these functions are available in
- * kernel32.dll library with K32 prefix.
- */
- MyGetModuleFileNameExW = NULL;
- MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(LPVOID)GetProcAddress(kernel32, "K32GetProcessImageFileNameW");
- MyEnumProcesses = (EnumProcessesProt)(LPVOID)GetProcAddress(kernel32, "K32EnumProcesses");
- if (!MyGetProcessImageFileNameW || !MyEnumProcesses)
- {
- /*
- * On older NT-based systems these functions are available in
- * psapi.dll library without K32 prefix.
- */
- prev_error_mode = change_error_mode(SEM_FAILCRITICALERRORS);
- psapi = LoadLibrary(TEXT("psapi.dll"));
- change_error_mode(prev_error_mode);
-
- if (!psapi)
- return NULL;
-
- /*
- * Function GetProcessImageFileNameW() is available in
- * Windows XP and higher systems. On older versions is
- * available function GetModuleFileNameExW().
- */
- MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(LPVOID)GetProcAddress(psapi, "GetProcessImageFileNameW");
- MyGetModuleFileNameExW = (GetModuleFileNameExWProt)(LPVOID)GetProcAddress(psapi, "GetModuleFileNameExW");
- MyEnumProcesses = (EnumProcessesProt)(LPVOID)GetProcAddress(psapi, "EnumProcesses");
- if ((!MyGetProcessImageFileNameW && !MyGetModuleFileNameExW) || !MyEnumProcesses)
- {
- FreeLibrary(psapi);
- return NULL;
- }
- }
-
- /* Make initial buffer size for 1024 processes. */
- size = 1024 * sizeof(*processes);
-
-retry:
- processes = (DWORD *)LocalAlloc(LPTR, size);
- if (!processes)
- {
- if (psapi)
- FreeLibrary(psapi);
- return NULL;
- }
-
- if (!MyEnumProcesses(processes, size, &length))
- {
- LocalFree(processes);
- if (psapi)
- FreeLibrary(psapi);
- return NULL;
- }
- else if (size == length)
- {
- /*
- * There is no indication given when the buffer is too small to
- * store all process identifiers. Therefore if returned length
- * is same as buffer size there can be more processes. Call
- * again with larger buffer.
- */
- LocalFree(processes);
- size *= 2;
- goto retry;
- }
-
- process = NULL;
- count = length / sizeof(*processes);
-
- for (i = 0; i < count; i++)
- {
- /* Skip System Idle Process. */
- if (processes[i] == 0)
- continue;
-
- /*
- * Function GetModuleFileNameExW() requires additional
- * PROCESS_VM_READ right as opposite to function
- * GetProcessImageFileNameW() which does not need it.
- */
- process = open_process_for_query(processes[i], MyGetProcessImageFileNameW ? FALSE : TRUE);
- if (!process)
- continue;
-
- /*
- * Set initial buffer size to 256 (wide) characters.
- * Final path length on the modern NT-based systems can be also larger.
- */
- size = 256;
- found_process = FALSE;
- partial_retry = 0;
-
-retry_path:
- path = (LPWSTR)LocalAlloc(LPTR, size * sizeof(*path));
- if (!path)
- goto end_path;
-
- if (MyGetProcessImageFileNameW)
- length = MyGetProcessImageFileNameW(process, path, size);
- else
- length = MyGetModuleFileNameExW(process, NULL, path, size);
-
- error = GetLastError();
-
- /*
- * GetModuleFileNameEx() returns zero and signal error ERROR_PARTIAL_COPY
- * when remote process is in the middle of updating its module table.
- * Sleep 10 ms and try again, max 10 attempts.
- */
- if (!MyGetProcessImageFileNameW)
- {
- if (length == 0 && error == ERROR_PARTIAL_COPY && partial_retry++ < 10)
- {
- Sleep(10);
- goto retry_path;
- }
- partial_retry = 0;
- }
-
- /*
- * When buffer is too small then function GetModuleFileNameEx() returns
- * its size argument on older systems (Windows XP) or its size minus
- * argument one on new systems (Windows 10) without signalling any error.
- * Function GetProcessImageFileNameW() on the other hand returns zero
- * value and signals error ERROR_INSUFFICIENT_BUFFER. So in all these
- * cases call function again with larger buffer.
- */
-
- if (MyGetProcessImageFileNameW && length == 0 && error != ERROR_INSUFFICIENT_BUFFER)
- goto end_path;
-
- if ((MyGetProcessImageFileNameW && length == 0) ||
- (!MyGetProcessImageFileNameW && (length == size || length == size-1)))
- {
- LocalFree(path);
- size *= 2;
- goto retry_path;
- }
-
- if (length && check_process_name(path, length, exe_file))
- found_process = TRUE;
-
-end_path:
- if (path)
- {
- LocalFree(path);
- path = NULL;
- }
-
- if (found_process)
- break;
-
- CloseHandle(process);
- process = NULL;
- }
-
- LocalFree(processes);
-
- if (psapi)
- FreeLibrary(psapi);
-
- return process;
-}
-
-/*
- * Try to open primary access token of the particular process with specified
- * rights. Before opening access token try to adjust DACL permissions of the
- * primary process access token, so following open does not fail on error
- * related to no open permissions. Revert DACL permissions after open attempt.
- * As following steps are not atomic, try to execute them more times in case
- * of possible race conditions caused by other threads or processes.
- */
-static HANDLE
-try_grant_permissions_and_open_process_token(HANDLE process, DWORD rights)
-{
- PSECURITY_DESCRIPTOR security_descriptor;
- HANDLE grant_token;
- PACL old_dacl;
- HANDLE token;
- DWORD retry;
- DWORD error;
-
- /*
- * This code is not atomic. Between grant and open calls can other
- * thread or process change or revert permissions. So try to execute
- * it more times.
- */
- for (retry = 0; retry < 10; retry++)
- {
- if (!grant_process_token_dacl_permissions(process, rights, &grant_token, &old_dacl, &security_descriptor))
- return NULL;
- if (!OpenProcessToken(process, rights, &token))
- {
- token = NULL;
- error = GetLastError();
- }
- revert_token_dacl_permissions(grant_token, old_dacl, security_descriptor);
- if (token)
- return token;
- else if (error != ERROR_ACCESS_DENIED)
- return NULL;
- }
-
- return NULL;
-}
-
-/*
- * Open primary access token of particular process handle with specified rights.
- * If permissions for specified rights are missing then try to grant them.
- */
-static HANDLE
-open_process_token_with_rights(HANDLE process, DWORD rights)
-{
- HANDLE old_token;
- HANDLE token;
-
- /* First try to open primary access token of process handle directly. */
- if (OpenProcessToken(process, rights, &token))
- return token;
-
- /*
- * If opening failed then it means that owner of the current thread
- * access token does not have permission for it. Try it again with
- * primary process access token.
- */
- if (change_token_to_primary(&old_token))
- {
- if (!OpenProcessToken(process, rights, &token))
- token = NULL;
- revert_to_token(old_token);
- if (token)
- return token;
- }
-
- /*
- * If opening is still failing then try to grant specified permissions
- * for the current thread and try to open it again.
- */
- token = try_grant_permissions_and_open_process_token(process, rights);
- if (token)
- return token;
-
- /*
- * And if it is still failing then try it again with granting
- * permissions for the primary process token of the current process.
- */
- if (change_token_to_primary(&old_token))
- {
- token = try_grant_permissions_and_open_process_token(process, rights);
- revert_to_token(old_token);
- if (token)
- return token;
- }
-
- /*
- * TODO: Sorry, no other option for now...
- * It could be possible to use Take Ownership Name privilege to
- * temporary change token owner of specified process to the owner of
- * the current thread token, grant permissions for current thread in
- * that process token, change ownership back to original one, open
- * that process token and revert granted permissions. But this is
- * not implemented yet.
- */
- return NULL;
-}
-
-/*
* Call supplied function Func with its Arg and if it fails with
* ERROR_PRIVILEGE_NOT_HELD then try to enable Tcb privilege and
* call Func with its Arg again.
@@ -1035,14 +150,14 @@ CallFuncWithTcbPrivilege(BOOL (*Func)(LPVOID), LPVOID Arg)
* If the current thread has already Tcb privilege enabled then there
* is some additional unhanded restriction.
*/
- if (have_privilege(luid_tcb_privilege))
+ if (win32_have_privilege(luid_tcb_privilege))
goto err_privilege_not_held;
/* Try to enable Tcb privilege and try function call again. */
- if (enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege))
+ if (win32_enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege))
{
ret = Func(Arg);
- revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege);
+ win32_revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege);
goto ret;
}
@@ -1058,7 +173,7 @@ CallFuncWithTcbPrivilege(BOOL (*Func)(LPVOID), LPVOID Arg)
* So try to enable it in case it is currently disabled.
*/
if (LookupPrivilegeValue(NULL, SE_IMPERSONATE_NAME, &luid_impersonate_privilege) &&
- !have_privilege(luid_impersonate_privilege))
+ !win32_have_privilege(luid_impersonate_privilege))
{
/*
* If current thread does not have Impersonate privilege enabled
@@ -1068,11 +183,11 @@ CallFuncWithTcbPrivilege(BOOL (*Func)(LPVOID), LPVOID Arg)
* affects all process threads). Both actions will be reverted
* at the end of this function.
*/
- if (enable_privilege(luid_impersonate_privilege, &revert_token_impersonate_privilege, &revert_only_impersonate_privilege))
+ if (win32_enable_privilege(luid_impersonate_privilege, &revert_token_impersonate_privilege, &revert_only_impersonate_privilege))
{
impersonate_privilege_enabled = TRUE;
}
- else if (enable_privilege(luid_impersonate_privilege, NULL, NULL))
+ else if (win32_enable_privilege(luid_impersonate_privilege, NULL, NULL))
{
impersonate_privilege_enabled = TRUE;
revert_token_impersonate_privilege = NULL;
@@ -1089,10 +204,10 @@ CallFuncWithTcbPrivilege(BOOL (*Func)(LPVOID), LPVOID Arg)
* thread requires Impersonate privilege, so enabling Tcb again
* could now pass.
*/
- if (enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege))
+ if (win32_enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege))
{
ret = Func(Arg);
- revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege);
+ win32_revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege);
goto ret;
}
}
@@ -1108,7 +223,7 @@ CallFuncWithTcbPrivilege(BOOL (*Func)(LPVOID), LPVOID Arg)
* administrators (unless it was disabled by local administrators).
*/
- lsass_process = find_and_open_process_for_query("lsass.exe");
+ lsass_process = win32_find_and_open_process_for_query("lsass.exe");
if (!lsass_process)
goto err_privilege_not_held;
@@ -1117,7 +232,7 @@ CallFuncWithTcbPrivilege(BOOL (*Func)(LPVOID), LPVOID Arg)
* rights. Just these two rights are required for impersonating other
* primary process token (impersonate right is really not required!).
*/
- lsass_token = open_process_token_with_rights(lsass_process, TOKEN_QUERY | TOKEN_DUPLICATE);
+ lsass_token = win32_open_process_token_with_rights(lsass_process, TOKEN_QUERY | TOKEN_DUPLICATE);
CloseHandle(lsass_process);
@@ -1128,7 +243,7 @@ CallFuncWithTcbPrivilege(BOOL (*Func)(LPVOID), LPVOID Arg)
* After successful open of the primary lsass.exe process access token,
* assign its copy for the current thread.
*/
- if (!change_token(lsass_token, &old_token))
+ if (!win32_change_token(lsass_token, &old_token))
goto err_privilege_not_held;
revert_to_old_token = TRUE;
@@ -1144,8 +259,8 @@ CallFuncWithTcbPrivilege(BOOL (*Func)(LPVOID), LPVOID Arg)
* be reverted. So there is no need to setup revert method for
* enabling privilege.
*/
- if (have_privilege(luid_tcb_privilege) ||
- !enable_privilege(luid_tcb_privilege, NULL, NULL))
+ if (win32_have_privilege(luid_tcb_privilege) ||
+ !win32_enable_privilege(luid_tcb_privilege, NULL, NULL))
goto err_privilege_not_held;
ret = Func(Arg);
@@ -1158,10 +273,10 @@ err_privilege_not_held:
ret:
if (revert_to_old_token)
- revert_to_token(old_token);
+ win32_revert_to_token(old_token);
if (impersonate_privilege_enabled)
- revert_privilege(luid_impersonate_privilege, revert_token_impersonate_privilege, revert_only_impersonate_privilege);
+ win32_revert_privilege(luid_impersonate_privilege, revert_token_impersonate_privilege, revert_only_impersonate_privilege);
if (lsass_token)
CloseHandle(lsass_token);
@@ -1228,9 +343,9 @@ SetProcessUserModeIOPL(VOID)
* It means that NT kernel does not show unwanted GUI message box to user
* when LoadLibrary() function fails.
*/
- prev_error_mode = change_error_mode(SEM_FAILCRITICALERRORS);
+ prev_error_mode = win32_change_error_mode(SEM_FAILCRITICALERRORS);
ntdll = LoadLibrary(TEXT("ntdll.dll"));
- change_error_mode(prev_error_mode);
+ win32_change_error_mode(prev_error_mode);
if (!ntdll)
{
SetLastError(ERROR_INVALID_FUNCTION);
diff --git a/lib/win32-helpers.c b/lib/win32-helpers.c
index dd47865..29ce5d8 100644
--- a/lib/win32-helpers.c
+++ b/lib/win32-helpers.c
@@ -9,10 +9,43 @@
*/
#include <windows.h>
+#include <aclapi.h>
#include <stdio.h> /* for sprintf() */
#include "win32-helpers.h"
+/* Unfortunately i586-mingw32msvc toolchain does not provide this constant. */
+#ifndef PROCESS_QUERY_LIMITED_INFORMATION
+#define PROCESS_QUERY_LIMITED_INFORMATION 0x1000
+#endif
+
+/*
+ * These psapi functions are available in kernel32.dll library with K32 prefix
+ * on Windows 7 and higher systems. On older Windows systems these functions are
+ * available in psapi.dll libary without K32 prefix. So resolve pointers to
+ * these functions dynamically at runtime from the available system library.
+ * Function GetProcessImageFileNameW() is not available on Windows 2000 and
+ * older systems.
+ */
+typedef BOOL (WINAPI *EnumProcessesProt)(DWORD *lpidProcess, DWORD cb, DWORD *cbNeeded);
+typedef DWORD (WINAPI *GetProcessImageFileNameWProt)(HANDLE hProcess, LPWSTR lpImageFileName, DWORD nSize);
+typedef DWORD (WINAPI *GetModuleFileNameExWProt)(HANDLE hProcess, HMODULE hModule, LPWSTR lpImageFileName, DWORD nSize);
+
+/*
+ * These aclapi functions are available in advapi.dll library on Windows NT 4.0
+ * and higher systems.
+ */
+typedef DWORD (WINAPI *GetSecurityInfoProt)(HANDLE handle, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, PSID *ppsidOwner, PSID *ppsidGroup, PACL *ppDacl, PACL *ppSacl, PSECURITY_DESCRIPTOR *ppSecurityDescriptor);
+typedef DWORD (WINAPI *SetSecurityInfoProt)(HANDLE handle, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, PSID psidOwner, PSID psidGroup, PACL pDacl, PACL pSacl);
+typedef DWORD (WINAPI *SetEntriesInAclProt)(ULONG cCountOfExplicitEntries, PEXPLICIT_ACCESS pListOfExplicitEntries, PACL OldAcl, PACL *NewAcl);
+
+/*
+ * This errhandlingapi function is available in kernel32.dll library on
+ * Windows 7 and higher systems.
+ */
+typedef BOOL (WINAPI *SetThreadErrorModeProt)(DWORD dwNewMode, LPDWORD lpOldMode);
+
+
const char *
win32_strerror(DWORD win32_error_id)
{
@@ -95,3 +128,856 @@ win32_is_32bit_on_win8_64bit_system(void)
return win32_is_32bit_on_64bit_system();
#endif
}
+
+/*
+ * Change error mode of the current thread. If it is not possible then change
+ * error mode of the whole process. Always returns previous error mode.
+ */
+UINT
+win32_change_error_mode(UINT new_mode)
+{
+ SetThreadErrorModeProt MySetThreadErrorMode = NULL;
+ HMODULE kernel32;
+ DWORD old_mode;
+
+ /*
+ * Function SetThreadErrorMode() was introduced in Windows 7, so use
+ * GetProcAddress() for compatibility with older systems.
+ */
+ kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
+ if (kernel32)
+ MySetThreadErrorMode = (SetThreadErrorModeProt)(LPVOID)GetProcAddress(kernel32, "SetThreadErrorMode");
+
+ if (MySetThreadErrorMode &&
+ MySetThreadErrorMode(new_mode, &old_mode))
+ return old_mode;
+
+ /*
+ * Fallback to function SetErrorMode() which modifies error mode of the
+ * whole process and returns old mode.
+ */
+ return SetErrorMode(new_mode);
+}
+
+/*
+ * Check if the current thread has particular privilege in current active access
+ * token. Case when it not possible to determinate it (e.g. current thread does
+ * not have permission to open its own current active access token) is evaluated
+ * as thread does not have that privilege.
+ */
+BOOL
+win32_have_privilege(LUID luid_privilege)
+{
+ PRIVILEGE_SET priv;
+ HANDLE token;
+ BOOL ret;
+
+ /*
+ * If the current thread does not have active access token then thread
+ * uses primary process access token for all permission checks.
+ */
+ if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token) &&
+ (GetLastError() != ERROR_NO_TOKEN ||
+ !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)))
+ return FALSE;
+
+ priv.PrivilegeCount = 1;
+ priv.Control = PRIVILEGE_SET_ALL_NECESSARY;
+ priv.Privilege[0].Luid = luid_privilege;
+ priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+ if (!PrivilegeCheck(token, &priv, &ret))
+ return FALSE;
+
+ return ret;
+}
+
+/*
+ * Enable or disable particular privilege in specified access token.
+ *
+ * Note that it is not possible to disable privilege in access token with
+ * SE_PRIVILEGE_ENABLED_BY_DEFAULT attribute. This function does not check
+ * this case and incorrectly returns no error even when disabling failed.
+ * Rationale for this decision: Simplification of this function as WinAPI
+ * call AdjustTokenPrivileges() does not signal error in this case too.
+ */
+static BOOL
+set_privilege(HANDLE token, LUID luid_privilege, BOOL enable)
+{
+ TOKEN_PRIVILEGES token_privileges;
+
+ token_privileges.PrivilegeCount = 1;
+ token_privileges.Privileges[0].Luid = luid_privilege;
+ token_privileges.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
+
+ /*
+ * WinAPI function AdjustTokenPrivileges() success also when not all
+ * privileges were enabled. It is always required to check for failure
+ * via GetLastError() call. AdjustTokenPrivileges() always sets error
+ * also when it success, as opposite to other WinAPI functions.
+ */
+ if (!AdjustTokenPrivileges(token, FALSE, &token_privileges, sizeof(token_privileges), NULL, NULL) ||
+ GetLastError() != ERROR_SUCCESS)
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * Change access token for the current thread to new specified access token.
+ * Previously active access token is stored in old_token variable and can be
+ * used for reverting to this access token. It is set to NULL if the current
+ * thread previously used primary process access token.
+ */
+BOOL
+win32_change_token(HANDLE new_token, HANDLE *old_token)
+{
+ HANDLE token;
+
+ if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &token))
+ {
+ if (GetLastError() != ERROR_NO_TOKEN)
+ return FALSE;
+ token = NULL;
+ }
+
+ if (!ImpersonateLoggedOnUser(new_token))
+ {
+ if (token)
+ CloseHandle(token);
+ return FALSE;
+ }
+
+ *old_token = token;
+ return TRUE;
+}
+
+/*
+ * Change access token for the current thread to the primary process access
+ * token. This function fails also when the current thread already uses primary
+ * process access token.
+ */
+static BOOL
+change_token_to_primary(HANDLE *old_token)
+{
+ HANDLE token;
+
+ if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &token))
+ return FALSE;
+
+ RevertToSelf();
+
+ *old_token = token;
+ return TRUE;
+}
+
+/*
+ * Revert to the specified access token for the current thread. When access
+ * token is specified as NULL then revert to the primary process access token.
+ * Use to revert after win32_change_token() or change_token_to_primary() call.
+ */
+VOID
+win32_revert_to_token(HANDLE token)
+{
+ /*
+ * If SetThreadToken() call fails then there is no option to revert to
+ * the specified previous thread access token. So in this case revert to
+ * the primary process access token.
+ */
+ if (!token || !SetThreadToken(NULL, token))
+ RevertToSelf();
+ if (token)
+ CloseHandle(token);
+}
+
+/*
+ * Enable particular privilege for the current thread. And set method how to
+ * revert this privilege (if to revert whole token or only privilege).
+ */
+BOOL
+win32_enable_privilege(LUID luid_privilege, HANDLE *revert_token, BOOL *revert_only_privilege)
+{
+ HANDLE thread_token;
+ HANDLE new_token;
+
+ if (OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &thread_token))
+ {
+ if (set_privilege(thread_token, luid_privilege, TRUE))
+ {
+ /*
+ * Indicate that correct revert method is just to
+ * disable privilege in access token.
+ */
+ if (revert_token && revert_only_privilege)
+ {
+ *revert_token = thread_token;
+ *revert_only_privilege = TRUE;
+ }
+ else
+ {
+ CloseHandle(thread_token);
+ }
+ return TRUE;
+ }
+ CloseHandle(thread_token);
+ /*
+ * If enabling privilege failed then try to enable it via
+ * primary process access token.
+ */
+ }
+
+ /*
+ * If the current thread has already active thread access token then
+ * open it with just impersonate right as it would be used only for
+ * future revert.
+ */
+ if (revert_token && revert_only_privilege)
+ {
+ if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &thread_token))
+ {
+ if (GetLastError() != ERROR_NO_TOKEN)
+ return FALSE;
+ thread_token = NULL;
+ }
+
+ /*
+ * If current thread has no access token (and uses primary
+ * process access token) or it does not have permission to
+ * adjust privileges or it does not have specified privilege
+ * then create a copy of the primary process access token,
+ * assign it for the current thread (= impersonate self)
+ * and then try adjusting privilege again.
+ */
+ if (!ImpersonateSelf(SecurityImpersonation))
+ {
+ if (thread_token)
+ CloseHandle(thread_token);
+ return FALSE;
+ }
+ }
+
+ if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &new_token))
+ {
+ /* thread_token is set only when we were asked for revert method. */
+ if (revert_token && revert_only_privilege)
+ win32_revert_to_token(thread_token);
+ return FALSE;
+ }
+
+ if (!set_privilege(new_token, luid_privilege, TRUE))
+ {
+ CloseHandle(new_token);
+ /* thread_token is set only when we were asked for revert method. */
+ if (revert_token && revert_only_privilege)
+ win32_revert_to_token(thread_token);
+ return FALSE;
+ }
+
+ /*
+ * Indicate that correct revert method is to change to the previous
+ * access token. Either to the primary process access token or to the
+ * previous thread access token.
+ */
+ if (revert_token && revert_only_privilege)
+ {
+ *revert_token = thread_token;
+ *revert_only_privilege = FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * Revert particular privilege for the current thread was previously enabled by
+ * win32_enable_privilege() call. Either disable privilege in specified access token
+ * or revert to previous access token.
+ */
+VOID
+win32_revert_privilege(LUID luid_privilege, HANDLE revert_token, BOOL revert_only_privilege)
+{
+ if (revert_only_privilege)
+ {
+ set_privilege(revert_token, luid_privilege, FALSE);
+ CloseHandle(revert_token);
+ }
+ else
+ {
+ win32_revert_to_token(revert_token);
+ }
+}
+
+/*
+ * Return owner of the access token used by the current thread. Buffer for
+ * returned owner needs to be released by LocalFree() call.
+ */
+static TOKEN_OWNER *
+get_current_token_owner(VOID)
+{
+ HANDLE token;
+ DWORD length;
+ TOKEN_OWNER *owner;
+
+ /*
+ * If the current thread does not have active access token then thread
+ * uses primary process access token for all permission checks.
+ */
+ if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token) &&
+ (GetLastError() != ERROR_NO_TOKEN ||
+ !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)))
+ return NULL;
+
+ if (!GetTokenInformation(token, TokenOwner, NULL, 0, &length) &&
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ {
+ CloseHandle(token);
+ return NULL;
+ }
+
+retry:
+ owner = (TOKEN_OWNER *)LocalAlloc(LPTR, length);
+ if (!owner)
+ {
+ CloseHandle(token);
+ return NULL;
+ }
+
+ if (!GetTokenInformation(token, TokenOwner, owner, length, &length))
+ {
+ /*
+ * Length of token owner (SID) buffer between two get calls may
+ * changes (e.g. by another thread of process), so retry.
+ */
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ {
+ LocalFree(owner);
+ goto retry;
+ }
+ LocalFree(owner);
+ CloseHandle(token);
+ return NULL;
+ }
+
+ CloseHandle(token);
+ return owner;
+}
+
+/*
+ * Grant particular permissions in the primary access token of the specified
+ * process for the owner of current thread token and set old DACL of the
+ * process access token for reverting permissions. Security descriptor is
+ * just memory buffer for old DACL.
+ */
+static BOOL
+grant_process_token_dacl_permissions(HANDLE process, DWORD permissions, HANDLE *token, PACL *old_dacl, PSECURITY_DESCRIPTOR *security_descriptor)
+{
+ GetSecurityInfoProt MyGetSecurityInfo;
+ SetSecurityInfoProt MySetSecurityInfo;
+ SetEntriesInAclProt MySetEntriesInAcl;
+ EXPLICIT_ACCESS explicit_access;
+ TOKEN_OWNER *owner;
+ HMODULE advapi32;
+ PACL new_dacl;
+
+ /*
+ * This source file already uses advapi32.dll library, so it is
+ * linked to executable and automatically loaded when starting
+ * current running process.
+ */
+ advapi32 = GetModuleHandle(TEXT("advapi32.dll"));
+ if (!advapi32)
+ return FALSE;
+
+ /*
+ * It does not matter if SetEntriesInAclA() or SetEntriesInAclW() is
+ * called as no string is passed to SetEntriesInAcl function.
+ */
+ MyGetSecurityInfo = (GetSecurityInfoProt)(LPVOID)GetProcAddress(advapi32, "GetSecurityInfo");
+ MySetSecurityInfo = (SetSecurityInfoProt)(LPVOID)GetProcAddress(advapi32, "SetSecurityInfo");
+ MySetEntriesInAcl = (SetEntriesInAclProt)(LPVOID)GetProcAddress(advapi32, "SetEntriesInAclA");
+ if (!MyGetSecurityInfo || !MySetSecurityInfo || !MySetEntriesInAcl)
+ return FALSE;
+
+ owner = get_current_token_owner();
+ if (!owner)
+ return FALSE;
+
+ /*
+ * READ_CONTROL is required for GetSecurityInfo(DACL_SECURITY_INFORMATION)
+ * and WRITE_DAC is required for SetSecurityInfo(DACL_SECURITY_INFORMATION).
+ */
+ if (!OpenProcessToken(process, READ_CONTROL | WRITE_DAC, token))
+ {
+ LocalFree(owner);
+ return FALSE;
+ }
+
+ if (MyGetSecurityInfo(*token, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, old_dacl, NULL, security_descriptor) != ERROR_SUCCESS)
+ {
+ LocalFree(owner);
+ CloseHandle(*token);
+ return FALSE;
+ }
+
+ /*
+ * Set new explicit access for the owner of the current thread access
+ * token with non-inherited granting access to specified permissions.
+ */
+ explicit_access.grfAccessPermissions = permissions;
+ explicit_access.grfAccessMode = GRANT_ACCESS;
+ explicit_access.grfInheritance = NO_PROPAGATE_INHERIT_ACE;
+ explicit_access.Trustee.pMultipleTrustee = NULL;
+ explicit_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
+ explicit_access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
+ explicit_access.Trustee.TrusteeType = TRUSTEE_IS_USER;
+ /*
+ * Unfortunately i586-mingw32msvc toolchain does not have pSid pointer
+ * member in Trustee union. So assign owner SID to ptstrName pointer
+ * member which aliases with pSid pointer member in the same union.
+ */
+ explicit_access.Trustee.ptstrName = (PVOID)owner->Owner;
+
+ if (MySetEntriesInAcl(1, &explicit_access, *old_dacl, &new_dacl) != ERROR_SUCCESS)
+ {
+ LocalFree(*security_descriptor);
+ LocalFree(owner);
+ CloseHandle(*token);
+ return FALSE;
+ }
+
+ if (MySetSecurityInfo(*token, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, new_dacl, NULL) != ERROR_SUCCESS)
+ {
+ LocalFree(new_dacl);
+ LocalFree(*security_descriptor);
+ LocalFree(owner);
+ CloseHandle(*token);
+ return FALSE;
+ }
+
+ LocalFree(new_dacl);
+ LocalFree(owner);
+ return TRUE;
+}
+
+/*
+ * Revert particular granted permissions in specified access token done by
+ * grant_process_token_dacl_permissions() call.
+ */
+static VOID
+revert_token_dacl_permissions(HANDLE token, PACL old_dacl, PSECURITY_DESCRIPTOR security_descriptor)
+{
+ SetSecurityInfoProt MySetSecurityInfo;
+ HMODULE advapi32;
+
+ /*
+ * This source file already uses advapi32.dll library, so it is
+ * linked to executable and automatically loaded when starting
+ * current running process.
+ */
+ advapi32 = GetModuleHandle(TEXT("advapi32.dll"));
+ if (advapi32)
+ {
+ MySetSecurityInfo = (SetSecurityInfoProt)(LPVOID)GetProcAddress(advapi32, "SetSecurityInfo");
+ MySetSecurityInfo(token, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, old_dacl, NULL);
+ }
+
+ LocalFree(security_descriptor);
+ CloseHandle(token);
+}
+
+/*
+ * Open process handle specified by the process id with the query right and
+ * optionally also with vm read right.
+ */
+static HANDLE
+open_process_for_query(DWORD pid, BOOL with_vm_read)
+{
+ BOOL revert_only_privilege;
+ LUID luid_debug_privilege;
+ OSVERSIONINFO version;
+ DWORD process_right;
+ HANDLE revert_token;
+ HANDLE process;
+
+ /*
+ * Some processes on Windows Vista and higher systems can be opened only
+ * with PROCESS_QUERY_LIMITED_INFORMATION right. This right is enough
+ * for accessing primary process token. But this right is not supported
+ * on older pre-Vista systems. When the current thread on these older
+ * systems does not have Debug privilege then OpenProcess() fails with
+ * ERROR_ACCESS_DENIED. If the current thread has Debug privilege then
+ * OpenProcess() success and returns handle to requested process.
+ * Problem is that this handle does not have PROCESS_QUERY_INFORMATION
+ * right and so cannot be used for accessing primary process token
+ * on those older systems. Moreover it has zero rights and therefore
+ * such handle is fully useless. So never try to use open process with
+ * PROCESS_QUERY_LIMITED_INFORMATION right on older systems than
+ * Windows Vista (NT 6.0).
+ */
+ version.dwOSVersionInfoSize = sizeof(version);
+ if (GetVersionEx(&version) &&
+ version.dwPlatformId == VER_PLATFORM_WIN32_NT &&
+ version.dwMajorVersion >= 6)
+ process_right = PROCESS_QUERY_LIMITED_INFORMATION;
+ else
+ process_right = PROCESS_QUERY_INFORMATION;
+
+ if (with_vm_read)
+ process_right |= PROCESS_VM_READ;
+
+ process = OpenProcess(process_right, FALSE, pid);
+ if (process)
+ return process;
+
+ /*
+ * It is possible to open only processes to which owner of the current
+ * thread access token has permissions. For opening other processing it
+ * is required to have Debug privilege enabled. By default local
+ * administrators have this privilege, but it is disabled. So try to
+ * enable it and then try to open process again.
+ */
+
+ if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid_debug_privilege))
+ return NULL;
+
+ if (!win32_enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege))
+ return NULL;
+
+ process = OpenProcess(process_right, FALSE, pid);
+
+ win32_revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
+
+ return process;
+}
+
+/*
+ * Check if process image path name (wide string) matches exe file name
+ * (7-bit ASCII string). Do case-insensitive string comparison. Process
+ * image path name can be in any namespace format (DOS, Win32, UNC, ...).
+ */
+static BOOL
+check_process_name(LPCWSTR path, DWORD path_length, LPCSTR exe_file)
+{
+ DWORD exe_file_length;
+ WCHAR c1;
+ UCHAR c2;
+ DWORD i;
+
+ exe_file_length = 0;
+ while (exe_file[exe_file_length] != '\0')
+ exe_file_length++;
+
+ /* Path must have backslash before exe file name. */
+ if (exe_file_length >= path_length ||
+ path[path_length-exe_file_length-1] != L'\\')
+ return FALSE;
+
+ for (i = 0; i < exe_file_length; i++)
+ {
+ c1 = path[path_length-exe_file_length+i];
+ c2 = exe_file[i];
+ /*
+ * Input string for comparison is 7-bit ASCII and file name part
+ * of path must not contain backslash as it is path separator.
+ */
+ if (c1 >= 0x80 || c2 >= 0x80 || c1 == L'\\')
+ return FALSE;
+ if (c1 >= L'a' && c1 <= L'z')
+ c1 -= L'a' - L'A';
+ if (c2 >= 'a' && c2 <= 'z')
+ c2 -= 'a' - 'A';
+ if (c1 != c2)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Open process handle with the query right specified by process exe file. */
+HANDLE
+win32_find_and_open_process_for_query(LPCSTR exe_file)
+{
+ GetProcessImageFileNameWProt MyGetProcessImageFileNameW;
+ GetModuleFileNameExWProt MyGetModuleFileNameExW;
+ EnumProcessesProt MyEnumProcesses;
+ HMODULE kernel32, psapi;
+ UINT prev_error_mode;
+ DWORD partial_retry;
+ BOOL found_process;
+ DWORD size, length;
+ DWORD *processes;
+ HANDLE process;
+ LPWSTR path;
+ DWORD error;
+ DWORD count;
+ DWORD i;
+
+ psapi = NULL;
+ kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
+ if (!kernel32)
+ return NULL;
+
+ /*
+ * On Windows 7 and higher systems these functions are available in
+ * kernel32.dll library with K32 prefix.
+ */
+ MyGetModuleFileNameExW = NULL;
+ MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(LPVOID)GetProcAddress(kernel32, "K32GetProcessImageFileNameW");
+ MyEnumProcesses = (EnumProcessesProt)(LPVOID)GetProcAddress(kernel32, "K32EnumProcesses");
+ if (!MyGetProcessImageFileNameW || !MyEnumProcesses)
+ {
+ /*
+ * On older NT-based systems these functions are available in
+ * psapi.dll library without K32 prefix.
+ */
+ prev_error_mode = win32_change_error_mode(SEM_FAILCRITICALERRORS);
+ psapi = LoadLibrary(TEXT("psapi.dll"));
+ win32_change_error_mode(prev_error_mode);
+
+ if (!psapi)
+ return NULL;
+
+ /*
+ * Function GetProcessImageFileNameW() is available in
+ * Windows XP and higher systems. On older versions is
+ * available function GetModuleFileNameExW().
+ */
+ MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(LPVOID)GetProcAddress(psapi, "GetProcessImageFileNameW");
+ MyGetModuleFileNameExW = (GetModuleFileNameExWProt)(LPVOID)GetProcAddress(psapi, "GetModuleFileNameExW");
+ MyEnumProcesses = (EnumProcessesProt)(LPVOID)GetProcAddress(psapi, "EnumProcesses");
+ if ((!MyGetProcessImageFileNameW && !MyGetModuleFileNameExW) || !MyEnumProcesses)
+ {
+ FreeLibrary(psapi);
+ return NULL;
+ }
+ }
+
+ /* Make initial buffer size for 1024 processes. */
+ size = 1024 * sizeof(*processes);
+
+retry:
+ processes = (DWORD *)LocalAlloc(LPTR, size);
+ if (!processes)
+ {
+ if (psapi)
+ FreeLibrary(psapi);
+ return NULL;
+ }
+
+ if (!MyEnumProcesses(processes, size, &length))
+ {
+ LocalFree(processes);
+ if (psapi)
+ FreeLibrary(psapi);
+ return NULL;
+ }
+ else if (size == length)
+ {
+ /*
+ * There is no indication given when the buffer is too small to
+ * store all process identifiers. Therefore if returned length
+ * is same as buffer size there can be more processes. Call
+ * again with larger buffer.
+ */
+ LocalFree(processes);
+ size *= 2;
+ goto retry;
+ }
+
+ process = NULL;
+ count = length / sizeof(*processes);
+
+ for (i = 0; i < count; i++)
+ {
+ /* Skip System Idle Process. */
+ if (processes[i] == 0)
+ continue;
+
+ /*
+ * Function GetModuleFileNameExW() requires additional
+ * PROCESS_VM_READ right as opposite to function
+ * GetProcessImageFileNameW() which does not need it.
+ */
+ process = open_process_for_query(processes[i], MyGetProcessImageFileNameW ? FALSE : TRUE);
+ if (!process)
+ continue;
+
+ /*
+ * Set initial buffer size to 256 (wide) characters.
+ * Final path length on the modern NT-based systems can be also larger.
+ */
+ size = 256;
+ found_process = FALSE;
+ partial_retry = 0;
+
+retry_path:
+ path = (LPWSTR)LocalAlloc(LPTR, size * sizeof(*path));
+ if (!path)
+ goto end_path;
+
+ if (MyGetProcessImageFileNameW)
+ length = MyGetProcessImageFileNameW(process, path, size);
+ else
+ length = MyGetModuleFileNameExW(process, NULL, path, size);
+
+ error = GetLastError();
+
+ /*
+ * GetModuleFileNameEx() returns zero and signal error ERROR_PARTIAL_COPY
+ * when remote process is in the middle of updating its module table.
+ * Sleep 10 ms and try again, max 10 attempts.
+ */
+ if (!MyGetProcessImageFileNameW)
+ {
+ if (length == 0 && error == ERROR_PARTIAL_COPY && partial_retry++ < 10)
+ {
+ Sleep(10);
+ goto retry_path;
+ }
+ partial_retry = 0;
+ }
+
+ /*
+ * When buffer is too small then function GetModuleFileNameEx() returns
+ * its size argument on older systems (Windows XP) or its size minus
+ * argument one on new systems (Windows 10) without signalling any error.
+ * Function GetProcessImageFileNameW() on the other hand returns zero
+ * value and signals error ERROR_INSUFFICIENT_BUFFER. So in all these
+ * cases call function again with larger buffer.
+ */
+
+ if (MyGetProcessImageFileNameW && length == 0 && error != ERROR_INSUFFICIENT_BUFFER)
+ goto end_path;
+
+ if ((MyGetProcessImageFileNameW && length == 0) ||
+ (!MyGetProcessImageFileNameW && (length == size || length == size-1)))
+ {
+ LocalFree(path);
+ size *= 2;
+ goto retry_path;
+ }
+
+ if (length && check_process_name(path, length, exe_file))
+ found_process = TRUE;
+
+end_path:
+ if (path)
+ {
+ LocalFree(path);
+ path = NULL;
+ }
+
+ if (found_process)
+ break;
+
+ CloseHandle(process);
+ process = NULL;
+ }
+
+ LocalFree(processes);
+
+ if (psapi)
+ FreeLibrary(psapi);
+
+ return process;
+}
+
+/*
+ * Try to open primary access token of the particular process with specified
+ * rights. Before opening access token try to adjust DACL permissions of the
+ * primary process access token, so following open does not fail on error
+ * related to no open permissions. Revert DACL permissions after open attempt.
+ * As following steps are not atomic, try to execute them more times in case
+ * of possible race conditions caused by other threads or processes.
+ */
+static HANDLE
+try_grant_permissions_and_open_process_token(HANDLE process, DWORD rights)
+{
+ PSECURITY_DESCRIPTOR security_descriptor;
+ HANDLE grant_token;
+ PACL old_dacl;
+ HANDLE token;
+ DWORD retry;
+ DWORD error;
+
+ /*
+ * This code is not atomic. Between grant and open calls can other
+ * thread or process change or revert permissions. So try to execute
+ * it more times.
+ */
+ for (retry = 0; retry < 10; retry++)
+ {
+ if (!grant_process_token_dacl_permissions(process, rights, &grant_token, &old_dacl, &security_descriptor))
+ return NULL;
+ if (!OpenProcessToken(process, rights, &token))
+ {
+ token = NULL;
+ error = GetLastError();
+ }
+ revert_token_dacl_permissions(grant_token, old_dacl, security_descriptor);
+ if (token)
+ return token;
+ else if (error != ERROR_ACCESS_DENIED)
+ return NULL;
+ }
+
+ return NULL;
+}
+
+/*
+ * Open primary access token of particular process handle with specified rights.
+ * If permissions for specified rights are missing then try to grant them.
+ */
+HANDLE
+win32_open_process_token_with_rights(HANDLE process, DWORD rights)
+{
+ HANDLE old_token;
+ HANDLE token;
+
+ /* First try to open primary access token of process handle directly. */
+ if (OpenProcessToken(process, rights, &token))
+ return token;
+
+ /*
+ * If opening failed then it means that owner of the current thread
+ * access token does not have permission for it. Try it again with
+ * primary process access token.
+ */
+ if (change_token_to_primary(&old_token))
+ {
+ if (!OpenProcessToken(process, rights, &token))
+ token = NULL;
+ win32_revert_to_token(old_token);
+ if (token)
+ return token;
+ }
+
+ /*
+ * If opening is still failing then try to grant specified permissions
+ * for the current thread and try to open it again.
+ */
+ token = try_grant_permissions_and_open_process_token(process, rights);
+ if (token)
+ return token;
+
+ /*
+ * And if it is still failing then try it again with granting
+ * permissions for the primary process token of the current process.
+ */
+ if (change_token_to_primary(&old_token))
+ {
+ token = try_grant_permissions_and_open_process_token(process, rights);
+ win32_revert_to_token(old_token);
+ if (token)
+ return token;
+ }
+
+ /*
+ * TODO: Sorry, no other option for now...
+ * It could be possible to use Take Ownership Name privilege to
+ * temporary change token owner of specified process to the owner of
+ * the current thread token, grant permissions for current thread in
+ * that process token, change ownership back to original one, open
+ * that process token and revert granted permissions. But this is
+ * not implemented yet.
+ */
+ return NULL;
+}
diff --git a/lib/win32-helpers.h b/lib/win32-helpers.h
index 18f75de..e5f4445 100644
--- a/lib/win32-helpers.h
+++ b/lib/win32-helpers.h
@@ -2,3 +2,11 @@ const char *win32_strerror(DWORD win32_error_id);
BOOL win32_is_non_nt_system(void);
BOOL win32_is_32bit_on_64bit_system(void);
BOOL win32_is_32bit_on_win8_64bit_system(void);
+UINT win32_change_error_mode(UINT new_mode);
+BOOL win32_have_privilege(LUID luid_privilege);
+BOOL win32_enable_privilege(LUID luid_privilege, HANDLE *revert_token, BOOL *revert_only_privilege);
+VOID win32_revert_privilege(LUID luid_privilege, HANDLE revert_token, BOOL revert_only_privilege);
+BOOL win32_change_token(HANDLE new_token, HANDLE *old_token);
+VOID win32_revert_to_token(HANDLE token);
+HANDLE win32_find_and_open_process_for_query(LPCSTR exe_file);
+HANDLE win32_open_process_token_with_rights(HANDLE process, DWORD rights);
diff --git a/lib/win32-kldbg.c b/lib/win32-kldbg.c
index bb00508..33f5751 100644
--- a/lib/win32-kldbg.c
+++ b/lib/win32-kldbg.c
@@ -15,7 +15,6 @@
#include <string.h> /* for memset() and memcpy() */
#include "internal.h"
-#include "i386-io-windows.h"
#include "win32-helpers.h"
#ifndef ERROR_NOT_FOUND
@@ -480,7 +479,7 @@ win32_kldbg_setup(struct pci_access *a)
return 0;
}
- if (!enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege))
+ if (!win32_enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege))
{
a->debug("Process does not have right to enable Debug privilege.");
CloseHandle(kldbg_dev);
@@ -502,7 +501,7 @@ win32_kldbg_setup(struct pci_access *a)
CloseHandle(kldbg_dev);
kldbg_dev = INVALID_HANDLE_VALUE;
- revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
+ win32_revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
revert_token = NULL;
revert_only_privilege = FALSE;
return 0;
@@ -538,7 +537,7 @@ win32_kldbg_cleanup(struct pci_access *a UNUSED)
if (debug_privilege_enabled)
{
- revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
+ win32_revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
revert_token = NULL;
revert_only_privilege = FALSE;
debug_privilege_enabled = FALSE;
diff --git a/lib/win32-sysdbg.c b/lib/win32-sysdbg.c
index 22ecc60..99ce607 100644
--- a/lib/win32-sysdbg.c
+++ b/lib/win32-sysdbg.c
@@ -11,7 +11,6 @@
#include <windows.h>
#include "internal.h"
-#include "i386-io-windows.h"
#include "win32-helpers.h"
#ifndef NTSTATUS
@@ -126,9 +125,9 @@ win32_sysdbg_setup(struct pci_access *a)
if (win32_sysdbg_initialized)
return 1;
- prev_error_mode = change_error_mode(SEM_FAILCRITICALERRORS);
+ prev_error_mode = win32_change_error_mode(SEM_FAILCRITICALERRORS);
ntdll = LoadLibrary(TEXT("ntdll.dll"));
- change_error_mode(prev_error_mode);
+ win32_change_error_mode(prev_error_mode);
if (!ntdll)
{
a->debug("Cannot open ntdll.dll library.");
@@ -180,7 +179,7 @@ win32_sysdbg_setup(struct pci_access *a)
return 0;
}
- if (!enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege))
+ if (!win32_enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege))
{
a->debug("Cannot enable Debug privilege.");
FreeLibrary(ntdll);
@@ -198,7 +197,7 @@ win32_sysdbg_setup(struct pci_access *a)
return 1;
}
- revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
+ win32_revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
revert_token = NULL;
revert_only_privilege = FALSE;
@@ -245,7 +244,7 @@ win32_sysdbg_cleanup(struct pci_access *a UNUSED)
if (debug_privilege_enabled)
{
- revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
+ win32_revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
revert_token = NULL;
revert_only_privilege = FALSE;
debug_privilege_enabled = FALSE;