1
+ #include "Hook.h"
2
+ #include "Queue.h"
3
+
4
+ #ifdef ALLOC_PRAGMA
5
+ #pragma alloc_text(PAGE, RemoveHook)
6
+ #endif ;
7
+
8
+ #define TRAMPOLINE_SIZE 32 // Extra bytes are added for alignment
9
+
10
+ NTSTATUS HookedNtQuerySystemInformation (SYSTEM_INFORMATION_CLASS SystemInformationClass , PVOID SystemInformation , ULONG SystemInformationLength , PULONG ReturnLength ) {
11
+ NTSTATUS status = STATUS_SUCCESS ;
12
+ __try {
13
+ // Wait for the event to be signaled, with an optional timeout if desired
14
+ KeWaitForSingleObject (& HookConfiguredEvent , Executive , KernelMode , FALSE, NULL );
15
+
16
+ if (myTrampoline == NULL ) {
17
+ return STATUS_UNSUCCESSFUL ;
18
+ }
19
+
20
+ status = ((NtQuerySystemInformation_t )myTrampoline )(SystemInformationClass , SystemInformation , SystemInformationLength , ReturnLength );
21
+
22
+ if (NT_SUCCESS (status ) && gDevice != NULL ) {
23
+ PDEVICE_EXTENSION deviceExtension = GetDeviceExtension (gDevice );
24
+
25
+ if (SystemInformationClass == SystemBasicInformation && deviceExtension -> CoreCount > 0 && deviceExtension -> CoreCount <= 128 ) {
26
+ PSYSTEM_BASIC_INFORMATION pInfo = (PSYSTEM_BASIC_INFORMATION )SystemInformation ;
27
+ pInfo -> NumberOfProcessors = deviceExtension -> CoreCount ;
28
+ }
29
+ }
30
+ }
31
+ __except (EXCEPTION_EXECUTE_HANDLER ) {
32
+ NTSTATUS exceptionCode = GetExceptionCode ();
33
+ DebugMessage ("Exception occurred in HookedNtQuerySystemInformation: 0x%x\n" , exceptionCode );
34
+ }
35
+
36
+ return status ;
37
+ }
38
+
39
+
40
+
41
+ static VOID PrintFunctionBytes (PVOID address , SIZE_T size ) {
42
+ PUCHAR bytePtr = (PUCHAR )address ;
43
+ DebugMessage ("First %Iu bytes at address %p:\n" , size , address );
44
+ for (SIZE_T i = 0 ; i < size ; i ++ ) {
45
+ DebugMessage ("0x%02X " , bytePtr [i ]);
46
+ }
47
+ DebugMessage ("\n" );
48
+ }
49
+
50
+ static PVOID AllocateAlignedExecutableMemory (SIZE_T size , ULONG tag ) {
51
+ PVOID alignedMemory = NULL ;
52
+ ULONG_PTR alignedSize = (size + PAGE_SIZE - 1 ) & ~(PAGE_SIZE - 1 ); // Round up to page size
53
+
54
+ // Allocate the memory, ensuring it’s executable and cache-aligned
55
+ alignedMemory = ExAllocatePoolWithTag (NonPagedPoolExecute , alignedSize , tag );
56
+ if (alignedMemory == NULL ) {
57
+ return NULL ;
58
+ }
59
+
60
+ // Ensure it’s aligned to a page boundary
61
+ if ((ULONG_PTR )alignedMemory % PAGE_SIZE != 0 ) {
62
+ ExFreePool (alignedMemory );
63
+ alignedMemory = ExAllocatePoolWithTag (NonPagedPoolExecute | POOL_COLD_ALLOCATION , alignedSize , tag );
64
+ }
65
+ return alignedMemory ;
66
+ }
67
+
68
+ static VOID WriteToReadOnlyMemory (PVOID target , PVOID source , SIZE_T size ) {
69
+ PMDL mdl = IoAllocateMdl (target , (ULONG )size , FALSE, FALSE, NULL );
70
+
71
+ if (mdl == NULL ) {
72
+ DebugMessage ("Failed to allocate MDL\n" );
73
+ return ;
74
+ }
75
+
76
+ MmBuildMdlForNonPagedPool (mdl ); // Builds the MDL for the given non-paged memory.
77
+
78
+ __try {
79
+ PVOID mappedAddress = MmMapLockedPagesSpecifyCache (mdl , KernelMode , MmNonCached , NULL , FALSE, NormalPagePriority );
80
+
81
+ if (mappedAddress != NULL ) {
82
+ RtlCopyMemory (mappedAddress , source , size );
83
+ MmUnmapLockedPages (mappedAddress , mdl );
84
+ }
85
+ }
86
+ __except (EXCEPTION_EXECUTE_HANDLER ) {
87
+ NTSTATUS exceptionCode = GetExceptionCode ();
88
+ DebugMessage ("Exception occurred while writing to read-only memory: 0x%x\n" , exceptionCode );
89
+ }
90
+
91
+ IoFreeMdl (mdl );
92
+ }
93
+
94
+
95
+ VOID InstallHook () {
96
+ const UCHAR bytesRemoved = 12 ;
97
+
98
+ // Get the address of NtQuerySystemInformation
99
+ UNICODE_STRING routineName = RTL_CONSTANT_STRING (L"NtQuerySystemInformation" );
100
+ OriginalNtQuerySystemInformation = (NtQuerySystemInformation_t )MmGetSystemRoutineAddress (& routineName );
101
+
102
+ if (OriginalNtQuerySystemInformation ) {
103
+ // Allocate memory for the trampoline using an MDL for cross-context access
104
+ myTrampolineMemory = AllocateAlignedExecutableMemory (TRAMPOLINE_SIZE , 'Hook' );
105
+ if (myTrampolineMemory == NULL ) {
106
+ DebugMessage ("Failed to allocate trampoline memory\n" );
107
+ return ;
108
+ }
109
+
110
+ // Create an MDL to describe trampolineMemory
111
+ myTrampolineMDL = IoAllocateMdl (myTrampolineMemory , TRAMPOLINE_SIZE , FALSE, FALSE, NULL );
112
+ if (myTrampolineMDL == NULL ) {
113
+ ExFreePool (myTrampolineMemory );
114
+ myTrampolineMemory = NULL ;
115
+ DebugMessage ("Failed to allocate MDL for trampoline\n" );
116
+ return ;
117
+ }
118
+
119
+ // Map the MDL pages for trampolineMemory
120
+ MmBuildMdlForNonPagedPool (myTrampolineMDL );
121
+ MmProtectMdlSystemAddress (myTrampolineMDL , PAGE_EXECUTE_READWRITE ); // Set execute, read, and write permissions
122
+ myTrampoline = MmMapLockedPagesSpecifyCache (myTrampolineMDL , KernelMode , MmNonCached , NULL , FALSE, NormalPagePriority );
123
+
124
+ if (myTrampoline == NULL ) {
125
+ ExFreePool (myTrampolineMemory );
126
+ IoFreeMdl (myTrampolineMDL );
127
+ DebugMessage ("Failed to map trampoline pages\n" );
128
+ return ;
129
+ }
130
+
131
+ ULONGLONG offsetBackToOriginal = (ULONGLONG )((uintptr_t )(PUCHAR )OriginalNtQuerySystemInformation + bytesRemoved );
132
+
133
+ RtlZeroMemory (myTrampoline , TRAMPOLINE_SIZE ); // Clear the trampoline memory to avoid unexpected execution of the last unused bytes
134
+ RtlCopyMemory (myTrampoline , (PVOID )OriginalNtQuerySystemInformation , bytesRemoved ); // Copy the first bytes from NtQuerySystemInformation to trampoline
135
+
136
+ // MOV REX, offsetBackToOriginal -> <OriginalNtQuerySystemInformation>
137
+ * ((PUCHAR )myTrampoline + bytesRemoved ) = 0x48 ; // REX
138
+ * ((PUCHAR )myTrampoline + bytesRemoved + 1 ) = 0xB8 ; // mov
139
+ * ((ULONGLONG * )((PUCHAR )myTrampoline + bytesRemoved + 2 )) = offsetBackToOriginal ; // mov (8 bytes)
140
+
141
+ // Indrect JMP to RAX aka `offsetBackToOriginal` (we cant use 0xE9 since that is a relative jump and only works in an address space of 32 bit)
142
+ * ((PUCHAR )myTrampoline + bytesRemoved + 10 ) = 0xFF ;
143
+ * ((PUCHAR )myTrampoline + bytesRemoved + 11 ) = 0xE0 ;
144
+
145
+
146
+ // Define the new jump instruction to `HookedNtQuerySystemInformation`
147
+ UCHAR jump [12 ] = { 0 };
148
+
149
+ // Step 1: MOV RAX, <HookedNtQuerySystemInformation>
150
+ jump [0 ] = 0x48 ; // REX prefix for 64-bit operand
151
+ jump [1 ] = 0xB8 ; // Opcode for `mov rax, <64-bit immediate>`
152
+ * ((ULONGLONG * )(jump + 2 )) = (ULONGLONG )HookedNtQuerySystemInformation ; // 64-bit address to jump to
153
+
154
+ // Step 2: JMP RAX (indirect jump)
155
+ jump [10 ] = 0xFF ; // Opcode for indirect jump
156
+ jump [11 ] = 0xE0 ; // ModR/M byte for `jmp rax`
157
+
158
+ // Apply the hook by writing the jump instruction at the start of NtQuerySystemInformation
159
+ WriteToReadOnlyMemory ((PVOID )OriginalNtQuerySystemInformation , jump , sizeof (jump ));
160
+ KeInvalidateAllCaches ();
161
+
162
+ // Signal the event to indicate hook configuration is complete
163
+ KeSetEvent (& HookConfiguredEvent , 0 , FALSE);
164
+ }
165
+ else {
166
+ DebugMessage ("Failed to retrieve address for NtQuerySystemInformation\n" );
167
+ }
168
+ }
169
+
170
+
171
+ VOID RemoveHook () {
172
+ PAGED_CODE ();
173
+
174
+ if (myTrampoline ) {
175
+ KeClearEvent (& HookConfiguredEvent ); // make our hook wait while we change its code
176
+
177
+ if (myTrampoline != NULL ) {
178
+ WriteToReadOnlyMemory ((PVOID )OriginalNtQuerySystemInformation , myTrampoline , 6 ); // Restore the original bytes in NtQuerySystemInformation
179
+ ExFreePoolWithTag (myTrampoline , 'Hook' );
180
+ myTrampoline = NULL ;
181
+ }
182
+
183
+ if (myTrampolineMemory != NULL ) {
184
+ ExFreePool (myTrampolineMemory );
185
+ myTrampolineMemory = NULL ;
186
+ }
187
+
188
+ if (myTrampolineMDL != NULL ) {
189
+ IoFreeMdl (myTrampolineMDL );
190
+ myTrampolineMDL = NULL ;
191
+ }
192
+
193
+ DebugMessage ("NtQuerySystemInformation unhooked successfully\n" );
194
+ KeSetEvent (& HookConfiguredEvent , 0 , FALSE); // Let any threads stuck on waiting continue but exit with STATUS_UNSUCCESSFUL
195
+ }
196
+ }
0 commit comments