Restore SEBPatch
This commit is contained in:
34
SafeExamBrowser.WindowsApi/Constants/AccessMask.cs
Normal file
34
SafeExamBrowser.WindowsApi/Constants/AccessMask.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Constants
|
||||
{
|
||||
/// <remarks>
|
||||
/// See https://docs.microsoft.com/de-de/windows/desktop/SecAuthZ/access-mask
|
||||
/// </remarks>
|
||||
[Flags]
|
||||
internal enum AccessMask : uint
|
||||
{
|
||||
DESKTOP_NONE = 0,
|
||||
DESKTOP_READOBJECTS = 0x0001,
|
||||
DESKTOP_CREATEWINDOW = 0x0002,
|
||||
DESKTOP_CREATEMENU = 0x0004,
|
||||
DESKTOP_HOOKCONTROL = 0x0008,
|
||||
DESKTOP_JOURNALRECORD = 0x0010,
|
||||
DESKTOP_JOURNALPLAYBACK = 0x0020,
|
||||
DESKTOP_ENUMERATE = 0x0040,
|
||||
DESKTOP_WRITEOBJECTS = 0x0080,
|
||||
DESKTOP_SWITCHDESKTOP = 0x0100,
|
||||
|
||||
GENERIC_ALL = (DESKTOP_READOBJECTS | DESKTOP_CREATEWINDOW | DESKTOP_CREATEMENU | DESKTOP_HOOKCONTROL | DESKTOP_JOURNALRECORD
|
||||
| DESKTOP_JOURNALPLAYBACK | DESKTOP_ENUMERATE | DESKTOP_WRITEOBJECTS | DESKTOP_SWITCHDESKTOP
|
||||
| Constant.STANDARD_RIGHTS_REQUIRED)
|
||||
}
|
||||
}
|
244
SafeExamBrowser.WindowsApi/Constants/Constant.cs
Normal file
244
SafeExamBrowser.WindowsApi/Constants/Constant.cs
Normal file
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Constants
|
||||
{
|
||||
internal static class Constant
|
||||
{
|
||||
/// <summary>
|
||||
/// A window has received mouse capture. This event is sent by the system, never by servers.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/dd318066(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const uint EVENT_SYSTEM_CAPTURESTART = 0x8;
|
||||
|
||||
/// <summary>
|
||||
/// The foreground window has changed. The system sends this event even if the foreground window has changed to another window in
|
||||
/// the same thread. Server applications never send this event.
|
||||
/// For this event, the WinEventProc callback function's hwnd parameter is the handle to the window that is in the foreground, the
|
||||
/// idObject parameter is OBJID_WINDOW, and the idChild parameter is CHILDID_SELF.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/dd318066(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const uint EVENT_SYSTEM_FOREGROUND = 0x3;
|
||||
|
||||
/// <summary>
|
||||
/// The large icon of a window. See https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-geticon#parameters.
|
||||
/// </summary>
|
||||
internal const int ICON_BIG = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The small icon of a window. See https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-geticon#parameters.
|
||||
/// </summary>
|
||||
internal const int ICON_SMALL = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The small icon of an application. If an application does not provide one, the system uses a system-generated icon for a window.
|
||||
/// See https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-geticon#parameters.
|
||||
/// </summary>
|
||||
internal const int ICON_SMALL2 = 2;
|
||||
|
||||
/// <summary>
|
||||
/// Minimize all open windows.
|
||||
/// </summary>
|
||||
internal const int MIN_ALL = 419;
|
||||
|
||||
/// <summary>
|
||||
/// Bitmask to evaluate the origin of a mouse event.
|
||||
///
|
||||
/// See https://docs.microsoft.com/en-us/windows/desktop/tablet/system-events-and-mouse-messages.
|
||||
/// </summary>
|
||||
internal const uint MOUSEEVENTF_MASK = 0xFFFFFF00;
|
||||
|
||||
/// <summary>
|
||||
/// The constant for a mouse event generated by a touch interface.
|
||||
///
|
||||
/// See https://docs.microsoft.com/en-us/windows/desktop/tablet/system-events-and-mouse-messages.
|
||||
/// </summary>
|
||||
internal const uint MOUSEEVENTF_FROMTOUCH = 0xFF515700;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the default priority class for processes, i.e. a process with no special scheduling needs.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686219(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int NORMAL_PRIORITY_CLASS = 0x20;
|
||||
|
||||
/// <summary>
|
||||
/// Standard access rights required for a desktop.
|
||||
///
|
||||
/// See https://docs.microsoft.com/de-de/windows/desktop/SecAuthZ/standard-access-rights.
|
||||
/// </summary>
|
||||
internal const int STANDARD_RIGHTS_REQUIRED = 0xF0000;
|
||||
|
||||
/// <summary>
|
||||
/// The constant for the name of a user object.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683238(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int UOI_NAME = 2;
|
||||
|
||||
/// <summary>
|
||||
/// The callback function is not mapped into the address space of the process that generates the event. Because the hook function
|
||||
/// is called across process boundaries, the system must queue events. Although this method is asynchronous, events are guaranteed
|
||||
/// to be in sequential order.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/dd373640(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const uint WINEVENT_OUTOFCONTEXT = 0x0;
|
||||
|
||||
/// <summary>
|
||||
/// Sent when the user selects a command item from a menu, when a control sends a notification message to its parent window, or
|
||||
/// when an accelerator keystroke is translated.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms647591(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int WM_COMMAND = 0x111;
|
||||
|
||||
/// <summary>
|
||||
/// Sent to a window to retrieve a handle to the large or small icon associated with a window. The system displays the large icon
|
||||
/// in the ALT+TAB dialog, and the small icon in the window caption.
|
||||
///
|
||||
/// See https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-geticon.
|
||||
/// </summary>
|
||||
internal const int WM_GETICON = 0x7F;
|
||||
|
||||
/// <summary>
|
||||
/// Posted to the window with the keyboard focus when a nonsystem key is pressed. A nonsystem key is a key that is pressed when
|
||||
/// the ALT key is not pressed.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms646280(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int WM_KEYDOWN = 0x100;
|
||||
|
||||
/// <summary>
|
||||
/// Posted to the window with the keyboard focus when a nonsystem key is released. A nonsystem key is a key that is pressed when
|
||||
/// the ALT key is not pressed, or a keyboard key that is pressed when a window has the keyboard focus.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms646281(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int WM_KEYUP = 0x101;
|
||||
|
||||
/// <summary>
|
||||
/// Posted when the user presses the left mouse button while the cursor is in the client area of a window. If the mouse is not
|
||||
/// captured, the message is posted to the window beneath the cursor. Otherwise, the message is posted to the window that has
|
||||
/// captured the mouse.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms645607(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int WM_LBUTTONDOWN = 0x201;
|
||||
|
||||
/// <summary>
|
||||
/// Posted when the user releases the left mouse button while the cursor is in the client area of a window. If the mouse is not
|
||||
/// captured, the message is posted to the window beneath the cursor. Otherwise, the message is posted to the window that has
|
||||
/// captured the mouse.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms645608(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int WM_LBUTTONUP = 0x202;
|
||||
|
||||
/// <summary>
|
||||
/// Posted when the user presses the middle mouse button while the cursor is in the client area of a window. If the mouse is not
|
||||
/// captured, the message is posted to the window beneath the cursor. Otherwise, the message is posted to the window that has
|
||||
/// captured the mouse.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms645610(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int WM_MBUTTONDOWN = 0x207;
|
||||
|
||||
/// <summary>
|
||||
/// Posted when the user releases the middle mouse button while the cursor is in the client area of a window. If the mouse is not
|
||||
/// captured, the message is posted to the window beneath the cursor. Otherwise, the message is posted to the window that has
|
||||
/// captured the mouse.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms645611(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int WM_MBUTTONUP = 0x208;
|
||||
|
||||
/// <summary>
|
||||
/// Posted to a window when the cursor moves. If the mouse is not captured, the message is posted to the window that contains the
|
||||
/// cursor. Otherwise, the message is posted to the window that has captured the mouse.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms645616(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int WM_MOUSEMOVE = 0x200;
|
||||
|
||||
/// <summary>
|
||||
/// Sent to the focus window when the mouse wheel is rotated. The DefWindowProc function propagates the message to the window's
|
||||
/// parent. There should be no internal forwarding of the message, since DefWindowProc propagates it up the parent chain until i
|
||||
/// finds a window that processes it.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms645617(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int WM_MOUSEWHEEL = 0x20A;
|
||||
|
||||
/// <summary>
|
||||
/// Posted when the user presses the right mouse button while the cursor is in the client area of a window. If the mouse is not
|
||||
/// captured, the message is posted to the window beneath the cursor. Otherwise, the message is posted to the window that has
|
||||
/// captured the mouse.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms646242(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int WM_RBUTTONDOWN = 0x204;
|
||||
|
||||
/// <summary>
|
||||
/// Posted when the user releases the right mouse button while the cursor is in the client area of a window. If the mouse is not
|
||||
/// captured, the message is posted to the window beneath the cursor. Otherwise, the message is posted to the window that has
|
||||
/// captured the mouse.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms646243(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int WM_RBUTTONUP = 0x205;
|
||||
|
||||
/// <summary>
|
||||
/// A window receives this message when the user chooses a command from the Window menu (formerly known as the system or control
|
||||
/// menu) or when the user chooses the maximize button, minimize button, restore button, or close button.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms646360(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int WM_SYSCOMMAND = 0x112;
|
||||
|
||||
/// <summary>
|
||||
/// Posted to the window with the keyboard focus when the user presses the F10 key (which activates the menu bar) or holds down
|
||||
/// the ALT key and then presses another key. It also occurs when no window currently has the keyboard focus; in this case, the
|
||||
/// WM_SYSKEYDOWN message is sent to the active window. The window that receives the message can distinguish between these two
|
||||
/// contexts by checking the context code in the lParam parameter.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms646286(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int WM_SYSKEYDOWN = 0x104;
|
||||
|
||||
/// <summary>
|
||||
/// Posted to the window with the keyboard focus when the user releases a key that was pressed while the ALT key was held down.
|
||||
/// It also occurs when no window currently has the keyboard focus; in this case, the WM_SYSKEYUP message is sent to the active
|
||||
/// window. The window that receives the message can distinguish between these two contexts by checking the context code in the
|
||||
/// lParam parameter.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms646287(v=vs.85).aspx
|
||||
/// </summary>
|
||||
internal const int WM_SYSKEYUP = 0x105;
|
||||
|
||||
/// <summary>
|
||||
/// Posted when the user presses the first or second X button while the cursor is in the client area of a window. If the mouse is
|
||||
/// not captured, the message is posted to the window beneath the cursor. Otherwise, the message is posted to the window that has
|
||||
/// captured the mouse.
|
||||
///
|
||||
/// See https://docs.microsoft.com/de-de/windows/desktop/inputdev/wm-xbuttondown.
|
||||
/// </summary>
|
||||
internal const int WM_XBUTTONDOWN = 0x20B;
|
||||
|
||||
/// <summary>
|
||||
/// Posted when the user releases the first or second X button while the cursor is in the client area of a window. If the mouse is
|
||||
/// not captured, the message is posted to the window beneath the cursor. Otherwise, the message is posted to the window that has
|
||||
/// captured the mouse.
|
||||
///
|
||||
/// See https://docs.microsoft.com/de-de/windows/desktop/inputdev/wm-xbuttonup.
|
||||
/// </summary>
|
||||
internal const int WM_XBUTTONUP = 0x20C;
|
||||
}
|
||||
}
|
100
SafeExamBrowser.WindowsApi/Constants/HookType.cs
Normal file
100
SafeExamBrowser.WindowsApi/Constants/HookType.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Constants
|
||||
{
|
||||
/// <remarks>
|
||||
/// See http://www.pinvoke.net/default.aspx/Enums/HookType.html.
|
||||
/// </remarks>
|
||||
internal enum HookType
|
||||
{
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that records input messages posted to the system message queue. This hook is useful for recording
|
||||
/// macros. For more information, see the JournalRecordProc hook procedure.
|
||||
/// </summary>
|
||||
WH_JOURNALRECORD = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that posts messages previously recorded by a WH_JOURNALRECORD hook procedure. For more information,
|
||||
/// see the JournalPlaybackProc hook procedure.
|
||||
/// </summary>
|
||||
WH_JOURNALPLAYBACK = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that monitors keystroke messages. For more information, see the KeyboardProc hook procedure.
|
||||
/// </summary>
|
||||
WH_KEYBOARD = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that monitors messages posted to a message queue. For more information, see the GetMsgProc hook
|
||||
/// procedure.
|
||||
/// </summary>
|
||||
WH_GETMESSAGE = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that monitors messages before the system sends them to the destination window procedure. For more
|
||||
/// information, see the CallWndProc hook procedure.
|
||||
/// </summary>
|
||||
WH_CALLWNDPROC = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that receives notifications useful to a CBT application. For more information, see the CBTProc hook
|
||||
/// procedure.
|
||||
/// </summary>
|
||||
WH_CBT = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that monitors messages generated as a result of an input event in a dialog box, message box, menu,
|
||||
/// or scroll bar. The hook procedure monitors these messages for all applications in the same desktop as the calling thread. For
|
||||
/// more information, see the SysMsgProc hook procedure.
|
||||
/// </summary>
|
||||
WH_SYSMSGFILTER = 6,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that monitors mouse messages. For more information, see the MouseProc hook procedure.
|
||||
/// </summary>
|
||||
WH_MOUSE = 7,
|
||||
|
||||
WH_HARDWARE = 8,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure useful for debugging other hook procedures. For more information, see the DebugProc hook procedure.
|
||||
/// </summary>
|
||||
WH_DEBUG = 9,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that receives notifications useful to shell applications. For more information, see the ShellProc
|
||||
/// hook procedure.
|
||||
/// </summary>
|
||||
WH_SHELL = 10,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that will be called when the application's foreground thread is about to become idle. This hook is
|
||||
/// useful for performing low priority tasks during idle time. For more information, see the ForegroundIdleProc hook procedure.
|
||||
/// </summary>
|
||||
WH_FOREGROUNDIDLE = 11,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that monitors messages after they have been processed by the destination window procedure. For more
|
||||
/// information, see the CallWndRetProc hook procedure.
|
||||
/// </summary>
|
||||
WH_CALLWNDPROCRET = 12,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that monitors low-level keyboard input events. For more information, see the LowLevelKeyboardProc
|
||||
/// hook procedure.
|
||||
/// </summary>
|
||||
WH_KEYBOARD_LL = 13,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that monitors low-level mouse input events. For more information, see the LowLevelMouseProc hook
|
||||
/// procedure.
|
||||
/// </summary>
|
||||
WH_MOUSE_LL = 14
|
||||
}
|
||||
}
|
59
SafeExamBrowser.WindowsApi/Constants/SPI.cs
Normal file
59
SafeExamBrowser.WindowsApi/Constants/SPI.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Constants
|
||||
{
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow.
|
||||
/// See http://www.pinvoke.net/default.aspx/Enums/SPI.html?diff=y.
|
||||
/// </remarks>
|
||||
internal enum SPI : uint
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves the full path of the bitmap file for the desktop wallpaper. The pvParam parameter must point to a buffer
|
||||
/// that receives a null-terminated path string. Set the uiParam parameter to the size, in characters, of the pvParam buffer.
|
||||
/// The returned string will not exceed MAX_PATH characters. If there is no desktop wallpaper, the returned string is empty.
|
||||
/// </summary>
|
||||
GETDESKWALLPAPER = 0x73,
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves information about the StickyKeys accessibility feature. The pvParam parameter must point to a STICKYKEYS structure
|
||||
/// that receives the information. Set the cbSize member of this structure and the uiParam parameter to sizeof(STICKYKEYS).
|
||||
/// </summary>
|
||||
GETSTICKYKEYS = 0x3A,
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the size of the work area on the primary display monitor. The work area is the portion of the screen
|
||||
/// not obscured by the system taskbar or by application desktop toolbars. The pvParam parameter must point to a
|
||||
/// RECT structure that receives the coordinates of the work area, expressed in virtual screen coordinates. To get
|
||||
/// the work area of a monitor other than the primary display monitor, call the GetMonitorInfo function.
|
||||
/// </summary>
|
||||
GETWORKAREA = 0x30,
|
||||
|
||||
/// <summary>
|
||||
/// Sets the desktop wallpaper. The value of the pvParam parameter determines the new wallpaper. To specify a wallpaper bitmap,
|
||||
/// set pvParam to point to a null-terminated string containing the name of a bitmap file. Setting pvParam to "" removes the
|
||||
/// wallpaper. Setting pvParam to SETWALLPAPER_DEFAULT or null reverts to the default wallpaper.
|
||||
/// </summary>
|
||||
SETDESKWALLPAPER = 0x14,
|
||||
|
||||
/// <summary>
|
||||
/// Sets the parameters of the StickyKeys accessibility feature. The pvParam parameter must point to a STICKYKEYS structure
|
||||
/// that contains the new parameters. Set the cbSize member of this structure and the uiParam parameter to sizeof(STICKYKEYS).
|
||||
/// </summary>
|
||||
SETSTICKYKEYS = 0x3B,
|
||||
|
||||
/// <summary>
|
||||
/// Sets the size of the work area. The work area is the portion of the screen not obscured by the system taskbar
|
||||
/// or by application desktop toolbars. The pvParam parameter is a pointer to a RECT structure that specifies the
|
||||
/// new work area rectangle, expressed in virtual screen coordinates. In a system with multiple display monitors,
|
||||
/// the function sets the work area of the monitor that contains the specified rectangle.
|
||||
/// </summary>
|
||||
SETWORKAREA = 0x2F,
|
||||
}
|
||||
}
|
36
SafeExamBrowser.WindowsApi/Constants/SPIF.cs
Normal file
36
SafeExamBrowser.WindowsApi/Constants/SPIF.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Constants
|
||||
{
|
||||
/// <remarks>
|
||||
/// See http://www.pinvoke.net/default.aspx/Enums/SPIF.html.
|
||||
/// </remarks>
|
||||
[Flags]
|
||||
internal enum SPIF
|
||||
{
|
||||
NONE = 0x00,
|
||||
|
||||
/// <summary>
|
||||
/// Writes the new system-wide parameter setting to the user profile.
|
||||
/// </summary>
|
||||
UPDATEINIFILE = 0x01,
|
||||
|
||||
/// <summary>
|
||||
/// Broadcasts the WM_SETTINGCHANGE message after updating the user profile.
|
||||
/// </summary>
|
||||
SENDCHANGE = 0x02,
|
||||
|
||||
/// <summary>
|
||||
/// Performs UPDATEINIFILE and SENDCHANGE.
|
||||
/// </summary>
|
||||
UPDATEANDCHANGE = 0x03
|
||||
}
|
||||
}
|
97
SafeExamBrowser.WindowsApi/Constants/ShowWindowCommand.cs
Normal file
97
SafeExamBrowser.WindowsApi/Constants/ShowWindowCommand.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Constants
|
||||
{
|
||||
/// <remarks>
|
||||
/// See http://www.pinvoke.net/default.aspx/Enums/ShowWindowCommand.html.
|
||||
/// </remarks>
|
||||
internal enum ShowWindowCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Hides the window and activates another window.
|
||||
/// </summary>
|
||||
Hide = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Activates and displays a window. If the window is minimized or
|
||||
/// maximized, the system restores it to its original size and position.
|
||||
/// An application should specify this flag when displaying the window
|
||||
/// for the first time.
|
||||
/// </summary>
|
||||
Normal = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Activates the window and displays it as a minimized window.
|
||||
/// </summary>
|
||||
ShowMinimized = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Maximizes the specified window.
|
||||
/// </summary>
|
||||
Maximize = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Activates the window and displays it as a maximized window.
|
||||
/// </summary>
|
||||
ShowMaximized = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Displays a window in its most recent size and position. This value
|
||||
/// is similar to <see cref="Win32.ShowWindowCommand.Normal"/>, except
|
||||
/// the window is not activated.
|
||||
/// </summary>
|
||||
ShowNoActivate = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Activates the window and displays it in its current size and position.
|
||||
/// </summary>
|
||||
Show = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Minimizes the specified window and activates the next top-level
|
||||
/// window in the Z order.
|
||||
/// </summary>
|
||||
Minimize = 6,
|
||||
|
||||
/// <summary>
|
||||
/// Displays the window as a minimized window. This value is similar to
|
||||
/// <see cref="Win32.ShowWindowCommand.ShowMinimized"/>, except the
|
||||
/// window is not activated.
|
||||
/// </summary>
|
||||
ShowMinNoActive = 7,
|
||||
|
||||
/// <summary>
|
||||
/// Displays the window in its current size and position. This value is
|
||||
/// similar to <see cref="Win32.ShowWindowCommand.Show"/>, except the
|
||||
/// window is not activated.
|
||||
/// </summary>
|
||||
ShowNA = 8,
|
||||
|
||||
/// <summary>
|
||||
/// Activates and displays the window. If the window is minimized or
|
||||
/// maximized, the system restores it to its original size and position.
|
||||
/// An application should specify this flag when restoring a minimized window.
|
||||
/// </summary>
|
||||
Restore = 9,
|
||||
|
||||
/// <summary>
|
||||
/// Sets the show state based on the SW_* value specified in the
|
||||
/// STARTUPINFO structure passed to the CreateProcess function by the
|
||||
/// program that started the application.
|
||||
/// </summary>
|
||||
ShowDefault = 10,
|
||||
|
||||
/// <summary>
|
||||
/// <b>Windows 2000/XP:</b> Minimizes a window, even if the thread
|
||||
/// that owns the window is not responding. This flag should only be
|
||||
/// used when minimizing windows from a different thread.
|
||||
/// </summary>
|
||||
ForceMinimize = 11
|
||||
}
|
||||
}
|
64
SafeExamBrowser.WindowsApi/Constants/StickyKeysFlags.cs
Normal file
64
SafeExamBrowser.WindowsApi/Constants/StickyKeysFlags.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Constants
|
||||
{
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-stickykeys#members
|
||||
/// </remarks>
|
||||
[Flags]
|
||||
internal enum StickyKeysFlags : int
|
||||
{
|
||||
/// <summary>
|
||||
/// If this flag is set, the StickyKeys feature is on.
|
||||
/// </summary>
|
||||
ON = 0x1,
|
||||
|
||||
/// <summary>
|
||||
/// If this flag is set, the StickyKeys feature is available.
|
||||
/// </summary>
|
||||
AVAILABLE = 0x2,
|
||||
|
||||
/// <summary>
|
||||
/// If this flag is set, the user can turn the StickyKeys feature on and off by pressing the SHIFT key five times.
|
||||
/// </summary>
|
||||
HOTKEYACTIVE = 0x4,
|
||||
|
||||
/// <summary>
|
||||
/// A confirmation dialog appears when the StickyKeys feature is activated by using the hot key.
|
||||
/// </summary>
|
||||
CONFIRMHOTKEY = 0x8,
|
||||
|
||||
/// <summary>
|
||||
/// If this flag is set, the system plays a siren sound when the user turns the StickyKeys feature on or off by using the hot key.
|
||||
/// </summary>
|
||||
HOTKEYSOUND = 0x10,
|
||||
|
||||
/// <summary>
|
||||
/// A visual indicator should be displayed when the StickyKeys feature is on.
|
||||
/// </summary>
|
||||
INDICATOR = 0x20,
|
||||
|
||||
/// <summary>
|
||||
/// If this flag is set, the system plays a sound when the user latches, locks, or releases modifier keys using the StickyKeys feature.
|
||||
/// </summary>
|
||||
AUDIBLEFEEDBACK = 0x40,
|
||||
|
||||
/// <summary>
|
||||
/// If this flag is set, pressing a modifier key twice in a row locks down the key until the user presses it a third time.
|
||||
/// </summary>
|
||||
TRISTATE = 0x80,
|
||||
|
||||
/// <summary>
|
||||
/// If this flag is set, releasing a modifier key that has been pressed in combination with any other key turns off the StickyKeys feature.
|
||||
/// </summary>
|
||||
TWOKEYSOFF = 0x100,
|
||||
}
|
||||
}
|
21
SafeExamBrowser.WindowsApi/Constants/SystemCommand.cs
Normal file
21
SafeExamBrowser.WindowsApi/Constants/SystemCommand.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms646360(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal enum SystemCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Closes the window.
|
||||
/// </summary>
|
||||
CLOSE = 0xF060
|
||||
}
|
||||
}
|
29
SafeExamBrowser.WindowsApi/Constants/ThreadAccess.cs
Normal file
29
SafeExamBrowser.WindowsApi/Constants/ThreadAccess.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Constants
|
||||
{
|
||||
/// <remarks>
|
||||
/// See https://docs.microsoft.com/en-us/windows/desktop/ProcThread/thread-security-and-access-rights.
|
||||
/// </remarks>
|
||||
[Flags]
|
||||
internal enum ThreadAccess : int
|
||||
{
|
||||
TERMINATE = 0x1,
|
||||
SUSPEND_RESUME = 0x2,
|
||||
GET_CONTEXT = 0x8,
|
||||
SET_CONTEXT = 0x10,
|
||||
SET_INFORMATION = 0x20,
|
||||
QUERY_INFORMATION = 0x40,
|
||||
SET_THREAD_TOKEN = 0x80,
|
||||
IMPERSONATE = 0x100,
|
||||
DIRECT_IMPERSONATION = 0x200
|
||||
}
|
||||
}
|
25
SafeExamBrowser.WindowsApi/Constants/VirtualKeyCode.cs
Normal file
25
SafeExamBrowser.WindowsApi/Constants/VirtualKeyCode.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Constants
|
||||
{
|
||||
/// <remarks>
|
||||
/// See https://docs.microsoft.com/en-us/windows/desktop/inputdev/virtual-key-codes.
|
||||
/// </remarks>
|
||||
internal enum VirtualKeyCode
|
||||
{
|
||||
A = 0x41,
|
||||
Q = 0x51,
|
||||
Delete = 0x2E,
|
||||
LeftAlt = 0xA4,
|
||||
LeftControl = 0xA2,
|
||||
LeftWindows = 0x5B,
|
||||
RightAlt = 0xA5,
|
||||
RightControl = 0xA3
|
||||
}
|
||||
}
|
17
SafeExamBrowser.WindowsApi/Delegates/EnumDesktopDelegate.cs
Normal file
17
SafeExamBrowser.WindowsApi/Delegates/EnumDesktopDelegate.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Delegates
|
||||
{
|
||||
/// <remarks>
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682612(v=vs.85).aspx
|
||||
/// </remarks>
|
||||
internal delegate bool EnumDesktopDelegate(string lpszDesktop, IntPtr lParam);
|
||||
}
|
17
SafeExamBrowser.WindowsApi/Delegates/EnumWindowsDelegate.cs
Normal file
17
SafeExamBrowser.WindowsApi/Delegates/EnumWindowsDelegate.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Delegates
|
||||
{
|
||||
/// <remarks>
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms633498(v=vs.85).aspx
|
||||
/// </remarks>
|
||||
internal delegate bool EnumWindowsDelegate(IntPtr hWnd, IntPtr lParam);
|
||||
}
|
17
SafeExamBrowser.WindowsApi/Delegates/EventDelegate.cs
Normal file
17
SafeExamBrowser.WindowsApi/Delegates/EventDelegate.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Delegates
|
||||
{
|
||||
/// <remarks>
|
||||
/// See https://docs.microsoft.com/de-de/windows/desktop/api/winuser/nc-winuser-wineventproc
|
||||
/// </remarks>
|
||||
internal delegate void EventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
|
||||
}
|
17
SafeExamBrowser.WindowsApi/Delegates/HookDelegate.cs
Normal file
17
SafeExamBrowser.WindowsApi/Delegates/HookDelegate.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Delegates
|
||||
{
|
||||
/// <remarks>
|
||||
/// See https://docs.microsoft.com/de-de/windows/desktop/api/winuser/nc-winuser-hookproc
|
||||
/// </remarks>
|
||||
internal delegate IntPtr HookDelegate(int nCode, IntPtr wParam, IntPtr lParam);
|
||||
}
|
42
SafeExamBrowser.WindowsApi/Desktops/Desktop.cs
Normal file
42
SafeExamBrowser.WindowsApi/Desktops/Desktop.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
using SafeExamBrowser.WindowsApi.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Desktops
|
||||
{
|
||||
internal class Desktop : IDesktop
|
||||
{
|
||||
public IntPtr Handle { get; private set; }
|
||||
public string Name { get; private set; }
|
||||
|
||||
public Desktop(IntPtr handle, string name)
|
||||
{
|
||||
Handle = handle;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"'{Name}' [{Handle}]";
|
||||
}
|
||||
}
|
||||
}
|
123
SafeExamBrowser.WindowsApi/Desktops/DesktopFactory.cs
Normal file
123
SafeExamBrowser.WindowsApi/Desktops/DesktopFactory.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.WindowsApi.Constants;
|
||||
using SafeExamBrowser.WindowsApi.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Desktops
|
||||
{
|
||||
public class DesktopFactory : IDesktopFactory
|
||||
{
|
||||
private readonly ILogger logger;
|
||||
private readonly Random random;
|
||||
|
||||
public DesktopFactory(ILogger logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.random = new Random();
|
||||
}
|
||||
|
||||
public IDesktop CreateNew(string name)
|
||||
{
|
||||
logger.Debug($"Attempting to create new desktop '{name}'...");
|
||||
|
||||
var handle = User32.CreateDesktop(name, IntPtr.Zero, IntPtr.Zero, 0, (uint) AccessMask.GENERIC_ALL, IntPtr.Zero);
|
||||
|
||||
if (handle == IntPtr.Zero)
|
||||
{
|
||||
logger.Error($"Failed to create new desktop '{name}'!");
|
||||
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
var desktop = new Desktop(handle, name);
|
||||
|
||||
logger.Debug($"Successfully created desktop {desktop}.");
|
||||
|
||||
return desktop;
|
||||
}
|
||||
|
||||
public IDesktop CreateRandom()
|
||||
{
|
||||
logger.Debug($"Attempting to create random desktop...");
|
||||
|
||||
var name = GenerateRandomDesktopName();
|
||||
var handle = User32.CreateDesktop(name, IntPtr.Zero, IntPtr.Zero, 0, (uint) AccessMask.GENERIC_ALL, IntPtr.Zero);
|
||||
|
||||
if (handle == IntPtr.Zero)
|
||||
{
|
||||
logger.Error($"Failed to create random desktop '{name}'!");
|
||||
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
var obfuscatedHandle = new IntPtr(random.Next(100, 10000));
|
||||
var obfuscatedName = GenerateRandomDesktopName();
|
||||
var desktop = new ObfuscatedDesktop(handle, name, obfuscatedHandle, obfuscatedName);
|
||||
|
||||
logger.Debug($"Successfully created random desktop {desktop}.");
|
||||
|
||||
return desktop;
|
||||
}
|
||||
|
||||
public IDesktop GetCurrent()
|
||||
{
|
||||
var threadId = Kernel32.GetCurrentThreadId();
|
||||
var handle = User32.GetThreadDesktop(threadId);
|
||||
var name = string.Empty;
|
||||
var nameLength = 0;
|
||||
|
||||
if (handle == IntPtr.Zero)
|
||||
{
|
||||
logger.Error($"Failed to get desktop handle for thread with ID = {threadId}!");
|
||||
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
logger.Debug($"Found desktop handle for thread with ID = {threadId}. Attempting to get desktop name...");
|
||||
|
||||
User32.GetUserObjectInformation(handle, Constant.UOI_NAME, IntPtr.Zero, 0, ref nameLength);
|
||||
|
||||
var namePointer = Marshal.AllocHGlobal(nameLength);
|
||||
var success = User32.GetUserObjectInformation(handle, Constant.UOI_NAME, namePointer, nameLength, ref nameLength);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
logger.Error($"Failed to retrieve name for desktop with handle = {handle}!");
|
||||
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
name = Marshal.PtrToStringAnsi(namePointer);
|
||||
Marshal.FreeHGlobal(namePointer);
|
||||
|
||||
var desktop = new Desktop(handle, name);
|
||||
|
||||
logger.Debug($"Successfully determined current desktop {desktop}.");
|
||||
|
||||
return desktop;
|
||||
}
|
||||
|
||||
private string GenerateRandomDesktopName()
|
||||
{
|
||||
var length = random.Next(5, 20);
|
||||
var name = new char[length];
|
||||
|
||||
for (var letter = 0; letter < length; letter++)
|
||||
{
|
||||
name[letter] = (char) (random.Next(2) == 0 && letter != 0 ? random.Next('a', 'z' + 1) : random.Next('A', 'Z' + 1));
|
||||
}
|
||||
|
||||
return new string(name);
|
||||
}
|
||||
}
|
||||
}
|
55
SafeExamBrowser.WindowsApi/Desktops/DesktopMonitor.cs
Normal file
55
SafeExamBrowser.WindowsApi/Desktops/DesktopMonitor.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Timers;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.WindowsApi.Constants;
|
||||
using SafeExamBrowser.WindowsApi.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Desktops
|
||||
{
|
||||
public class DesktopMonitor : IDesktopMonitor
|
||||
{
|
||||
private readonly ILogger logger;
|
||||
private readonly Timer timer;
|
||||
|
||||
private IDesktop desktop;
|
||||
|
||||
public DesktopMonitor(ILogger logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.timer = new Timer(1000);
|
||||
}
|
||||
|
||||
public void Start(IDesktop desktop)
|
||||
{
|
||||
this.desktop = desktop;
|
||||
|
||||
timer.AutoReset = false;
|
||||
timer.Elapsed += Timer_Elapsed;
|
||||
timer.Start();
|
||||
|
||||
logger.Info($"Started monitoring desktop {desktop}.");
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
timer.Stop();
|
||||
timer.Elapsed -= Timer_Elapsed;
|
||||
|
||||
logger.Info($"Stopped monitoring desktop {desktop}.");
|
||||
}
|
||||
|
||||
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
29
SafeExamBrowser.WindowsApi/Desktops/ObfuscatedDesktop.cs
Normal file
29
SafeExamBrowser.WindowsApi/Desktops/ObfuscatedDesktop.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Desktops
|
||||
{
|
||||
internal class ObfuscatedDesktop : Desktop
|
||||
{
|
||||
private readonly IntPtr obfuscatedHandle;
|
||||
private readonly string obfuscatedName;
|
||||
|
||||
public ObfuscatedDesktop(IntPtr handle, string name, IntPtr obfuscatedHandle, string obfuscatedName) : base(handle, name)
|
||||
{
|
||||
this.obfuscatedHandle = obfuscatedHandle;
|
||||
this.obfuscatedName = obfuscatedName;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"'{obfuscatedName}' [{obfuscatedHandle}]";
|
||||
}
|
||||
}
|
||||
}
|
74
SafeExamBrowser.WindowsApi/ExplorerShell.cs
Normal file
74
SafeExamBrowser.WindowsApi/ExplorerShell.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.WindowsApi.Contracts;
|
||||
using SafeExamBrowser.WindowsApi.Types;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi
|
||||
{
|
||||
public class ExplorerShell : IExplorerShell
|
||||
{
|
||||
private ILogger logger;
|
||||
private INativeMethods nativeMethods;
|
||||
private IList<Window> minimizedWindows = new List<Window>();
|
||||
|
||||
public ExplorerShell(ILogger logger, INativeMethods nativeMethods)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.nativeMethods = nativeMethods;
|
||||
this.minimizedWindows = new List<Window>();
|
||||
}
|
||||
|
||||
public void HideAllWindows()
|
||||
{
|
||||
logger.Info("Searching for windows to be minimized...");
|
||||
logger.Info("Minimizing all open windows...");
|
||||
logger.Info("Open windows successfully minimized.");
|
||||
}
|
||||
|
||||
public void RestoreAllWindows()
|
||||
{
|
||||
logger.Info("Restoring all minimized windows...");
|
||||
logger.Info("Minimized windows successfully restored.");
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void KillExplorerShell(int processId)
|
||||
{
|
||||
var process = new System.Diagnostics.Process();
|
||||
var taskkillPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "taskkill.exe");
|
||||
|
||||
logger.Warn("Failed to gracefully terminate explorer shell, attempting forceful termination...");
|
||||
|
||||
process.StartInfo.Arguments = $"/F /PID {processId}";
|
||||
process.StartInfo.CreateNoWindow = true;
|
||||
process.StartInfo.FileName = taskkillPath;
|
||||
process.StartInfo.UseShellExecute = false;
|
||||
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
|
||||
process.StartInfo.WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
|
||||
process.Start();
|
||||
process.WaitForExit();
|
||||
}
|
||||
}
|
||||
}
|
128
SafeExamBrowser.WindowsApi/Hooks/KeyboardHook.cs
Normal file
128
SafeExamBrowser.WindowsApi/Hooks/KeyboardHook.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using SafeExamBrowser.WindowsApi.Constants;
|
||||
using SafeExamBrowser.WindowsApi.Contracts.Events;
|
||||
using SafeExamBrowser.WindowsApi.Delegates;
|
||||
using SafeExamBrowser.WindowsApi.Types;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Hooks
|
||||
{
|
||||
internal class KeyboardHook
|
||||
{
|
||||
private bool altPressed, ctrlPressed;
|
||||
private KeyboardHookCallback callback;
|
||||
private IntPtr handle;
|
||||
private HookDelegate hookDelegate;
|
||||
|
||||
internal Guid Id { get; private set; }
|
||||
|
||||
internal KeyboardHook(KeyboardHookCallback callback)
|
||||
{
|
||||
this.callback = callback;
|
||||
this.Id = Guid.NewGuid();
|
||||
}
|
||||
|
||||
internal void Attach()
|
||||
{
|
||||
var process = System.Diagnostics.Process.GetCurrentProcess();
|
||||
var module = process.MainModule;
|
||||
var moduleHandle = Kernel32.GetModuleHandle(module.ModuleName);
|
||||
|
||||
// IMPORTANT:
|
||||
// Ensures that the hook delegate does not get garbage collected prematurely, as it will be passed to unmanaged code.
|
||||
// Not doing so will result in a <c>CallbackOnCollectedDelegate</c> error and subsequent application crash!
|
||||
hookDelegate = new HookDelegate(LowLevelKeyboardProc);
|
||||
handle = User32.SetWindowsHookEx(HookType.WH_KEYBOARD_LL, hookDelegate, moduleHandle, 0);
|
||||
}
|
||||
|
||||
internal bool Detach()
|
||||
{
|
||||
return User32.UnhookWindowsHookEx(handle);
|
||||
}
|
||||
|
||||
private IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam)
|
||||
{
|
||||
if (nCode >= 0)
|
||||
{
|
||||
var keyData = (KBDLLHOOKSTRUCT) Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));
|
||||
var modifier = GetModifiers(keyData, wParam.ToInt32());
|
||||
var state = GetState(wParam.ToInt32());
|
||||
|
||||
if (callback((int) keyData.KeyCode, modifier, state))
|
||||
{
|
||||
return (IntPtr) 1;
|
||||
}
|
||||
}
|
||||
|
||||
return User32.CallNextHookEx(handle, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
private KeyState GetState(int wParam)
|
||||
{
|
||||
switch (wParam)
|
||||
{
|
||||
case Constant.WM_KEYDOWN:
|
||||
case Constant.WM_SYSKEYDOWN:
|
||||
return KeyState.Pressed;
|
||||
case Constant.WM_KEYUP:
|
||||
case Constant.WM_SYSKEYUP:
|
||||
return KeyState.Released;
|
||||
default:
|
||||
return KeyState.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
private KeyModifier GetModifiers(KBDLLHOOKSTRUCT keyData, int wParam)
|
||||
{
|
||||
var modifier = KeyModifier.None;
|
||||
|
||||
TrackCtrlAndAlt(keyData, wParam);
|
||||
|
||||
if (altPressed || keyData.Flags.HasFlag(KBDLLHOOKSTRUCTFlags.LLKHF_ALTDOWN))
|
||||
{
|
||||
modifier |= KeyModifier.Alt;
|
||||
}
|
||||
|
||||
if (ctrlPressed)
|
||||
{
|
||||
modifier |= KeyModifier.Ctrl;
|
||||
}
|
||||
|
||||
return modifier;
|
||||
}
|
||||
|
||||
private void TrackCtrlAndAlt(KBDLLHOOKSTRUCT keyData, int wParam)
|
||||
{
|
||||
var keyCode = keyData.KeyCode;
|
||||
|
||||
if (keyCode == (uint) VirtualKeyCode.LeftControl || keyCode == (uint) VirtualKeyCode.RightControl)
|
||||
{
|
||||
ctrlPressed = IsPressed(wParam);
|
||||
}
|
||||
else if (keyCode == (uint) VirtualKeyCode.LeftAlt || keyCode == (uint) VirtualKeyCode.RightAlt)
|
||||
{
|
||||
altPressed = IsPressed(wParam);
|
||||
}
|
||||
|
||||
if (ctrlPressed && altPressed && keyCode == (uint) VirtualKeyCode.Delete)
|
||||
{
|
||||
// When the Secure Attention Sequence is pressed, the WM_KEYUP / WM_SYSKEYUP messages for CTRL and ALT get lost...
|
||||
ctrlPressed = false;
|
||||
altPressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsPressed(int wParam)
|
||||
{
|
||||
return wParam == Constant.WM_KEYDOWN || wParam == Constant.WM_SYSKEYDOWN;
|
||||
}
|
||||
}
|
||||
}
|
126
SafeExamBrowser.WindowsApi/Hooks/MouseHook.cs
Normal file
126
SafeExamBrowser.WindowsApi/Hooks/MouseHook.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using SafeExamBrowser.WindowsApi.Constants;
|
||||
using SafeExamBrowser.WindowsApi.Contracts.Events;
|
||||
using SafeExamBrowser.WindowsApi.Delegates;
|
||||
using SafeExamBrowser.WindowsApi.Types;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Hooks
|
||||
{
|
||||
internal class MouseHook
|
||||
{
|
||||
private MouseHookCallback callback;
|
||||
private IntPtr handle;
|
||||
private HookDelegate hookDelegate;
|
||||
|
||||
internal Guid Id { get; private set; }
|
||||
|
||||
internal MouseHook(MouseHookCallback callback)
|
||||
{
|
||||
this.callback = callback;
|
||||
this.Id = Guid.NewGuid();
|
||||
}
|
||||
|
||||
internal void Attach()
|
||||
{
|
||||
var process = System.Diagnostics.Process.GetCurrentProcess();
|
||||
var module = process.MainModule;
|
||||
var moduleHandle = Kernel32.GetModuleHandle(module.ModuleName);
|
||||
|
||||
// IMPORTANT:
|
||||
// Ensures that the hook delegate does not get garbage collected prematurely, as it will be passed to unmanaged code.
|
||||
// Not doing so will result in a <c>CallbackOnCollectedDelegate</c> error and subsequent application crash!
|
||||
hookDelegate = new HookDelegate(LowLevelMouseProc);
|
||||
handle = User32.SetWindowsHookEx(HookType.WH_MOUSE_LL, hookDelegate, moduleHandle, 0);
|
||||
}
|
||||
|
||||
internal bool Detach()
|
||||
{
|
||||
return User32.UnhookWindowsHookEx(handle);
|
||||
}
|
||||
|
||||
private IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam)
|
||||
{
|
||||
if (nCode >= 0 && !Ignore(wParam.ToInt32()))
|
||||
{
|
||||
var mouseData = (MSLLHOOKSTRUCT) Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
|
||||
var button = GetButton(wParam.ToInt32());
|
||||
var state = GetState(wParam.ToInt32());
|
||||
var info = GetInfo(mouseData);
|
||||
|
||||
if (callback(button, state, info))
|
||||
{
|
||||
return (IntPtr) 1;
|
||||
}
|
||||
}
|
||||
|
||||
return User32.CallNextHookEx(handle, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
private bool Ignore(int wParam)
|
||||
{
|
||||
// For performance reasons, ignore mouse movement and wheel rotation...
|
||||
return wParam == Constant.WM_MOUSEMOVE || wParam == Constant.WM_MOUSEWHEEL;
|
||||
}
|
||||
|
||||
private MouseButton GetButton(int wParam)
|
||||
{
|
||||
switch (wParam)
|
||||
{
|
||||
case Constant.WM_LBUTTONDOWN:
|
||||
case Constant.WM_LBUTTONUP:
|
||||
return MouseButton.Left;
|
||||
case Constant.WM_MBUTTONDOWN:
|
||||
case Constant.WM_MBUTTONUP:
|
||||
return MouseButton.Middle;
|
||||
case Constant.WM_RBUTTONDOWN:
|
||||
case Constant.WM_RBUTTONUP:
|
||||
return MouseButton.Right;
|
||||
case Constant.WM_XBUTTONDOWN:
|
||||
case Constant.WM_XBUTTONUP:
|
||||
return MouseButton.Auxiliary;
|
||||
default:
|
||||
return MouseButton.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
private MouseInformation GetInfo(MSLLHOOKSTRUCT mouseData)
|
||||
{
|
||||
var info = new MouseInformation();
|
||||
var extraInfo = mouseData.DwExtraInfo.ToUInt32();
|
||||
|
||||
info.IsTouch = (extraInfo & Constant.MOUSEEVENTF_MASK) == Constant.MOUSEEVENTF_FROMTOUCH;
|
||||
info.X = mouseData.Point.X;
|
||||
info.Y = mouseData.Point.Y;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
private MouseButtonState GetState(int wParam)
|
||||
{
|
||||
switch (wParam)
|
||||
{
|
||||
case Constant.WM_LBUTTONDOWN:
|
||||
case Constant.WM_MBUTTONDOWN:
|
||||
case Constant.WM_RBUTTONDOWN:
|
||||
case Constant.WM_XBUTTONDOWN:
|
||||
return MouseButtonState.Pressed;
|
||||
case Constant.WM_LBUTTONUP:
|
||||
case Constant.WM_MBUTTONUP:
|
||||
case Constant.WM_RBUTTONUP:
|
||||
case Constant.WM_XBUTTONUP:
|
||||
return MouseButtonState.Released;
|
||||
default:
|
||||
return MouseButtonState.Unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
66
SafeExamBrowser.WindowsApi/Hooks/SystemHook.cs
Normal file
66
SafeExamBrowser.WindowsApi/Hooks/SystemHook.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using SafeExamBrowser.WindowsApi.Constants;
|
||||
using SafeExamBrowser.WindowsApi.Contracts.Events;
|
||||
using SafeExamBrowser.WindowsApi.Delegates;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Hooks
|
||||
{
|
||||
internal class SystemHook
|
||||
{
|
||||
private SystemEventCallback callback;
|
||||
private AutoResetEvent detachEvent, detachResultAvailableEvent;
|
||||
private bool detachSuccess;
|
||||
private EventDelegate eventDelegate;
|
||||
private uint eventId;
|
||||
private IntPtr handle;
|
||||
|
||||
internal Guid Id { get; private set; }
|
||||
|
||||
public SystemHook(SystemEventCallback callback, uint eventId)
|
||||
{
|
||||
this.callback = callback;
|
||||
this.detachEvent = new AutoResetEvent(false);
|
||||
this.detachResultAvailableEvent = new AutoResetEvent(false);
|
||||
this.eventId = eventId;
|
||||
this.Id = Guid.NewGuid();
|
||||
}
|
||||
|
||||
internal void Attach()
|
||||
{
|
||||
// IMPORTANT:
|
||||
// Ensures that the hook delegate does not get garbage collected prematurely, as it will be passed to unmanaged code.
|
||||
// Not doing so will result in a <c>CallbackOnCollectedDelegate</c> error and subsequent application crash!
|
||||
eventDelegate = new EventDelegate(LowLevelSystemProc);
|
||||
handle = User32.SetWinEventHook(eventId, eventId, IntPtr.Zero, eventDelegate, 0, 0, Constant.WINEVENT_OUTOFCONTEXT);
|
||||
}
|
||||
|
||||
internal void AwaitDetach()
|
||||
{
|
||||
detachEvent.WaitOne();
|
||||
detachSuccess = User32.UnhookWinEvent(handle);
|
||||
detachResultAvailableEvent.Set();
|
||||
}
|
||||
|
||||
internal bool Detach()
|
||||
{
|
||||
detachEvent.Set();
|
||||
detachResultAvailableEvent.WaitOne();
|
||||
|
||||
return detachSuccess;
|
||||
}
|
||||
|
||||
private void LowLevelSystemProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
|
||||
{
|
||||
callback(hwnd);
|
||||
}
|
||||
}
|
||||
}
|
55
SafeExamBrowser.WindowsApi/Kernel32.cs
Normal file
55
SafeExamBrowser.WindowsApi/Kernel32.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using SafeExamBrowser.WindowsApi.Constants;
|
||||
using SafeExamBrowser.WindowsApi.Types;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to the native Windows API exposed by <c>kernel32.dll</c>.
|
||||
/// </summary>
|
||||
internal class Kernel32
|
||||
{
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern bool CloseHandle(IntPtr hObject);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern bool CreateProcess(
|
||||
string lpApplicationName,
|
||||
string lpCommandLine,
|
||||
IntPtr lpProcessAttributes,
|
||||
IntPtr lpThreadAttributes,
|
||||
bool bInheritHandles,
|
||||
int dwCreationFlags,
|
||||
IntPtr lpEnvironment,
|
||||
string lpCurrentDirectory,
|
||||
ref STARTUPINFO lpStartupInfo,
|
||||
ref PROCESS_INFORMATION lpProcessInformation);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern int GetCurrentThreadId();
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern IntPtr GetModuleHandle(string lpModuleName);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern int ResumeThread(IntPtr hThread);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern int SuspendThread(IntPtr hThread);
|
||||
}
|
||||
}
|
541
SafeExamBrowser.WindowsApi/NativeMethods.cs
Normal file
541
SafeExamBrowser.WindowsApi/NativeMethods.cs
Normal file
@@ -0,0 +1,541 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using SafeExamBrowser.WindowsApi.Constants;
|
||||
using SafeExamBrowser.WindowsApi.Contracts;
|
||||
using SafeExamBrowser.WindowsApi.Contracts.Events;
|
||||
using SafeExamBrowser.WindowsApi.Hooks;
|
||||
using SafeExamBrowser.WindowsApi.Types;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi
|
||||
{
|
||||
public class NativeMethods : INativeMethods
|
||||
{
|
||||
private readonly ConcurrentDictionary<Guid, KeyboardHook> KeyboardHooks = new ConcurrentDictionary<Guid, KeyboardHook>();
|
||||
private readonly ConcurrentDictionary<Guid, MouseHook> MouseHooks = new ConcurrentDictionary<Guid, MouseHook>();
|
||||
private readonly ConcurrentDictionary<Guid, SystemHook> SystemHooks = new ConcurrentDictionary<Guid, SystemHook>();
|
||||
|
||||
/// <summary>
|
||||
/// Upon finalization, unregister all active system events and hooks...
|
||||
/// </summary>
|
||||
~NativeMethods()
|
||||
{
|
||||
foreach (var hook in SystemHooks.Values)
|
||||
{
|
||||
hook.Detach();
|
||||
}
|
||||
|
||||
foreach (var hook in KeyboardHooks.Values)
|
||||
{
|
||||
hook.Detach();
|
||||
}
|
||||
|
||||
foreach (var hook in MouseHooks.Values)
|
||||
{
|
||||
hook.Detach();
|
||||
}
|
||||
}
|
||||
|
||||
public void ActivateWindow(IntPtr handle)
|
||||
{
|
||||
var placement = new WINDOWPLACEMENT();
|
||||
|
||||
User32.BringWindowToTop(handle);
|
||||
User32.SetForegroundWindow(handle);
|
||||
|
||||
placement.length = Marshal.SizeOf(placement);
|
||||
User32.GetWindowPlacement(handle, ref placement);
|
||||
|
||||
if (placement.showCmd == (int) ShowWindowCommand.ShowMinimized)
|
||||
{
|
||||
User32.ShowWindowAsync(handle, (int) ShowWindowCommand.Restore);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeregisterKeyboardHook(Guid hookId)
|
||||
{
|
||||
var hook = KeyboardHooks.Values.FirstOrDefault(h => h.Id == hookId);
|
||||
|
||||
if (hook != default)
|
||||
{
|
||||
var success = hook.Detach();
|
||||
|
||||
if (!success)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
KeyboardHooks.TryRemove(hookId, out _);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeregisterMouseHook(Guid hookId)
|
||||
{
|
||||
var hook = MouseHooks.Values.FirstOrDefault(h => h.Id == hookId);
|
||||
|
||||
if (hook != default)
|
||||
{
|
||||
var success = hook.Detach();
|
||||
|
||||
if (!success)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
MouseHooks.TryRemove(hookId, out _);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeregisterSystemEventHook(Guid hookId)
|
||||
{
|
||||
var hook = SystemHooks.Values.FirstOrDefault(h => h.Id == hookId);
|
||||
|
||||
if (hook != default)
|
||||
{
|
||||
var success = hook.Detach();
|
||||
|
||||
if (!success)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
SystemHooks.TryRemove(hookId, out _);
|
||||
}
|
||||
}
|
||||
|
||||
public bool DisableStickyKeys()
|
||||
{
|
||||
var success = TryGetStickyKeys(out var state);
|
||||
|
||||
if (success)
|
||||
{
|
||||
(state as StickyKeysState).Flags &= ~StickyKeysFlags.AVAILABLE;
|
||||
(state as StickyKeysState).Flags &= ~StickyKeysFlags.HOTKEYACTIVE;
|
||||
(state as StickyKeysState).Flags &= ~StickyKeysFlags.ON;
|
||||
|
||||
success = TrySetStickyKeys(state);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void EmptyClipboard()
|
||||
{
|
||||
var success = true;
|
||||
|
||||
success &= User32.OpenClipboard(IntPtr.Zero);
|
||||
success &= User32.EmptyClipboard();
|
||||
success &= User32.CloseClipboard();
|
||||
|
||||
if (!success)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
}
|
||||
|
||||
public bool EnableStickyKeys()
|
||||
{
|
||||
var success = TryGetStickyKeys(out var state);
|
||||
|
||||
if (success)
|
||||
{
|
||||
(state as StickyKeysState).Flags |= StickyKeysFlags.AVAILABLE;
|
||||
(state as StickyKeysState).Flags |= StickyKeysFlags.HOTKEYACTIVE;
|
||||
(state as StickyKeysState).Flags |= StickyKeysFlags.ON;
|
||||
|
||||
success = TrySetStickyKeys(state);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public (int x, int y) GetCursorPosition()
|
||||
{
|
||||
var position = new POINT();
|
||||
|
||||
User32.GetCursorPos(ref position);
|
||||
|
||||
return (position.X, position.Y);
|
||||
}
|
||||
|
||||
public IEnumerable<IntPtr> GetOpenWindows()
|
||||
{
|
||||
var windows = new List<IntPtr>();
|
||||
|
||||
bool EnumWindows(IntPtr hWnd, IntPtr lParam)
|
||||
{
|
||||
if (hWnd != GetShellWindowHandle() && User32.IsWindowVisible(hWnd) && User32.GetWindowTextLength(hWnd) > 0)
|
||||
{
|
||||
windows.Add(hWnd);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
var success = User32.EnumWindows(EnumWindows, IntPtr.Zero);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
return windows;
|
||||
}
|
||||
|
||||
public uint GetProcessIdFor(IntPtr window)
|
||||
{
|
||||
User32.GetWindowThreadProcessId(window, out var processId);
|
||||
|
||||
return processId;
|
||||
}
|
||||
|
||||
public IntPtr GetShellWindowHandle()
|
||||
{
|
||||
return User32.FindWindow("Shell_TrayWnd", default);
|
||||
}
|
||||
|
||||
public uint GetShellProcessId()
|
||||
{
|
||||
var handle = GetShellWindowHandle();
|
||||
var threadId = User32.GetWindowThreadProcessId(handle, out var processId);
|
||||
|
||||
return processId;
|
||||
}
|
||||
|
||||
public string GetWallpaperPath()
|
||||
{
|
||||
const int MAX_PATH = 260;
|
||||
var buffer = new String('\0', MAX_PATH);
|
||||
var success = User32.SystemParametersInfo(SPI.GETDESKWALLPAPER, buffer.Length, buffer, SPIF.NONE);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
var path = buffer.Substring(0, buffer.IndexOf('\0'));
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public IntPtr GetWindowIcon(IntPtr window)
|
||||
{
|
||||
var icon = User32.SendMessage(window, Constant.WM_GETICON, new IntPtr(Constant.ICON_BIG), IntPtr.Zero);
|
||||
|
||||
if (icon == IntPtr.Zero)
|
||||
{
|
||||
icon = User32.SendMessage(window, Constant.WM_GETICON, new IntPtr(Constant.ICON_SMALL), IntPtr.Zero);
|
||||
}
|
||||
|
||||
if (icon == IntPtr.Zero)
|
||||
{
|
||||
icon = User32.SendMessage(window, Constant.WM_GETICON, new IntPtr(Constant.ICON_SMALL2), IntPtr.Zero);
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
public string GetWindowTitle(IntPtr window)
|
||||
{
|
||||
var length = User32.GetWindowTextLength(window);
|
||||
|
||||
if (length > 0)
|
||||
{
|
||||
var builder = new StringBuilder(length);
|
||||
|
||||
User32.GetWindowText(window, builder, length + 1);
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public IBounds GetWorkingArea()
|
||||
{
|
||||
var workingArea = new RECT();
|
||||
var success = User32.SystemParametersInfo(SPI.GETWORKAREA, 0, ref workingArea, SPIF.NONE);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
return workingArea.ToBounds();
|
||||
}
|
||||
|
||||
public bool HasInternetConnection()
|
||||
{
|
||||
return WinInet.InternetGetConnectedState(out _, 0);
|
||||
}
|
||||
|
||||
public bool HideWindow(IntPtr window)
|
||||
{
|
||||
return User32.ShowWindow(window, (int) ShowWindowCommand.Hide);
|
||||
}
|
||||
|
||||
public void MinimizeAllOpenWindows()
|
||||
{
|
||||
var handle = GetShellWindowHandle();
|
||||
|
||||
User32.SendMessage(handle, Constant.WM_COMMAND, (IntPtr) Constant.MIN_ALL, IntPtr.Zero);
|
||||
}
|
||||
|
||||
public void PostCloseMessageToShell()
|
||||
{
|
||||
// NOTE: The close message 0x5B4 posted to the shell is undocumented and not officially supported:
|
||||
// https://stackoverflow.com/questions/5689904/gracefully-exit-explorer-programmatically/5705965#5705965
|
||||
|
||||
var handle = GetShellWindowHandle();
|
||||
var success = User32.PostMessage(handle, 0x5B4, IntPtr.Zero, IntPtr.Zero);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
}
|
||||
|
||||
public Guid RegisterKeyboardHook(KeyboardHookCallback callback)
|
||||
{
|
||||
var hookId = default(Guid);
|
||||
var hookReadyEvent = new AutoResetEvent(false);
|
||||
var hookThread = new Thread(() =>
|
||||
{
|
||||
var hook = new KeyboardHook(callback);
|
||||
var sleepEvent = new AutoResetEvent(false);
|
||||
|
||||
hook.Attach();
|
||||
hookId = hook.Id;
|
||||
KeyboardHooks[hookId] = hook;
|
||||
hookReadyEvent.Set();
|
||||
|
||||
while (true)
|
||||
{
|
||||
sleepEvent.WaitOne();
|
||||
}
|
||||
});
|
||||
|
||||
hookThread.SetApartmentState(ApartmentState.STA);
|
||||
hookThread.IsBackground = true;
|
||||
hookThread.Start();
|
||||
|
||||
hookReadyEvent.WaitOne();
|
||||
|
||||
return hookId;
|
||||
}
|
||||
|
||||
public Guid RegisterMouseHook(MouseHookCallback callback)
|
||||
{
|
||||
var hookId = default(Guid);
|
||||
var hookReadyEvent = new AutoResetEvent(false);
|
||||
var hookThread = new Thread(() =>
|
||||
{
|
||||
var hook = new MouseHook(callback);
|
||||
var sleepEvent = new AutoResetEvent(false);
|
||||
|
||||
hook.Attach();
|
||||
hookId = hook.Id;
|
||||
MouseHooks[hookId] = hook;
|
||||
hookReadyEvent.Set();
|
||||
|
||||
while (true)
|
||||
{
|
||||
sleepEvent.WaitOne();
|
||||
}
|
||||
});
|
||||
|
||||
hookThread.SetApartmentState(ApartmentState.STA);
|
||||
hookThread.IsBackground = true;
|
||||
hookThread.Start();
|
||||
|
||||
hookReadyEvent.WaitOne();
|
||||
|
||||
return hookId;
|
||||
}
|
||||
|
||||
public Guid RegisterSystemCaptureStartEvent(SystemEventCallback callback)
|
||||
{
|
||||
return RegisterSystemEvent(callback, Constant.EVENT_SYSTEM_CAPTURESTART);
|
||||
}
|
||||
|
||||
public Guid RegisterSystemForegroundEvent(SystemEventCallback callback)
|
||||
{
|
||||
return RegisterSystemEvent(callback, Constant.EVENT_SYSTEM_FOREGROUND);
|
||||
}
|
||||
|
||||
internal Guid RegisterSystemEvent(SystemEventCallback callback, uint eventId)
|
||||
{
|
||||
var hookId = default(Guid);
|
||||
var hookReadyEvent = new AutoResetEvent(false);
|
||||
var hookThread = new Thread(() =>
|
||||
{
|
||||
var hook = new SystemHook(callback, eventId);
|
||||
|
||||
hook.Attach();
|
||||
hookId = hook.Id;
|
||||
SystemHooks[hookId] = hook;
|
||||
hookReadyEvent.Set();
|
||||
hook.AwaitDetach();
|
||||
});
|
||||
|
||||
hookThread.SetApartmentState(ApartmentState.STA);
|
||||
hookThread.IsBackground = true;
|
||||
hookThread.Start();
|
||||
|
||||
hookReadyEvent.WaitOne();
|
||||
|
||||
return hookId;
|
||||
}
|
||||
|
||||
public void RemoveWallpaper()
|
||||
{
|
||||
SetWallpaper(string.Empty);
|
||||
}
|
||||
|
||||
public void RestoreWindow(IntPtr window)
|
||||
{
|
||||
User32.ShowWindow(window, (int) ShowWindowCommand.Restore);
|
||||
}
|
||||
|
||||
public bool ResumeThread(int threadId)
|
||||
{
|
||||
const int FAILURE = -1;
|
||||
var handle = Kernel32.OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint) threadId);
|
||||
|
||||
if (handle == IntPtr.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = Kernel32.ResumeThread(handle);
|
||||
var success = result != FAILURE;
|
||||
|
||||
return success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Kernel32.CloseHandle(handle);
|
||||
}
|
||||
}
|
||||
|
||||
public void SendCloseMessageTo(IntPtr window)
|
||||
{
|
||||
User32.SendMessage(window, Constant.WM_SYSCOMMAND, (IntPtr) SystemCommand.CLOSE, IntPtr.Zero);
|
||||
}
|
||||
|
||||
public void SetAlwaysOnState(bool display = true, bool system = true)
|
||||
{
|
||||
if (display || system)
|
||||
{
|
||||
var state = EXECUTION_STATE.CONTINUOUS;
|
||||
|
||||
state |= display ? EXECUTION_STATE.DISPLAY_REQUIRED : 0x0;
|
||||
state |= system ? EXECUTION_STATE.SYSTEM_REQUIRED : 0x0;
|
||||
|
||||
Kernel32.SetThreadExecutionState(state);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetWallpaper(string filePath)
|
||||
{
|
||||
var success = User32.SystemParametersInfo(SPI.SETDESKWALLPAPER, 0, filePath, SPIF.NONE);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
}
|
||||
|
||||
public void SetWorkingArea(IBounds bounds)
|
||||
{
|
||||
var workingArea = new RECT { Left = bounds.Left, Top = bounds.Top, Right = bounds.Right, Bottom = bounds.Bottom };
|
||||
var success = User32.SystemParametersInfo(SPI.SETWORKAREA, 0, ref workingArea, SPIF.NONE);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
}
|
||||
|
||||
public bool SuspendThread(int threadId)
|
||||
{
|
||||
const int FAILURE = -1;
|
||||
var handle = Kernel32.OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint) threadId);
|
||||
|
||||
if (handle == IntPtr.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = Kernel32.SuspendThread(handle);
|
||||
var success = result != FAILURE;
|
||||
|
||||
return success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Kernel32.CloseHandle(handle);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetStickyKeys(out IStickyKeysState state)
|
||||
{
|
||||
var stickyKeys = new STICKYKEYS();
|
||||
|
||||
state = default;
|
||||
stickyKeys.cbSize = Marshal.SizeOf(typeof(STICKYKEYS));
|
||||
|
||||
var success = User32.SystemParametersInfo(SPI.GETSTICKYKEYS, stickyKeys.cbSize, ref stickyKeys, SPIF.NONE);
|
||||
|
||||
if (success)
|
||||
{
|
||||
state = new StickyKeysState
|
||||
{
|
||||
Flags = stickyKeys.dwFlags,
|
||||
IsAvailable = stickyKeys.dwFlags.HasFlag(StickyKeysFlags.AVAILABLE),
|
||||
IsEnabled = stickyKeys.dwFlags.HasFlag(StickyKeysFlags.ON),
|
||||
IsHotkeyActive = stickyKeys.dwFlags.HasFlag(StickyKeysFlags.HOTKEYACTIVE)
|
||||
};
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public bool TrySetStickyKeys(IStickyKeysState state)
|
||||
{
|
||||
var success = false;
|
||||
var stickyKeys = new STICKYKEYS();
|
||||
|
||||
if (state is StickyKeysState stateWithFlags)
|
||||
{
|
||||
stickyKeys.cbSize = Marshal.SizeOf(typeof(STICKYKEYS));
|
||||
stickyKeys.dwFlags = stateWithFlags.Flags;
|
||||
|
||||
success = User32.SystemParametersInfo(SPI.SETSTICKYKEYS, stickyKeys.cbSize, ref stickyKeys, SPIF.NONE);
|
||||
}
|
||||
else
|
||||
{
|
||||
success = state.IsEnabled ? EnableStickyKeys() : DisableStickyKeys();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
}
|
||||
}
|
171
SafeExamBrowser.WindowsApi/Processes/Process.cs
Normal file
171
SafeExamBrowser.WindowsApi/Processes/Process.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.WindowsApi.Contracts;
|
||||
using SafeExamBrowser.WindowsApi.Contracts.Events;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Processes
|
||||
{
|
||||
internal class Process : IProcess
|
||||
{
|
||||
private readonly ILogger logger;
|
||||
private readonly System.Diagnostics.Process process;
|
||||
|
||||
private bool eventInitialized;
|
||||
|
||||
public bool HasTerminated
|
||||
{
|
||||
get { return IsTerminated(); }
|
||||
}
|
||||
|
||||
public int Id
|
||||
{
|
||||
get { return process.Id; }
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
public string OriginalName { get; }
|
||||
public string Path { get; }
|
||||
public string Signature { get; }
|
||||
|
||||
private event ProcessTerminatedEventHandler TerminatedEvent;
|
||||
|
||||
public event ProcessTerminatedEventHandler Terminated
|
||||
{
|
||||
add { TerminatedEvent += value; InitializeEvent(); }
|
||||
remove { TerminatedEvent -= value; }
|
||||
}
|
||||
|
||||
internal Process(System.Diagnostics.Process process, string name, string originalName, ILogger logger, string path, string signature)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.process = process;
|
||||
this.Name = name;
|
||||
this.OriginalName = originalName;
|
||||
this.Path = path;
|
||||
this.Signature = signature?.ToLower();
|
||||
}
|
||||
|
||||
public string GetAdditionalInfo()
|
||||
{
|
||||
var info = new StringBuilder();
|
||||
|
||||
info.Append($"Original Name: {(string.IsNullOrWhiteSpace(OriginalName) ? "n/a" : $"'{OriginalName}'")}, ");
|
||||
info.Append($"Path: {(string.IsNullOrWhiteSpace(Path) ? "n/a" : $"'{Path}'")}, ");
|
||||
info.Append($"Signature: {(string.IsNullOrWhiteSpace(Signature) ? "n/a" : Signature)}");
|
||||
|
||||
return info.ToString();
|
||||
}
|
||||
|
||||
public bool TryClose(int timeout_ms = 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
logger.Debug("Attempting to close process...");
|
||||
process.Refresh();
|
||||
|
||||
var success = process.CloseMainWindow();
|
||||
|
||||
if (success)
|
||||
{
|
||||
logger.Debug("Successfully sent close message to main window.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn("Failed to send close message to main window!");
|
||||
}
|
||||
|
||||
return success && WaitForTermination(timeout_ms);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error("Failed to close main window!", e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryKill(int timeout_ms = 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
logger.Debug("Attempting to kill process...");
|
||||
|
||||
process.Refresh();
|
||||
process.Kill();
|
||||
|
||||
return WaitForTermination(timeout_ms);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error("Failed to kill process!", e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"'{Name}' ({Id})";
|
||||
}
|
||||
|
||||
private bool IsTerminated()
|
||||
{
|
||||
try
|
||||
{
|
||||
process.Refresh();
|
||||
|
||||
return process.HasExited;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error("Failed to check whether process is terminated!", e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void InitializeEvent()
|
||||
{
|
||||
if (!eventInitialized)
|
||||
{
|
||||
eventInitialized = true;
|
||||
|
||||
process.Exited += Process_Exited;
|
||||
process.EnableRaisingEvents = true;
|
||||
|
||||
logger.Debug("Initialized termination event.");
|
||||
}
|
||||
}
|
||||
|
||||
private bool WaitForTermination(int timeout_ms)
|
||||
{
|
||||
var terminated = process.WaitForExit(timeout_ms);
|
||||
|
||||
if (terminated)
|
||||
{
|
||||
logger.Debug($"Process has terminated within {timeout_ms}ms.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn($"Process failed to terminate within {timeout_ms}ms!");
|
||||
}
|
||||
|
||||
return terminated;
|
||||
}
|
||||
|
||||
private void Process_Exited(object sender, EventArgs e)
|
||||
{
|
||||
TerminatedEvent?.Invoke(process.ExitCode);
|
||||
logger.Debug("Process has terminated.");
|
||||
}
|
||||
}
|
||||
}
|
243
SafeExamBrowser.WindowsApi/Processes/ProcessFactory.cs
Normal file
243
SafeExamBrowser.WindowsApi/Processes/ProcessFactory.cs
Normal file
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.WindowsApi.Constants;
|
||||
using SafeExamBrowser.WindowsApi.Contracts;
|
||||
using SafeExamBrowser.WindowsApi.Types;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Processes
|
||||
{
|
||||
public class ProcessFactory : IProcessFactory
|
||||
{
|
||||
private readonly IModuleLogger logger;
|
||||
|
||||
public IDesktop StartupDesktop { private get; set; }
|
||||
|
||||
public ProcessFactory(IModuleLogger logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public IList<IProcess> GetAllRunning()
|
||||
{
|
||||
var processes = new List<IProcess>();
|
||||
var running = System.Diagnostics.Process.GetProcesses();
|
||||
var names = LoadAllProcessNames();
|
||||
|
||||
foreach (var process in running)
|
||||
{
|
||||
if (names.Any(n => n.processId == process.Id))
|
||||
{
|
||||
var (_, name, originalName, path, signature) = names.First(n => n.processId == process.Id);
|
||||
|
||||
processes.Add(new Process(process, name, originalName, LoggerFor(process, name), path, signature));
|
||||
}
|
||||
}
|
||||
|
||||
return processes;
|
||||
}
|
||||
|
||||
public IProcess StartNew(string path, params string[] args)
|
||||
{
|
||||
var raw = default(System.Diagnostics.Process);
|
||||
|
||||
logger.Info($"Attempting to start process '{path}'...");
|
||||
|
||||
if (StartupDesktop != default(IDesktop))
|
||||
{
|
||||
raw = StartOnDesktop(path, args);
|
||||
}
|
||||
else
|
||||
{
|
||||
raw = StartNormal(path, args);
|
||||
}
|
||||
|
||||
var (name, originalName, _, signature) = LoadProcessNamesFor(raw);
|
||||
var process = new Process(raw, name, originalName, LoggerFor(raw, name), path, signature);
|
||||
|
||||
logger.Info($"Successfully started process '{path}' with ID = {process.Id}.");
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
public bool TryGetById(int id, out IProcess process)
|
||||
{
|
||||
process = default;
|
||||
|
||||
try
|
||||
{
|
||||
var raw = System.Diagnostics.Process.GetProcessById(id);
|
||||
var (name, originalName, path, signature) = LoadProcessNamesFor(raw);
|
||||
|
||||
process = new Process(raw, name, originalName, LoggerFor(raw, name), path, signature);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error($"Failed to get process with ID = {id}!", e);
|
||||
}
|
||||
|
||||
return process != default(IProcess);
|
||||
}
|
||||
|
||||
private IEnumerable<(int processId, string name, string originalName, string path, string signature)> LoadAllProcessNames()
|
||||
{
|
||||
var names = new List<(int, string, string, string, string)>();
|
||||
|
||||
try
|
||||
{
|
||||
using (var searcher = new ManagementObjectSearcher($"SELECT ProcessId, Name, ExecutablePath FROM Win32_Process"))
|
||||
using (var results = searcher.Get())
|
||||
{
|
||||
var processData = results.Cast<ManagementObject>().ToList();
|
||||
|
||||
foreach (var process in processData)
|
||||
{
|
||||
using (process)
|
||||
{
|
||||
var name = Convert.ToString(process["Name"]);
|
||||
var originalName = default(string);
|
||||
var path = Convert.ToString(process["ExecutablePath"]);
|
||||
var processId = Convert.ToInt32(process["ProcessId"]);
|
||||
var signature = default(string);
|
||||
|
||||
if (File.Exists(path))
|
||||
{
|
||||
TryLoadOriginalName(path, out originalName);
|
||||
TryLoadSignature(path, out signature);
|
||||
}
|
||||
|
||||
names.Add((processId, name, originalName, path, signature));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error("Failed to load process names!", e);
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
private (string name, string originalName, string path, string signature) LoadProcessNamesFor(System.Diagnostics.Process process)
|
||||
{
|
||||
var name = process.ProcessName;
|
||||
var originalName = default(string);
|
||||
var path = default(string);
|
||||
var signature = default(string);
|
||||
|
||||
try
|
||||
{
|
||||
using (var searcher = new ManagementObjectSearcher($"SELECT Name, ExecutablePath FROM Win32_Process WHERE ProcessId = {process.Id}"))
|
||||
using (var results = searcher.Get())
|
||||
using (var processData = results.Cast<ManagementObject>().First())
|
||||
{
|
||||
name = Convert.ToString(processData["Name"]);
|
||||
path = Convert.ToString(processData["ExecutablePath"]);
|
||||
|
||||
if (File.Exists(path))
|
||||
{
|
||||
TryLoadOriginalName(path, out originalName);
|
||||
TryLoadSignature(path, out signature);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error($"Failed to load process names for {process.ProcessName}!", e);
|
||||
}
|
||||
|
||||
return (name, originalName, path, signature);
|
||||
}
|
||||
|
||||
private ILogger LoggerFor(System.Diagnostics.Process process, string name)
|
||||
{
|
||||
return logger.CloneFor($"{nameof(Process)} '{name}' ({process.Id})");
|
||||
}
|
||||
|
||||
private System.Diagnostics.Process StartNormal(string path, params string[] args)
|
||||
{
|
||||
var process = new System.Diagnostics.Process();
|
||||
|
||||
process.StartInfo.Arguments = string.Join(" ", args);
|
||||
process.StartInfo.FileName = path;
|
||||
process.StartInfo.UseShellExecute = false;
|
||||
process.StartInfo.WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
|
||||
process.Start();
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
private System.Diagnostics.Process StartOnDesktop(string path, params string[] args)
|
||||
{
|
||||
var commandLine = $"{'"' + path + '"'} {string.Join(" ", args)}";
|
||||
var processInfo = new PROCESS_INFORMATION();
|
||||
var startupInfo = new STARTUPINFO();
|
||||
|
||||
startupInfo.cb = Marshal.SizeOf(startupInfo);
|
||||
startupInfo.lpDesktop = StartupDesktop?.Name;
|
||||
|
||||
var success = Kernel32.CreateProcess(null, commandLine, IntPtr.Zero, IntPtr.Zero, true, Constant.NORMAL_PRIORITY_CLASS, IntPtr.Zero, null, ref startupInfo, ref processInfo);
|
||||
|
||||
if (success)
|
||||
{
|
||||
return System.Diagnostics.Process.GetProcessById(processInfo.dwProcessId);
|
||||
}
|
||||
|
||||
var errorCode = Marshal.GetLastWin32Error();
|
||||
|
||||
logger.Error($"Failed to start process '{path}' on desktop '{StartupDesktop}'! Error code: {errorCode}.");
|
||||
|
||||
throw new Win32Exception(errorCode);
|
||||
}
|
||||
|
||||
private bool TryLoadOriginalName(string path, out string originalName)
|
||||
{
|
||||
originalName = default;
|
||||
|
||||
try
|
||||
{
|
||||
originalName = FileVersionInfo.GetVersionInfo(path).OriginalFilename;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return originalName != default;
|
||||
}
|
||||
|
||||
private bool TryLoadSignature(string path, out string signature)
|
||||
{
|
||||
signature = default;
|
||||
|
||||
try
|
||||
{
|
||||
using (var certificate = X509Certificate.CreateFromSignedFile(path))
|
||||
{
|
||||
signature = certificate.GetCertHashString();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return signature != default;
|
||||
}
|
||||
}
|
||||
}
|
33
SafeExamBrowser.WindowsApi/Properties/AssemblyInfo.cs
Normal file
33
SafeExamBrowser.WindowsApi/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("SafeExamBrowser.WindowsApi")]
|
||||
[assembly: AssemblyDescription("Safe Exam Browser")]
|
||||
[assembly: AssemblyCompany("ETH Zürich")]
|
||||
[assembly: AssemblyProduct("SafeExamBrowser.WindowsApi")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2024 ETH Zürich, IT Services")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("73724659-4150-4792-a94e-42f5f3c1b696")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("3.8.0.742")]
|
||||
[assembly: AssemblyFileVersion("3.8.0.742")]
|
||||
[assembly: AssemblyInformationalVersion("1.0.0.0")]
|
112
SafeExamBrowser.WindowsApi/SafeExamBrowser.WindowsApi.csproj
Normal file
112
SafeExamBrowser.WindowsApi/SafeExamBrowser.WindowsApi.csproj
Normal file
@@ -0,0 +1,112 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{73724659-4150-4792-A94E-42F5F3C1B696}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>SafeExamBrowser.WindowsApi</RootNamespace>
|
||||
<AssemblyName>SafeExamBrowser.WindowsApi</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Management" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Constants\Constant.cs" />
|
||||
<Compile Include="Constants\HookType.cs" />
|
||||
<Compile Include="Constants\StickyKeysFlags.cs" />
|
||||
<Compile Include="Constants\ThreadAccess.cs" />
|
||||
<Compile Include="Constants\VirtualKeyCode.cs" />
|
||||
<Compile Include="Delegates\EnumDesktopDelegate.cs" />
|
||||
<Compile Include="Delegates\EnumWindowsDelegate.cs" />
|
||||
<Compile Include="Delegates\EventDelegate.cs" />
|
||||
<Compile Include="Delegates\HookDelegate.cs" />
|
||||
<Compile Include="Desktops\Desktop.cs" />
|
||||
<Compile Include="Desktops\DesktopFactory.cs" />
|
||||
<Compile Include="Desktops\DesktopMonitor.cs" />
|
||||
<Compile Include="Desktops\ObfuscatedDesktop.cs" />
|
||||
<Compile Include="ExplorerShell.cs" />
|
||||
<Compile Include="Hooks\MouseHook.cs" />
|
||||
<Compile Include="Hooks\SystemHook.cs" />
|
||||
<Compile Include="Processes\Process.cs" />
|
||||
<Compile Include="Processes\ProcessFactory.cs" />
|
||||
<Compile Include="Constants\AccessMask.cs" />
|
||||
<Compile Include="Types\Bounds.cs" />
|
||||
<Compile Include="Types\EXECUTION_STATE.cs" />
|
||||
<Compile Include="Types\KBDLLHOOKSTRUCT.cs" />
|
||||
<Compile Include="Types\KBDLLHOOKSTRUCTFlags.cs" />
|
||||
<Compile Include="Constants\ShowWindowCommand.cs" />
|
||||
<Compile Include="Constants\SystemCommand.cs" />
|
||||
<Compile Include="Kernel32.cs" />
|
||||
<Compile Include="Hooks\KeyboardHook.cs" />
|
||||
<Compile Include="NativeMethods.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Constants\SPI.cs" />
|
||||
<Compile Include="Constants\SPIF.cs" />
|
||||
<Compile Include="Types\MSLLHOOKSTRUCT.cs" />
|
||||
<Compile Include="Types\POINT.cs" />
|
||||
<Compile Include="Types\PROCESS_INFORMATION.cs" />
|
||||
<Compile Include="Types\RECT.cs" />
|
||||
<Compile Include="Types\STARTUPINFO.cs" />
|
||||
<Compile Include="Types\STICKYKEYS.cs" />
|
||||
<Compile Include="Types\StickyKeysState.cs" />
|
||||
<Compile Include="Types\Window.cs" />
|
||||
<Compile Include="Types\WINDOWPLACEMENT.cs" />
|
||||
<Compile Include="User32.cs" />
|
||||
<Compile Include="WinInet.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Logging.Contracts\SafeExamBrowser.Logging.Contracts.csproj">
|
||||
<Project>{64ea30fb-11d4-436a-9c2b-88566285363e}</Project>
|
||||
<Name>SafeExamBrowser.Logging.Contracts</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.WindowsApi.Contracts\SafeExamBrowser.WindowsApi.Contracts.csproj">
|
||||
<Project>{7016f080-9aa5-41b2-a225-385ad877c171}</Project>
|
||||
<Name>SafeExamBrowser.WindowsApi.Contracts</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
20
SafeExamBrowser.WindowsApi/Types/Bounds.cs
Normal file
20
SafeExamBrowser.WindowsApi/Types/Bounds.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using SafeExamBrowser.WindowsApi.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Types
|
||||
{
|
||||
internal class Bounds : IBounds
|
||||
{
|
||||
public int Left { get; set; }
|
||||
public int Top { get; set; }
|
||||
public int Right { get; set; }
|
||||
public int Bottom { get; set; }
|
||||
}
|
||||
}
|
25
SafeExamBrowser.WindowsApi/Types/EXECUTION_STATE.cs
Normal file
25
SafeExamBrowser.WindowsApi/Types/EXECUTION_STATE.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Types
|
||||
{
|
||||
/// <remarks>
|
||||
/// See http://www.pinvoke.net/default.aspx/kernel32/SetThreadExecutionState.html.
|
||||
/// See https://msdn.microsoft.com/en-us/library/aa373208(v=vs.85).aspx.
|
||||
/// </remarks>
|
||||
[Flags]
|
||||
public enum EXECUTION_STATE : uint
|
||||
{
|
||||
AWAYMODE_REQUIRED = 0x00000040,
|
||||
CONTINUOUS = 0x80000000,
|
||||
DISPLAY_REQUIRED = 0x00000002,
|
||||
SYSTEM_REQUIRED = 0x00000001
|
||||
}
|
||||
}
|
48
SafeExamBrowser.WindowsApi/Types/KBDLLHOOKSTRUCT.cs
Normal file
48
SafeExamBrowser.WindowsApi/Types/KBDLLHOOKSTRUCT.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Types
|
||||
{
|
||||
/// <remarks>
|
||||
/// See http://www.pinvoke.net/default.aspx/Structures/KBDLLHOOKSTRUCT.html.
|
||||
/// </remarks>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct KBDLLHOOKSTRUCT
|
||||
{
|
||||
/// <summary>
|
||||
/// A virtual-key code. The code must be a value in the range 1 to 254.
|
||||
/// </summary>
|
||||
internal uint KeyCode;
|
||||
|
||||
/// <summary>
|
||||
/// A hardware scan code for the key.
|
||||
/// </summary>
|
||||
internal uint ScanCode;
|
||||
|
||||
/// <summary>
|
||||
/// The extended-key flag, event-injected flags, context code, and transition-state flag. This member is specified as follows. An
|
||||
/// application can use the following values to test the keystroke flags. Testing LLKHF_INJECTED (bit 4) will tell you whether the
|
||||
/// event was injected. If it was, then testing LLKHF_LOWER_IL_INJECTED (bit 1) will tell you whether or not the event was injected
|
||||
/// from a process running at lower integrity level.
|
||||
/// </summary>
|
||||
internal KBDLLHOOKSTRUCTFlags Flags;
|
||||
|
||||
/// <summary>
|
||||
/// The time stamp for this message, equivalent to what <c>GetMessageTime</c> would return for this message.
|
||||
/// </summary>
|
||||
internal uint Time;
|
||||
|
||||
/// <summary>
|
||||
/// Additional information associated with the message.
|
||||
/// </summary>
|
||||
internal IntPtr DwExtraInfo;
|
||||
}
|
||||
}
|
36
SafeExamBrowser.WindowsApi/Types/KBDLLHOOKSTRUCTFlags.cs
Normal file
36
SafeExamBrowser.WindowsApi/Types/KBDLLHOOKSTRUCTFlags.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Types
|
||||
{
|
||||
/// <remarks>
|
||||
/// See http://www.pinvoke.net/default.aspx/Structures/KBDLLHOOKSTRUCT.html.
|
||||
/// </remarks>
|
||||
internal enum KBDLLHOOKSTRUCTFlags
|
||||
{
|
||||
/// <summary>
|
||||
/// Test the extended-key flag.
|
||||
/// </summary>
|
||||
LLKHF_EXTENDED = 0x01,
|
||||
|
||||
/// <summary>
|
||||
/// Test the event-injected (from any process) flag.
|
||||
/// </summary>
|
||||
LLKHF_INJECTED = 0x10,
|
||||
|
||||
/// <summary>
|
||||
/// Test the context code.
|
||||
/// </summary>
|
||||
LLKHF_ALTDOWN = 0x20,
|
||||
|
||||
/// <summary>
|
||||
/// Test the transition-state flag.
|
||||
/// </summary>
|
||||
LLKHF_UP = 0x80
|
||||
}
|
||||
}
|
23
SafeExamBrowser.WindowsApi/Types/MSLLHOOKSTRUCT.cs
Normal file
23
SafeExamBrowser.WindowsApi/Types/MSLLHOOKSTRUCT.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Types
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct MSLLHOOKSTRUCT
|
||||
{
|
||||
internal POINT Point;
|
||||
internal int MouseData;
|
||||
internal int Flags;
|
||||
internal int Time;
|
||||
internal UIntPtr DwExtraInfo;
|
||||
}
|
||||
}
|
19
SafeExamBrowser.WindowsApi/Types/POINT.cs
Normal file
19
SafeExamBrowser.WindowsApi/Types/POINT.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Types
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct POINT
|
||||
{
|
||||
internal int X;
|
||||
internal int Y;
|
||||
}
|
||||
}
|
26
SafeExamBrowser.WindowsApi/Types/PROCESS_INFORMATION.cs
Normal file
26
SafeExamBrowser.WindowsApi/Types/PROCESS_INFORMATION.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Types
|
||||
{
|
||||
/// <remarks>
|
||||
/// See http://pinvoke.net/default.aspx/Structures/PROCESS_INFORMATION.html.
|
||||
/// See https://msdn.microsoft.com/en-us/library/ms684873(VS.85).aspx.
|
||||
/// </remarks>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct PROCESS_INFORMATION
|
||||
{
|
||||
public IntPtr hProcess;
|
||||
public IntPtr hThread;
|
||||
public int dwProcessId;
|
||||
public int dwThreadId;
|
||||
}
|
||||
}
|
36
SafeExamBrowser.WindowsApi/Types/RECT.cs
Normal file
36
SafeExamBrowser.WindowsApi/Types/RECT.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using SafeExamBrowser.WindowsApi.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Types
|
||||
{
|
||||
/// <remarks>
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/dd162897(v=vs.85).aspx.
|
||||
/// </remarks>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct RECT
|
||||
{
|
||||
internal int Left;
|
||||
internal int Top;
|
||||
internal int Right;
|
||||
internal int Bottom;
|
||||
|
||||
internal IBounds ToBounds()
|
||||
{
|
||||
return new Bounds
|
||||
{
|
||||
Left = Left,
|
||||
Top = Top,
|
||||
Right = Right,
|
||||
Bottom = Bottom
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
39
SafeExamBrowser.WindowsApi/Types/STARTUPINFO.cs
Normal file
39
SafeExamBrowser.WindowsApi/Types/STARTUPINFO.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Types
|
||||
{
|
||||
/// <remarks>
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686331(v=vs.85).aspx.
|
||||
/// </remarks>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct STARTUPINFO
|
||||
{
|
||||
public int cb;
|
||||
public string lpReserved;
|
||||
public string lpDesktop;
|
||||
public string lpTitle;
|
||||
public int dwX;
|
||||
public int dwY;
|
||||
public int dwXSize;
|
||||
public int dwYSize;
|
||||
public int dwXCountChars;
|
||||
public int dwYCountChars;
|
||||
public int dwFillAttribute;
|
||||
public int dwFlags;
|
||||
public short wShowWindow;
|
||||
public short cbReserved2;
|
||||
public IntPtr lpReserved2;
|
||||
public IntPtr hStdInput;
|
||||
public IntPtr hStdOutput;
|
||||
public IntPtr hStdError;
|
||||
}
|
||||
}
|
23
SafeExamBrowser.WindowsApi/Types/STICKYKEYS.cs
Normal file
23
SafeExamBrowser.WindowsApi/Types/STICKYKEYS.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using SafeExamBrowser.WindowsApi.Constants;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Types
|
||||
{
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-stickykeys.
|
||||
/// </remarks>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct STICKYKEYS
|
||||
{
|
||||
internal int cbSize;
|
||||
internal StickyKeysFlags dwFlags;
|
||||
}
|
||||
}
|
22
SafeExamBrowser.WindowsApi/Types/StickyKeysState.cs
Normal file
22
SafeExamBrowser.WindowsApi/Types/StickyKeysState.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using SafeExamBrowser.WindowsApi.Constants;
|
||||
using SafeExamBrowser.WindowsApi.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Types
|
||||
{
|
||||
public class StickyKeysState : IStickyKeysState
|
||||
{
|
||||
internal StickyKeysFlags Flags { get; set; }
|
||||
|
||||
public bool IsAvailable { get; internal set; }
|
||||
public bool IsEnabled { get; internal set; }
|
||||
public bool IsHotkeyActive { get; internal set; }
|
||||
}
|
||||
}
|
22
SafeExamBrowser.WindowsApi/Types/WINDOWPLACEMENT.cs
Normal file
22
SafeExamBrowser.WindowsApi/Types/WINDOWPLACEMENT.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System.Drawing;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Types
|
||||
{
|
||||
internal struct WINDOWPLACEMENT
|
||||
{
|
||||
public int length;
|
||||
public int flags;
|
||||
public int showCmd;
|
||||
public Point ptMinPosition;
|
||||
public Point ptMaxPosition;
|
||||
public Rectangle rcNormalPosition;
|
||||
}
|
||||
}
|
18
SafeExamBrowser.WindowsApi/Types/Window.cs
Normal file
18
SafeExamBrowser.WindowsApi/Types/Window.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Types
|
||||
{
|
||||
internal struct Window
|
||||
{
|
||||
internal IntPtr Handle { get; set; }
|
||||
internal string Title { get; set; }
|
||||
}
|
||||
}
|
122
SafeExamBrowser.WindowsApi/User32.cs
Normal file
122
SafeExamBrowser.WindowsApi/User32.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using SafeExamBrowser.WindowsApi.Constants;
|
||||
using SafeExamBrowser.WindowsApi.Delegates;
|
||||
using SafeExamBrowser.WindowsApi.Types;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to the native Windows API exposed by <c>user32.dll</c>.
|
||||
/// </summary>
|
||||
internal static class User32
|
||||
{
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool CloseClipboard();
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool CloseDesktop(IntPtr hDesktop);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern IntPtr CreateDesktop(string lpszDesktop, IntPtr lpszDevice, IntPtr pDevmode, int dwFlags, uint dwDesiredAccess, IntPtr lpsa);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool BringWindowToTop(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool EmptyClipboard();
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool EnumDesktops(IntPtr hwinsta, EnumDesktopDelegate lpEnumFunc, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool EnumWindows(EnumWindowsDelegate enumProc, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool GetCursorPos(ref POINT pt);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern IntPtr GetThreadDesktop(int dwThreadId);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern IntPtr GetProcessWindowStation();
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool GetUserObjectInformation(IntPtr hObj, int nIndex, IntPtr pvInfo, int nLength, ref int lpnLengthNeeded);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern int GetWindowTextLength(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool IsWindowVisible(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool OpenClipboard(IntPtr hWndNewOwner);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern IntPtr OpenInputDesktop(uint dwFlags, bool fInherit, uint dwDesiredAccess);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool PostMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, EventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern IntPtr SetWindowsHookEx(HookType hookType, HookDelegate lpfn, IntPtr hMod, uint dwThreadId);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool SwitchDesktop(IntPtr hDesktop);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool SystemParametersInfo(SPI uiAction, uint uiParam, ref RECT pvParam, SPIF fWinIni);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool SystemParametersInfo(SPI uiAction, int uiParam, ref STICKYKEYS pvParam, SPIF fWinIni);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool SystemParametersInfo(SPI uiAction, int uiParam, string pvParam, SPIF fWinIni);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool UnhookWinEvent(IntPtr hWinEventHook);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool UnhookWindowsHookEx(IntPtr hhk);
|
||||
}
|
||||
}
|
21
SafeExamBrowser.WindowsApi/WinInet.cs
Normal file
21
SafeExamBrowser.WindowsApi/WinInet.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to the native Windows API exposed by <c>wininet.dll</c>.
|
||||
/// </summary>
|
||||
internal static class WinInet
|
||||
{
|
||||
[DllImport("wininet.dll", SetLastError = true)]
|
||||
internal static extern bool InternetGetConnectedState(out int description, int reservedValue);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user