├── Image └── mouse_input_processing_internals.png ├── MouHidInputHook ├── object_util.h ├── MouHidInputHook.inf ├── pe.h ├── mouhid.h ├── LICENSE ├── io_util.h ├── mouhid_monitor.h ├── log.h ├── debug.h ├── nt.h ├── object_util.cpp ├── MouHidInputHook.vcxproj.filters ├── log.cpp ├── pe.cpp ├── mouclass.h ├── mouhid_hook_manager.h ├── io_util.cpp ├── MouHidInputHook.vcxproj ├── driver.cpp ├── mouhid_monitor.cpp ├── mouclass.cpp ├── mouhid.cpp └── mouhid_hook_manager.cpp ├── MouHidMonitor ├── log.h ├── README.md ├── driver.h ├── LICENSE ├── debug.h ├── MouHidMonitor.vcxproj.filters ├── driver.cpp ├── MouHidMonitor.vcxproj └── main.cpp ├── LICENSE ├── Common ├── time.h └── ioctl.h ├── MouHidInputHook.sln ├── .gitignore └── README.md /Image/mouse_input_processing_internals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fake-cheater/MouHidInputHook/HEAD/Image/mouse_input_processing_internals.png -------------------------------------------------------------------------------- /MouHidInputHook/object_util.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | _IRQL_requires_max_(APC_LEVEL) 15 | _IRQL_requires_same_ 16 | _Check_return_ 17 | EXTERN_C 18 | NTSTATUS 19 | ObuQueryNameString( 20 | _In_ PVOID pObject, 21 | _Outptr_result_nullonfailure_ POBJECT_NAME_INFORMATION* ppObjectNameInfo 22 | ); 23 | -------------------------------------------------------------------------------- /MouHidMonitor/log.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | #include 15 | 16 | #if defined(_DEBUG) 17 | #define DBG_PRINT printf 18 | #else 19 | // 20 | // Debug level messages are disabled in release builds. 21 | // 22 | #define DBG_PRINT 23 | #endif 24 | 25 | #define INF_PRINT printf 26 | #define WRN_PRINT printf 27 | #define ERR_PRINT printf 28 | -------------------------------------------------------------------------------- /MouHidMonitor/README.md: -------------------------------------------------------------------------------- 1 | # MouHidMonitor 2 | 3 | MouHidMonitor is a user mode client for the **MouHidInputHook** driver which enables the logging of mouse input data packets in the input stream of HID USB mouse devices. 4 | 5 | ## Usage 6 | 7 | 1. Enable test signing on the host machine. 8 | 2. Load the MouHidInputHook driver. 9 | 3. Execute MouHidMonitor.exe. 10 | 4. Use a kernel debugger or the DbgView Sysinternals tool to read the logged packet data. 11 | 5. Press **ENTER** to terminate the MouHidMonitor session. 12 | 13 | ## Notes 14 | 15 | * The debug configuration uses the multi-threaded debug runtime library to reduce library requirements. 16 | -------------------------------------------------------------------------------- /MouHidInputHook/MouHidInputHook.inf: -------------------------------------------------------------------------------- 1 | ; 2 | ; MouHidInputHook.inf 3 | ; 4 | 5 | [Version] 6 | Signature="$WINDOWS NT$" 7 | Class=System 8 | ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318} 9 | Provider=%ManufacturerName% 10 | DriverVer= 11 | CatalogFile=MouHidInputHook.cat 12 | 13 | [DestinationDirs] 14 | DefaultDestDir = 12 15 | 16 | 17 | [SourceDisksNames] 18 | 1 = %DiskName%,,,"" 19 | 20 | [SourceDisksFiles] 21 | 22 | 23 | [Manufacturer] 24 | %ManufacturerName%=Standard,NT$ARCH$ 25 | 26 | [Standard.NT$ARCH$] 27 | 28 | 29 | [Strings] 30 | ManufacturerName="" ;TODO: Replace with your manufacturer name 31 | ClassName="" 32 | DiskName="MouHidInputHook Source Disk" 33 | -------------------------------------------------------------------------------- /MouHidInputHook/pe.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | #include 15 | 16 | _IRQL_requires_(PASSIVE_LEVEL) 17 | _IRQL_requires_same_ 18 | _Check_return_ 19 | EXTERN_C 20 | NTSTATUS 21 | PeGetSectionsByCharacteristics( 22 | _In_ ULONG_PTR ImageBase, 23 | _In_ ULONG Characteristics, 24 | _Outptr_result_nullonfailure_ PIMAGE_SECTION_HEADER** pppSectionHeaders, 25 | _Out_ PULONG pnSectionHeaders 26 | ); 27 | 28 | _IRQL_requires_(PASSIVE_LEVEL) 29 | _IRQL_requires_same_ 30 | _Check_return_ 31 | EXTERN_C 32 | NTSTATUS 33 | PeGetExecutableSections( 34 | _In_ ULONG_PTR ImageBase, 35 | _Outptr_result_nullonfailure_ PIMAGE_SECTION_HEADER** pppSectionHeaders, 36 | _Out_ PULONG pnSectionHeaders 37 | ); 38 | -------------------------------------------------------------------------------- /MouHidMonitor/driver.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | //============================================================================= 15 | // Meta Interface 16 | //============================================================================= 17 | _Check_return_ 18 | BOOL 19 | DrvInitialization(); 20 | 21 | _Check_return_ 22 | BOOL 23 | DrvTermination(); 24 | 25 | //============================================================================= 26 | // Public Interface 27 | //============================================================================= 28 | _Check_return_ 29 | BOOL 30 | DrvQueryMouHidInputMonitor( 31 | _Out_ PBOOL pfEnabled 32 | ); 33 | 34 | _Check_return_ 35 | BOOL 36 | DrvEnableMouHidInputMonitor(); 37 | 38 | _Check_return_ 39 | BOOL 40 | DrvDisableMouHidInputMonitor(); 41 | -------------------------------------------------------------------------------- /MouHidInputHook/mouhid.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | //============================================================================= 15 | // Constants 16 | //============================================================================= 17 | #define MOUHID_DRIVER_OBJECT_PATH_U L"\\Driver\\mouhid" 18 | 19 | //============================================================================= 20 | // Meta Interface 21 | //============================================================================= 22 | _IRQL_requires_(PASSIVE_LEVEL) 23 | _IRQL_requires_same_ 24 | _Check_return_ 25 | EXTERN_C 26 | NTSTATUS 27 | MhdDriverEntry(); 28 | 29 | //============================================================================= 30 | // Public Interface 31 | //============================================================================= 32 | _Check_return_ 33 | EXTERN_C 34 | SIZE_T 35 | MhdGetConnectDataFieldOffset(); 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 changeofpace 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MouHidInputHook/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 changeofpace 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MouHidMonitor/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 changeofpace 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Common/time.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #pragma once 11 | 12 | #if defined(_KERNEL_MODE) 13 | #include 14 | #else 15 | #include 16 | #endif 17 | 18 | // 19 | // NT time is measured in units of 100-nanosecond intervals. 20 | // 21 | #define SYSTEM_TIME_UNIT_MICROSECOND ((LONGLONG)(10)) 22 | #define SYSTEM_TIME_UNIT_MILLISECOND ((LONGLONG)(10000)) 23 | #define SYSTEM_TIME_UNIT_SECOND ((LONGLONG)(10000000)) 24 | 25 | #define RELATIVE_INTERVAL(Interval) (-Interval) 26 | 27 | FORCEINLINE 28 | VOID 29 | MakeRelativeIntervalSeconds( 30 | _Inout_ PLARGE_INTEGER pInterval, 31 | _In_ LONGLONG Seconds 32 | ) 33 | { 34 | pInterval->QuadPart = RELATIVE_INTERVAL(Seconds * SYSTEM_TIME_UNIT_SECOND); 35 | } 36 | 37 | FORCEINLINE 38 | VOID 39 | MakeRelativeIntervalMilliseconds( 40 | _Inout_ PLARGE_INTEGER pInterval, 41 | _In_ LONGLONG Milliseconds 42 | ) 43 | { 44 | pInterval->QuadPart = 45 | RELATIVE_INTERVAL(Milliseconds * SYSTEM_TIME_UNIT_MILLISECOND); 46 | } 47 | -------------------------------------------------------------------------------- /MouHidMonitor/debug.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | #include "log.h" 15 | 16 | /*++ 17 | 18 | Macro Name: 19 | 20 | VERIFY 21 | 22 | Macro Description: 23 | 24 | A validation macro which ASSERTs in debug build configurations and logs 25 | failures in release build configurations. 26 | 27 | --*/ 28 | #if defined(VERIFY) 29 | #error "Unexpected identifier conflict. (VERIFY)" 30 | #endif 31 | 32 | #if defined(_DEBUG) 33 | #define VERIFY(Expression) (_ASSERT(Expression)) 34 | #else 35 | #define VERIFY(Expression) \ 36 | { \ 37 | if (!(Expression)) \ 38 | { \ 39 | ERR_PRINT("\'" #Expression "\' failed: %u\n", GetLastError()); \ 40 | } \ 41 | } 42 | #endif 43 | -------------------------------------------------------------------------------- /MouHidInputHook/io_util.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | _IRQL_requires_(PASSIVE_LEVEL) 15 | _IRQL_requires_same_ 16 | _Check_return_ 17 | EXTERN_C 18 | NTSTATUS 19 | IouEnumerateDeviceObjectList( 20 | _In_ PDRIVER_OBJECT pDriverObject, 21 | _Outptr_result_nullonfailure_ PDEVICE_OBJECT** pppDeviceObjectList, 22 | _Out_ PULONG pnDeviceObjectList 23 | ); 24 | 25 | _IRQL_requires_(PASSIVE_LEVEL) 26 | _IRQL_requires_same_ 27 | EXTERN_C 28 | VOID 29 | IouFreeDeviceObjectList( 30 | _Pre_notnull_ __drv_freesMem(Mem) PDEVICE_OBJECT* ppDeviceObjectList, 31 | _In_ ULONG nDeviceObjectList 32 | ); 33 | 34 | _IRQL_requires_(PASSIVE_LEVEL) 35 | _IRQL_requires_same_ 36 | _Check_return_ 37 | EXTERN_C 38 | PDEVICE_OBJECT 39 | IouGetUpperDeviceObject( 40 | _In_ PDEVICE_OBJECT pDeviceObject 41 | ); 42 | 43 | #if defined(DBG) 44 | _IRQL_requires_(PASSIVE_LEVEL) 45 | _IRQL_requires_same_ 46 | EXTERN_C 47 | VOID 48 | IouPrintDeviceObjectList( 49 | _In_ PWSTR pwzDriverName, 50 | _In_ PDEVICE_OBJECT* ppDeviceObjectList, 51 | _In_ ULONG nDeviceObjectList 52 | ); 53 | #endif 54 | -------------------------------------------------------------------------------- /MouHidMonitor/MouHidMonitor.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | 14 | 15 | Source Files 16 | 17 | 18 | Source Files 19 | 20 | 21 | 22 | 23 | Header Files 24 | 25 | 26 | Header Files 27 | 28 | 29 | Header Files 30 | 31 | 32 | Header Files 33 | 34 | 35 | -------------------------------------------------------------------------------- /MouHidInputHook/mouhid_monitor.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | //============================================================================= 15 | // Meta Interface 16 | //============================================================================= 17 | _IRQL_requires_(PASSIVE_LEVEL) 18 | _IRQL_requires_same_ 19 | _Check_return_ 20 | EXTERN_C 21 | NTSTATUS 22 | MhmDriverEntry(); 23 | 24 | _IRQL_requires_(PASSIVE_LEVEL) 25 | _IRQL_requires_same_ 26 | EXTERN_C 27 | VOID 28 | MhmDriverUnload(); 29 | 30 | //============================================================================= 31 | // Public Interface 32 | //============================================================================= 33 | _IRQL_requires_(PASSIVE_LEVEL) 34 | _IRQL_requires_same_ 35 | _Check_return_ 36 | EXTERN_C 37 | NTSTATUS 38 | MhmQueryMouHidMonitor( 39 | _Out_ PBOOLEAN pfEnabled 40 | ); 41 | 42 | _IRQL_requires_(PASSIVE_LEVEL) 43 | _IRQL_requires_same_ 44 | _Check_return_ 45 | EXTERN_C 46 | NTSTATUS 47 | MhmEnableMouHidMonitor(); 48 | 49 | _IRQL_requires_(PASSIVE_LEVEL) 50 | _IRQL_requires_same_ 51 | _Check_return_ 52 | EXTERN_C 53 | NTSTATUS 54 | MhmDisableMouHidMonitor(); 55 | -------------------------------------------------------------------------------- /MouHidInputHook/log.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | //============================================================================= 15 | // Enumerations 16 | //============================================================================= 17 | typedef enum _LOG_LEVEL 18 | { 19 | LogLevelDebug, 20 | LogLevelInfo, 21 | LogLevelWarning, 22 | LogLevelError, 23 | 24 | } LOG_LEVEL, *PLOG_LEVEL; 25 | 26 | //============================================================================= 27 | // Private Interface 28 | //============================================================================= 29 | _IRQL_requires_same_ 30 | EXTERN_C 31 | NTSTATUS 32 | LogpPrint( 33 | _In_ LOG_LEVEL LogLevel, 34 | _In_z_ _Printf_format_string_ PCSTR pszFormat, 35 | ... 36 | ); 37 | 38 | //============================================================================= 39 | // Public Interface 40 | //============================================================================= 41 | #if defined(DBG) 42 | #define DBG_PRINT(Format, ...) \ 43 | (LogpPrint(LogLevelDebug, (Format), __VA_ARGS__)) 44 | #else 45 | // 46 | // Debug level messages are disabled in release builds. 47 | // 48 | #define DBG_PRINT(Format, ...) 49 | #endif 50 | 51 | #define INF_PRINT(Format, ...) (LogpPrint(LogLevelInfo, (Format), __VA_ARGS__)) 52 | #define WRN_PRINT(Format, ...) \ 53 | (LogpPrint(LogLevelWarning, (Format), __VA_ARGS__)) 54 | #define ERR_PRINT(Format, ...) \ 55 | (LogpPrint(LogLevelError, (Format), __VA_ARGS__)) 56 | -------------------------------------------------------------------------------- /MouHidInputHook/debug.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | #include "log.h" 15 | 16 | /*++ 17 | 18 | Macro Name: 19 | 20 | DEBUG_BREAK 21 | 22 | Macro Description: 23 | 24 | A software breakpoint which is only executed if all of the following 25 | conditions are true: 26 | 27 | 1. Debug build configuration. 28 | 29 | 2. Debugging was enabled on the machine at boot time. 30 | 31 | 3. A kernel debugger is currently attached to the machine. 32 | 33 | --*/ 34 | #if defined(DBG) 35 | #define DEBUG_BREAK \ 36 | if (!(KD_DEBUGGER_NOT_PRESENT)) \ 37 | { \ 38 | DbgBreakPoint(); \ 39 | } 40 | #else 41 | #define DEBUG_BREAK 42 | #endif 43 | 44 | /*++ 45 | 46 | Macro Name: 47 | 48 | VERIFY 49 | 50 | Macro Description: 51 | 52 | A validation macro which ASSERTs in debug build configurations and logs 53 | failures in release build configurations. 54 | 55 | Remarks: 56 | 57 | NT_VERIFY and RTL_SOFT_ASSERT are similar utility macros. 58 | 59 | --*/ 60 | #if defined(VERIFY) 61 | #error "Unexpected identifier conflict. (VERIFY)" 62 | #endif 63 | 64 | #if defined(DBG) 65 | #define VERIFY(NtExpression) (NT_ASSERT(NT_SUCCESS(NtExpression))) 66 | #else 67 | #define VERIFY(NtExpression) \ 68 | { \ 69 | NTSTATUS Verify_NtStatus_ = (NtExpression); \ 70 | if (!NT_SUCCESS(Verify_NtStatus_)) \ 71 | { \ 72 | ERR_PRINT("\'" #NtExpression "\' failed: 0x%X\n", Verify_NtStatus_);\ 73 | } \ 74 | } 75 | #endif 76 | -------------------------------------------------------------------------------- /Common/ioctl.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #pragma once 11 | 12 | #if defined(_KERNEL_MODE) 13 | #include 14 | #else 15 | #include 16 | #endif 17 | 18 | #include 19 | 20 | //============================================================================= 21 | // Names 22 | //============================================================================= 23 | #define DRIVER_NAME_U L"MouHidInputHook" 24 | #define LOCAL_DEVICE_PATH_U (L"\\\\.\\" DRIVER_NAME_U) 25 | #define NT_DEVICE_NAME_U (L"\\Device\\" DRIVER_NAME_U) 26 | #define SYMBOLIC_LINK_NAME_U (L"\\DosDevices\\" DRIVER_NAME_U) 27 | 28 | //============================================================================= 29 | // Ioctls 30 | //============================================================================= 31 | #define FILE_DEVICE_MOUHID_INPUT_HOOK 51382 32 | 33 | #define IOCTL_QUERY_MOUHID_INPUT_MONITOR \ 34 | CTL_CODE( \ 35 | FILE_DEVICE_MOUHID_INPUT_HOOK, \ 36 | 3300, \ 37 | METHOD_BUFFERED, \ 38 | FILE_ANY_ACCESS) 39 | 40 | #define IOCTL_ENABLE_MOUHID_INPUT_MONITOR \ 41 | CTL_CODE( \ 42 | FILE_DEVICE_MOUHID_INPUT_HOOK, \ 43 | 3500, \ 44 | METHOD_BUFFERED, \ 45 | FILE_ANY_ACCESS) 46 | 47 | #define IOCTL_DISABLE_MOUHID_INPUT_MONITOR \ 48 | CTL_CODE( \ 49 | FILE_DEVICE_MOUHID_INPUT_HOOK, \ 50 | 3501, \ 51 | METHOD_BUFFERED, \ 52 | FILE_ANY_ACCESS) 53 | 54 | //============================================================================= 55 | // IOCTL_QUERY_MOUHID_INPUT_MONITOR 56 | //============================================================================= 57 | typedef struct _QUERY_MOUHID_INPUT_MONITOR_REPLY 58 | { 59 | BOOLEAN Enabled; 60 | 61 | } QUERY_MOUHID_INPUT_MONITOR_REPLY, *PQUERY_MOUHID_INPUT_MONITOR_REPLY; 62 | -------------------------------------------------------------------------------- /MouHidInputHook/nt.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | //============================================================================= 15 | // Macros 16 | //============================================================================= 17 | #define OFFSET_POINTER(Pointer, Offset, Type) \ 18 | ((Type*)(((PUCHAR)(Pointer)) + (Offset))) 19 | 20 | #define POINTER_OFFSET(Offset, Base) \ 21 | ((SIZE_T)(((ULONG_PTR)(Offset)) - ((ULONG_PTR)(Base)))) 22 | 23 | //============================================================================= 24 | // Globals 25 | //============================================================================= 26 | EXTERN_C POBJECT_TYPE* IoDriverObjectType; 27 | 28 | //============================================================================= 29 | // Object Interface 30 | //============================================================================= 31 | EXTERN_C 32 | NTSTATUS 33 | NTAPI 34 | ObReferenceObjectByName( 35 | _In_ PUNICODE_STRING ObjectName, 36 | _In_ ULONG Attributes, 37 | _In_opt_ PACCESS_STATE AccessState, 38 | _In_opt_ ACCESS_MASK DesiredAccess, 39 | _In_ POBJECT_TYPE ObjectType, 40 | _In_ KPROCESSOR_MODE AccessMode, 41 | _Inout_opt_ PVOID ParseContext, 42 | _Out_ PVOID* Object 43 | ); 44 | 45 | //============================================================================= 46 | // Process Interface 47 | //============================================================================= 48 | EXTERN_C 49 | PUCHAR 50 | NTAPI 51 | PsGetProcessImageFileName( 52 | _In_ PEPROCESS Process 53 | ); 54 | 55 | EXTERN_C 56 | NTSTATUS 57 | NTAPI 58 | PsAcquireProcessExitSynchronization( 59 | _In_ PEPROCESS Process 60 | ); 61 | 62 | EXTERN_C 63 | VOID 64 | NTAPI 65 | PsReleaseProcessExitSynchronization( 66 | _In_ PEPROCESS Process 67 | ); 68 | 69 | //============================================================================= 70 | // Rtl Interface 71 | //============================================================================= 72 | EXTERN_C 73 | PIMAGE_NT_HEADERS 74 | NTAPI 75 | RtlImageNtHeader( 76 | _In_ PVOID ImageBase 77 | ); 78 | 79 | EXTERN_C 80 | PVOID 81 | NTAPI 82 | RtlPcToFileHeader( 83 | _In_ PVOID PcValue, 84 | _Out_ PVOID* BaseOfImage 85 | ); 86 | -------------------------------------------------------------------------------- /MouHidInputHook.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.271 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MouHidInputHook", "MouHidInputHook\MouHidInputHook.vcxproj", "{B1633E0D-8DBF-4913-AAF7-8E3FB891FDC9}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MouHidMonitor", "MouHidMonitor\MouHidMonitor.vcxproj", "{84AFD7F5-416B-455C-81ED-1C7E5B11CDB6}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {B1633E0D-8DBF-4913-AAF7-8E3FB891FDC9}.Debug|x64.ActiveCfg = Debug|x64 19 | {B1633E0D-8DBF-4913-AAF7-8E3FB891FDC9}.Debug|x64.Build.0 = Debug|x64 20 | {B1633E0D-8DBF-4913-AAF7-8E3FB891FDC9}.Debug|x64.Deploy.0 = Debug|x64 21 | {B1633E0D-8DBF-4913-AAF7-8E3FB891FDC9}.Debug|x86.ActiveCfg = Debug|Win32 22 | {B1633E0D-8DBF-4913-AAF7-8E3FB891FDC9}.Debug|x86.Build.0 = Debug|Win32 23 | {B1633E0D-8DBF-4913-AAF7-8E3FB891FDC9}.Debug|x86.Deploy.0 = Debug|Win32 24 | {B1633E0D-8DBF-4913-AAF7-8E3FB891FDC9}.Release|x64.ActiveCfg = Release|x64 25 | {B1633E0D-8DBF-4913-AAF7-8E3FB891FDC9}.Release|x64.Build.0 = Release|x64 26 | {B1633E0D-8DBF-4913-AAF7-8E3FB891FDC9}.Release|x64.Deploy.0 = Release|x64 27 | {B1633E0D-8DBF-4913-AAF7-8E3FB891FDC9}.Release|x86.ActiveCfg = Release|Win32 28 | {B1633E0D-8DBF-4913-AAF7-8E3FB891FDC9}.Release|x86.Build.0 = Release|Win32 29 | {B1633E0D-8DBF-4913-AAF7-8E3FB891FDC9}.Release|x86.Deploy.0 = Release|Win32 30 | {84AFD7F5-416B-455C-81ED-1C7E5B11CDB6}.Debug|x64.ActiveCfg = Debug|x64 31 | {84AFD7F5-416B-455C-81ED-1C7E5B11CDB6}.Debug|x64.Build.0 = Debug|x64 32 | {84AFD7F5-416B-455C-81ED-1C7E5B11CDB6}.Debug|x86.ActiveCfg = Debug|Win32 33 | {84AFD7F5-416B-455C-81ED-1C7E5B11CDB6}.Debug|x86.Build.0 = Debug|Win32 34 | {84AFD7F5-416B-455C-81ED-1C7E5B11CDB6}.Release|x64.ActiveCfg = Release|x64 35 | {84AFD7F5-416B-455C-81ED-1C7E5B11CDB6}.Release|x64.Build.0 = Release|x64 36 | {84AFD7F5-416B-455C-81ED-1C7E5B11CDB6}.Release|x86.ActiveCfg = Release|Win32 37 | {84AFD7F5-416B-455C-81ED-1C7E5B11CDB6}.Release|x86.Build.0 = Release|Win32 38 | EndGlobalSection 39 | GlobalSection(SolutionProperties) = preSolution 40 | HideSolutionNode = FALSE 41 | EndGlobalSection 42 | GlobalSection(ExtensibilityGlobals) = postSolution 43 | SolutionGuid = {D3BEAACE-9A8E-475A-9E2C-2C7B61293F9D} 44 | EndGlobalSection 45 | EndGlobal 46 | -------------------------------------------------------------------------------- /MouHidInputHook/object_util.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #include "object_util.h" 11 | 12 | #define NTSTRSAFE_NO_CB_FUNCTIONS 13 | 14 | #include 15 | 16 | #include "log.h" 17 | #include "nt.h" 18 | 19 | 20 | _Use_decl_annotations_ 21 | EXTERN_C 22 | NTSTATUS 23 | ObuQueryNameString( 24 | PVOID pObject, 25 | POBJECT_NAME_INFORMATION* ppObjectNameInfo 26 | ) 27 | /*++ 28 | 29 | Routine Description: 30 | 31 | This function is a convenience wrapper for ObQueryNameString. 32 | 33 | Parameters: 34 | 35 | pObject - Pointer to the object to be queried. 36 | 37 | ppObjectNameInfo - Returns a pointer to an allocated buffer for the object 38 | name information for the specified object. If the object is unnamed 39 | then the unicode string object in the returned buffer is zeroed. The 40 | buffer is allocated from the NonPaged pool. 41 | 42 | Remarks: 43 | 44 | If successful, the caller must free the returned object name information 45 | buffer by calling ExFreePool. 46 | 47 | --*/ 48 | { 49 | POBJECT_NAME_INFORMATION pObjectNameInfo = NULL; 50 | ULONG cbReturnLength = 0; 51 | NTSTATUS ntstatus = STATUS_SUCCESS; 52 | 53 | // 54 | // Zero out parameters. 55 | // 56 | *ppObjectNameInfo = NULL; 57 | 58 | ntstatus = ObQueryNameString(pObject, NULL, 0, &cbReturnLength); 59 | if (STATUS_INFO_LENGTH_MISMATCH != ntstatus) 60 | { 61 | ERR_PRINT("ObQueryNameString failed: 0x%X (Unexpected)\n", ntstatus); 62 | ntstatus = STATUS_UNSUCCESSFUL; 63 | goto exit; 64 | } 65 | 66 | pObjectNameInfo = (POBJECT_NAME_INFORMATION)ExAllocatePool( 67 | NonPagedPool, 68 | cbReturnLength); 69 | if (!pObjectNameInfo) 70 | { 71 | ntstatus = STATUS_INSUFFICIENT_RESOURCES; 72 | goto exit; 73 | } 74 | // 75 | RtlSecureZeroMemory(pObjectNameInfo, cbReturnLength); 76 | 77 | ntstatus = ObQueryNameString( 78 | pObject, 79 | pObjectNameInfo, 80 | cbReturnLength, 81 | &cbReturnLength); 82 | if (!NT_SUCCESS(ntstatus)) 83 | { 84 | ERR_PRINT("ObQueryNameString failed: 0x%X\n", ntstatus); 85 | goto exit; 86 | } 87 | 88 | // 89 | // Set out parameters. 90 | // 91 | *ppObjectNameInfo = pObjectNameInfo; 92 | 93 | exit: 94 | if (!NT_SUCCESS(ntstatus)) 95 | { 96 | if (pObjectNameInfo) 97 | { 98 | ExFreePool(pObjectNameInfo); 99 | } 100 | } 101 | 102 | return ntstatus; 103 | } 104 | -------------------------------------------------------------------------------- /MouHidInputHook/MouHidInputHook.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | 14 | 15 | Source Files 16 | 17 | 18 | Source Files 19 | 20 | 21 | Source Files 22 | 23 | 24 | Source Files 25 | 26 | 27 | Source Files 28 | 29 | 30 | Source Files 31 | 32 | 33 | Source Files 34 | 35 | 36 | Source Files 37 | 38 | 39 | Source Files 40 | 41 | 42 | 43 | 44 | Header Files 45 | 46 | 47 | Header Files 48 | 49 | 50 | Header Files 51 | 52 | 53 | Header Files 54 | 55 | 56 | Header Files 57 | 58 | 59 | Header Files 60 | 61 | 62 | Header Files 63 | 64 | 65 | Header Files 66 | 67 | 68 | Header Files 69 | 70 | 71 | Header Files 72 | 73 | 74 | Header Files 75 | 76 | 77 | Header Files 78 | 79 | 80 | -------------------------------------------------------------------------------- /MouHidInputHook/log.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #include "log.h" 11 | 12 | #define NTSTRSAFE_NO_CB_FUNCTIONS 13 | 14 | #include 15 | 16 | #include "debug.h" 17 | #include "nt.h" 18 | 19 | 20 | //============================================================================= 21 | // Constants 22 | //============================================================================= 23 | #define OUTPUT_BUFFER_CCH_MAX 512 24 | #define TIME_BUFFER_CCH_MAX 20 25 | #define MESSAGE_BUFFER_CCH_MAX \ 26 | (OUTPUT_BUFFER_CCH_MAX - TIME_BUFFER_CCH_MAX - 80) 27 | 28 | 29 | //============================================================================= 30 | // Private Interface 31 | //============================================================================= 32 | _Use_decl_annotations_ 33 | EXTERN_C 34 | NTSTATUS 35 | LogpPrint( 36 | LOG_LEVEL LogLevel, 37 | PCSTR pszFormat, 38 | ... 39 | ) 40 | { 41 | PCHAR pszLogLevel = NULL; 42 | LARGE_INTEGER SystemTime = {}; 43 | LARGE_INTEGER LocalTime = {}; 44 | TIME_FIELDS TimeFields = {}; 45 | CHAR szTimeBuffer[TIME_BUFFER_CCH_MAX] = {}; 46 | va_list VarArgs = {}; 47 | CHAR szMessageBuffer[MESSAGE_BUFFER_CCH_MAX] = {}; 48 | CHAR szOutputBuffer[OUTPUT_BUFFER_CCH_MAX] = {}; 49 | NTSTATUS ntstatus = STATUS_SUCCESS; 50 | 51 | // 52 | // Set the log level prefix. 53 | // 54 | switch (LogLevel) 55 | { 56 | case LogLevelDebug: pszLogLevel = "DBG"; break; 57 | case LogLevelInfo: pszLogLevel = "INF"; break; 58 | case LogLevelWarning: pszLogLevel = "WRN"; break; 59 | case LogLevelError: pszLogLevel = "ERR"; break; 60 | default: 61 | ntstatus = STATUS_INVALID_PARAMETER_1; 62 | DEBUG_BREAK; 63 | goto exit; 64 | } 65 | 66 | // 67 | // Query the current local time. 68 | // 69 | KeQuerySystemTime(&SystemTime); 70 | ExSystemTimeToLocalTime(&SystemTime, &LocalTime); 71 | RtlTimeToTimeFields(&LocalTime, &TimeFields); 72 | 73 | ntstatus = RtlStringCchPrintfA( 74 | szTimeBuffer, 75 | RTL_NUMBER_OF(szTimeBuffer), 76 | "%02hd:%02hd:%02hd.%03hd", 77 | TimeFields.Hour, 78 | TimeFields.Minute, 79 | TimeFields.Second, 80 | TimeFields.Milliseconds); 81 | if (!NT_SUCCESS(ntstatus)) 82 | { 83 | DEBUG_BREAK; 84 | goto exit; 85 | } 86 | 87 | va_start(VarArgs, pszFormat); 88 | ntstatus = RtlStringCchVPrintfA( 89 | szMessageBuffer, 90 | RTL_NUMBER_OF(szMessageBuffer), 91 | pszFormat, 92 | VarArgs); 93 | va_end(VarArgs); 94 | if (!NT_SUCCESS(ntstatus)) 95 | { 96 | DEBUG_BREAK; 97 | goto exit; 98 | } 99 | 100 | ntstatus = RtlStringCchPrintfA( 101 | szOutputBuffer, 102 | RTL_NUMBER_OF(szOutputBuffer), 103 | "%s %s %04Iu:%04Iu %-15s %s", 104 | szTimeBuffer, 105 | pszLogLevel, 106 | (ULONG_PTR)PsGetProcessId(PsGetCurrentProcess()), 107 | (ULONG_PTR)PsGetCurrentThreadId(), 108 | PsGetProcessImageFileName(PsGetCurrentProcess()), 109 | szMessageBuffer); 110 | if (!NT_SUCCESS(ntstatus)) 111 | { 112 | DEBUG_BREAK; 113 | goto exit; 114 | } 115 | 116 | ntstatus = DbgPrintEx( 117 | DPFLTR_DEFAULT_ID, 118 | DPFLTR_ERROR_LEVEL, 119 | "%s", 120 | szOutputBuffer); 121 | if (!NT_SUCCESS(ntstatus)) 122 | { 123 | DEBUG_BREAK; 124 | goto exit; 125 | } 126 | 127 | exit: 128 | return ntstatus; 129 | } 130 | -------------------------------------------------------------------------------- /MouHidInputHook/pe.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #include "pe.h" 11 | 12 | #include 13 | 14 | #include "nt.h" 15 | 16 | 17 | _Use_decl_annotations_ 18 | EXTERN_C 19 | NTSTATUS 20 | PeGetSectionsByCharacteristics( 21 | ULONG_PTR ImageBase, 22 | ULONG Characteristics, 23 | PIMAGE_SECTION_HEADER** pppSectionHeaders, 24 | PULONG pnSectionHeaders 25 | ) 26 | /*++ 27 | 28 | Routine Description: 29 | 30 | Returns the image section header pointer of each section in the image with 31 | the specified characteristics. 32 | 33 | Parameters: 34 | 35 | ImageBase - The base address of the target image. 36 | 37 | Characteristics - A bitmask of image section characteristics to match 38 | against. 39 | 40 | pppSectionHeaders - Returns a pointer to an allocated array of image 41 | section header pointers for sections with the specified 42 | characteristics. The array is allocated from the NonPaged pool. 43 | 44 | pnSectionHeaders - Returns the number of elements in the allocated array. 45 | 46 | Remarks: 47 | 48 | If successful, the caller must free the returned array by calling 49 | ExFreePool. 50 | 51 | --*/ 52 | { 53 | PIMAGE_NT_HEADERS pNtHeaders = NULL; 54 | PIMAGE_SECTION_HEADER pSectionHeader = NULL; 55 | USHORT i = 0; 56 | ULONG nSectionHeaders = 0; 57 | SIZE_T cbSectionHeaders = 0; 58 | ULONG j = 0; 59 | PIMAGE_SECTION_HEADER* ppSectionHeaders = NULL; 60 | NTSTATUS ntstatus = STATUS_SUCCESS; 61 | 62 | // 63 | // Zero out parameters. 64 | // 65 | *pppSectionHeaders = NULL; 66 | *pnSectionHeaders = 0; 67 | 68 | pNtHeaders = RtlImageNtHeader((PVOID)ImageBase); 69 | if (!pNtHeaders) 70 | { 71 | ntstatus = STATUS_INVALID_IMAGE_FORMAT; 72 | goto exit; 73 | } 74 | 75 | // 76 | // Determine the number of sections which have the specified 77 | // characteristics. 78 | // 79 | pSectionHeader = IMAGE_FIRST_SECTION(pNtHeaders); 80 | 81 | for (i = 0; 82 | i < pNtHeaders->FileHeader.NumberOfSections; 83 | ++i, ++pSectionHeader) 84 | { 85 | if (pSectionHeader->Characteristics & Characteristics) 86 | { 87 | nSectionHeaders++; 88 | } 89 | } 90 | // 91 | if (!nSectionHeaders) 92 | { 93 | ntstatus = STATUS_NOT_FOUND; 94 | goto exit; 95 | } 96 | 97 | // 98 | // Allocate and initialize the returned array. 99 | // 100 | cbSectionHeaders = nSectionHeaders * sizeof(*ppSectionHeaders); 101 | 102 | ppSectionHeaders = (PIMAGE_SECTION_HEADER*)ExAllocatePool( 103 | NonPagedPool, 104 | cbSectionHeaders); 105 | if (!ppSectionHeaders) 106 | { 107 | ntstatus = STATUS_INSUFFICIENT_RESOURCES; 108 | goto exit; 109 | } 110 | 111 | pSectionHeader = IMAGE_FIRST_SECTION(pNtHeaders); 112 | 113 | for (i = 0, j = 0; 114 | i < pNtHeaders->FileHeader.NumberOfSections; 115 | ++i, ++pSectionHeader) 116 | { 117 | if (pSectionHeader->Characteristics & Characteristics) 118 | { 119 | ppSectionHeaders[j] = pSectionHeader; 120 | j++; 121 | } 122 | } 123 | 124 | // 125 | // Set out parameters. 126 | // 127 | *pppSectionHeaders = ppSectionHeaders; 128 | *pnSectionHeaders = nSectionHeaders; 129 | 130 | exit: 131 | return ntstatus; 132 | } 133 | 134 | 135 | _Use_decl_annotations_ 136 | EXTERN_C 137 | NTSTATUS 138 | PeGetExecutableSections( 139 | ULONG_PTR ImageBase, 140 | PIMAGE_SECTION_HEADER** pppSectionHeaders, 141 | PULONG pnSectionHeaders 142 | ) 143 | { 144 | return PeGetSectionsByCharacteristics( 145 | ImageBase, 146 | IMAGE_SCN_MEM_EXECUTE, 147 | pppSectionHeaders, 148 | pnSectionHeaders); 149 | } 150 | -------------------------------------------------------------------------------- /MouHidMonitor/driver.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #include "driver.h" 11 | 12 | #include "debug.h" 13 | 14 | #include "../Common/ioctl.h" 15 | 16 | 17 | //============================================================================= 18 | // Private Types 19 | //============================================================================= 20 | typedef struct _DRIVER_CONTEXT 21 | { 22 | HANDLE DeviceHandle; 23 | 24 | } DRIVER_CONTEXT, *PDRIVER_CONTEXT; 25 | 26 | 27 | //============================================================================= 28 | // Module Globals 29 | //============================================================================= 30 | static DRIVER_CONTEXT g_DriverContext = {}; 31 | 32 | 33 | //============================================================================= 34 | // Meta Interface 35 | //============================================================================= 36 | _Use_decl_annotations_ 37 | BOOL 38 | DrvInitialization() 39 | { 40 | HANDLE hDevice = INVALID_HANDLE_VALUE; 41 | BOOL status = TRUE; 42 | 43 | hDevice = CreateFileW( 44 | LOCAL_DEVICE_PATH_U, 45 | GENERIC_READ | GENERIC_WRITE, 46 | FILE_SHARE_READ | FILE_SHARE_WRITE, 47 | NULL, 48 | OPEN_EXISTING, 49 | FILE_ATTRIBUTE_NORMAL, 50 | NULL); 51 | if (INVALID_HANDLE_VALUE == hDevice) 52 | { 53 | status = FALSE; 54 | goto exit; 55 | } 56 | 57 | // 58 | // Initialize the global context. 59 | // 60 | g_DriverContext.DeviceHandle = hDevice; 61 | 62 | exit: 63 | if (!status) 64 | { 65 | if (INVALID_HANDLE_VALUE != hDevice) 66 | { 67 | VERIFY(CloseHandle(hDevice)); 68 | } 69 | } 70 | 71 | return status; 72 | } 73 | 74 | 75 | _Use_decl_annotations_ 76 | BOOL 77 | DrvTermination() 78 | { 79 | BOOL status = TRUE; 80 | 81 | if (!g_DriverContext.DeviceHandle) 82 | { 83 | goto exit; 84 | } 85 | 86 | status = CloseHandle(g_DriverContext.DeviceHandle); 87 | if (!status) 88 | { 89 | goto exit; 90 | } 91 | 92 | // 93 | // Update the global context. 94 | // 95 | g_DriverContext.DeviceHandle = NULL; 96 | 97 | exit: 98 | return status; 99 | } 100 | 101 | 102 | //============================================================================= 103 | // Public Interface 104 | //============================================================================= 105 | 106 | // 107 | // Suppress signed/unsigned mismatch warnings for Ioctl codes. 108 | // 109 | #pragma warning(push) 110 | #pragma warning(disable:4245) 111 | 112 | _Use_decl_annotations_ 113 | BOOL 114 | DrvQueryMouHidInputMonitor( 115 | PBOOL pfEnabled 116 | ) 117 | { 118 | QUERY_MOUHID_INPUT_MONITOR_REPLY Reply = {}; 119 | DWORD cbReturned = 0; 120 | BOOL status = TRUE; 121 | 122 | // 123 | // Zero out parameters. 124 | // 125 | *pfEnabled = FALSE; 126 | 127 | status = DeviceIoControl( 128 | g_DriverContext.DeviceHandle, 129 | IOCTL_QUERY_MOUHID_INPUT_MONITOR, 130 | NULL, 131 | 0, 132 | &Reply, 133 | sizeof(Reply), 134 | &cbReturned, 135 | NULL); 136 | if (!status) 137 | { 138 | goto exit; 139 | } 140 | 141 | // 142 | // Set out parameters. 143 | // 144 | *pfEnabled = Reply.Enabled; 145 | 146 | exit: 147 | return status; 148 | } 149 | 150 | 151 | _Use_decl_annotations_ 152 | BOOL 153 | DrvEnableMouHidInputMonitor() 154 | { 155 | DWORD cbReturned = 0; 156 | BOOL status = TRUE; 157 | 158 | status = DeviceIoControl( 159 | g_DriverContext.DeviceHandle, 160 | IOCTL_ENABLE_MOUHID_INPUT_MONITOR, 161 | NULL, 162 | 0, 163 | NULL, 164 | 0, 165 | &cbReturned, 166 | NULL); 167 | if (!status) 168 | { 169 | goto exit; 170 | } 171 | 172 | exit: 173 | return status; 174 | } 175 | 176 | 177 | _Use_decl_annotations_ 178 | BOOL 179 | DrvDisableMouHidInputMonitor() 180 | { 181 | DWORD cbReturned = 0; 182 | BOOL status = TRUE; 183 | 184 | status = DeviceIoControl( 185 | g_DriverContext.DeviceHandle, 186 | IOCTL_DISABLE_MOUHID_INPUT_MONITOR, 187 | NULL, 188 | 0, 189 | NULL, 190 | 0, 191 | &cbReturned, 192 | NULL); 193 | if (!status) 194 | { 195 | goto exit; 196 | } 197 | 198 | exit: 199 | return status; 200 | } 201 | 202 | #pragma warning(pop) // disable:4245 203 | -------------------------------------------------------------------------------- /MouHidInputHook/mouclass.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | #include 15 | 16 | //============================================================================= 17 | // Constants 18 | //============================================================================= 19 | #define MOUCLASS_DRIVER_OBJECT_PATH_U L"\\Driver\\mouclass" 20 | 21 | //============================================================================= 22 | // Enumerations 23 | //============================================================================= 24 | typedef enum _MOUSE_PNP_NOTIFICATION_EVENT 25 | { 26 | MousePnpNotificationEventInvalid = 0, 27 | MousePnpNotificationEventArrival, 28 | MousePnpNotificationEventRemoval, 29 | 30 | } MOUSE_PNP_NOTIFICATION_EVENT, *PMOUSE_PNP_NOTIFICATION_EVENT; 31 | 32 | //============================================================================= 33 | // Public Types 34 | //============================================================================= 35 | /*++ 36 | 37 | Type Name: 38 | 39 | MOUSE_SERVICE_CALLBACK_ROUTINE 40 | 41 | Type Description: 42 | 43 | The mouse class service callback routine. 44 | 45 | Parameters: 46 | 47 | pDeviceObject - Pointer to the mouse class device object to receive the 48 | mouse input data packets. 49 | 50 | pInputDataStart - Pointer to the array of input packets to be copied to the 51 | class data queue. 52 | 53 | pInputDataEnd - Pointer to the input packet which marks the end of the 54 | input packet array. 55 | 56 | pnInputDataConsumed - Returns the number of input packets copied to the 57 | class data queue by the routine. 58 | 59 | Remarks: 60 | 61 | This is the mouse class implementation of a PSERVICE_CALLBACK_ROUTINE. 62 | 63 | --*/ 64 | _IRQL_requires_(DISPATCH_LEVEL) 65 | _IRQL_requires_same_ 66 | typedef 67 | VOID 68 | NTAPI 69 | MOUSE_SERVICE_CALLBACK_ROUTINE( 70 | _In_ PDEVICE_OBJECT pDeviceObject, 71 | _In_ PMOUSE_INPUT_DATA pInputDataStart, 72 | _In_ PMOUSE_INPUT_DATA pInputDataEnd, 73 | _Inout_ PULONG pnInputDataConsumed 74 | ); 75 | 76 | typedef MOUSE_SERVICE_CALLBACK_ROUTINE *PMOUSE_SERVICE_CALLBACK_ROUTINE; 77 | 78 | /*++ 79 | 80 | Type Name: 81 | 82 | MOUSE_PNP_NOTIFICATION_CALLBACK_ROUTINE 83 | 84 | Type Description: 85 | 86 | A callback which is invoked each time a mouse device is added to or removed 87 | from the system. 88 | 89 | Parameters: 90 | 91 | Event - The PnP device interface change event which caused the callback to 92 | be invoked. 93 | 94 | pContext - Pointer to caller-defined data specified at callback 95 | registration. 96 | 97 | Remarks: 98 | 99 | WARNING Mouse PnP notification callbacks are invoked as part of the PnP 100 | notification chain so they must not block or execute for a significant 101 | amount of time. 102 | 103 | WARNING It is not safe for a mouse PnP notification callback to invoke 104 | MclRegisterMousePnpNotificationCallback or 105 | MclUnregisterMousePnpNotificationCallback. 106 | 107 | --*/ 108 | _IRQL_requires_max_(PASSIVE_LEVEL) 109 | typedef 110 | VOID 111 | NTAPI 112 | MOUSE_PNP_NOTIFICATION_CALLBACK_ROUTINE( 113 | _In_ MOUSE_PNP_NOTIFICATION_EVENT Event, 114 | _Inout_opt_ PVOID pContext 115 | ); 116 | 117 | typedef MOUSE_PNP_NOTIFICATION_CALLBACK_ROUTINE 118 | *PMOUSE_PNP_NOTIFICATION_CALLBACK_ROUTINE; 119 | 120 | //============================================================================= 121 | // Meta Interface 122 | //============================================================================= 123 | _IRQL_requires_(PASSIVE_LEVEL) 124 | _IRQL_requires_same_ 125 | _Check_return_ 126 | EXTERN_C 127 | NTSTATUS 128 | MclDriverEntry( 129 | _In_ PDRIVER_OBJECT pDriverObject 130 | ); 131 | 132 | _IRQL_requires_(PASSIVE_LEVEL) 133 | _IRQL_requires_same_ 134 | EXTERN_C 135 | VOID 136 | MclDriverUnload(); 137 | 138 | //============================================================================= 139 | // Public Interface 140 | //============================================================================= 141 | _IRQL_requires_(PASSIVE_LEVEL) 142 | _IRQL_requires_same_ 143 | _Check_return_ 144 | EXTERN_C 145 | NTSTATUS 146 | MclRegisterMousePnpNotificationCallback( 147 | _In_ PMOUSE_PNP_NOTIFICATION_CALLBACK_ROUTINE pCallback, 148 | _In_opt_ PVOID pContext, 149 | _Outptr_result_nullonfailure_ PHANDLE pRegistrationHandle 150 | ); 151 | 152 | _IRQL_requires_(PASSIVE_LEVEL) 153 | _IRQL_requires_same_ 154 | EXTERN_C 155 | VOID 156 | MclUnregisterMousePnpNotificationCallback( 157 | _Inout_ HANDLE RegistrationHandle 158 | ); 159 | 160 | _IRQL_requires_max_(DISPATCH_LEVEL) 161 | _IRQL_requires_same_ 162 | EXTERN_C 163 | VOID 164 | MclPrintInputPacket( 165 | _In_ ULONG64 PacketId, 166 | _In_ PMOUSE_SERVICE_CALLBACK_ROUTINE pServiceCallback, 167 | _In_ PDEVICE_OBJECT pDeviceObject, 168 | _In_ PMOUSE_INPUT_DATA pInputPacket 169 | ); 170 | 171 | #if defined(DBG) 172 | _IRQL_requires_(PASSIVE_LEVEL) 173 | _IRQL_requires_same_ 174 | _Check_return_ 175 | EXTERN_C 176 | NTSTATUS 177 | MclPrintMouClassDeviceObjects(); 178 | #endif 179 | -------------------------------------------------------------------------------- /MouHidInputHook/mouhid_hook_manager.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | #include "mouclass.h" 15 | 16 | //============================================================================= 17 | // Public Types 18 | //============================================================================= 19 | /*++ 20 | 21 | Type Name: 22 | 23 | MHK_HOOK_CALLBACK_ROUTINE 24 | 25 | Type Description: 26 | 27 | An MHK hook callback is invoked each time the MouHid driver invokes a 28 | mouse class service callback to copy mouse input data packets from a hooked 29 | MouHid device object to the class data queue of a mouse class device 30 | object. 31 | 32 | Parameters: 33 | 34 | pServiceCallbackOriginal - Pointer to the original class service callback. 35 | 36 | pClassDeviceObject - Pointer to the mouse class device object to receive 37 | the mouse input data packets. 38 | 39 | pInputDataStart - Pointer to the array of input packets to be copied to the 40 | class data queue. 41 | 42 | pInputDataEnd - Pointer to the input packet which marks the end of the 43 | input packet array. 44 | 45 | pnInputDataConsumed - Returns the number of input packets copied to the 46 | class data queue by the routine. 47 | 48 | pContext - Pointer to caller-defined data to be passed as the context 49 | parameter each time the callback is invoked. The context data must 50 | reside in NonPaged memory. 51 | 52 | Remarks: 53 | 54 | An MHK hook callback can filter, modify, and inject input packets like a 55 | standard mouse filter driver. For example, a callback can inject ten mouse 56 | input data packets using the following strategy: 57 | 58 | 1. Allocate storage for ten packets from the NonPaged pool. 59 | 60 | 2. Initialize each packet. 61 | 62 | 3. Invoke the original class service callback using the pointer to the 63 | synthesized packets for the 'pInputDataStart' parameter. 64 | 65 | NOTE This effectively drops the original packet(s). 66 | 67 | 4. Inspect '*pnInputDataConsumed' to determine how many packets were 68 | copied to the class data queue by the routine. 69 | 70 | --*/ 71 | _IRQL_requires_(DISPATCH_LEVEL) 72 | _IRQL_requires_same_ 73 | typedef 74 | VOID 75 | NTAPI 76 | MHK_HOOK_CALLBACK_ROUTINE( 77 | _In_ PMOUSE_SERVICE_CALLBACK_ROUTINE pServiceCallbackOriginal, 78 | _In_ PDEVICE_OBJECT pClassDeviceObject, 79 | _In_ PMOUSE_INPUT_DATA pInputDataStart, 80 | _In_ PMOUSE_INPUT_DATA pInputDataEnd, 81 | _Inout_ PULONG pnInputDataConsumed, 82 | _Inout_opt_ PVOID pContext 83 | ); 84 | 85 | typedef MHK_HOOK_CALLBACK_ROUTINE *PMHK_HOOK_CALLBACK_ROUTINE; 86 | 87 | /*++ 88 | 89 | Type Name: 90 | 91 | MHK_NOTIFICATION_CALLBACK_ROUTINE 92 | 93 | Type Description: 94 | 95 | An MHK notification callback is invoked when a mouse related PNP event 96 | invalidates the hook environment established when the callback is 97 | registered. 98 | 99 | Parameters: 100 | 101 | RegistrationHandle - The opaque registration handle returned from 102 | MhkRegisterCallbacks. 103 | 104 | Event - The PnP device interface change event which caused the callback to 105 | be invoked. 106 | 107 | pContext - Pointer to caller-defined data specified at callback 108 | registration. 109 | 110 | Remarks: 111 | 112 | The MouHid Hook Manager unregisters the registration entry specified by 113 | 'RegistrationHandle' before invoking its notification callback. 114 | 115 | WARNING It is not safe for an MHK notification callback to invoke 116 | MhkRegisterCallbacks or MhkUnregisterCallbacks. 117 | 118 | --*/ 119 | _IRQL_requires_(PASSIVE_LEVEL) 120 | _IRQL_requires_same_ 121 | typedef 122 | VOID 123 | NTAPI 124 | MHK_NOTIFICATION_CALLBACK_ROUTINE( 125 | _In_ HANDLE RegistrationHandle, 126 | _In_ MOUSE_PNP_NOTIFICATION_EVENT Event, 127 | _Inout_opt_ PVOID pContext 128 | ); 129 | 130 | typedef MHK_NOTIFICATION_CALLBACK_ROUTINE *PMHK_NOTIFICATION_CALLBACK_ROUTINE; 131 | 132 | //============================================================================= 133 | // Meta Interface 134 | //============================================================================= 135 | _IRQL_requires_(PASSIVE_LEVEL) 136 | _IRQL_requires_same_ 137 | _Check_return_ 138 | EXTERN_C 139 | NTSTATUS 140 | MhkDriverEntry(); 141 | 142 | _IRQL_requires_(PASSIVE_LEVEL) 143 | _IRQL_requires_same_ 144 | EXTERN_C 145 | VOID 146 | MhkDriverUnload(); 147 | 148 | //============================================================================= 149 | // Public Interface 150 | //============================================================================= 151 | _IRQL_requires_(PASSIVE_LEVEL) 152 | _IRQL_requires_same_ 153 | _Check_return_ 154 | EXTERN_C 155 | NTSTATUS 156 | MhkRegisterCallbacks( 157 | _In_ PMHK_HOOK_CALLBACK_ROUTINE pHookCallback, 158 | _In_opt_ PMHK_NOTIFICATION_CALLBACK_ROUTINE pNotificationCallback, 159 | _In_opt_ PVOID pContext, 160 | _Outptr_result_nullonfailure_ PHANDLE pRegistrationHandle 161 | ); 162 | 163 | _IRQL_requires_(PASSIVE_LEVEL) 164 | _IRQL_requires_same_ 165 | _Check_return_ 166 | EXTERN_C 167 | NTSTATUS 168 | MhkUnregisterCallbacks( 169 | _In_ HANDLE RegistrationHandle 170 | ); 171 | -------------------------------------------------------------------------------- /MouHidInputHook/io_util.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #include "io_util.h" 11 | 12 | #include "log.h" 13 | #include "object_util.h" 14 | 15 | 16 | _Use_decl_annotations_ 17 | EXTERN_C 18 | NTSTATUS 19 | IouEnumerateDeviceObjectList( 20 | PDRIVER_OBJECT pDriverObject, 21 | PDEVICE_OBJECT** pppDeviceObjectList, 22 | PULONG pnDeviceObjectList 23 | ) 24 | /*++ 25 | 26 | Routine Description: 27 | 28 | A convenience wrapper for IoEnumerateDeviceObjectList. 29 | 30 | Parameters: 31 | 32 | pDriverObject - Pointer to the target driver object. 33 | 34 | pppDeviceObjectList - Returns a pointer to an allocated device object list 35 | for the target driver object. This function increments the reference 36 | count of every device object in the list. The list is allocated from 37 | the NonPaged pool. 38 | 39 | pnDeviceObjectList - Returns the number of elements in the allocated list. 40 | 41 | Remarks: 42 | 43 | If successful, the caller must free the returned list by calling 44 | IouFreeDeviceObjectList. 45 | 46 | NOTE The returned device object list is a snapshot of the devices which 47 | were attached to the target driver object at the time of the call. 48 | 49 | --*/ 50 | { 51 | ULONG NumberOfElements = 0; 52 | PDEVICE_OBJECT* ppDeviceObjectList = NULL; 53 | ULONG cbDeviceObjectList = 0; 54 | ULONG nDeviceObjectList = 0; 55 | NTSTATUS ntstatus = STATUS_SUCCESS; 56 | 57 | // 58 | // Zero out parameters. 59 | // 60 | *pppDeviceObjectList = NULL; 61 | *pnDeviceObjectList = 0; 62 | 63 | // 64 | // NOTE We start with an initial list size of one so that we do not attempt 65 | // to allocate zero memory. 66 | // 67 | for (NumberOfElements = 1;; NumberOfElements = nDeviceObjectList) 68 | { 69 | cbDeviceObjectList = NumberOfElements * sizeof(*ppDeviceObjectList); 70 | 71 | ppDeviceObjectList = (PDEVICE_OBJECT*)ExAllocatePool( 72 | NonPagedPool, 73 | cbDeviceObjectList); 74 | if (!ppDeviceObjectList) 75 | { 76 | ntstatus = STATUS_INSUFFICIENT_RESOURCES; 77 | goto exit; 78 | } 79 | // 80 | RtlSecureZeroMemory(ppDeviceObjectList, cbDeviceObjectList); 81 | 82 | ntstatus = IoEnumerateDeviceObjectList( 83 | pDriverObject, 84 | ppDeviceObjectList, 85 | cbDeviceObjectList, 86 | &nDeviceObjectList); 87 | if (NT_SUCCESS(ntstatus)) 88 | { 89 | break; 90 | } 91 | else if (STATUS_BUFFER_TOO_SMALL != ntstatus) 92 | { 93 | ERR_PRINT("IoEnumerateDeviceObjectList failed: 0x%X\n", ntstatus); 94 | nDeviceObjectList = NumberOfElements; 95 | goto exit; 96 | } 97 | 98 | IouFreeDeviceObjectList(ppDeviceObjectList, NumberOfElements); 99 | } 100 | 101 | // 102 | // Set out parameters. 103 | // 104 | *pppDeviceObjectList = ppDeviceObjectList; 105 | *pnDeviceObjectList = nDeviceObjectList; 106 | 107 | exit: 108 | if (!NT_SUCCESS(ntstatus)) 109 | { 110 | if (ppDeviceObjectList) 111 | { 112 | NT_ASSERT(nDeviceObjectList); 113 | 114 | IouFreeDeviceObjectList(ppDeviceObjectList, nDeviceObjectList); 115 | } 116 | } 117 | 118 | return ntstatus; 119 | } 120 | 121 | 122 | _Use_decl_annotations_ 123 | EXTERN_C 124 | VOID 125 | IouFreeDeviceObjectList( 126 | PDEVICE_OBJECT* ppDeviceObjectList, 127 | ULONG nDeviceObjectList 128 | ) 129 | { 130 | ULONG i = 0; 131 | 132 | for (i = 0; i < nDeviceObjectList; ++i) 133 | { 134 | if (ppDeviceObjectList[i]) 135 | { 136 | ObDereferenceObject(ppDeviceObjectList[i]); 137 | } 138 | } 139 | 140 | ExFreePool(ppDeviceObjectList); 141 | } 142 | 143 | 144 | // 145 | // KeReleaseQueuedSpinLock SAL annotations do not mirror 146 | // KeAcquireQueuedSpinLock annotations. 147 | // 148 | #pragma warning(suppress: 28166) 149 | // 150 | _Use_decl_annotations_ 151 | EXTERN_C 152 | PDEVICE_OBJECT 153 | IouGetUpperDeviceObject( 154 | PDEVICE_OBJECT pDeviceObject 155 | ) 156 | /*++ 157 | 158 | Routine Description: 159 | 160 | Returns a referenced pointer to the next upper-level device object on the 161 | driver stack or NULL if there is no upper-level device. 162 | 163 | Parameters: 164 | 165 | pDeviceObject - Pointer to the target device object. 166 | 167 | Remarks: 168 | 169 | If successful, the caller must dereference the returned device object 170 | pointer by calling ObDereferenceObject. 171 | 172 | --*/ 173 | { 174 | KIRQL PreviousIrql = 0; 175 | PDEVICE_OBJECT pAttachedDevice = NULL; 176 | 177 | PreviousIrql = KeAcquireQueuedSpinLock(LockQueueIoDatabaseLock); 178 | 179 | pAttachedDevice = pDeviceObject->AttachedDevice; 180 | if (pAttachedDevice) 181 | { 182 | ObReferenceObject(pAttachedDevice); 183 | } 184 | 185 | KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, PreviousIrql); 186 | 187 | return pAttachedDevice; 188 | } 189 | 190 | 191 | #if defined(DBG) 192 | _Use_decl_annotations_ 193 | EXTERN_C 194 | VOID 195 | IouPrintDeviceObjectList( 196 | PWSTR pwzDriverName, 197 | PDEVICE_OBJECT* ppDeviceObjectList, 198 | ULONG nDeviceObjectList 199 | ) 200 | { 201 | POBJECT_NAME_INFORMATION pObjectNameInfo = NULL; 202 | NTSTATUS ntstatus = STATUS_SUCCESS; 203 | 204 | DBG_PRINT("Device Object List (DriverName = %ls):\n", pwzDriverName); 205 | 206 | for (ULONG i = 0; i < nDeviceObjectList; ++i) 207 | { 208 | ntstatus = ObuQueryNameString(ppDeviceObjectList[i], &pObjectNameInfo); 209 | if (!NT_SUCCESS(ntstatus)) 210 | { 211 | ERR_PRINT("ObuQueryNameString failed: 0x%X (%u)\n", ntstatus, i); 212 | continue; 213 | } 214 | 215 | if (pObjectNameInfo->Name.Buffer) 216 | { 217 | DBG_PRINT(" %u: DeviceObject = %p, Name = %wZ\n", 218 | i, 219 | ppDeviceObjectList[i], 220 | pObjectNameInfo->Name); 221 | } 222 | else 223 | { 224 | DBG_PRINT(" %u: DeviceObject = %p, Name = (unnamed)\n", 225 | i, 226 | ppDeviceObjectList[i]); 227 | } 228 | 229 | ExFreePool(pObjectNameInfo); 230 | } 231 | } 232 | #endif 233 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015/2017 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # Visual Studio 2017 auto generated files 31 | Generated\ Files/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # Benchmark Results 47 | BenchmarkDotNet.Artifacts/ 48 | 49 | # .NET Core 50 | project.lock.json 51 | project.fragment.lock.json 52 | artifacts/ 53 | 54 | # StyleCop 55 | StyleCopReport.xml 56 | 57 | # Files built by Visual Studio 58 | *_i.c 59 | *_p.c 60 | *_i.h 61 | *.ilk 62 | *.meta 63 | *.obj 64 | *.iobj 65 | *.pch 66 | *.pdb 67 | *.ipdb 68 | *.pgc 69 | *.pgd 70 | *.rsp 71 | *.sbr 72 | *.tlb 73 | *.tli 74 | *.tlh 75 | *.tmp 76 | *.tmp_proj 77 | *.log 78 | *.vspscc 79 | *.vssscc 80 | .builds 81 | *.pidb 82 | *.svclog 83 | *.scc 84 | 85 | # Chutzpah Test files 86 | _Chutzpah* 87 | 88 | # Visual C++ cache files 89 | ipch/ 90 | *.aps 91 | *.ncb 92 | *.opendb 93 | *.opensdf 94 | *.sdf 95 | *.cachefile 96 | *.VC.db 97 | *.VC.VC.opendb 98 | 99 | # Visual Studio profiler 100 | *.psess 101 | *.vsp 102 | *.vspx 103 | *.sap 104 | 105 | # Visual Studio Trace Files 106 | *.e2e 107 | 108 | # TFS 2012 Local Workspace 109 | $tf/ 110 | 111 | # Guidance Automation Toolkit 112 | *.gpState 113 | 114 | # ReSharper is a .NET coding add-in 115 | _ReSharper*/ 116 | *.[Rr]e[Ss]harper 117 | *.DotSettings.user 118 | 119 | # JustCode is a .NET coding add-in 120 | .JustCode 121 | 122 | # TeamCity is a build add-in 123 | _TeamCity* 124 | 125 | # DotCover is a Code Coverage Tool 126 | *.dotCover 127 | 128 | # AxoCover is a Code Coverage Tool 129 | .axoCover/* 130 | !.axoCover/settings.json 131 | 132 | # Visual Studio code coverage results 133 | *.coverage 134 | *.coveragexml 135 | 136 | # NCrunch 137 | _NCrunch_* 138 | .*crunch*.local.xml 139 | nCrunchTemp_* 140 | 141 | # MightyMoose 142 | *.mm.* 143 | AutoTest.Net/ 144 | 145 | # Web workbench (sass) 146 | .sass-cache/ 147 | 148 | # Installshield output folder 149 | [Ee]xpress/ 150 | 151 | # DocProject is a documentation generator add-in 152 | DocProject/buildhelp/ 153 | DocProject/Help/*.HxT 154 | DocProject/Help/*.HxC 155 | DocProject/Help/*.hhc 156 | DocProject/Help/*.hhk 157 | DocProject/Help/*.hhp 158 | DocProject/Help/Html2 159 | DocProject/Help/html 160 | 161 | # Click-Once directory 162 | publish/ 163 | 164 | # Publish Web Output 165 | *.[Pp]ublish.xml 166 | *.azurePubxml 167 | # Note: Comment the next line if you want to checkin your web deploy settings, 168 | # but database connection strings (with potential passwords) will be unencrypted 169 | *.pubxml 170 | *.publishproj 171 | 172 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 173 | # checkin your Azure Web App publish settings, but sensitive information contained 174 | # in these scripts will be unencrypted 175 | PublishScripts/ 176 | 177 | # NuGet Packages 178 | *.nupkg 179 | # The packages folder can be ignored because of Package Restore 180 | **/[Pp]ackages/* 181 | # except build/, which is used as an MSBuild target. 182 | !**/[Pp]ackages/build/ 183 | # Uncomment if necessary however generally it will be regenerated when needed 184 | #!**/[Pp]ackages/repositories.config 185 | # NuGet v3's project.json files produces more ignorable files 186 | *.nuget.props 187 | *.nuget.targets 188 | 189 | # Microsoft Azure Build Output 190 | csx/ 191 | *.build.csdef 192 | 193 | # Microsoft Azure Emulator 194 | ecf/ 195 | rcf/ 196 | 197 | # Windows Store app package directories and files 198 | AppPackages/ 199 | BundleArtifacts/ 200 | Package.StoreAssociation.xml 201 | _pkginfo.txt 202 | *.appx 203 | 204 | # Visual Studio cache files 205 | # files ending in .cache can be ignored 206 | *.[Cc]ache 207 | # but keep track of directories ending in .cache 208 | !*.[Cc]ache/ 209 | 210 | # Others 211 | ClientBin/ 212 | ~$* 213 | *~ 214 | *.dbmdl 215 | *.dbproj.schemaview 216 | *.jfm 217 | *.pfx 218 | *.publishsettings 219 | orleans.codegen.cs 220 | 221 | # Including strong name files can present a security risk 222 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 223 | #*.snk 224 | 225 | # Since there are multiple workflows, uncomment next line to ignore bower_components 226 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 227 | #bower_components/ 228 | 229 | # RIA/Silverlight projects 230 | Generated_Code/ 231 | 232 | # Backup & report files from converting an old project file 233 | # to a newer Visual Studio version. Backup files are not needed, 234 | # because we have git ;-) 235 | _UpgradeReport_Files/ 236 | Backup*/ 237 | UpgradeLog*.XML 238 | UpgradeLog*.htm 239 | ServiceFabricBackup/ 240 | *.rptproj.bak 241 | 242 | # SQL Server files 243 | *.mdf 244 | *.ldf 245 | *.ndf 246 | 247 | # Business Intelligence projects 248 | *.rdl.data 249 | *.bim.layout 250 | *.bim_*.settings 251 | *.rptproj.rsuser 252 | 253 | # Microsoft Fakes 254 | FakesAssemblies/ 255 | 256 | # GhostDoc plugin setting file 257 | *.GhostDoc.xml 258 | 259 | # Node.js Tools for Visual Studio 260 | .ntvs_analysis.dat 261 | node_modules/ 262 | 263 | # Visual Studio 6 build log 264 | *.plg 265 | 266 | # Visual Studio 6 workspace options file 267 | *.opt 268 | 269 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 270 | *.vbw 271 | 272 | # Visual Studio LightSwitch build output 273 | **/*.HTMLClient/GeneratedArtifacts 274 | **/*.DesktopClient/GeneratedArtifacts 275 | **/*.DesktopClient/ModelManifest.xml 276 | **/*.Server/GeneratedArtifacts 277 | **/*.Server/ModelManifest.xml 278 | _Pvt_Extensions 279 | 280 | # Paket dependency manager 281 | .paket/paket.exe 282 | paket-files/ 283 | 284 | # FAKE - F# Make 285 | .fake/ 286 | 287 | # JetBrains Rider 288 | .idea/ 289 | *.sln.iml 290 | 291 | # CodeRush 292 | .cr/ 293 | 294 | # Python Tools for Visual Studio (PTVS) 295 | __pycache__/ 296 | *.pyc 297 | 298 | # Cake - Uncomment if you are using it 299 | # tools/** 300 | # !tools/packages.config 301 | 302 | # Tabs Studio 303 | *.tss 304 | 305 | # Telerik's JustMock configuration file 306 | *.jmconfig 307 | 308 | # BizTalk build output 309 | *.btp.cs 310 | *.btm.cs 311 | *.odx.cs 312 | *.xsd.cs 313 | 314 | # OpenCover UI analysis results 315 | OpenCover/ 316 | 317 | # Azure Stream Analytics local run output 318 | ASALocalRun/ 319 | 320 | # MSBuild Binary and Structured Log 321 | *.binlog 322 | 323 | # NVidia Nsight GPU debugger configuration file 324 | *.nvuser 325 | 326 | # MFractors (Xamarin productivity tool) working folder 327 | .mfractor/ 328 | -------------------------------------------------------------------------------- /MouHidInputHook/MouHidInputHook.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {B1633E0D-8DBF-4913-AAF7-8E3FB891FDC9} 23 | {dd38f7fc-d7bd-488b-9242-7d8754cde80d} 24 | v4.5 25 | 12.0 26 | Debug 27 | Win32 28 | MouHidInputHook 29 | 30 | 31 | 32 | Windows7 33 | true 34 | WindowsKernelModeDriver10.0 35 | Driver 36 | WDM 37 | false 38 | Desktop 39 | 40 | 41 | Windows7 42 | false 43 | WindowsKernelModeDriver10.0 44 | Driver 45 | WDM 46 | false 47 | Desktop 48 | 49 | 50 | Windows7 51 | true 52 | WindowsKernelModeDriver10.0 53 | Driver 54 | WDM 55 | false 56 | Desktop 57 | 58 | 59 | Windows7 60 | false 61 | WindowsKernelModeDriver10.0 62 | Driver 63 | WDM 64 | false 65 | Desktop 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | DbgengKernelDebugger 77 | $(SolutionDir)bin\$(Platform)\$(Configuration)\ 78 | $(Platform)\$(Configuration)\ 79 | 80 | 81 | DbgengKernelDebugger 82 | $(SolutionDir)bin\$(Platform)\$(Configuration)\ 83 | $(Platform)\$(Configuration)\ 84 | 85 | 86 | DbgengKernelDebugger 87 | $(SolutionDir)bin\$(Platform)\$(Configuration)\ 88 | $(Platform)\$(Configuration)\ 89 | 90 | 91 | DbgengKernelDebugger 92 | $(SolutionDir)bin\$(Platform)\$(Configuration)\ 93 | $(Platform)\$(Configuration)\ 94 | 95 | 96 | 97 | 28751;28175;4748;4505;%(DisableSpecificWarnings) 98 | 99 | 100 | 101 | 102 | 28751;28175;4748;4505;%(DisableSpecificWarnings) 103 | 104 | 105 | 106 | 107 | 28751;28175;4603;4627;4986;4987;4996;4505;%(DisableSpecificWarnings) 108 | 109 | 110 | 111 | 112 | 28751;28175;4603;4627;4986;4987;4996;4505;%(DisableSpecificWarnings) 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /MouHidMonitor/MouHidMonitor.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {84AFD7F5-416B-455C-81ED-1C7E5B11CDB6} 24 | MouHidMonitor 25 | 10.0.17763.0 26 | 27 | 28 | 29 | Application 30 | true 31 | v141 32 | MultiByte 33 | false 34 | 35 | 36 | Application 37 | false 38 | v141 39 | true 40 | MultiByte 41 | false 42 | 43 | 44 | Application 45 | true 46 | v141 47 | MultiByte 48 | false 49 | 50 | 51 | Application 52 | false 53 | v141 54 | true 55 | MultiByte 56 | false 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | $(SolutionDir)bin\$(Platform)\$(Configuration)\ 78 | $(Platform)\$(Configuration)\ 79 | 80 | 81 | $(SolutionDir)bin\$(Platform)\$(Configuration)\ 82 | $(Platform)\$(Configuration)\ 83 | 84 | 85 | $(SolutionDir)bin\$(Platform)\$(Configuration)\ 86 | 87 | 88 | $(SolutionDir)bin\$(Platform)\$(Configuration)\ 89 | 90 | 91 | 92 | Level4 93 | Disabled 94 | true 95 | true 96 | MultiThreadedDebug 97 | 98 | 99 | ntdll.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 100 | Console 101 | 102 | 103 | 104 | 105 | Level4 106 | Disabled 107 | true 108 | true 109 | MultiThreadedDebug 110 | 111 | 112 | ntdll.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 113 | Console 114 | 115 | 116 | 117 | 118 | Level4 119 | MaxSpeed 120 | true 121 | true 122 | true 123 | true 124 | 125 | 126 | true 127 | true 128 | ntdll.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 129 | Console 130 | 131 | 132 | 133 | 134 | Level4 135 | MaxSpeed 136 | true 137 | true 138 | true 139 | true 140 | 141 | 142 | true 143 | true 144 | ntdll.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 145 | Console 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /MouHidInputHook/driver.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #include 11 | 12 | #include "debug.h" 13 | #include "log.h" 14 | #include "mouhid.h" 15 | #include "mouhid_hook_manager.h" 16 | #include "mouhid_monitor.h" 17 | 18 | #include "../Common/ioctl.h" 19 | 20 | 21 | //============================================================================= 22 | // Private Prototypes 23 | //============================================================================= 24 | EXTERN_C 25 | DRIVER_INITIALIZE 26 | DriverEntry; 27 | 28 | EXTERN_C 29 | static 30 | DRIVER_UNLOAD 31 | DriverUnload; 32 | 33 | _Dispatch_type_(IRP_MJ_CREATE) 34 | EXTERN_C 35 | static 36 | DRIVER_DISPATCH 37 | DispatchCreate; 38 | 39 | _Dispatch_type_(IRP_MJ_CLOSE) 40 | EXTERN_C 41 | static 42 | DRIVER_DISPATCH 43 | DispatchClose; 44 | 45 | _Dispatch_type_(IRP_MJ_DEVICE_CONTROL) 46 | EXTERN_C 47 | static 48 | DRIVER_DISPATCH 49 | DispatchDeviceControl; 50 | 51 | 52 | //============================================================================= 53 | // Meta Interface 54 | //============================================================================= 55 | _Use_decl_annotations_ 56 | EXTERN_C 57 | NTSTATUS 58 | DriverEntry( 59 | PDRIVER_OBJECT pDriverObject, 60 | PUNICODE_STRING pRegistryPath 61 | ) 62 | { 63 | PDEVICE_OBJECT pDeviceObject = NULL; 64 | UNICODE_STRING usDeviceName = {}; 65 | UNICODE_STRING usSymbolicLinkName = {}; 66 | BOOLEAN fSymbolicLinkCreated = FALSE; 67 | BOOLEAN fMclLoaded = FALSE; 68 | BOOLEAN fMhkLoaded = FALSE; 69 | BOOLEAN fMhmLoaded = FALSE; 70 | NTSTATUS ntstatus = STATUS_SUCCESS; 71 | 72 | UNREFERENCED_PARAMETER(pRegistryPath); 73 | 74 | DBG_PRINT("Loading %ls.\n", NT_DEVICE_NAME_U); 75 | 76 | usDeviceName = RTL_CONSTANT_STRING(NT_DEVICE_NAME_U); 77 | 78 | ntstatus = IoCreateDevice( 79 | pDriverObject, 80 | 0, 81 | &usDeviceName, 82 | FILE_DEVICE_MOUHID_INPUT_HOOK, 83 | FILE_DEVICE_SECURE_OPEN, 84 | TRUE, 85 | &pDeviceObject); 86 | if (!NT_SUCCESS(ntstatus)) 87 | { 88 | ERR_PRINT("IoCreateDevice failed: 0x%X\n", ntstatus); 89 | goto exit; 90 | } 91 | // 92 | pDriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreate; 93 | pDriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchClose; 94 | pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = 95 | DispatchDeviceControl; 96 | pDriverObject->DriverUnload = DriverUnload; 97 | 98 | // 99 | // Create a symbolic link for the user mode client. 100 | // 101 | usSymbolicLinkName = RTL_CONSTANT_STRING(SYMBOLIC_LINK_NAME_U); 102 | 103 | ntstatus = IoCreateSymbolicLink(&usSymbolicLinkName, &usDeviceName); 104 | if (!NT_SUCCESS(ntstatus)) 105 | { 106 | ERR_PRINT("IoCreateSymbolicLink failed: 0x%X\n", ntstatus); 107 | goto exit; 108 | } 109 | // 110 | fSymbolicLinkCreated = TRUE; 111 | 112 | // 113 | // Load the driver modules. 114 | // 115 | ntstatus = MhdDriverEntry(); 116 | if (!NT_SUCCESS(ntstatus)) 117 | { 118 | ERR_PRINT("MhdDriverEntry failed: 0x%X\n", ntstatus); 119 | goto exit; 120 | } 121 | 122 | ntstatus = MclDriverEntry(pDriverObject); 123 | if (!NT_SUCCESS(ntstatus)) 124 | { 125 | ERR_PRINT("MclDriverEntry failed: 0x%X\n", ntstatus); 126 | goto exit; 127 | } 128 | // 129 | fMclLoaded = TRUE; 130 | 131 | ntstatus = MhkDriverEntry(); 132 | if (!NT_SUCCESS(ntstatus)) 133 | { 134 | ERR_PRINT("MhkDriverEntry failed: 0x%X\n", ntstatus); 135 | goto exit; 136 | } 137 | // 138 | fMhkLoaded = TRUE; 139 | 140 | ntstatus = MhmDriverEntry(); 141 | if (!NT_SUCCESS(ntstatus)) 142 | { 143 | ERR_PRINT("MhmDriverEntry failed: 0x%X\n", ntstatus); 144 | goto exit; 145 | } 146 | // 147 | fMhmLoaded = TRUE; 148 | 149 | DBG_PRINT("%ls loaded.\n", NT_DEVICE_NAME_U); 150 | 151 | exit: 152 | if (!NT_SUCCESS(ntstatus)) 153 | { 154 | if (fMhmLoaded) 155 | { 156 | MhmDriverUnload(); 157 | } 158 | 159 | if (fMhkLoaded) 160 | { 161 | MhkDriverUnload(); 162 | } 163 | 164 | if (fMclLoaded) 165 | { 166 | MclDriverUnload(); 167 | } 168 | 169 | if (fSymbolicLinkCreated) 170 | { 171 | VERIFY(IoDeleteSymbolicLink(&usSymbolicLinkName)); 172 | } 173 | 174 | if (pDeviceObject) 175 | { 176 | IoDeleteDevice(pDeviceObject); 177 | } 178 | } 179 | 180 | return ntstatus; 181 | } 182 | 183 | 184 | _Use_decl_annotations_ 185 | EXTERN_C 186 | static 187 | VOID 188 | DriverUnload( 189 | PDRIVER_OBJECT pDriverObject 190 | ) 191 | { 192 | UNICODE_STRING usSymbolicLinkName = {}; 193 | 194 | DBG_PRINT("Unloading %ls.\n", NT_DEVICE_NAME_U); 195 | 196 | // 197 | // Unload the driver modules. 198 | // 199 | MhmDriverUnload(); 200 | MhkDriverUnload(); 201 | MclDriverUnload(); 202 | 203 | // 204 | // Release driver resources. 205 | // 206 | usSymbolicLinkName = RTL_CONSTANT_STRING(SYMBOLIC_LINK_NAME_U); 207 | 208 | VERIFY(IoDeleteSymbolicLink(&usSymbolicLinkName)); 209 | 210 | if (pDriverObject->DeviceObject) 211 | { 212 | IoDeleteDevice(pDriverObject->DeviceObject); 213 | } 214 | 215 | DBG_PRINT("%ls unloaded.\n", NT_DEVICE_NAME_U); 216 | } 217 | 218 | 219 | //============================================================================= 220 | // Private Interface 221 | //============================================================================= 222 | _Use_decl_annotations_ 223 | EXTERN_C 224 | static 225 | NTSTATUS 226 | DispatchCreate( 227 | PDEVICE_OBJECT pDeviceObject, 228 | PIRP pIrp 229 | ) 230 | { 231 | UNREFERENCED_PARAMETER(pDeviceObject); 232 | DBG_PRINT("Processing IRP_MJ_CREATE.\n"); 233 | IoCompleteRequest(pIrp, IO_NO_INCREMENT); 234 | return STATUS_SUCCESS; 235 | } 236 | 237 | 238 | _Use_decl_annotations_ 239 | EXTERN_C 240 | static 241 | NTSTATUS 242 | DispatchClose( 243 | PDEVICE_OBJECT pDeviceObject, 244 | PIRP pIrp 245 | ) 246 | { 247 | NTSTATUS ntstatus = STATUS_SUCCESS; 248 | 249 | UNREFERENCED_PARAMETER(pDeviceObject); 250 | 251 | DBG_PRINT("Processing IRP_MJ_CLOSE.\n"); 252 | 253 | // 254 | // Manually disable the MouHid Monitor in case the user mode client failed 255 | // to disable it. 256 | // 257 | VERIFY(MhmDisableMouHidMonitor()); 258 | 259 | IoCompleteRequest(pIrp, IO_NO_INCREMENT); 260 | 261 | return ntstatus; 262 | } 263 | 264 | 265 | _Use_decl_annotations_ 266 | EXTERN_C 267 | static 268 | NTSTATUS 269 | DispatchDeviceControl( 270 | PDEVICE_OBJECT pDeviceObject, 271 | PIRP pIrp 272 | ) 273 | { 274 | PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp); 275 | PVOID pSystemBuffer = pIrp->AssociatedIrp.SystemBuffer; 276 | ULONG cbInput = pIrpStack->Parameters.DeviceIoControl.InputBufferLength; 277 | ULONG cbOutput = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength; 278 | PQUERY_MOUHID_INPUT_MONITOR_REPLY pQueryMouHidInputMonitorReply = NULL; 279 | ULONG_PTR Information = 0; 280 | NTSTATUS ntstatus = STATUS_SUCCESS; 281 | 282 | UNREFERENCED_PARAMETER(pDeviceObject); 283 | 284 | switch (pIrpStack->Parameters.DeviceIoControl.IoControlCode) 285 | { 286 | case IOCTL_QUERY_MOUHID_INPUT_MONITOR: 287 | DBG_PRINT("Processing IOCTL_QUERY_MOUHID_INPUT_MONITOR.\n"); 288 | 289 | if (cbInput) 290 | { 291 | ntstatus = STATUS_INVALID_PARAMETER_4; 292 | goto exit; 293 | } 294 | 295 | pQueryMouHidInputMonitorReply = 296 | (PQUERY_MOUHID_INPUT_MONITOR_REPLY)pSystemBuffer; 297 | if (!pQueryMouHidInputMonitorReply) 298 | { 299 | ntstatus = STATUS_INVALID_PARAMETER_5; 300 | goto exit; 301 | } 302 | 303 | if (sizeof(*pQueryMouHidInputMonitorReply) != cbOutput) 304 | { 305 | ntstatus = STATUS_INVALID_BUFFER_SIZE; 306 | goto exit; 307 | } 308 | 309 | ntstatus = MhmQueryMouHidMonitor( 310 | &pQueryMouHidInputMonitorReply->Enabled); 311 | if (!NT_SUCCESS(ntstatus)) 312 | { 313 | ERR_PRINT("MhmQueryMouHidMonitor failed: 0x%X\n", ntstatus); 314 | goto exit; 315 | } 316 | 317 | Information = sizeof(*pQueryMouHidInputMonitorReply); 318 | 319 | break; 320 | 321 | case IOCTL_ENABLE_MOUHID_INPUT_MONITOR: 322 | DBG_PRINT("Processing IOCTL_ENABLE_MOUHID_INPUT_MONITOR.\n"); 323 | 324 | if (cbInput || cbOutput) 325 | { 326 | ntstatus = STATUS_INVALID_PARAMETER; 327 | goto exit; 328 | } 329 | 330 | ntstatus = MhmEnableMouHidMonitor(); 331 | if (!NT_SUCCESS(ntstatus)) 332 | { 333 | ERR_PRINT("MhmEnableMouHidMonitor failed: 0x%X\n", ntstatus); 334 | goto exit; 335 | } 336 | 337 | break; 338 | 339 | case IOCTL_DISABLE_MOUHID_INPUT_MONITOR: 340 | DBG_PRINT("Processing IOCTL_DISABLE_MOUHID_INPUT_MONITOR.\n"); 341 | 342 | if (cbInput || cbOutput) 343 | { 344 | ntstatus = STATUS_INVALID_PARAMETER; 345 | goto exit; 346 | } 347 | 348 | ntstatus = MhmDisableMouHidMonitor(); 349 | if (!NT_SUCCESS(ntstatus)) 350 | { 351 | ERR_PRINT("MhmDisableMouHidMonitor failed: 0x%X\n", ntstatus); 352 | goto exit; 353 | } 354 | 355 | break; 356 | 357 | default: 358 | ERR_PRINT( 359 | "Unhandled IOCTL." 360 | " (MajorFunction = %hhu, MinorFunction = %hhu)\n", 361 | pIrpStack->MajorFunction, 362 | pIrpStack->MinorFunction); 363 | ntstatus = STATUS_UNSUCCESSFUL; 364 | goto exit; 365 | } 366 | 367 | exit: 368 | pIrp->IoStatus.Information = Information; 369 | pIrp->IoStatus.Status = ntstatus; 370 | 371 | IoCompleteRequest(pIrp, IO_NO_INCREMENT); 372 | 373 | return ntstatus; 374 | } 375 | -------------------------------------------------------------------------------- /MouHidMonitor/main.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #include 11 | 12 | #include "debug.h" 13 | #include "driver.h" 14 | 15 | #include "log.h" 16 | 17 | 18 | //============================================================================= 19 | // Constants 20 | //============================================================================= 21 | #define CONSOLE_MODE_OPTIONS 0 22 | 23 | #define INPUT_EVENT_BUFFER_SIZE 64 24 | 25 | #define EXIT_PROCESS_VIRTUAL_KEY (VK_RETURN) 26 | 27 | #define QUERY_THREAD_INTERVAL_MS 5000 28 | #define QUERY_THREAD_EXIT_TIMEOUT_MS 10000 29 | 30 | 31 | //============================================================================= 32 | // Private Types 33 | //============================================================================= 34 | typedef struct _CONSOLE_CONTEXT 35 | { 36 | HANDLE StandardInputHandle; 37 | 38 | BOOL RestorePreviousMode; 39 | DWORD PreviousMode; 40 | 41 | } CONSOLE_CONTEXT, *PCONSOLE_CONTEXT; 42 | 43 | typedef struct _MOUHID_MONITOR_CLIENT_CONTEXT 44 | { 45 | BOOL Active; 46 | CONSOLE_CONTEXT Console; 47 | 48 | } MOUHID_MONITOR_CLIENT_CONTEXT, *PMOUHID_MONITOR_CLIENT_CONTEXT; 49 | 50 | 51 | //============================================================================= 52 | // Module Globals 53 | //============================================================================= 54 | EXTERN_C static MOUHID_MONITOR_CLIENT_CONTEXT g_ClientContext = {}; 55 | 56 | 57 | //============================================================================= 58 | // Private Interface 59 | //============================================================================= 60 | _Check_return_ 61 | static 62 | DWORD 63 | WINAPI 64 | QueryMouHidMonitorThread( 65 | _In_ PVOID pContext 66 | ) 67 | { 68 | PMOUHID_MONITOR_CLIENT_CONTEXT pClientContext = NULL; 69 | BOOL fMouHidInputMonitorEnabled = FALSE; 70 | DWORD exitstatus = ERROR_SUCCESS; 71 | 72 | pClientContext = (PMOUHID_MONITOR_CLIENT_CONTEXT)pContext; 73 | 74 | for (; pClientContext->Active;) 75 | { 76 | Sleep(QUERY_THREAD_INTERVAL_MS); 77 | 78 | // 79 | // If either of these calls fail then continue to the next iteration so 80 | // that we can make another attempt. 81 | // 82 | if (!DrvQueryMouHidInputMonitor(&fMouHidInputMonitorEnabled)) 83 | { 84 | ERR_PRINT("DrvQueryMouHidInputMonitor failed: %u\n", 85 | GetLastError()); 86 | continue; 87 | } 88 | // 89 | if (!fMouHidInputMonitorEnabled) 90 | { 91 | INF_PRINT( 92 | "Detected mouse device changes. Enabling MouHid Monitor.\n"); 93 | 94 | if (!DrvEnableMouHidInputMonitor()) 95 | { 96 | ERR_PRINT("DrvEnableMouHidInputMonitor failed: %u\n", 97 | GetLastError()); 98 | continue; 99 | } 100 | } 101 | } 102 | 103 | return exitstatus; 104 | } 105 | 106 | 107 | _Check_return_ 108 | static 109 | BOOL 110 | WaitForExitEvent( 111 | _In_ HANDLE hStdIn 112 | ) 113 | { 114 | INPUT_RECORD InputEvents[INPUT_EVENT_BUFFER_SIZE] = {}; 115 | DWORD nEventsRead = 0; 116 | DWORD i = 0; 117 | BOOL status = TRUE; 118 | 119 | INF_PRINT("MouHid Input Monitor enabled.\n"); 120 | INF_PRINT("Press ENTER to exit.\n"); 121 | 122 | for (;;) 123 | { 124 | status = ReadConsoleInputW( 125 | hStdIn, 126 | InputEvents, 127 | ARRAYSIZE(InputEvents), 128 | &nEventsRead); 129 | if (!status) 130 | { 131 | ERR_PRINT("ReadConsoleInputW failed: %u\n", GetLastError()); 132 | goto exit; 133 | } 134 | 135 | for (i = 0; i < nEventsRead; ++i) 136 | { 137 | if (KEY_EVENT != InputEvents[i].EventType) 138 | { 139 | continue; 140 | } 141 | 142 | if (EXIT_PROCESS_VIRTUAL_KEY == 143 | InputEvents[i].Event.KeyEvent.wVirtualKeyCode) 144 | { 145 | INF_PRINT("Exiting.\n"); 146 | goto exit; 147 | } 148 | } 149 | } 150 | 151 | exit: 152 | return status; 153 | } 154 | 155 | 156 | _Check_return_ 157 | static 158 | BOOL 159 | WINAPI 160 | CtrlSignalHandlerRoutine( 161 | _In_ DWORD dwCtrlType 162 | ) 163 | { 164 | switch (dwCtrlType) 165 | { 166 | case CTRL_C_EVENT: 167 | case CTRL_BREAK_EVENT: 168 | if (g_ClientContext.Console.RestorePreviousMode) 169 | { 170 | VERIFY(SetConsoleMode( 171 | g_ClientContext.Console.StandardInputHandle, 172 | g_ClientContext.Console.PreviousMode)); 173 | 174 | g_ClientContext.Console.RestorePreviousMode = FALSE; 175 | } 176 | 177 | break; 178 | 179 | case CTRL_CLOSE_EVENT: 180 | case CTRL_LOGOFF_EVENT: 181 | case CTRL_SHUTDOWN_EVENT: 182 | default: 183 | break; 184 | } 185 | 186 | return FALSE; 187 | } 188 | 189 | 190 | //============================================================================= 191 | // Meta Interface 192 | //============================================================================= 193 | int 194 | main( 195 | _In_ int argc, 196 | _In_ char* argv[] 197 | ) 198 | { 199 | HANDLE hStdIn = NULL; 200 | BOOL fDriverInitialized = FALSE; 201 | BOOL fMouHidInputMonitorEnabled = FALSE; 202 | DWORD PreviousMode = 0; 203 | HANDLE hThread = NULL; 204 | DWORD ThreadId = 0; 205 | DWORD waitstatus = 0; 206 | DWORD ThreadExitCode = 0; 207 | int mainstatus = EXIT_SUCCESS; 208 | 209 | UNREFERENCED_PARAMETER(argc); 210 | UNREFERENCED_PARAMETER(argv); 211 | 212 | hStdIn = GetStdHandle(STD_INPUT_HANDLE); 213 | if (INVALID_HANDLE_VALUE == hStdIn || !hStdIn) 214 | { 215 | ERR_PRINT("GetStdHandle failed: %u\n", GetLastError()); 216 | mainstatus = EXIT_FAILURE; 217 | goto exit; 218 | } 219 | // 220 | g_ClientContext.Console.StandardInputHandle = hStdIn; 221 | 222 | if (!SetConsoleCtrlHandler(CtrlSignalHandlerRoutine, TRUE)) 223 | { 224 | ERR_PRINT("SetConsoleCtrlHandler failed: %u\n", GetLastError()); 225 | mainstatus = EXIT_FAILURE; 226 | goto exit; 227 | } 228 | 229 | if (!DrvInitialization()) 230 | { 231 | ERR_PRINT("DrvInitialization failed: %u\n", GetLastError()); 232 | mainstatus = EXIT_FAILURE; 233 | goto exit; 234 | } 235 | // 236 | fDriverInitialized = TRUE; 237 | 238 | if (!DrvQueryMouHidInputMonitor(&fMouHidInputMonitorEnabled)) 239 | { 240 | ERR_PRINT("DrvQueryMouHidInputMonitor failed: %u\n", GetLastError()); 241 | mainstatus = EXIT_FAILURE; 242 | goto exit; 243 | } 244 | // 245 | if (fMouHidInputMonitorEnabled) 246 | { 247 | WRN_PRINT("MouHid Monitor is already enabled.\n"); 248 | goto exit; 249 | } 250 | 251 | if (!GetConsoleMode(hStdIn, &PreviousMode)) 252 | { 253 | ERR_PRINT("GetConsoleMode failed: %u\n", GetLastError()); 254 | mainstatus = EXIT_FAILURE; 255 | goto exit; 256 | } 257 | // 258 | g_ClientContext.Console.PreviousMode = PreviousMode; 259 | g_ClientContext.Console.RestorePreviousMode = TRUE; 260 | 261 | // 262 | // Disable console input. 263 | // 264 | if (!SetConsoleMode(hStdIn, CONSOLE_MODE_OPTIONS)) 265 | { 266 | ERR_PRINT("SetConsoleMode failed: %u\n", GetLastError()); 267 | mainstatus = EXIT_FAILURE; 268 | goto exit; 269 | } 270 | 271 | if (!DrvEnableMouHidInputMonitor()) 272 | { 273 | ERR_PRINT("DrvEnableMouHidInputMonitor failed: %u\n", GetLastError()); 274 | mainstatus = EXIT_FAILURE; 275 | goto exit; 276 | } 277 | // 278 | fMouHidInputMonitorEnabled = TRUE; 279 | g_ClientContext.Active = TRUE; 280 | 281 | // 282 | // Create a thread which queries the MouHid monitor state at a defined 283 | // interval. 284 | // 285 | hThread = CreateThread( 286 | NULL, 287 | 0, 288 | QueryMouHidMonitorThread, 289 | &g_ClientContext, 290 | 0, 291 | &ThreadId); 292 | if (!hThread) 293 | { 294 | ERR_PRINT("CreateThread failed: %u\n", GetLastError()); 295 | mainstatus = EXIT_FAILURE; 296 | goto exit; 297 | } 298 | 299 | if (!WaitForExitEvent(hStdIn)) 300 | { 301 | ERR_PRINT("WaitForExitEvent failed: %u\n", GetLastError()); 302 | mainstatus = EXIT_FAILURE; 303 | goto exit; 304 | } 305 | 306 | // 307 | // Update the client context to indicate that the query thread should exit. 308 | // 309 | g_ClientContext.Active = FALSE; 310 | 311 | // 312 | // Wait for the query thread to exit. 313 | // 314 | waitstatus = WaitForSingleObject(hThread, QUERY_THREAD_EXIT_TIMEOUT_MS); 315 | switch (waitstatus) 316 | { 317 | case WAIT_OBJECT_0: 318 | break; 319 | 320 | case WAIT_TIMEOUT: 321 | ERR_PRINT("Timedout waiting for query thread to exit.\n"); 322 | mainstatus = EXIT_FAILURE; 323 | goto exit; 324 | 325 | case WAIT_FAILED: 326 | ERR_PRINT("WaitForSingleObject failed: %u\n", GetLastError()); 327 | mainstatus = EXIT_FAILURE; 328 | goto exit; 329 | 330 | default: 331 | ERR_PRINT("Unexpected wait status: %u\n", waitstatus); 332 | mainstatus = EXIT_FAILURE; 333 | goto exit; 334 | } 335 | 336 | if (!GetExitCodeThread(hThread, &ThreadExitCode)) 337 | { 338 | ERR_PRINT("GetExitCodeThread failed: %u\n", GetLastError()); 339 | mainstatus = EXIT_FAILURE; 340 | goto exit; 341 | } 342 | // 343 | if (ERROR_SUCCESS != ThreadExitCode) 344 | { 345 | ERR_PRINT("Unexpected query thread exit code: %u\n", ThreadExitCode); 346 | mainstatus = EXIT_FAILURE; 347 | goto exit; 348 | } 349 | 350 | exit: 351 | if (hThread) 352 | { 353 | VERIFY(CloseHandle(hThread)); 354 | } 355 | 356 | if (fMouHidInputMonitorEnabled) 357 | { 358 | VERIFY(DrvDisableMouHidInputMonitor()); 359 | } 360 | 361 | if (hStdIn && g_ClientContext.Console.RestorePreviousMode) 362 | { 363 | VERIFY(SetConsoleMode(hStdIn, g_ClientContext.Console.PreviousMode)); 364 | } 365 | 366 | if (fDriverInitialized) 367 | { 368 | VERIFY(DrvTermination()); 369 | } 370 | 371 | return mainstatus; 372 | } 373 | -------------------------------------------------------------------------------- /MouHidInputHook/mouhid_monitor.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #include "mouhid_monitor.h" 11 | 12 | #include 13 | 14 | #include "debug.h" 15 | #include "log.h" 16 | #include "mouclass.h" 17 | #include "mouhid_hook_manager.h" 18 | 19 | 20 | //============================================================================= 21 | // Constants 22 | //============================================================================= 23 | #define MODULE_TITLE "MouHid Monitor" 24 | 25 | 26 | //============================================================================= 27 | // Private Types 28 | //============================================================================= 29 | typedef struct _HOOK_CALLBACK_CONTEXT 30 | { 31 | _Interlocked_ volatile POINTER_ALIGNMENT LONG64 PacketIndex; 32 | 33 | } HOOK_CALLBACK_CONTEXT, *PHOOK_CALLBACK_CONTEXT; 34 | 35 | typedef struct _MOUHID_MONITOR_CONTEXT 36 | { 37 | POINTER_ALIGNMENT ERESOURCE Resource; 38 | _Guarded_by_(Resource) HANDLE RegistrationHandle; 39 | _Guarded_by_(Resource) PHOOK_CALLBACK_CONTEXT CallbackContext; 40 | 41 | } MOUHID_MONITOR_CONTEXT, *PMOUHID_MONITOR_CONTEXT; 42 | 43 | 44 | //============================================================================= 45 | // Module Globals 46 | //============================================================================= 47 | EXTERN_C static MOUHID_MONITOR_CONTEXT g_MhmContext = {}; 48 | 49 | 50 | //============================================================================= 51 | // Private Prototypes 52 | //============================================================================= 53 | EXTERN_C 54 | static 55 | MHK_HOOK_CALLBACK_ROUTINE 56 | MhmpHookCallback; 57 | 58 | EXTERN_C 59 | static 60 | MHK_NOTIFICATION_CALLBACK_ROUTINE 61 | MhmpNotificationCallback; 62 | 63 | 64 | //============================================================================= 65 | // Meta Interface 66 | //============================================================================= 67 | _Use_decl_annotations_ 68 | EXTERN_C 69 | NTSTATUS 70 | MhmDriverEntry() 71 | /*++ 72 | 73 | Routine Description: 74 | 75 | Initializes the MouHid Monitor module. 76 | 77 | Required Modules: 78 | 79 | None 80 | 81 | Remarks: 82 | 83 | If successful, the caller must call MhmDriverUnload when the driver is 84 | unloaded. 85 | 86 | --*/ 87 | { 88 | BOOLEAN fResourceInitialized = FALSE; 89 | NTSTATUS ntstatus = STATUS_SUCCESS; 90 | 91 | DBG_PRINT("Loading %s.\n", MODULE_TITLE); 92 | 93 | ntstatus = ExInitializeResourceLite(&g_MhmContext.Resource); 94 | if (!NT_SUCCESS(ntstatus)) 95 | { 96 | ERR_PRINT("ExInitializeResourceLite failed: 0x%X\n", ntstatus); 97 | goto exit; 98 | } 99 | // 100 | fResourceInitialized = TRUE; 101 | 102 | DBG_PRINT("%s loaded.\n", MODULE_TITLE); 103 | 104 | exit: 105 | if (!NT_SUCCESS(ntstatus)) 106 | { 107 | if (fResourceInitialized) 108 | { 109 | VERIFY(ExDeleteResourceLite(&g_MhmContext.Resource)); 110 | } 111 | } 112 | 113 | return ntstatus; 114 | } 115 | 116 | 117 | _Use_decl_annotations_ 118 | EXTERN_C 119 | VOID 120 | MhmDriverUnload() 121 | { 122 | DBG_PRINT("Unloading %s.\n", MODULE_TITLE); 123 | 124 | VERIFY(MhmDisableMouHidMonitor()); 125 | 126 | VERIFY(ExDeleteResourceLite(&g_MhmContext.Resource)); 127 | 128 | DBG_PRINT("%s unloaded.\n", MODULE_TITLE); 129 | } 130 | 131 | 132 | //============================================================================= 133 | // Public Interface 134 | //============================================================================= 135 | _Use_decl_annotations_ 136 | EXTERN_C 137 | NTSTATUS 138 | MhmQueryMouHidMonitor( 139 | PBOOLEAN pfEnabled 140 | ) 141 | { 142 | NTSTATUS ntstatus = STATUS_SUCCESS; 143 | 144 | // 145 | // Zero out parameters. 146 | // 147 | *pfEnabled = FALSE; 148 | 149 | ExEnterCriticalRegionAndAcquireResourceShared(&g_MhmContext.Resource); 150 | 151 | // 152 | // Set out parameters. 153 | // 154 | if (g_MhmContext.RegistrationHandle) 155 | { 156 | *pfEnabled = TRUE; 157 | } 158 | 159 | ExReleaseResourceAndLeaveCriticalRegion(&g_MhmContext.Resource); 160 | 161 | return ntstatus; 162 | } 163 | 164 | 165 | _Use_decl_annotations_ 166 | EXTERN_C 167 | NTSTATUS 168 | MhmEnableMouHidMonitor() 169 | /*++ 170 | 171 | Routine Description: 172 | 173 | Registers an MHK callback which logs mouse input data packets in the input 174 | packet stream. 175 | 176 | Remarks: 177 | 178 | If successful, the caller must disable the monitor by calling 179 | MhmDisableMouHidMonitor. 180 | 181 | --*/ 182 | { 183 | PHOOK_CALLBACK_CONTEXT pCallbackContext = NULL; 184 | HANDLE RegistrationHandle = NULL; 185 | NTSTATUS ntstatus = STATUS_SUCCESS; 186 | 187 | DBG_PRINT("Enabling %s.\n", MODULE_TITLE); 188 | 189 | ExEnterCriticalRegionAndAcquireResourceExclusive(&g_MhmContext.Resource); 190 | 191 | if (g_MhmContext.RegistrationHandle) 192 | { 193 | ERR_PRINT("%s is already enabled.\n", MODULE_TITLE); 194 | ntstatus = STATUS_ALREADY_REGISTERED; 195 | goto exit; 196 | } 197 | 198 | pCallbackContext = (PHOOK_CALLBACK_CONTEXT)ExAllocatePool( 199 | NonPagedPool, 200 | sizeof(*pCallbackContext)); 201 | if (!pCallbackContext) 202 | { 203 | ntstatus = STATUS_INSUFFICIENT_RESOURCES; 204 | goto exit; 205 | } 206 | // 207 | RtlSecureZeroMemory(pCallbackContext, sizeof(*pCallbackContext)); 208 | 209 | ntstatus = MhkRegisterCallbacks( 210 | MhmpHookCallback, 211 | MhmpNotificationCallback, 212 | pCallbackContext, 213 | &RegistrationHandle); 214 | if (!NT_SUCCESS(ntstatus)) 215 | { 216 | ERR_PRINT("MhkRegisterCallbacks failed: 0x%X\n", ntstatus); 217 | goto exit; 218 | } 219 | 220 | DBG_PRINT("%s enabled.\n", MODULE_TITLE); 221 | 222 | // 223 | // Update the global context. 224 | // 225 | g_MhmContext.RegistrationHandle = RegistrationHandle; 226 | g_MhmContext.CallbackContext = pCallbackContext; 227 | 228 | exit: 229 | if (!NT_SUCCESS(ntstatus)) 230 | { 231 | if (pCallbackContext) 232 | { 233 | ExFreePool(pCallbackContext); 234 | } 235 | } 236 | 237 | ExReleaseResourceAndLeaveCriticalRegion(&g_MhmContext.Resource); 238 | 239 | return ntstatus; 240 | } 241 | 242 | 243 | _Use_decl_annotations_ 244 | EXTERN_C 245 | NTSTATUS 246 | MhmDisableMouHidMonitor() 247 | { 248 | HANDLE RegistrationHandle = NULL; 249 | NTSTATUS ntstatus = STATUS_SUCCESS; 250 | 251 | DBG_PRINT("Disabling %s.\n", MODULE_TITLE); 252 | 253 | ExEnterCriticalRegionAndAcquireResourceExclusive(&g_MhmContext.Resource); 254 | 255 | RegistrationHandle = g_MhmContext.RegistrationHandle; 256 | if (!RegistrationHandle) 257 | { 258 | WRN_PRINT("%s is not enabled.\n", MODULE_TITLE); 259 | goto exit; 260 | } 261 | 262 | ntstatus = MhkUnregisterCallbacks(RegistrationHandle); 263 | if (!NT_SUCCESS(ntstatus)) 264 | { 265 | ERR_PRINT("MhkUnregisterCallbacks failed: 0x%X\n", ntstatus); 266 | goto exit; 267 | } 268 | 269 | ExFreePool(g_MhmContext.CallbackContext); 270 | 271 | // 272 | // Update the global context. 273 | // 274 | g_MhmContext.RegistrationHandle = NULL; 275 | g_MhmContext.CallbackContext = NULL; 276 | 277 | DBG_PRINT("%s disabled.\n", MODULE_TITLE); 278 | 279 | exit: 280 | ExReleaseResourceAndLeaveCriticalRegion(&g_MhmContext.Resource); 281 | 282 | return ntstatus; 283 | } 284 | 285 | 286 | //============================================================================= 287 | // Private Interface 288 | //============================================================================= 289 | _Use_decl_annotations_ 290 | EXTERN_C 291 | static 292 | VOID 293 | NTAPI 294 | MhmpHookCallback( 295 | PMOUSE_SERVICE_CALLBACK_ROUTINE pServiceCallbackOriginal, 296 | PDEVICE_OBJECT pClassDeviceObject, 297 | PMOUSE_INPUT_DATA pInputDataStart, 298 | PMOUSE_INPUT_DATA pInputDataEnd, 299 | PULONG pnInputDataConsumed, 300 | PVOID pContext 301 | ) 302 | { 303 | PHOOK_CALLBACK_CONTEXT pCallbackContext = NULL; 304 | PMOUSE_INPUT_DATA pInputPacket = NULL; 305 | ULONG64 PacketIndex = 0; 306 | 307 | pCallbackContext = (PHOOK_CALLBACK_CONTEXT)pContext; 308 | 309 | // 310 | // Log each packet in the input buffer. 311 | // 312 | for (pInputPacket = pInputDataStart; 313 | pInputPacket < pInputDataEnd; 314 | ++pInputPacket) 315 | { 316 | PacketIndex = InterlockedIncrement64(&pCallbackContext->PacketIndex); 317 | 318 | MclPrintInputPacket( 319 | PacketIndex, 320 | pServiceCallbackOriginal, 321 | pClassDeviceObject, 322 | pInputPacket); 323 | } 324 | 325 | // 326 | // Invoke the original service callback. 327 | // 328 | pServiceCallbackOriginal( 329 | pClassDeviceObject, 330 | pInputDataStart, 331 | pInputDataEnd, 332 | pnInputDataConsumed); 333 | } 334 | 335 | 336 | _Use_decl_annotations_ 337 | EXTERN_C 338 | static 339 | VOID 340 | NTAPI 341 | MhmpNotificationCallback( 342 | HANDLE RegistrationHandle, 343 | MOUSE_PNP_NOTIFICATION_EVENT Event, 344 | PVOID pContext 345 | ) 346 | { 347 | UNREFERENCED_PARAMETER(pContext); 348 | 349 | #if defined(DBG) 350 | if (MousePnpNotificationEventArrival == Event) 351 | { 352 | DBG_PRINT("Received MHK notification. (Arrival)\n"); 353 | } 354 | else if (MousePnpNotificationEventRemoval == Event) 355 | { 356 | DBG_PRINT("Received MHK notification. (Removal)\n"); 357 | } 358 | else 359 | { 360 | ERR_PRINT("Received MHK notification. (Unknown)\n"); 361 | DEBUG_BREAK; 362 | } 363 | #else 364 | UNREFERENCED_PARAMETER(Event); 365 | #endif 366 | 367 | ExEnterCriticalRegionAndAcquireResourceExclusive(&g_MhmContext.Resource); 368 | 369 | if (g_MhmContext.RegistrationHandle != RegistrationHandle) 370 | { 371 | ERR_PRINT("Unexpected registration handle: %p\n", RegistrationHandle); 372 | DEBUG_BREAK; 373 | goto exit; 374 | } 375 | 376 | // 377 | // The MouHid Hook Manager unregistered our MHK callbacks before it 378 | // invoked this notification routine. Release the callback context and 379 | // update the global context. 380 | // 381 | ExFreePool(g_MhmContext.CallbackContext); 382 | 383 | g_MhmContext.RegistrationHandle = NULL; 384 | g_MhmContext.CallbackContext = NULL; 385 | 386 | DBG_PRINT("%s disabled.\n", MODULE_TITLE); 387 | 388 | exit: 389 | ExReleaseResourceAndLeaveCriticalRegion(&g_MhmContext.Resource); 390 | } 391 | -------------------------------------------------------------------------------- /MouHidInputHook/mouclass.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #include "mouclass.h" 11 | 12 | #include 13 | 14 | #include "debug.h" 15 | #include "log.h" 16 | 17 | #if defined(DBG) 18 | #include "io_util.h" 19 | #include "nt.h" 20 | #endif 21 | 22 | 23 | //============================================================================= 24 | // Constants 25 | //============================================================================= 26 | #define MODULE_TITLE "MouClass Manager" 27 | 28 | 29 | //============================================================================= 30 | // Private Types 31 | //============================================================================= 32 | typedef struct _PNP_NOTIFICATION_CONTEXT 33 | { 34 | _Interlocked_ volatile POINTER_ALIGNMENT LONG64 ArrivalEvents; 35 | _Interlocked_ volatile POINTER_ALIGNMENT LONG64 RemovalEvents; 36 | 37 | } PNP_NOTIFICATION_CONTEXT, *PPNP_NOTIFICATION_CONTEXT; 38 | 39 | typedef struct _MCL_REGISTRATION_ENTRY 40 | { 41 | LIST_ENTRY ListEntry; 42 | PMOUSE_PNP_NOTIFICATION_CALLBACK_ROUTINE Callback; 43 | PVOID Context; 44 | 45 | } MCL_REGISTRATION_ENTRY, *PMCL_REGISTRATION_ENTRY; 46 | 47 | typedef struct _MOUCLASS_MANAGER 48 | { 49 | PVOID PnpNotificationHandle; 50 | PNP_NOTIFICATION_CONTEXT PnpNotificationContext; 51 | 52 | POINTER_ALIGNMENT ERESOURCE Resource; 53 | _Guarded_by_(Resource) LIST_ENTRY RegisteredCallbackListHead; 54 | 55 | } MOUCLASS_MANAGER, *PMOUCLASS_MANAGER; 56 | 57 | 58 | //============================================================================= 59 | // Module Globals 60 | //============================================================================= 61 | EXTERN_C static MOUCLASS_MANAGER g_MclManager = {}; 62 | 63 | 64 | //============================================================================= 65 | // Private Prototypes 66 | //============================================================================= 67 | _Requires_lock_not_held_(g_MclManager.Resource) 68 | EXTERN_C 69 | static 70 | DRIVER_NOTIFICATION_CALLBACK_ROUTINE 71 | MclpPnpNotificationCallbackRoutine; 72 | 73 | 74 | //============================================================================= 75 | // Meta Interface 76 | //============================================================================= 77 | _Use_decl_annotations_ 78 | EXTERN_C 79 | NTSTATUS 80 | MclDriverEntry( 81 | PDRIVER_OBJECT pDriverObject 82 | ) 83 | /*++ 84 | 85 | Routine Description: 86 | 87 | Initializes the MouClass Manager module. 88 | 89 | Parameters: 90 | 91 | pDriverObject - Pointer to the driver object for this driver. 92 | 93 | Required Modules: 94 | 95 | None 96 | 97 | Remarks: 98 | 99 | If successful, the caller must call MclDriverUnload when the driver is 100 | unloaded. 101 | 102 | --*/ 103 | { 104 | PVOID PnpNotificationHandle = NULL; 105 | BOOLEAN fPnpCallbackRegistered = FALSE; 106 | BOOLEAN fResourceInitialized = FALSE; 107 | NTSTATUS ntstatus = STATUS_SUCCESS; 108 | 109 | DBG_PRINT("Loading %s.\n", MODULE_TITLE); 110 | 111 | #if defined(DBG) 112 | VERIFY(MclPrintMouClassDeviceObjects()); 113 | #endif 114 | 115 | // 116 | // Register for mouse device interface notifications. 117 | // 118 | ntstatus = IoRegisterPlugPlayNotification( 119 | EventCategoryDeviceInterfaceChange, 120 | 0, 121 | (PVOID)&GUID_DEVINTERFACE_MOUSE, 122 | pDriverObject, 123 | MclpPnpNotificationCallbackRoutine, 124 | &g_MclManager.PnpNotificationContext, 125 | &PnpNotificationHandle); 126 | if (!NT_SUCCESS(ntstatus)) 127 | { 128 | ERR_PRINT("IoRegisterPlugPlayNotification failed: 0x%X\n", ntstatus); 129 | goto exit; 130 | } 131 | // 132 | fPnpCallbackRegistered = TRUE; 133 | 134 | ntstatus = ExInitializeResourceLite(&g_MclManager.Resource); 135 | if (!NT_SUCCESS(ntstatus)) 136 | { 137 | ERR_PRINT("ExInitializeResourceLite failed: 0x%X\n", ntstatus); 138 | goto exit; 139 | } 140 | // 141 | fResourceInitialized = TRUE; 142 | 143 | // 144 | // Initialize the global context. 145 | // 146 | g_MclManager.PnpNotificationHandle = PnpNotificationHandle; 147 | InitializeListHead(&g_MclManager.RegisteredCallbackListHead); 148 | 149 | DBG_PRINT("%s loaded.\n", MODULE_TITLE); 150 | 151 | exit: 152 | if (!NT_SUCCESS(ntstatus)) 153 | { 154 | if (fResourceInitialized) 155 | { 156 | VERIFY(ExDeleteResourceLite(&g_MclManager.Resource)); 157 | } 158 | 159 | if (fPnpCallbackRegistered) 160 | { 161 | VERIFY(IoUnregisterPlugPlayNotificationEx(PnpNotificationHandle)); 162 | } 163 | } 164 | 165 | return ntstatus; 166 | } 167 | 168 | 169 | _Use_decl_annotations_ 170 | EXTERN_C 171 | VOID 172 | MclDriverUnload() 173 | { 174 | DBG_PRINT("Unloading %s.\n", MODULE_TITLE); 175 | 176 | NT_ASSERT(IsListEmpty(&g_MclManager.RegisteredCallbackListHead)); 177 | 178 | VERIFY(IoUnregisterPlugPlayNotificationEx( 179 | g_MclManager.PnpNotificationHandle)); 180 | 181 | VERIFY(ExDeleteResourceLite(&g_MclManager.Resource)); 182 | 183 | DBG_PRINT("%s unloaded.\n", MODULE_TITLE); 184 | } 185 | 186 | 187 | //============================================================================= 188 | // Public Interface 189 | //============================================================================= 190 | _Use_decl_annotations_ 191 | EXTERN_C 192 | NTSTATUS 193 | MclRegisterMousePnpNotificationCallback( 194 | PMOUSE_PNP_NOTIFICATION_CALLBACK_ROUTINE pCallback, 195 | PVOID pContext, 196 | PHANDLE pRegistrationHandle 197 | ) 198 | /*++ 199 | 200 | Routine Description: 201 | 202 | Registers a mouse PnP notification callback. 203 | 204 | Parameters: 205 | 206 | pCallback - Pointer to the callback to be registered. 207 | 208 | pContext - Pointer to caller-defined data to be passed as the context 209 | parameter each time the callback is invoked. 210 | 211 | pRegistrationHandle - Returns an opaque registration handle to be used to 212 | unregister the callback. 213 | 214 | Remarks: 215 | 216 | If successful, the caller must unregister the callback by calling 217 | MclUnregisterMousePnpNotificationCallback. 218 | 219 | --*/ 220 | { 221 | PMCL_REGISTRATION_ENTRY pEntry = NULL; 222 | NTSTATUS ntstatus = STATUS_SUCCESS; 223 | 224 | // 225 | // Zero out parameters. 226 | // 227 | *pRegistrationHandle = NULL; 228 | 229 | // 230 | // Allocate and initialize a new registration entry. 231 | // 232 | pEntry = (PMCL_REGISTRATION_ENTRY)ExAllocatePool( 233 | NonPagedPool, 234 | sizeof(*pEntry)); 235 | if (!pEntry) 236 | { 237 | ntstatus = STATUS_INSUFFICIENT_RESOURCES; 238 | goto exit; 239 | } 240 | // 241 | RtlSecureZeroMemory(pEntry, sizeof(*pEntry)); 242 | 243 | pEntry->Callback = pCallback; 244 | pEntry->Context = pContext; 245 | 246 | // 247 | // Insert the new entry into the registered callback list. 248 | // 249 | ExEnterCriticalRegionAndAcquireResourceExclusive(&g_MclManager.Resource); 250 | 251 | InsertTailList( 252 | &g_MclManager.RegisteredCallbackListHead, 253 | &pEntry->ListEntry); 254 | 255 | ExReleaseResourceAndLeaveCriticalRegion(&g_MclManager.Resource); 256 | 257 | DBG_PRINT( 258 | "Registered mouse PnP notification callback." 259 | " (Callback = %p, Context = %p, RegistrationHandle = %p)\n", 260 | pCallback, 261 | pContext, 262 | pEntry); 263 | 264 | // 265 | // Set out parameters. 266 | // 267 | *pRegistrationHandle = (HANDLE)pEntry; 268 | 269 | exit: 270 | return ntstatus; 271 | } 272 | 273 | 274 | _Use_decl_annotations_ 275 | EXTERN_C 276 | VOID 277 | MclUnregisterMousePnpNotificationCallback( 278 | HANDLE RegistrationHandle 279 | ) 280 | { 281 | PMCL_REGISTRATION_ENTRY pEntry = NULL; 282 | 283 | pEntry = (PMCL_REGISTRATION_ENTRY)RegistrationHandle; 284 | 285 | ExEnterCriticalRegionAndAcquireResourceExclusive(&g_MclManager.Resource); 286 | 287 | RemoveEntryList(&pEntry->ListEntry); 288 | 289 | ExReleaseResourceAndLeaveCriticalRegion(&g_MclManager.Resource); 290 | 291 | ExFreePool(pEntry); 292 | 293 | DBG_PRINT( 294 | "Unregistered mouse PnP notification callback." 295 | " (RegistrationHandle = %p)\n", 296 | RegistrationHandle); 297 | } 298 | 299 | 300 | _Use_decl_annotations_ 301 | EXTERN_C 302 | VOID 303 | MclPrintInputPacket( 304 | ULONG64 PacketId, 305 | PMOUSE_SERVICE_CALLBACK_ROUTINE pServiceCallback, 306 | PDEVICE_OBJECT pDeviceObject, 307 | PMOUSE_INPUT_DATA pInputPacket 308 | ) 309 | { 310 | INF_PRINT( 311 | "Mouse Packet %I64u: SC=%p DO=%p ID=%hu IF=0x%03hX BF=0x%03hX" 312 | " BD=0x%04hX RB=0x%X EX=0x%X LX=%d LY=%d\n", 313 | PacketId, 314 | pServiceCallback, 315 | pDeviceObject, 316 | pInputPacket->UnitId, 317 | pInputPacket->Flags, 318 | pInputPacket->ButtonFlags, 319 | pInputPacket->ButtonData, 320 | pInputPacket->RawButtons, 321 | pInputPacket->ExtraInformation, 322 | pInputPacket->LastX, 323 | pInputPacket->LastY); 324 | } 325 | 326 | 327 | //============================================================================= 328 | // Private Interface 329 | //============================================================================= 330 | _Use_decl_annotations_ 331 | EXTERN_C 332 | static 333 | NTSTATUS 334 | MclpPnpNotificationCallbackRoutine( 335 | PVOID pNotificationStructure, 336 | PVOID pNotificationContext 337 | ) 338 | { 339 | PDEVICE_INTERFACE_CHANGE_NOTIFICATION pNotification = NULL; 340 | PPNP_NOTIFICATION_CONTEXT pContext = NULL; 341 | MOUSE_PNP_NOTIFICATION_EVENT Event = {}; 342 | ULONG64 nEvents = 0; 343 | BOOLEAN fResourceAcquired = FALSE; 344 | PLIST_ENTRY pListEntry = NULL; 345 | PMCL_REGISTRATION_ENTRY pEntry = NULL; 346 | NTSTATUS ntstatus = STATUS_SUCCESS; 347 | 348 | pNotification = 349 | (PDEVICE_INTERFACE_CHANGE_NOTIFICATION)pNotificationStructure; 350 | pContext = (PPNP_NOTIFICATION_CONTEXT)pNotificationContext; 351 | 352 | if (IsEqualGUID(pNotification->Event, GUID_DEVICE_INTERFACE_ARRIVAL)) 353 | { 354 | Event = MousePnpNotificationEventArrival; 355 | 356 | nEvents = InterlockedIncrement64(&pContext->ArrivalEvents); 357 | 358 | DBG_PRINT("Device interface arrival (%I64u): %wZ\n", 359 | nEvents, 360 | pNotification->SymbolicLinkName); 361 | } 362 | else if (IsEqualGUID(pNotification->Event, GUID_DEVICE_INTERFACE_REMOVAL)) 363 | { 364 | Event = MousePnpNotificationEventRemoval; 365 | 366 | nEvents = InterlockedIncrement64(&pContext->RemovalEvents); 367 | 368 | DBG_PRINT("Device interface removal (%I64u): %wZ\n", 369 | nEvents, 370 | pNotification->SymbolicLinkName); 371 | } 372 | else 373 | { 374 | ERR_PRINT( 375 | "Unexpected device interface change GUID." 376 | " (SymbolicLinkName = %wZ)\n", 377 | pNotification->SymbolicLinkName); 378 | DEBUG_BREAK; 379 | goto exit; 380 | } 381 | 382 | // 383 | // Invoke each registered callback. 384 | // 385 | ExEnterCriticalRegionAndAcquireResourceShared(&g_MclManager.Resource); 386 | fResourceAcquired = TRUE; 387 | 388 | for (pListEntry = g_MclManager.RegisteredCallbackListHead.Flink; 389 | pListEntry != &g_MclManager.RegisteredCallbackListHead; 390 | pListEntry = pListEntry->Flink) 391 | { 392 | pEntry = CONTAINING_RECORD( 393 | pListEntry, 394 | MCL_REGISTRATION_ENTRY, 395 | ListEntry); 396 | 397 | pEntry->Callback(Event, pEntry->Context); 398 | } 399 | 400 | exit: 401 | if (fResourceAcquired) 402 | { 403 | ExReleaseResourceAndLeaveCriticalRegion(&g_MclManager.Resource); 404 | } 405 | 406 | return ntstatus; 407 | } 408 | 409 | 410 | #if defined(DBG) 411 | _IRQL_requires_(PASSIVE_LEVEL) 412 | _IRQL_requires_same_ 413 | _Check_return_ 414 | EXTERN_C 415 | NTSTATUS 416 | MclPrintMouClassDeviceObjects() 417 | { 418 | UNICODE_STRING usDriverObject = {}; 419 | PDRIVER_OBJECT pDriverObject = NULL; 420 | BOOLEAN fHasDriverObjectReference = FALSE; 421 | PDEVICE_OBJECT* ppDeviceObjectList = NULL; 422 | ULONG nDeviceObjectList = 0; 423 | NTSTATUS ntstatus = STATUS_SUCCESS; 424 | 425 | usDriverObject = RTL_CONSTANT_STRING(MOUCLASS_DRIVER_OBJECT_PATH_U); 426 | 427 | ntstatus = ObReferenceObjectByName( 428 | &usDriverObject, 429 | OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 430 | NULL, 431 | 0, 432 | *IoDriverObjectType, 433 | KernelMode, 434 | NULL, 435 | (PVOID*)&pDriverObject); 436 | if (!NT_SUCCESS(ntstatus)) 437 | { 438 | ERR_PRINT("ObReferenceObjectByName failed: 0x%X\n", ntstatus); 439 | goto exit; 440 | } 441 | // 442 | fHasDriverObjectReference = TRUE; 443 | 444 | // 445 | // Get a snapshot of all the device objects. 446 | // 447 | ntstatus = IouEnumerateDeviceObjectList( 448 | pDriverObject, 449 | &ppDeviceObjectList, 450 | &nDeviceObjectList); 451 | if (!NT_SUCCESS(ntstatus)) 452 | { 453 | ERR_PRINT("IouEnumerateDeviceObjectList failed: 0x%X\n", ntstatus); 454 | goto exit; 455 | } 456 | 457 | IouPrintDeviceObjectList( 458 | MOUCLASS_DRIVER_OBJECT_PATH_U, 459 | ppDeviceObjectList, 460 | nDeviceObjectList); 461 | 462 | exit: 463 | if (ppDeviceObjectList) 464 | { 465 | IouFreeDeviceObjectList(ppDeviceObjectList, nDeviceObjectList); 466 | } 467 | 468 | if (fHasDriverObjectReference) 469 | { 470 | ObDereferenceObject(pDriverObject); 471 | } 472 | 473 | return ntstatus; 474 | } 475 | #endif 476 | -------------------------------------------------------------------------------- /MouHidInputHook/mouhid.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #include "mouhid.h" 11 | 12 | #include 13 | 14 | #include "io_util.h" 15 | #include "log.h" 16 | #include "nt.h" 17 | #include "pe.h" 18 | 19 | 20 | //============================================================================= 21 | // Constants 22 | //============================================================================= 23 | #define MODULE_TITLE "MouHid Context" 24 | 25 | // 26 | // We choose a search limit by adding a small delta to the actual connect data 27 | // offset. This delta accounts for the field offset changing in future 28 | // versions of the MouHid driver. 29 | // 30 | // The actual offset can be found in the IOCTL_INTERNAL_MOUSE_CONNECT case of 31 | // the MouHid IRP_MJ_INTERNAL_DEVICE_CONTROL handler, mouhid!MouHid_IOCTL: 32 | // 33 | // Platform: Windows 7 x64 34 | // File: mouhid.sys 35 | // Version: 6.1.7600.16385 (win7_rtm.090713-1255) 36 | // Ioctl Code: 0xF0203 (IOCTL_INTERNAL_MOUSE_CONNECT) 37 | // 38 | // Annotated Assembly: 39 | // 40 | // mov rax, [rsi+_IO_STACK_LOCATION.Parameters.DeviceIoControl.Type3InputBuffer] 41 | // movdqu xmm0, xmmword ptr [rax+CONNECT_DATA.ClassDeviceObject] 42 | // movdqu xmmword ptr [r12+MOUHID_DEVICE_EXTENSION.ConnectData_B8.ClassDeviceObject], xmm0 43 | // 44 | // Unannotated Assembly: 45 | // 46 | // mov rax, [rsi+20h] 47 | // movdqu xmm0, xmmword ptr [rax] 48 | // movdqu xmmword ptr [r12+0B8h], xmm0 49 | // 50 | #define DEVICE_EXTENSION_SEARCH_SIZE 0x100 51 | 52 | 53 | //============================================================================= 54 | // Private Types 55 | //============================================================================= 56 | typedef struct _MOUHID_CONTEXT 57 | { 58 | // 59 | // The field offset, in bytes, of the CONNECT_DATA object in the device 60 | // extension of a MouHid device object. 61 | // 62 | SIZE_T ConnectDataFieldOffset; 63 | 64 | } MOUHID_CONTEXT, *PMOUHID_CONTEXT; 65 | 66 | 67 | //============================================================================= 68 | // Module Globals 69 | //============================================================================= 70 | EXTERN_C static MOUHID_CONTEXT g_MhdContext = {}; 71 | 72 | 73 | //============================================================================= 74 | // Private Prototypes 75 | //============================================================================= 76 | _IRQL_requires_(PASSIVE_LEVEL) 77 | _IRQL_requires_same_ 78 | _Check_return_ 79 | EXTERN_C 80 | static 81 | NTSTATUS 82 | MhdpResolveConnectDataFieldOffsetForDevice( 83 | _In_ PDEVICE_OBJECT pDeviceObject, 84 | _Out_ PSIZE_T pcbConnectDataFieldOffset 85 | ); 86 | 87 | _IRQL_requires_(PASSIVE_LEVEL) 88 | _IRQL_requires_same_ 89 | _Check_return_ 90 | EXTERN_C 91 | static 92 | NTSTATUS 93 | MhdpResolveConnectDataFieldOffset( 94 | _Out_ PSIZE_T pcbConnectDataFieldOffset 95 | ); 96 | 97 | 98 | //============================================================================= 99 | // Meta Interface 100 | //============================================================================= 101 | _Use_decl_annotations_ 102 | EXTERN_C 103 | NTSTATUS 104 | MhdDriverEntry() 105 | /*++ 106 | 107 | Routine Description: 108 | 109 | Initializes the MouHid Context module. 110 | 111 | Required Modules: 112 | 113 | None 114 | 115 | --*/ 116 | { 117 | SIZE_T cbConnectDataFieldOffset = 0; 118 | NTSTATUS ntstatus = STATUS_SUCCESS; 119 | 120 | DBG_PRINT("Loading %s.\n", MODULE_TITLE); 121 | 122 | ntstatus = MhdpResolveConnectDataFieldOffset(&cbConnectDataFieldOffset); 123 | if (!NT_SUCCESS(ntstatus)) 124 | { 125 | ERR_PRINT("MhdpResolveConnectDataFieldOffset failed: 0x%X\n", 126 | ntstatus); 127 | goto exit; 128 | } 129 | 130 | // 131 | // Initialize the global context. 132 | // 133 | g_MhdContext.ConnectDataFieldOffset = cbConnectDataFieldOffset; 134 | 135 | DBG_PRINT("%s loaded:\n", MODULE_TITLE); 136 | DBG_PRINT(" ConnectDataFieldOffset: 0x%IX\n", 137 | g_MhdContext.ConnectDataFieldOffset); 138 | 139 | exit: 140 | return ntstatus; 141 | } 142 | 143 | 144 | //============================================================================= 145 | // Public Interface 146 | //============================================================================= 147 | _Use_decl_annotations_ 148 | EXTERN_C 149 | SIZE_T 150 | MhdGetConnectDataFieldOffset() 151 | { 152 | return g_MhdContext.ConnectDataFieldOffset; 153 | } 154 | 155 | 156 | //============================================================================= 157 | // Private Interface 158 | //============================================================================= 159 | _Use_decl_annotations_ 160 | EXTERN_C 161 | static 162 | NTSTATUS 163 | MhdpResolveConnectDataFieldOffsetForDevice( 164 | PDEVICE_OBJECT pDeviceObject, 165 | PSIZE_T pcbConnectDataFieldOffset 166 | ) 167 | /*++ 168 | 169 | Routine Description: 170 | 171 | Dynamically resolves the field offset of the CONNECT_DATA object in the 172 | device extension of the specified MouHid device object. 173 | 174 | Parameters: 175 | 176 | pDeviceObject - Referenced pointer to a MouHid device object. 177 | 178 | pcbConnectDataFieldOffset - Returns the field offset of the CONNECT_DATA 179 | for the specified MouHid device object. 180 | 181 | Remarks: 182 | 183 | This routine utilizes knowledge of the MouClass communication protocol as a 184 | heuristic for resolving the CONNECT_DATA field offset. 185 | 186 | Heuristic: 187 | 188 | 1. Obtain a list of all the MouHid device objects. 189 | 190 | 2. For each MouHid device object: 191 | 192 | 2.a Get the device object attached to the MouHid device object. 193 | 194 | 2.b Get the address range of every executable image section in the 195 | driver of the attached device object. 196 | 197 | 2.c Search the device extension of the MouHid device object for a 198 | valid CONNECT_DATA object by interpreting each pointer-aligned 199 | address as a CONNECT_DATA candidate. A candidate is valid if it 200 | meets the following criteria: 201 | 202 | i. The 'ClassDeviceObject' field matches the attached 203 | device object. 204 | 205 | ii. The 'ClassService' field points to an address contained 206 | in one of the executable image sections from (2.b). 207 | 208 | NOTE This heuristic may be applicable to other types of mouse device 209 | stacks, e.g., PS/2. 210 | 211 | --*/ 212 | { 213 | PDEVICE_OBJECT pAttachedDevice = NULL; 214 | PVOID pDriverStart = NULL; 215 | ULONG_PTR ImageBase = 0; 216 | PIMAGE_SECTION_HEADER* ppExecutableSections = NULL; 217 | ULONG nExecutableSections = 0; 218 | PVOID pDeviceExtension = NULL; 219 | SIZE_T Span = 0; 220 | ULONG_PTR SearchEnd = 0; 221 | PCONNECT_DATA pConnectData = NULL; 222 | ULONG i = 0; 223 | ULONG_PTR SectionBase = 0; 224 | BOOLEAN fClassServiceValidated = FALSE; 225 | SIZE_T cbConnectDataFieldOffset = 0; 226 | NTSTATUS ntstatus = STATUS_SUCCESS; 227 | 228 | // 229 | // Zero out parameters. 230 | // 231 | *pcbConnectDataFieldOffset = 0; 232 | 233 | DBG_PRINT( 234 | "Resolving MouHid connect data field offset for device." 235 | " (DeviceObject = %p)\n", 236 | pDeviceObject); 237 | 238 | // 239 | // Get a referenced pointer to the device object attached to the current 240 | // MouHid device object. 241 | // 242 | pAttachedDevice = IouGetUpperDeviceObject(pDeviceObject); 243 | if (!pAttachedDevice) 244 | { 245 | ERR_PRINT("Unexpected AttachedDevice. (DeviceObject = %p)\n", 246 | pDeviceObject); 247 | ntstatus = STATUS_UNSUCCESSFUL; 248 | goto exit; 249 | } 250 | 251 | // 252 | // Get the executable image sections for the driver of the attached device. 253 | // 254 | pDriverStart = pAttachedDevice->DriverObject->DriverStart; 255 | if (!pDriverStart) 256 | { 257 | ERR_PRINT("Unexpected DriverStart. (DriverObject = %p)\n", 258 | pAttachedDevice->DriverObject); 259 | ntstatus = STATUS_UNSUCCESSFUL; 260 | goto exit; 261 | } 262 | 263 | if (!RtlPcToFileHeader(pDriverStart, (PVOID*)&ImageBase)) 264 | { 265 | ERR_PRINT("RtlPcToFileHeader failed. (PcValue = %p)\n", pDriverStart); 266 | ntstatus = STATUS_UNSUCCESSFUL; 267 | goto exit; 268 | } 269 | 270 | ntstatus = PeGetExecutableSections( 271 | ImageBase, 272 | &ppExecutableSections, 273 | &nExecutableSections); 274 | if (!NT_SUCCESS(ntstatus)) 275 | { 276 | ERR_PRINT("PeGetExecutableSections failed: 0x%X\n", ntstatus); 277 | goto exit; 278 | } 279 | 280 | pDeviceExtension = pDeviceObject->DeviceExtension; 281 | 282 | // 283 | // Clamp the search range. 284 | // 285 | Span = ADDRESS_AND_SIZE_TO_SPAN_PAGES( 286 | pDeviceExtension, 287 | DEVICE_EXTENSION_SEARCH_SIZE); 288 | if (1 == Span) 289 | { 290 | SearchEnd = 291 | (ULONG_PTR)pDeviceExtension + DEVICE_EXTENSION_SEARCH_SIZE; 292 | } 293 | else 294 | { 295 | SearchEnd = (ULONG_PTR)( 296 | PAGE_ALIGN((ULONG_PTR)pDeviceExtension + PAGE_SIZE)); 297 | } 298 | 299 | // 300 | // Search the device extension for a valid connect data object. 301 | // 302 | for (pConnectData = (PCONNECT_DATA)pDeviceExtension; 303 | (ULONG_PTR)pConnectData + sizeof(*pConnectData) <= SearchEnd; 304 | pConnectData = 305 | OFFSET_POINTER(pConnectData, sizeof(ULONG_PTR), CONNECT_DATA)) 306 | { 307 | // 308 | // Filter invalid ClassDeviceObject values. 309 | // 310 | if (pConnectData->ClassDeviceObject != pAttachedDevice) 311 | { 312 | continue; 313 | } 314 | 315 | // 316 | // Filter ClassService values which do not point to an address inside 317 | // an executable image section in the driver of the attached device 318 | // object. 319 | // 320 | for (i = 0; i < nExecutableSections; ++i) 321 | { 322 | SectionBase = ImageBase + ppExecutableSections[i]->VirtualAddress; 323 | 324 | if ((ULONG_PTR)pConnectData->ClassService >= SectionBase && 325 | (ULONG_PTR)pConnectData->ClassService < 326 | SectionBase + ppExecutableSections[i]->Misc.VirtualSize) 327 | { 328 | fClassServiceValidated = TRUE; 329 | break; 330 | } 331 | } 332 | // 333 | if (!fClassServiceValidated) 334 | { 335 | continue; 336 | } 337 | 338 | // 339 | // If we found multiple matches then our heuristic is flawed. 340 | // 341 | if (cbConnectDataFieldOffset) 342 | { 343 | ERR_PRINT("Found multiple field offset candidates.\n"); 344 | ntstatus = STATUS_INTERNAL_ERROR; 345 | goto exit; 346 | } 347 | 348 | cbConnectDataFieldOffset = 349 | POINTER_OFFSET(pConnectData, pDeviceExtension); 350 | } 351 | // 352 | if (!cbConnectDataFieldOffset) 353 | { 354 | ERR_PRINT( 355 | "Failed to resolve connect data field offset for device." 356 | " (DeviceObject = %p)\n", 357 | pDeviceObject); 358 | ntstatus = STATUS_UNSUCCESSFUL; 359 | goto exit; 360 | } 361 | 362 | // 363 | // Set out parameters. 364 | // 365 | *pcbConnectDataFieldOffset = cbConnectDataFieldOffset; 366 | 367 | exit: 368 | if (ppExecutableSections) 369 | { 370 | ExFreePool(ppExecutableSections); 371 | } 372 | 373 | if (pAttachedDevice) 374 | { 375 | ObDereferenceObject(pAttachedDevice); 376 | } 377 | 378 | return ntstatus; 379 | } 380 | 381 | 382 | _Use_decl_annotations_ 383 | EXTERN_C 384 | static 385 | NTSTATUS 386 | MhdpResolveConnectDataFieldOffset( 387 | PSIZE_T pcbConnectDataFieldOffset 388 | ) 389 | { 390 | UNICODE_STRING usMouHidDriverObject = {}; 391 | PDRIVER_OBJECT pMouHidDriverObject = NULL; 392 | BOOLEAN fHasDriverObjectReference = FALSE; 393 | PDEVICE_OBJECT* ppMouHidDeviceObjectList = NULL; 394 | ULONG nMouHidDeviceObjectList = 0; 395 | ULONG i = 0; 396 | SIZE_T cbFieldOffset = 0; 397 | SIZE_T cbFieldOffsetCandidate = 0; 398 | NTSTATUS ntstatus = STATUS_SUCCESS; 399 | 400 | // 401 | // Zero out parameters. 402 | // 403 | *pcbConnectDataFieldOffset = 0; 404 | 405 | DBG_PRINT("Resolving MouHid connect data field offset.\n"); 406 | 407 | // 408 | // Open the MouHid driver object. 409 | // 410 | usMouHidDriverObject = RTL_CONSTANT_STRING(MOUHID_DRIVER_OBJECT_PATH_U); 411 | 412 | ntstatus = ObReferenceObjectByName( 413 | &usMouHidDriverObject, 414 | OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 415 | NULL, 416 | 0, 417 | *IoDriverObjectType, 418 | KernelMode, 419 | NULL, 420 | (PVOID*)&pMouHidDriverObject); 421 | if (!NT_SUCCESS(ntstatus)) 422 | { 423 | ERR_PRINT("ObReferenceObjectByName failed: 0x%X\n", ntstatus); 424 | goto exit; 425 | } 426 | // 427 | fHasDriverObjectReference = TRUE; 428 | 429 | // 430 | // Get a snapshot of all the MouHid device objects. 431 | // 432 | ntstatus = IouEnumerateDeviceObjectList( 433 | pMouHidDriverObject, 434 | &ppMouHidDeviceObjectList, 435 | &nMouHidDeviceObjectList); 436 | if (!NT_SUCCESS(ntstatus)) 437 | { 438 | ERR_PRINT("IouEnumerateDeviceObjectList failed: 0x%X\n", ntstatus); 439 | goto exit; 440 | } 441 | 442 | #if defined(DBG) 443 | IouPrintDeviceObjectList( 444 | MOUHID_DRIVER_OBJECT_PATH_U, 445 | ppMouHidDeviceObjectList, 446 | nMouHidDeviceObjectList); 447 | #endif 448 | 449 | // 450 | // Apply the connect data heuristic to every MouHid device object to verify 451 | // that each device object yields the same offset. 452 | // 453 | for (i = 0; i < nMouHidDeviceObjectList; ++i) 454 | { 455 | ntstatus = MhdpResolveConnectDataFieldOffsetForDevice( 456 | ppMouHidDeviceObjectList[i], 457 | &cbFieldOffset); 458 | if (!NT_SUCCESS(ntstatus)) 459 | { 460 | ERR_PRINT( 461 | "MhdpResolveConnectDataFieldOffsetForDevice failed: 0x%X\n", 462 | ntstatus); 463 | goto exit; 464 | } 465 | 466 | if (cbFieldOffsetCandidate) 467 | { 468 | if (cbFieldOffsetCandidate != cbFieldOffset) 469 | { 470 | ERR_PRINT("Found multiple field offset candidates.\n"); 471 | ntstatus = STATUS_INTERNAL_ERROR; 472 | goto exit; 473 | } 474 | } 475 | else 476 | { 477 | cbFieldOffsetCandidate = cbFieldOffset; 478 | } 479 | } 480 | // 481 | if (!cbFieldOffsetCandidate) 482 | { 483 | ERR_PRINT("Failed to resolve connect data field offset.\n"); 484 | ntstatus = STATUS_UNSUCCESSFUL; 485 | goto exit; 486 | } 487 | 488 | // 489 | // Set out parameters. 490 | // 491 | *pcbConnectDataFieldOffset = cbFieldOffsetCandidate; 492 | 493 | exit: 494 | if (ppMouHidDeviceObjectList) 495 | { 496 | IouFreeDeviceObjectList( 497 | ppMouHidDeviceObjectList, 498 | nMouHidDeviceObjectList); 499 | } 500 | 501 | if (fHasDriverObjectReference) 502 | { 503 | ObDereferenceObject(pMouHidDriverObject); 504 | } 505 | 506 | return ntstatus; 507 | } 508 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MouHidInputHook 2 | 3 | MouHidInputHook enables users to filter, modify, and inject mouse input data packets into the input data stream of HID USB mouse devices without modifying the mouse device stacks. 4 | 5 | The [MouHid Hook Manager](./MouHidInputHook/mouhid_hook_manager.cpp) emulates the hook strategy used by the [Moufiltr](https://github.com/microsoft/Windows-driver-samples/tree/master/input/moufiltr) driver by hooking the [CONNECT_DATA](https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/kbdmou/ns-kbdmou-_connect_data "CONNECT_DATA structure") object inside each MouHid device object. The MouHid driver uses the **ClassService** field of a CONNECT_DATA object to transfer mouse input data packets to mouse class data queues. By emulating this strategy, we have access to each data packet generated by the mouse device without needing to install a mouse filter driver. This emulation technique has the following benefits: 6 | 7 | 1. We can safely unhook mouse device stacks and unload the MouHidInputHook driver without needing to unload the hooked device stacks. A standard filter driver for a PnP device can only be unloaded after all of the device objects attached to it are destroyed. 8 | 9 | 2. The technique is PatchGuard safe. 10 | 11 | 3. The technique is relatively stealthy from the perspective of a kernel anti-cheat because: 12 | 13 | 1. The field offset of the CONNECT_DATA object inside a MouHid device object is not defined in a public header. 14 | 15 | 2. We do not modify the HID USB mouse device stack(s) by attaching a filter device object. 16 | 17 | A kernel anti-cheat must resort to heuristics or other potentially unsafe vectors in order to detect the presence of this technique. This is generally impractical because an anti-cheat driver must be reliable, i.e., avoid using undocumented information, in order to support the player base of the protected video game. 18 | 19 | This project uses a heuristic to resolve the CONNECT_DATA field offset during driver initialization (described below). 20 | 21 | The MouHid Hook Manager supports PnP events by registering a PnP notification callback for mouse device interface changes. This callback is invoked each time a MouClass device object is added to or removed from the system. 22 | 23 | ## MouHid Monitor 24 | 25 | The [MouHid Monitor](./MouHidInputHook/mouhid_monitor.cpp) is an example hook callback which logs the content of each data packet generated by a HID USB mouse. Users can utilize this feature to debug how mouse actions, e.g., moving the mouse or clicking a mouse button, are represented as a sequence of one or more data packets. 26 | 27 | For example, we can determine the sequence of data packets required to emulate a left mouse button click action by loading the driver, running the client, and manually clicking the left mouse button. The following items are log output excerpts after performing this action on a local machine and in a virtual machine: 28 | 29 | The column data elements refer to CONNECT_DATA and MOUSE_INPUT_DATA fields: 30 | 31 | SC: Class service callback invoked to transfer the packet. CONNECT_DATA.ClassService 32 | DO: Device object which contains the target data queue. CONNECT_DATA.ClassDeviceObject 33 | ID: Target device unit id, e.g., '\Device\PointerClassX'. MOUSE_INPUT_DATA.UnitId 34 | IF: Indicator flags. MOUSE_INPUT_DATA.Flags 35 | BF: Transition state of the mouse buttons. MOUSE_INPUT_DATA.ButtonFlags 36 | BD: Wheel data for 'SCROLL' action packets. MOUSE_INPUT_DATA.ButtonData 37 | RB: Raw button state. MOUSE_INPUT_DATA.RawButtons 38 | EX: Device-specific data. MOUSE_INPUT_DATA.ExtraInformation 39 | LX: Signed relative or absolute motion in X direction. MOUSE_INPUT_DATA.LastX 40 | LY: Signed relative or absolute motion in Y direction. MOUSE_INPUT_DATA.LastY 41 | 42 | ================================= OUTPUT ================================== 43 | 44 | Platform: Windows 7 SP1 x64 45 | Environment: Local machine 46 | Mouse: Logitech G303 47 | Action: Hold mouse in the air, click left mouse button, put mouse on surface, move the mouse 48 | 49 | Packet 63: SC=FFFFF880040A8858 DO=FFFFFA800FDE78B0 ID=1 IF=0x000 BF=0x000 BD=0 RB=0 EX=0 LX=0 LY=-1 50 | D ->Packet 64: SC=FFFFF880040A8858 DO=FFFFFA800FDE78B0 ID=1 IF=0x000 BF=0x001 BD=0 RB=0 EX=0 LX=0 LY=0 51 | U ->Packet 65: SC=FFFFF880040A8858 DO=FFFFFA800FDE78B0 ID=1 IF=0x000 BF=0x002 BD=0 RB=0 EX=0 LX=0 LY=0 52 | Packet 66: SC=FFFFF880040A8858 DO=FFFFFA800FDE78B0 ID=1 IF=0x000 BF=0x000 BD=0 RB=0 EX=0 LX=0 LY=1 53 | Packet 67: SC=FFFFF880040A8858 DO=FFFFFA800FDE78B0 ID=1 IF=0x000 BF=0x000 BD=0 RB=0 EX=0 LX=0 LY=1 54 | 55 | Platform: Windows 7 SP1 x64 56 | Environment: VMware virtual machine, vmusbmouse.sys mouse filter driver active 57 | Mouse: Logitech G303 58 | Action: Hold mouse in the air, click left mouse button, put mouse on surface, move the mouse 59 | 60 | Packet 75: SC=FFFFF880025630C0 DO=FFFFFA80040F6A40 ID=1 IF=0x003 BF=0x000 BD=0 RB=0 EX=0 LX=12580 LY=25436 61 | D ->Packet 76: SC=FFFFF88003FE5858 DO=FFFFFA8004106CF0 ID=2 IF=0x000 BF=0x001 BD=0 RB=0 EX=0 LX=0 LY=0 62 | Packet 77: SC=FFFFF880025630C0 DO=FFFFFA80040F6A40 ID=1 IF=0x003 BF=0x000 BD=0 RB=0 EX=0 LX=12580 LY=25436 63 | U ->Packet 78: SC=FFFFF88003FE5858 DO=FFFFFA8004106CF0 ID=2 IF=0x000 BF=0x002 BD=0 RB=0 EX=0 LX=0 LY=0 64 | Packet 79: SC=FFFFF880025630C0 DO=FFFFFA80040F6A40 ID=1 IF=0x003 BF=0x000 BD=0 RB=0 EX=0 LX=12580 LY=25436 65 | Packet 80: SC=FFFFF880025630C0 DO=FFFFFA80040F6A40 ID=1 IF=0x003 BF=0x000 BD=0 RB=0 EX=0 LX=12528 LY=25436 66 | Packet 81: SC=FFFFF880025630C0 DO=FFFFFA80040F6A40 ID=1 IF=0x003 BF=0x000 BD=0 RB=0 EX=0 LX=12474 LY=25544 67 | Packet 82: SC=FFFFF880025630C0 DO=FFFFFA80040F6A40 ID=1 IF=0x003 BF=0x000 BD=0 RB=0 EX=0 LX=12422 LY=25544 68 | 69 | Note: The above output was modified to increase readability. 70 | 71 | The data packet for the left mouse button down action is indicated by **D ->**, and the data packet for the left mouse button up action is indicated by **U ->**. 72 | 73 | We can infer the following rules for these environments: 74 | 75 | 1. The local machine environment uses **MOUSE_MOVE_RELATIVE** for mouse movement actions, and the virtual machine environment uses **MOUSE_MOVE_ABSOLUTE**. 76 | 77 | 2. The local machine environment contains one HID USB mouse device stack, and the virtual machine environment contains two HID USB mouse device stacks. The virtual machine environment uses different (ClassDeviceObject, ClassService) pairs for button action packets and movement action packets. Each pair is represented by a CONNECT_DATA object inside a MouHid device object. Therefore, there must be two MouHid device objects and two device stacks. 78 | 79 | 3. The virtual machine environment contains multiple unit ids. This implies that there are two named mouse class device objects: **\Device\PointerClass1** and **\Device\PointerClass2**. There must be two class data queues: one for button action packets and one for movement action packets. Therefore, a data packet should never contain both button data and movement data. Conversely, data packets in the local machine environment can contain both data types because there is one class data queue. 80 | 81 | 4. The virtual machine contains a third party filter driver, vmusbmouse, which generates a movement data packet between a button-down packet and its corresponding button-up packet. This may be related to the mouse smoothing feature of VMware Tools. 82 | 83 | ### MouClassInputInjection 84 | 85 | The [MouClassInputInjection](https://github.com/changeofpace/MouClassInputInjection) project is an application of the knowledge acquired from using the **MouHid Monitor**. This project uses a MouHidInputHook hook callback to dynamically resolve the packet data rules for the HID USB mouse device stacks on the host machine. These rules are used to synthesize and inject valid data packets into the input data stream. 86 | 87 | ## Projects 88 | 89 | ### MouHidInputHook 90 | 91 | The core driver project which implements the hook interface. 92 | 93 | ### MouHidMonitor 94 | 95 | A command line **MouHidInputHook** client which enables the MouHid Monitor. 96 | 97 | ## Input Processing Internals 98 | 99 | ### Input Class Drivers 100 | 101 | The input class drivers, kbdclass.sys and mouclass.sys, allow hardware-independent operation of input devices by enforcing a non-standard communication protocol between device objects in an input device stack. This protocol divides the device stack into two substacks: the hardware-independent upper stack and the hardware-dependent lower stack. The lower stack transfers input data from a physical device to the upper stack via the [class service callback](https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/kbdmou/nc-kbdmou-pservice_callback_routine "PSERVICE_CALLBACK_ROUTINE callback function"). The class service callback ensures that the upper stack always receives input data in a normalized format. 102 | 103 | ### Class Service Callback 104 | 105 | The class service callback for an input device stack is established by the input class driver's **AddDevice** routine. This routine performs the following actions: 106 | 107 | 1. Creates an upper-level class filter device object. 108 | 2. Attaches the new device object to the input device stack. 109 | 3. Initializes a [CONNECT_DATA](https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/kbdmou/ns-kbdmou-_connect_data "CONNECT_DATA structure") object for this class device object. 110 | 4. Sends the connect data object inside a connect-device request down the device stack. 111 | 112 | The device object whose driver completes this request is the top-level device object in the lower substack. This driver stores the connect data object inside the device extension of the corresponding device object. 113 | 114 | ### Class Data Queue 115 | 116 | A class device object contains a circular buffer of input data packets in its device extension. This buffer, referred to as the class data queue, is effectively a producer/consumer queue. 117 | 118 | The lower substack produces data packets by invoking the class service callback when new input data is available. The class service callback copies the input data from the input buffer maintained by the lower substack device to the class data queue. 119 | 120 | The win32k subsystem consumes data packets by reading the input device via nt!ZwReadFile. ZwReadFile issues an irp to the top of the input device stack which is ultimately handled by the IRP_MJ_READ handler of the input class driver. This handler copies data packets from the class data queue to the irp's system buffer. 121 | 122 | ### Input Processing 123 | 124 | The following diagram depicts the input processing system for a HID USB mouse device on Windows 7 SP1 x64. 125 | 126 |

127 | 128 |

129 | 130 | #### Upper Substack 131 | 132 | 1. The upper substack read cycle begins in win32k!StartDeviceRead. This routine issues a read request for a mouse device by invoking nt!ZwReadFile: 133 | 134 | ```C++ 135 | ZwReadFile( 136 | MouseDeviceHandle, // Handle to the mouse device to read packets from 137 | // (referred to as the 'target mouse device') 138 | NULL, 139 | win32k!InputApc, // Apc routine executed when the read is completed 140 | MouseDeviceInfo, // Pointer to the DEVICEINFO object for the target 141 | // mouse device (see win32k!gpDeviceInfoList) 142 | IoStatusBlock, 143 | Buffer, // MOUSE_INPUT_DATA buffer inside MouseDeviceInfo 144 | Length, 145 | &win32k!gZero, 146 | 0); 147 | ``` 148 | 149 | 2. nt!ZwReadFile sends an IRP_MJ_READ irp to the top of the target mouse device stack. 150 | 151 | 3. The irp is ultimately processed by the MouClass IRP_MJ_READ handler, mouclass!MouseClassRead. This routine validates the irp then invokes mouclass!MouseClassHandleRead. 152 | 153 | If the class data queue of the target mouse device object contains new input data packets then mouclass!MouseClassHandleRead invokes mouclass!MouseClassReadCopyData. This routine copies the new data packets from the class data queue to the irp's system buffer. 154 | 155 | If the class data queue does not contain new input data packets then: 156 | 157 | 1. The irp is appended to a linked list in the device extension of the target mouse device object (referred to as the pending irp list). 158 | 159 | 2. STATUS_PENDING is returned to nt!NtReadFile. 160 | 161 | 4. The win32k!InputApc routine is invoked when the irp is completed. This routine invokes win32k!ProcessMouseInput via a function pointer in the DEVICE_TEMPLATE object for the mouse device type in the DEVICE_TEMPLATE array, win32k!aDeviceTemplate. win32k!ProcessMouseInput applies movement data from the data packets to the user desktop and queues each packet to win32k!gMouseEventQueue. The raw input thread processes this queue inside win32k!RawInputThread. Finally, win32k!InputApc restarts the read cycle by invoking win32k!StartDeviceRead. 162 | 163 | #### Lower Substack 164 | 165 | 1. The lower substack read cycle begins in mouhid!MouHid_StartRead. This routine initializes (reuses) an IRP_MJ_READ irp, sets the completion routine to mouhid!MouHid_ReadComplete, and then sends it to the next lower device object in the device stack, a HidUsb device object, via nt!IofCallDriver. The irp is routed to the IRP_MJ_READ handler defined in the HidUsb driver object, HIDCLASS!HidpMajorHandler. 166 | 167 | 2. HIDCLASS!HidpMajorHandler invokes HIDCLASS!HidpIrpMajorRead. The irp routing and processing beyond this point is outside the scope of this analysis. 168 | 169 | 3. USBPORT!USBPORT_Core_iCompleteDoneTransfer invokes nt!IopfCompleteRequest to complete the irp after new input data is read from the physical device. The irp's completion routine, mouhid!MouHid_ReadComplete, is invoked. This routine converts the input data from its hardware-dependent format, HID report, to the hardware-independent format, [MOUSE_INPUT_DATA](https://docs.microsoft.com/en-us/windows/win32/api/ntddmou/ns-ntddmou-mouse_input_data "MOUSE_INPUT_DATA structure") packet. The converted packets are stored in the device extension of the MouHid device object associated with the completed irp. 170 | 171 | 4. mouhid!MouHid_ReadComplete invokes the [mouse class service callback](https://docs.microsoft.com/en-us/previous-versions/ff542394%28v%3dvs.85%29 "MouseClassServiceCallback"), mouclass!MouseClassServiceCallback, via the ClassService field of the CONNECT_DATA object in the device extension of the MouHid device object associated with the completed irp: 172 | 173 | ```C++ 174 | PMOUHID_DEVICE_EXTENSION DeviceExtension = MouHidDeviceObject->DeviceExtension; 175 | PCONNECT_DATA ConnectData = &DeviceExtension->ConnectData; 176 | ULONG InputDataConsumed = 0; 177 | 178 | ((MOUSE_SERVICE_CALLBACK_ROUTINE)ConnectData->ClassService)( 179 | ConnectData->ClassDeviceObject, 180 | DeviceExtension->InputDataStart, 181 | DeviceExtension->InputDataEnd, 182 | &InputDataConsumed); 183 | ``` 184 | 185 | mouclass!MouseClassServiceCallback uses data packets from the **InputDataStart** buffer to complete each irp in the pending irp list of the class device object. The remaining input data packets in the InputDataStart buffer are copied to the class data queue of the class device object. Finally, each serviced irp is completed via nt!IofCompleteRequest. This action is directly connected to item [4] in the **Upper Substack** description above. 186 | 187 | 5. mouhid!MouHid_ReadComplete restarts the read cycle by invoking mouhid!MouHid_StartRead. The **Hook Point** indicates where the MouHid Hook Manager installs the class service hook. 188 | 189 | ## Connect Data Heuristic 190 | 191 | The [MouHid](./MouHidInputHook/mouhid.cpp) module uses a heuristic to dynamically resolve the **CONNECT_DATA** field inside the MouHid device extension. This heuristic is based on the MouClass initialiation protocol so it may be applicable to other mouse device types. i.e., This heuristic can potentially be used for any mouse device stack which uses the MouClass driver. The following is a summary of the heuristic: 192 | 193 | 1. Obtain a list of all the MouHid device objects. 194 | 195 | 2. For each MouHid device object: 196 | 197 | 1. Get the device object attached to the MouHid device object. 198 | 199 | 2. Get the address range of every executable image section in the driver of 200 | the attached device object. 201 | 202 | 3. Search the device extension of the MouHid device object for a valid 203 | CONNECT_DATA object by interpreting each pointer-aligned address as a 204 | CONNECT_DATA candidate. A candidate is valid if it meets the following 205 | criteria: 206 | 207 | 1. The **ClassDeviceObject** field matches the attached device object. 208 | 209 | 2. The **ClassService** field points to an address contained in one of 210 | the executable image sections from (ii). 211 | 212 | ## Notes 213 | 214 | * The MouHidInputHook project was developed for Windows 7 SP1 x64. Support for other platforms is unknown. 215 | * The MouHidInputHook hook technique is PatchGuard safe. 216 | -------------------------------------------------------------------------------- /MouHidInputHook/mouhid_hook_manager.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2019 changeofpace. All rights reserved. 4 | 5 | Use of this source code is governed by the MIT license. See the 'LICENSE' file 6 | for more information. 7 | 8 | --*/ 9 | 10 | #include "mouhid_hook_manager.h" 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "debug.h" 17 | #include "io_util.h" 18 | #include "log.h" 19 | #include "nt.h" 20 | #include "mouclass.h" 21 | #include "mouhid.h" 22 | #include "object_util.h" 23 | 24 | #include "../Common/time.h" 25 | 26 | 27 | //============================================================================= 28 | // Constants 29 | //============================================================================= 30 | #define MODULE_TITLE "MouHid Hook Manager" 31 | 32 | #define UNHOOK_DELAY_INTERVAL_MS 250 33 | 34 | 35 | //============================================================================= 36 | // Private Types 37 | //============================================================================= 38 | typedef struct _MOUHID_DEVICE_OBJECT 39 | { 40 | // 41 | // Referenced device object pointer for this MouHid device. 42 | // 43 | PDEVICE_OBJECT DeviceObject; 44 | 45 | // 46 | // Pointer to the connect data object in the device extension of 47 | // 'DeviceObject'. 48 | // 49 | POINTER_ALIGNMENT PCONNECT_DATA ConnectData; 50 | 51 | PMOUSE_SERVICE_CALLBACK_ROUTINE ServiceCallbackOriginal; 52 | 53 | } MOUHID_DEVICE_OBJECT, *PMOUHID_DEVICE_OBJECT; 54 | 55 | /*++ 56 | 57 | Type Name: 58 | 59 | MOUHID_HOOK_CONTEXT 60 | 61 | Type Description: 62 | 63 | Contains the array of hooked MouHid device objects for a mouse device 64 | stack. 65 | 66 | --*/ 67 | typedef struct _MOUHID_HOOK_CONTEXT 68 | { 69 | PMOUSE_SERVICE_CALLBACK_ROUTINE ServiceCallbackHook; 70 | 71 | ULONG NumberOfDeviceObjects; 72 | MOUHID_DEVICE_OBJECT DeviceObjectArray[ANYSIZE_ARRAY]; 73 | 74 | } MOUHID_HOOK_CONTEXT, *PMOUHID_HOOK_CONTEXT; 75 | 76 | typedef struct _MHK_REGISTRATION_ENTRY 77 | { 78 | PMHK_HOOK_CALLBACK_ROUTINE HookCallback; 79 | PMHK_NOTIFICATION_CALLBACK_ROUTINE NotificationCallback; 80 | PVOID Context; 81 | 82 | } MHK_REGISTRATION_ENTRY, *PMHK_REGISTRATION_ENTRY; 83 | 84 | typedef struct _MOUHID_HOOK_MANAGER 85 | { 86 | HANDLE MousePnpNotificationHandle; 87 | 88 | POINTER_ALIGNMENT ERESOURCE Resource; 89 | 90 | _Guarded_by_(Resource) BOOLEAN HookActive; 91 | _Guarded_by_(Resource) PMOUHID_HOOK_CONTEXT HookContext; 92 | 93 | _Guarded_by_(Resource) PMHK_REGISTRATION_ENTRY RegistrationEntry; 94 | 95 | } MOUHID_HOOK_MANAGER, *PMOUHID_HOOK_MANAGER; 96 | 97 | 98 | //============================================================================= 99 | // Module Globals 100 | //============================================================================= 101 | EXTERN_C static MOUHID_HOOK_MANAGER g_MhkManager = {}; 102 | 103 | 104 | //============================================================================= 105 | // Private Prototypes 106 | //============================================================================= 107 | _Requires_lock_not_held_(g_MhkManager.Resource) 108 | EXTERN_C 109 | static 110 | MOUSE_PNP_NOTIFICATION_CALLBACK_ROUTINE 111 | MhkpMousePnpNotificationCallbackRoutine; 112 | 113 | _Requires_exclusive_lock_held_(g_MhkManager.Resource) 114 | _IRQL_requires_(PASSIVE_LEVEL) 115 | _IRQL_requires_same_ 116 | _Check_return_ 117 | EXTERN_C 118 | static 119 | NTSTATUS 120 | MhkpUnregisterCallbacks( 121 | _Inout_ HANDLE RegistrationHandle 122 | ); 123 | 124 | _IRQL_requires_(PASSIVE_LEVEL) 125 | _IRQL_requires_same_ 126 | _Check_return_ 127 | EXTERN_C 128 | static 129 | NTSTATUS 130 | MhkpCreateHookContext( 131 | _In_ PMOUSE_SERVICE_CALLBACK_ROUTINE pServiceCallbackHook, 132 | _Outptr_result_nullonfailure_ PMOUHID_HOOK_CONTEXT* ppHookContext 133 | ); 134 | 135 | _IRQL_requires_(PASSIVE_LEVEL) 136 | _IRQL_requires_same_ 137 | _Check_return_ 138 | EXTERN_C 139 | static 140 | VOID 141 | MhkpFreeHookContext( 142 | _Pre_notnull_ __drv_freesMem(Mem) PMOUHID_HOOK_CONTEXT pHookContext 143 | ); 144 | 145 | #if defined(DBG) 146 | _IRQL_requires_(HIGH_LEVEL) 147 | _IRQL_requires_same_ 148 | EXTERN_C 149 | static 150 | VOID 151 | MhkpPrintHookContext( 152 | _In_ PMOUHID_HOOK_CONTEXT pHookContext 153 | ); 154 | #endif 155 | 156 | _IRQL_requires_(PASSIVE_LEVEL) 157 | _IRQL_requires_same_ 158 | EXTERN_C 159 | static 160 | VOID 161 | MhkpInstallConnectDataHooks( 162 | _Inout_ PMOUHID_HOOK_CONTEXT pHookContext 163 | ); 164 | 165 | _Requires_exclusive_lock_held_(g_MhkManager.Resource) 166 | _IRQL_requires_(PASSIVE_LEVEL) 167 | _IRQL_requires_same_ 168 | _Check_return_ 169 | EXTERN_C 170 | static 171 | NTSTATUS 172 | MhkpHookMouHidDeviceObjects( 173 | _In_ PMOUSE_SERVICE_CALLBACK_ROUTINE pServiceCallbackHook 174 | ); 175 | 176 | _IRQL_requires_(PASSIVE_LEVEL) 177 | _IRQL_requires_same_ 178 | EXTERN_C 179 | static 180 | VOID 181 | MhkpUninstallConnectDataHooks( 182 | _Inout_ PMOUHID_HOOK_CONTEXT pHookContext 183 | ); 184 | 185 | _Requires_exclusive_lock_held_(g_MhkManager.Resource) 186 | _IRQL_requires_(PASSIVE_LEVEL) 187 | _IRQL_requires_same_ 188 | EXTERN_C 189 | static 190 | VOID 191 | MhkpUnhookMouHidDeviceObjects(); 192 | 193 | EXTERN_C 194 | static 195 | MOUSE_SERVICE_CALLBACK_ROUTINE 196 | MhkpServiceCallbackHook; 197 | 198 | 199 | //============================================================================= 200 | // Meta Interface 201 | //============================================================================= 202 | _Use_decl_annotations_ 203 | EXTERN_C 204 | NTSTATUS 205 | MhkDriverEntry() 206 | /*++ 207 | 208 | Routine Description: 209 | 210 | Initializes the MouHid Hook Manager module. 211 | 212 | Required Modules: 213 | 214 | MouClass Manager 215 | 216 | Remarks: 217 | 218 | If successful, the caller must call MhkDriverUnload when the driver is 219 | unloaded. 220 | 221 | --*/ 222 | { 223 | BOOLEAN fResourceInitialized = FALSE; 224 | HANDLE MousePnpNotificationHandle = NULL; 225 | BOOLEAN fCallbackRegistered = FALSE; 226 | NTSTATUS ntstatus = STATUS_SUCCESS; 227 | 228 | DBG_PRINT("Loading %s.\n", MODULE_TITLE); 229 | 230 | // 231 | // NOTE We must initialize the resource before registering the mouse 232 | // notification callback because the notification callback uses the 233 | // resource. 234 | // 235 | ntstatus = ExInitializeResourceLite(&g_MhkManager.Resource); 236 | if (!NT_SUCCESS(ntstatus)) 237 | { 238 | ERR_PRINT("ExInitializeResourceLite failed: 0x%X\n", ntstatus); 239 | goto exit; 240 | } 241 | // 242 | fResourceInitialized = TRUE; 243 | 244 | ntstatus = MclRegisterMousePnpNotificationCallback( 245 | MhkpMousePnpNotificationCallbackRoutine, 246 | NULL, 247 | &MousePnpNotificationHandle); 248 | if (!NT_SUCCESS(ntstatus)) 249 | { 250 | ERR_PRINT("MclRegisterMousePnpNotificationCallback failed: 0x%X\n", 251 | ntstatus); 252 | goto exit; 253 | } 254 | // 255 | fCallbackRegistered = TRUE; 256 | 257 | // 258 | // Initialize the global context. 259 | // 260 | g_MhkManager.MousePnpNotificationHandle = MousePnpNotificationHandle; 261 | 262 | DBG_PRINT("%s loaded.\n", MODULE_TITLE); 263 | 264 | exit: 265 | if (!NT_SUCCESS(ntstatus)) 266 | { 267 | if (fCallbackRegistered) 268 | { 269 | MclUnregisterMousePnpNotificationCallback( 270 | MousePnpNotificationHandle); 271 | } 272 | 273 | if (fResourceInitialized) 274 | { 275 | VERIFY(ExDeleteResourceLite(&g_MhkManager.Resource)); 276 | } 277 | } 278 | 279 | return ntstatus; 280 | } 281 | 282 | 283 | _Use_decl_annotations_ 284 | EXTERN_C 285 | VOID 286 | MhkDriverUnload() 287 | { 288 | DBG_PRINT("Unloading %s.\n", MODULE_TITLE); 289 | 290 | NT_ASSERT(!g_MhkManager.HookActive); 291 | NT_ASSERT(!g_MhkManager.HookContext); 292 | NT_ASSERT(!g_MhkManager.RegistrationEntry); 293 | 294 | MclUnregisterMousePnpNotificationCallback( 295 | g_MhkManager.MousePnpNotificationHandle); 296 | 297 | VERIFY(ExDeleteResourceLite(&g_MhkManager.Resource)); 298 | 299 | DBG_PRINT("%s unloaded.\n", MODULE_TITLE); 300 | } 301 | 302 | 303 | //============================================================================= 304 | // Public Interface 305 | //============================================================================= 306 | _Use_decl_annotations_ 307 | EXTERN_C 308 | NTSTATUS 309 | MhkRegisterCallbacks( 310 | PMHK_HOOK_CALLBACK_ROUTINE pHookCallback, 311 | PMHK_NOTIFICATION_CALLBACK_ROUTINE pNotificationCallback, 312 | PVOID pContext, 313 | PHANDLE pRegistrationHandle 314 | ) 315 | /*++ 316 | 317 | Routine Description: 318 | 319 | Registers an MHK hook callback and an optional MHK notification callback. 320 | If registration succeeds then each MouHid device object is hooked. 321 | 322 | Parameters: 323 | 324 | pHookCallback - Pointer to the MHK hook callback to be registered. 325 | 326 | pNotificationCallback - Pointer to the MHK notification callback to be 327 | registered. 328 | 329 | pContext - Pointer to caller-defined data to be passed as the context 330 | parameter each time the callbacks are invoked. 331 | 332 | pRegistrationHandle - Returns an opaque registration handle to be used to 333 | unregister the callbacks. 334 | 335 | Remarks: 336 | 337 | If successful, the caller must unregister the callbacks by calling 338 | MhkUnregisterCallbacks. 339 | 340 | NOTE This routine modifies the device extension of every MouHid device 341 | object. 342 | 343 | --*/ 344 | { 345 | PMHK_REGISTRATION_ENTRY pEntry = NULL; 346 | NTSTATUS ntstatus = STATUS_SUCCESS; 347 | 348 | // 349 | // Zero out parameters. 350 | // 351 | *pRegistrationHandle = NULL; 352 | 353 | DBG_PRINT( 354 | "Registering MHK callbacks." 355 | " (HookCallback = %p, NotificationCallback = %p, Context = %p)\n", 356 | pHookCallback, 357 | pNotificationCallback, 358 | pContext); 359 | 360 | ExEnterCriticalRegionAndAcquireResourceExclusive(&g_MhkManager.Resource); 361 | 362 | // 363 | // Fail if a registration entry already exists. 364 | // 365 | if (g_MhkManager.RegistrationEntry) 366 | { 367 | ERR_PRINT("Registration limit reached.\n"); 368 | ntstatus = STATUS_IMPLEMENTATION_LIMIT; 369 | goto exit; 370 | } 371 | 372 | // 373 | // Allocate and initialize the new registration entry. 374 | // 375 | pEntry = (PMHK_REGISTRATION_ENTRY)ExAllocatePool( 376 | NonPagedPool, 377 | sizeof(*pEntry)); 378 | if (!pEntry) 379 | { 380 | ntstatus = STATUS_INSUFFICIENT_RESOURCES; 381 | goto exit; 382 | } 383 | // 384 | RtlSecureZeroMemory(pEntry, sizeof(*pEntry)); 385 | 386 | pEntry->HookCallback = pHookCallback; 387 | pEntry->NotificationCallback = pNotificationCallback; 388 | pEntry->Context = pContext; 389 | 390 | // 391 | // Update the global context. 392 | // 393 | g_MhkManager.RegistrationEntry = pEntry; 394 | 395 | if (!g_MhkManager.HookActive) 396 | { 397 | ntstatus = MhkpHookMouHidDeviceObjects(MhkpServiceCallbackHook); 398 | if (!NT_SUCCESS(ntstatus)) 399 | { 400 | ERR_PRINT("MhkpHookMouHidDeviceObjects failed: 0x%X\n", ntstatus); 401 | goto exit; 402 | } 403 | } 404 | 405 | DBG_PRINT("MHK callbacks registered. (RegistrationHandle = %p)\n", pEntry); 406 | 407 | // 408 | // Set out parameters. 409 | // 410 | *pRegistrationHandle = (HANDLE)pEntry; 411 | 412 | exit: 413 | if (!NT_SUCCESS(ntstatus)) 414 | { 415 | if (g_MhkManager.RegistrationEntry) 416 | { 417 | g_MhkManager.RegistrationEntry = NULL; 418 | } 419 | 420 | if (pEntry) 421 | { 422 | ExFreePool(pEntry); 423 | } 424 | } 425 | 426 | ExReleaseResourceAndLeaveCriticalRegion(&g_MhkManager.Resource); 427 | 428 | return ntstatus; 429 | } 430 | 431 | 432 | _Use_decl_annotations_ 433 | EXTERN_C 434 | NTSTATUS 435 | MhkUnregisterCallbacks( 436 | HANDLE RegistrationHandle 437 | ) 438 | { 439 | NTSTATUS ntstatus = STATUS_SUCCESS; 440 | 441 | ExEnterCriticalRegionAndAcquireResourceExclusive(&g_MhkManager.Resource); 442 | 443 | ntstatus = MhkpUnregisterCallbacks(RegistrationHandle); 444 | if (!NT_SUCCESS(ntstatus)) 445 | { 446 | ERR_PRINT("MhkpUnregisterCallbacks failed: 0x%X\n", ntstatus); 447 | goto exit; 448 | } 449 | 450 | exit: 451 | ExReleaseResourceAndLeaveCriticalRegion(&g_MhkManager.Resource); 452 | 453 | return ntstatus; 454 | } 455 | 456 | 457 | //============================================================================= 458 | // Private Interface 459 | //============================================================================= 460 | _Use_decl_annotations_ 461 | EXTERN_C 462 | static 463 | VOID 464 | MhkpMousePnpNotificationCallbackRoutine( 465 | MOUSE_PNP_NOTIFICATION_EVENT Event, 466 | PVOID pContext 467 | ) 468 | { 469 | PMHK_REGISTRATION_ENTRY pEntry = NULL; 470 | HANDLE RegistrationHandle = NULL; 471 | PMHK_NOTIFICATION_CALLBACK_ROUTINE pRegisteredNotificationCallback = NULL; 472 | PVOID pRegisteredCallbackContext = NULL; 473 | 474 | UNREFERENCED_PARAMETER(pContext); 475 | 476 | #if defined(DBG) 477 | if (MousePnpNotificationEventArrival == Event) 478 | { 479 | DBG_PRINT("Received mouse PnP notification. (Arrival)\n"); 480 | } 481 | else if (MousePnpNotificationEventRemoval == Event) 482 | { 483 | DBG_PRINT("Received mouse PnP notification. (Removal)\n"); 484 | } 485 | else 486 | { 487 | ERR_PRINT("Received mouse PnP notification. (Unknown)\n"); 488 | DEBUG_BREAK; 489 | } 490 | #else 491 | UNREFERENCED_PARAMETER(Event); 492 | #endif 493 | 494 | ExEnterCriticalRegionAndAcquireResourceExclusive(&g_MhkManager.Resource); 495 | 496 | pEntry = g_MhkManager.RegistrationEntry; 497 | if (pEntry) 498 | { 499 | // 500 | // Store the registration entry information because it will be reset 501 | // during the unregister call below. 502 | // 503 | RegistrationHandle = (HANDLE)pEntry; 504 | pRegisteredNotificationCallback = pEntry->NotificationCallback; 505 | pRegisteredCallbackContext = pEntry->Context; 506 | 507 | VERIFY(MhkpUnregisterCallbacks((HANDLE)pEntry)); 508 | } 509 | 510 | ExReleaseResourceAndLeaveCriticalRegion(&g_MhkManager.Resource); 511 | 512 | // 513 | // Invoke the notification callback after releasing the resource so that 514 | // the callback cannot deadlock the system by invoking a public MHK 515 | // function. 516 | // 517 | // WARNING This strategy currently enables a race condition where the 518 | // registrant attempts to unregister their MHK callbacks before their MHK 519 | // notification callback is invoked below. This unregister attempt will 520 | // fail because we previously unregistered the MHK callbacks. 521 | // 522 | if (pRegisteredNotificationCallback) 523 | { 524 | DBG_PRINT( 525 | "Invoking MHK notification callback. (RegistrationHandle = %p)\n", 526 | RegistrationHandle); 527 | 528 | // 529 | // NOTE 'RegistrationHandle' now points to freed memory. 530 | // 531 | pRegisteredNotificationCallback( 532 | RegistrationHandle, 533 | Event, 534 | pRegisteredCallbackContext); 535 | } 536 | } 537 | 538 | 539 | _Use_decl_annotations_ 540 | EXTERN_C 541 | static 542 | NTSTATUS 543 | MhkpUnregisterCallbacks( 544 | HANDLE RegistrationHandle 545 | ) 546 | { 547 | PMHK_REGISTRATION_ENTRY pEntry = NULL; 548 | NTSTATUS ntstatus = STATUS_SUCCESS; 549 | 550 | DBG_PRINT("Unregistering MHK callbacks. (RegistrationHandle = %p)\n", 551 | RegistrationHandle); 552 | 553 | pEntry = (PMHK_REGISTRATION_ENTRY)RegistrationHandle; 554 | 555 | // 556 | // Fail if the specified handle does not match the active registration 557 | // entry. 558 | // 559 | if (pEntry != g_MhkManager.RegistrationEntry) 560 | { 561 | ERR_PRINT("Invalid registration handle: %p\n", RegistrationHandle); 562 | ntstatus = STATUS_INVALID_PARAMETER; 563 | goto exit; 564 | } 565 | 566 | if (g_MhkManager.HookActive) 567 | { 568 | MhkpUnhookMouHidDeviceObjects(); 569 | } 570 | 571 | ExFreePool(g_MhkManager.RegistrationEntry); 572 | 573 | // 574 | // Update the global context. 575 | // 576 | g_MhkManager.RegistrationEntry = NULL; 577 | 578 | DBG_PRINT("MHK callbacks unregistered.\n"); 579 | 580 | exit: 581 | return ntstatus; 582 | } 583 | 584 | 585 | _Use_decl_annotations_ 586 | EXTERN_C 587 | static 588 | NTSTATUS 589 | MhkpCreateHookContext( 590 | PMOUSE_SERVICE_CALLBACK_ROUTINE pServiceCallbackHook, 591 | PMOUHID_HOOK_CONTEXT* ppHookContext 592 | ) 593 | { 594 | SIZE_T cbConnectDataFieldOffset = 0; 595 | UNICODE_STRING usDriverObject = {}; 596 | PDRIVER_OBJECT pDriverObject = NULL; 597 | BOOLEAN fHasDriverObjectReference = FALSE; 598 | PDEVICE_OBJECT* ppDeviceObjectList = NULL; 599 | ULONG nDeviceObjectList = 0; 600 | SIZE_T cbHookContext = 0; 601 | PMOUHID_HOOK_CONTEXT pHookContext = NULL; 602 | ULONG i = 0; 603 | PDEVICE_OBJECT pDeviceObject = NULL; 604 | PMOUHID_DEVICE_OBJECT pElement = NULL; 605 | NTSTATUS ntstatus = STATUS_SUCCESS; 606 | 607 | // 608 | // Zero out parameters. 609 | // 610 | *ppHookContext = NULL; 611 | 612 | DBG_PRINT("Creating MouHid hook context.\n"); 613 | 614 | cbConnectDataFieldOffset = MhdGetConnectDataFieldOffset(); 615 | if (!cbConnectDataFieldOffset) 616 | { 617 | ERR_PRINT("MhdGetConnectDataFieldOffset failed.\n"); 618 | ntstatus = STATUS_INTERNAL_ERROR; 619 | goto exit; 620 | } 621 | 622 | // 623 | // Open the MouHid driver object. 624 | // 625 | usDriverObject = RTL_CONSTANT_STRING(MOUHID_DRIVER_OBJECT_PATH_U); 626 | 627 | ntstatus = ObReferenceObjectByName( 628 | &usDriverObject, 629 | OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 630 | NULL, 631 | 0, 632 | *IoDriverObjectType, 633 | KernelMode, 634 | NULL, 635 | (PVOID*)&pDriverObject); 636 | if (!NT_SUCCESS(ntstatus)) 637 | { 638 | ERR_PRINT("ObReferenceObjectByName failed: 0x%X\n", ntstatus); 639 | goto exit; 640 | } 641 | // 642 | fHasDriverObjectReference = TRUE; 643 | 644 | // 645 | // Get a snapshot of all the MouHid device objects. 646 | // 647 | ntstatus = IouEnumerateDeviceObjectList( 648 | pDriverObject, 649 | &ppDeviceObjectList, 650 | &nDeviceObjectList); 651 | if (!NT_SUCCESS(ntstatus)) 652 | { 653 | ERR_PRINT("IouEnumerateDeviceObjectList failed: 0x%X\n", ntstatus); 654 | goto exit; 655 | } 656 | 657 | #if defined(DBG) 658 | IouPrintDeviceObjectList( 659 | MOUHID_DRIVER_OBJECT_PATH_U, 660 | ppDeviceObjectList, 661 | nDeviceObjectList); 662 | #endif 663 | 664 | // 665 | // Allocate and initialize the new hook context. 666 | // 667 | cbHookContext = UFIELD_OFFSET( 668 | MOUHID_HOOK_CONTEXT, 669 | DeviceObjectArray[nDeviceObjectList]); 670 | 671 | pHookContext = (PMOUHID_HOOK_CONTEXT)ExAllocatePool( 672 | NonPagedPool, 673 | cbHookContext); 674 | if (!pHookContext) 675 | { 676 | ntstatus = STATUS_INSUFFICIENT_RESOURCES; 677 | goto exit; 678 | } 679 | // 680 | RtlSecureZeroMemory(pHookContext, cbHookContext); 681 | 682 | pHookContext->ServiceCallbackHook = pServiceCallbackHook; 683 | pHookContext->NumberOfDeviceObjects = nDeviceObjectList; 684 | 685 | for (i = 0; i < nDeviceObjectList; ++i) 686 | { 687 | pDeviceObject = ppDeviceObjectList[i]; 688 | 689 | // 690 | // We reference each MouHid device object to ensure that the memory 691 | // backing their device extensions remains valid even if the MouHid 692 | // driver is unloaded. This allows us to safely uninstall connect data 693 | // hooks from 'stale' MouHid device objects. 694 | // 695 | ObReferenceObject(pDeviceObject); 696 | 697 | pElement = &pHookContext->DeviceObjectArray[i]; 698 | 699 | pElement->DeviceObject = pDeviceObject; 700 | 701 | pElement->ConnectData = OFFSET_POINTER( 702 | pDeviceObject->DeviceExtension, 703 | cbConnectDataFieldOffset, 704 | CONNECT_DATA); 705 | } 706 | 707 | // 708 | // Set out parameters. 709 | // 710 | *ppHookContext = pHookContext; 711 | 712 | exit: 713 | if (!NT_SUCCESS(ntstatus)) 714 | { 715 | if (pHookContext) 716 | { 717 | ExFreePool(pHookContext); 718 | } 719 | } 720 | 721 | if (ppDeviceObjectList) 722 | { 723 | IouFreeDeviceObjectList(ppDeviceObjectList, nDeviceObjectList); 724 | } 725 | 726 | if (fHasDriverObjectReference) 727 | { 728 | ObDereferenceObject(pDriverObject); 729 | } 730 | 731 | return ntstatus; 732 | } 733 | 734 | 735 | _Use_decl_annotations_ 736 | EXTERN_C 737 | static 738 | VOID 739 | MhkpFreeHookContext( 740 | PMOUHID_HOOK_CONTEXT pHookContext 741 | ) 742 | { 743 | ULONG i = 0; 744 | 745 | for (i = 0; i < pHookContext->NumberOfDeviceObjects; ++i) 746 | { 747 | ObDereferenceObject(pHookContext->DeviceObjectArray[i].DeviceObject); 748 | } 749 | 750 | ExFreePool(pHookContext); 751 | } 752 | 753 | 754 | #if defined(DBG) 755 | _Use_decl_annotations_ 756 | EXTERN_C 757 | static 758 | VOID 759 | MhkpPrintHookContext( 760 | PMOUHID_HOOK_CONTEXT pHookContext 761 | ) 762 | { 763 | ULONG i = 0; 764 | PMOUHID_DEVICE_OBJECT pElement = NULL; 765 | 766 | DBG_PRINT("MouHid Hook Context:\n"); 767 | 768 | for (i = 0; i < pHookContext->NumberOfDeviceObjects; ++i) 769 | { 770 | pElement = &pHookContext->DeviceObjectArray[i]; 771 | 772 | DBG_PRINT("%u.\n", i); 773 | DBG_PRINT(" DeviceObject: %p\n", pElement->DeviceObject); 774 | DBG_PRINT(" DeviceExtension: %p\n", 775 | pElement->DeviceObject->DeviceExtension); 776 | DBG_PRINT(" ConnectData: %p\n", pElement->ConnectData); 777 | DBG_PRINT(" ClassDeviceObject: %p\n", 778 | pElement->ConnectData->ClassDeviceObject); 779 | DBG_PRINT(" ClassService: %p\n", 780 | pElement->ConnectData->ClassService); 781 | } 782 | } 783 | #endif 784 | 785 | 786 | _Use_decl_annotations_ 787 | EXTERN_C 788 | static 789 | VOID 790 | MhkpInstallConnectDataHooks( 791 | PMOUHID_HOOK_CONTEXT pHookContext 792 | ) 793 | { 794 | ULONG i = 0; 795 | PMOUHID_DEVICE_OBJECT pElement = NULL; 796 | 797 | DBG_PRINT("Installing connect data hooks:\n"); 798 | 799 | for (i = 0; i < pHookContext->NumberOfDeviceObjects; ++i) 800 | { 801 | pElement = &pHookContext->DeviceObjectArray[i]; 802 | 803 | pElement->ServiceCallbackOriginal = 804 | (PMOUSE_SERVICE_CALLBACK_ROUTINE)InterlockedExchangePointer( 805 | &pElement->ConnectData->ClassService, 806 | pHookContext->ServiceCallbackHook); 807 | 808 | DBG_PRINT( 809 | " %u. Hooked: %p -> %p (DeviceObject = %p)\n", 810 | i, 811 | pElement->ServiceCallbackOriginal, 812 | pElement->ConnectData->ClassService, 813 | pElement->DeviceObject); 814 | } 815 | } 816 | 817 | 818 | _Use_decl_annotations_ 819 | EXTERN_C 820 | static 821 | NTSTATUS 822 | MhkpHookMouHidDeviceObjects( 823 | PMOUSE_SERVICE_CALLBACK_ROUTINE pServiceCallbackHook 824 | ) 825 | { 826 | PMOUHID_HOOK_CONTEXT pHookContext = NULL; 827 | NTSTATUS ntstatus = STATUS_SUCCESS; 828 | 829 | DBG_PRINT("Hooking MouHid device objects.\n"); 830 | 831 | ntstatus = MhkpCreateHookContext(pServiceCallbackHook, &pHookContext); 832 | if (!NT_SUCCESS(ntstatus)) 833 | { 834 | ERR_PRINT("MhkpCreateHookContext failed: 0x%X\n", ntstatus); 835 | goto exit; 836 | } 837 | 838 | // 839 | // Set the hook context pointer in the global context before we install the 840 | // connect data hooks so that our service callback hook can access it. 841 | // 842 | g_MhkManager.HookContext = pHookContext; 843 | 844 | MhkpInstallConnectDataHooks(pHookContext); 845 | 846 | // 847 | // Update the global context. 848 | // 849 | g_MhkManager.HookActive = TRUE; 850 | 851 | exit: 852 | if (!NT_SUCCESS(ntstatus)) 853 | { 854 | if (pHookContext) 855 | { 856 | MhkpFreeHookContext(pHookContext); 857 | } 858 | } 859 | 860 | return ntstatus; 861 | } 862 | 863 | 864 | _Use_decl_annotations_ 865 | EXTERN_C 866 | static 867 | VOID 868 | MhkpUninstallConnectDataHooks( 869 | PMOUHID_HOOK_CONTEXT pHookContext 870 | ) 871 | { 872 | ULONG i = 0; 873 | PMOUHID_DEVICE_OBJECT pElement = NULL; 874 | PVOID pExchangeResult = NULL; 875 | 876 | DBG_PRINT("Uninstalling connect data hooks:\n"); 877 | 878 | for (i = 0; i < pHookContext->NumberOfDeviceObjects; ++i) 879 | { 880 | pElement = &pHookContext->DeviceObjectArray[i]; 881 | 882 | pExchangeResult = InterlockedExchangePointer( 883 | &pElement->ConnectData->ClassService, 884 | pElement->ServiceCallbackOriginal); 885 | if (pExchangeResult != pHookContext->ServiceCallbackHook) 886 | { 887 | ERR_PRINT("Unexpected ClassService: %p (DeviceObject = %p)\n", 888 | pExchangeResult, 889 | pElement->DeviceObject); 890 | DEBUG_BREAK; 891 | } 892 | 893 | DBG_PRINT( 894 | " %u. Unhooked: %p -> %p (DeviceObject = %p)\n", 895 | i, 896 | pExchangeResult, 897 | pElement->ConnectData->ClassService, 898 | pElement->DeviceObject); 899 | } 900 | } 901 | 902 | 903 | _Use_decl_annotations_ 904 | EXTERN_C 905 | static 906 | VOID 907 | MhkpUnhookMouHidDeviceObjects() 908 | { 909 | PMOUHID_HOOK_CONTEXT pHookContext = NULL; 910 | LARGE_INTEGER DelayInterval = {}; 911 | 912 | DBG_PRINT("Unhooking MouHid device objects.\n"); 913 | 914 | pHookContext = g_MhkManager.HookContext; 915 | 916 | MhkpUninstallConnectDataHooks(pHookContext); 917 | 918 | // 919 | // Delay execution to mitigate the race condition where we free the hook 920 | // context before all threads have exited the service callback hook. 921 | // 922 | MakeRelativeIntervalMilliseconds(&DelayInterval, UNHOOK_DELAY_INTERVAL_MS); 923 | 924 | VERIFY(KeDelayExecutionThread(KernelMode, FALSE, &DelayInterval)); 925 | 926 | // 927 | // Update the global context. 928 | // 929 | g_MhkManager.HookActive = FALSE; 930 | g_MhkManager.HookContext = NULL; 931 | 932 | MhkpFreeHookContext(pHookContext); 933 | } 934 | 935 | 936 | _Use_decl_annotations_ 937 | EXTERN_C 938 | static 939 | VOID 940 | NTAPI 941 | MhkpServiceCallbackHook( 942 | PDEVICE_OBJECT pDeviceObject, 943 | PMOUSE_INPUT_DATA pInputDataStart, 944 | PMOUSE_INPUT_DATA pInputDataEnd, 945 | PULONG pnInputDataConsumed 946 | ) 947 | /*++ 948 | 949 | Routine Description: 950 | 951 | The mouse class service callback hook. 952 | 953 | Parameters: 954 | 955 | pDeviceObject - Pointer to the mouse class device object to receive the 956 | mouse input data packets. 957 | 958 | pInputDataStart - Pointer to the array of input packets to be copied to the 959 | class data queue. 960 | 961 | pInputDataEnd - Pointer to the input packet which marks the end of the 962 | input packet array. 963 | 964 | pnInputDataConsumed - Returns the number of input packets copied to the 965 | class data queue by the routine. 966 | 967 | Remarks: 968 | 969 | This routine is installed in the 'ClassService' field of the CONNECT_DATA 970 | object inside the device extension of a hooked MouHid device object. 971 | 972 | --*/ 973 | { 974 | PMOUHID_HOOK_CONTEXT pHookContext = NULL; 975 | ULONG i = 0; 976 | PMOUSE_SERVICE_CALLBACK_ROUTINE pServiceCallbackOriginal = NULL; 977 | PMOUHID_DEVICE_OBJECT pElement = NULL; 978 | 979 | pHookContext = g_MhkManager.HookContext; 980 | 981 | // 982 | // Map the target class device object to its original service callback. 983 | // 984 | for (i = 0; i < pHookContext->NumberOfDeviceObjects; ++i) 985 | { 986 | pElement = &pHookContext->DeviceObjectArray[i]; 987 | 988 | if (pElement->ConnectData->ClassDeviceObject == pDeviceObject) 989 | { 990 | pServiceCallbackOriginal = pElement->ServiceCallbackOriginal; 991 | break; 992 | } 993 | } 994 | // 995 | if (!pServiceCallbackOriginal) 996 | { 997 | ERR_PRINT("Unhandled class device object: %p\n", pDeviceObject); 998 | DEBUG_BREAK; 999 | goto exit; 1000 | } 1001 | 1002 | g_MhkManager.RegistrationEntry->HookCallback( 1003 | pServiceCallbackOriginal, 1004 | pDeviceObject, 1005 | pInputDataStart, 1006 | pInputDataEnd, 1007 | pnInputDataConsumed, 1008 | g_MhkManager.RegistrationEntry->Context); 1009 | 1010 | exit: 1011 | return; 1012 | } 1013 | --------------------------------------------------------------------------------