+/*
+ * ProcessUserModeIOPL is syscall for NT kernel to change x86 IOPL
+ * of the current running process to 3.
+ *
+ * Process handle argument for ProcessUserModeIOPL is ignored and
+ * IOPL is always changed for the current running process. So pass
+ * GetCurrentProcess() handle for documentation purpose. Process
+ * information buffer and length are unused for ProcessUserModeIOPL.
+ *
+ * ProcessUserModeIOPL may success (return value >= 0) or may fail
+ * because it is not implemented or because of missing privilege.
+ * Other errors are not defined, so handle them as unknown.
+ */
+static BOOL
+SetProcessUserModeIOPLFunc(LPVOID Arg)
+{
+ NtSetInformationProcessProt NtSetInformationProcessPtr = (NtSetInformationProcessProt)Arg;
+ NTSTATUS nt_status = NtSetInformationProcessPtr(GetCurrentProcess(), ProcessUserModeIOPL, NULL, 0);
+ if (nt_status >= 0)
+ return TRUE;
+
+ if (nt_status == STATUS_NOT_IMPLEMENTED)
+ SetLastError(ERROR_INVALID_FUNCTION);
+ else if (nt_status == STATUS_PRIVILEGE_NOT_HELD)
+ SetLastError(ERROR_PRIVILEGE_NOT_HELD);
+ else /* TODO: convert NT STATUS to WIN32 ERROR */
+ SetLastError(ERROR_GEN_FAILURE);
+
+ return FALSE;
+}
+
+/*
+ * Set x86 I/O Privilege Level to 3 for the whole current NT process. Do it via
+ * NtSetInformationProcess() call with ProcessUserModeIOPL information class,
+ * which is supported by 32-bit Windows NT kernel versions and requires Tcb
+ * privilege.
+ */
+static BOOL
+SetProcessUserModeIOPL(VOID)
+{
+ NtSetInformationProcessProt NtSetInformationProcessPtr;
+ UINT prev_error_mode;
+ HMODULE ntdll;
+ BOOL ret;
+
+ /*
+ * Load ntdll.dll library with disabled critical-error-handler message box.
+ * 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);
+ ntdll = LoadLibrary(TEXT("ntdll.dll"));
+ change_error_mode(prev_error_mode);
+ if (!ntdll)
+ {
+ SetLastError(ERROR_INVALID_FUNCTION);
+ return FALSE;
+ }
+
+ /* Retrieve pointer to NtSetInformationProcess() function. */
+ NtSetInformationProcessPtr = (NtSetInformationProcessProt)(LPVOID)GetProcAddress(ntdll, "NtSetInformationProcess");
+ if (!NtSetInformationProcessPtr)
+ {
+ FreeLibrary(ntdll);
+ SetLastError(ERROR_INVALID_FUNCTION);
+ return FALSE;
+ }
+
+ /* Call ProcessUserModeIOPL with Tcb privilege. */
+ ret = CallFuncWithTcbPrivilege(SetProcessUserModeIOPLFunc, (LPVOID)NtSetInformationProcessPtr);
+
+ FreeLibrary(ntdll);
+
+ if (!ret)
+ return FALSE;
+
+ /*
+ * Some Windows NT kernel versions (e.g. Windows 2003 x64) do not
+ * implement ProcessUserModeIOPL syscall at all but incorrectly
+ * returns success when it is called by user process. So always
+ * after this call verify that IOPL is set to 3.
+ */
+ if (read_iopl() != 3)
+ {
+ SetLastError(ERROR_INVALID_FUNCTION);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+