+/*
+ * Define __readeflags() for MSVC and GCC compilers.
+ * MSVC since version 14.00 included in WDK 6001 and since version 15.00
+ * included in VS 2008 provides __readeflags() intrinsic for both 32 and 64-bit
+ * modes. WDK 6001 defines macro __BUILDMACHINE__ to value WinDDK. VS 2008 does
+ * not define this macro at all. MSVC throws error if name of user defined
+ * function conflicts with some MSVC intrinsic.
+ * MSVC supports inline assembly via __asm keyword in 32-bit mode only.
+ * GCC version 4.9.0 and higher provides __builtin_ia32_readeflags_uXX()
+ * builtin for XX-mode. This builtin is also available as __readeflags()
+ * function indirectly via <x86intrin.h> header file.
+ */
+#if defined(_MSC_VER) && (_MSC_VER >= 1500 || (_MSC_VER >= 1400 && defined(__BUILDMACHINE__)))
+#pragma intrinsic(__readeflags)
+#elif defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 9) || (__GNUC__ > 4))
+#include <x86intrin.h>
+#elif defined(_MSC_VER) && defined(_M_IX86)
+static inline unsigned int
+__readeflags(void)
+{
+ __asm pushfd;
+ __asm pop eax;
+}
+#elif defined(__GNUC__)
+static inline unsigned
+#ifdef __x86_64__
+long long
+#endif
+int
+__readeflags(void)
+{
+ unsigned
+#ifdef __x86_64__
+ long long
+#endif
+ int eflags;
+ asm volatile ("pushf\n\tpop %0\n" : "=r" (eflags));
+ return eflags;
+}
+#else
+#error "Unsupported compiler"
+#endif
+
+/* 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.
+ * Function NtSetInformationProcess() is available in ntdll.dll library on all
+ * Windows systems but marked as it can be removed in some future version.
+ */
+#ifndef NTSTATUS
+#define NTSTATUS LONG
+#endif
+#ifndef STATUS_NOT_IMPLEMENTED
+#define STATUS_NOT_IMPLEMENTED (NTSTATUS)0xC0000002
+#endif
+#ifndef STATUS_PRIVILEGE_NOT_HELD
+#define STATUS_PRIVILEGE_NOT_HELD (NTSTATUS)0xC0000061
+#endif
+#ifndef PROCESSINFOCLASS
+#define PROCESSINFOCLASS DWORD
+#endif
+#ifndef ProcessUserModeIOPL
+#define ProcessUserModeIOPL 16
+#endif
+typedef NTSTATUS (NTAPI *NtSetInformationProcessProt)(HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength);
+
+/*
+ * 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)