Skip to content

Commit d42c613

Browse files
CloneClone
Clone
authored and
Clone
committed
Add project files.
1 parent 719a281 commit d42c613

File tree

11 files changed

+952
-0
lines changed

11 files changed

+952
-0
lines changed

KernelHooking.sln

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 16
4+
VisualStudioVersion = 16.0.34601.136
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KernelTrampolineHooking", "KernelTrampolineHooking\KernelTrampolineHooking.vcxproj", "{47B8C5F8-9DDA-4C1B-91EC-C9149B13393A}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|ARM = Debug|ARM
11+
Debug|ARM64 = Debug|ARM64
12+
Debug|x64 = Debug|x64
13+
Debug|x86 = Debug|x86
14+
Release|ARM = Release|ARM
15+
Release|ARM64 = Release|ARM64
16+
Release|x64 = Release|x64
17+
Release|x86 = Release|x86
18+
EndGlobalSection
19+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
20+
{47B8C5F8-9DDA-4C1B-91EC-C9149B13393A}.Debug|ARM.ActiveCfg = Debug|ARM
21+
{47B8C5F8-9DDA-4C1B-91EC-C9149B13393A}.Debug|ARM.Build.0 = Debug|ARM
22+
{47B8C5F8-9DDA-4C1B-91EC-C9149B13393A}.Debug|ARM.Deploy.0 = Debug|ARM
23+
{47B8C5F8-9DDA-4C1B-91EC-C9149B13393A}.Debug|ARM64.ActiveCfg = Debug|ARM64
24+
{47B8C5F8-9DDA-4C1B-91EC-C9149B13393A}.Debug|ARM64.Build.0 = Debug|ARM64
25+
{47B8C5F8-9DDA-4C1B-91EC-C9149B13393A}.Debug|ARM64.Deploy.0 = Debug|ARM64
26+
{47B8C5F8-9DDA-4C1B-91EC-C9149B13393A}.Debug|x64.ActiveCfg = Debug|x64
27+
{47B8C5F8-9DDA-4C1B-91EC-C9149B13393A}.Debug|x64.Build.0 = Debug|x64
28+
{47B8C5F8-9DDA-4C1B-91EC-C9149B13393A}.Debug|x64.Deploy.0 = Debug|x64
29+
{47B8C5F8-9DDA-4C1B-91EC-C9149B13393A}.Debug|x86.ActiveCfg = Debug|Win32
30+
{47B8C5F8-9DDA-4C1B-91EC-C9149B13393A}.Debug|x86.Build.0 = Debug|Win32
31+
{47B8C5F8-9DDA-4C1B-91EC-C9149B13393A}.Debug|x86.Deploy.0 = Debug|Win32
32+
{47B8C5F8-9DDA-4C1B-91EC-C9149B13393A}.Release|ARM.ActiveCfg = Release|ARM
33+
{47B8C5F8-9DDA-4C1B-91EC-C9149B13393A}.Release|ARM.Build.0 = Release|ARM
34+
{47B8C5F8-9DDA-4C1B-91EC-C9149B13393A}.Release|ARM.Deploy.0 = Release|ARM
35+
{47B8C5F8-9DDA-4C1B-91EC-C9149B13393A}.Release|ARM64.ActiveCfg = Release|ARM64
36+
{47B8C5F8-9DDA-4C1B-91EC-C9149B13393A}.Release|ARM64.Build.0 = Release|ARM64
37+
{47B8C5F8-9DDA-4C1B-91EC-C9149B13393A}.Release|ARM64.Deploy.0 = Release|ARM64
38+
{47B8C5F8-9DDA-4C1B-91EC-C9149B13393A}.Release|x64.ActiveCfg = Release|x64
39+
{47B8C5F8-9DDA-4C1B-91EC-C9149B13393A}.Release|x64.Build.0 = Release|x64
40+
{47B8C5F8-9DDA-4C1B-91EC-C9149B13393A}.Release|x64.Deploy.0 = Release|x64
41+
{47B8C5F8-9DDA-4C1B-91EC-C9149B13393A}.Release|x86.ActiveCfg = Release|Win32
42+
{47B8C5F8-9DDA-4C1B-91EC-C9149B13393A}.Release|x86.Build.0 = Release|Win32
43+
{47B8C5F8-9DDA-4C1B-91EC-C9149B13393A}.Release|x86.Deploy.0 = Release|Win32
44+
EndGlobalSection
45+
GlobalSection(SolutionProperties) = preSolution
46+
HideSolutionNode = FALSE
47+
EndGlobalSection
48+
GlobalSection(ExtensibilityGlobals) = postSolution
49+
SolutionGuid = {24376BC5-265C-4ADD-85D2-6316A4748B81}
50+
EndGlobalSection
51+
EndGlobal

KernelTrampolineHooking/Common.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#pragma once
2+
3+
#ifndef Common_H
4+
#define Common_H
5+
6+
#include <ntifs.h>
7+
#include <ntddk.h>
8+
#include <wdm.h>
9+
#include <wdf.h>
10+
11+
#define DebugMessage(x, ...) DbgPrintEx(0, 0, x, __VA_ARGS__)
12+
13+
typedef struct _SYSTEM_BASIC_INFORMATION {
14+
ULONG Reserved;
15+
ULONG TimerResolution;
16+
ULONG PageSize;
17+
ULONG NumberOfPhysicalPages;
18+
ULONG LowestPhysicalPageNumber;
19+
ULONG HighestPhysicalPageNumber;
20+
ULONG AllocationGranularity;
21+
ULONG MinimumUserModeAddress;
22+
ULONG MaximumUserModeAddress;
23+
KAFFINITY ActiveProcessorsAffinityMask;
24+
UCHAR NumberOfProcessors; // This is the field we will modify
25+
} SYSTEM_BASIC_INFORMATION, * PSYSTEM_BASIC_INFORMATION;
26+
27+
28+
typedef enum _SYSTEM_INFORMATION_CLASS {
29+
SystemBasicInformation = 0,
30+
// Theres a lot more of these but I deleted them to save space since we wont be using them
31+
} SYSTEM_INFORMATION_CLASS;
32+
33+
34+
typedef NTSTATUS(*NtQuerySystemInformation_t)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
35+
36+
37+
KEVENT HookConfiguredEvent; // Used to prevent other threads from using the function we are hooking while making the hook
38+
39+
#endif // Common_H

KernelTrampolineHooking/Driver.c

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#include "Driver.h"
2+
#include "Queue.h"
3+
#include "Hook.h"
4+
5+
#ifdef ALLOC_PRAGMA
6+
#pragma alloc_text (INIT, DriverEntry) // After DriverEntry runs all code denoted by 'INIT' will be discarded
7+
#pragma alloc_text (PAGE, DriverUnload) // 'PAGE' means this code section can be paged out while we are waiting for it to run (only works at IRQL of 0 aka Passive Level)
8+
#endif;
9+
10+
11+
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
12+
UNREFERENCED_PARAMETER(RegistryPath);
13+
14+
WDF_OBJECT_ATTRIBUTES deviceAttributes;
15+
WDF_DRIVER_CONFIG config;
16+
WDFDRIVER driver;
17+
NTSTATUS status;
18+
19+
// Initlaize the global fields in our header files
20+
myTrampolineMemory = NULL;
21+
myTrampolineMDL = NULL;
22+
myTrampoline = NULL;
23+
gDevice = NULL;
24+
KeInitializeEvent(&HookConfiguredEvent, NotificationEvent, FALSE);
25+
26+
DECLARE_CONST_UNICODE_STRING(ntDeviceName, NTDEVICE_NAME);
27+
WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK);
28+
29+
if (!NT_SUCCESS(status = WdfDriverCreate(DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &config, &driver))) {
30+
DebugMessage("WdfDriverCreate failed: 0x%x\n", status);
31+
return status;
32+
}
33+
34+
PWDFDEVICE_INIT pDeviceInit = WdfControlDeviceInitAllocate(driver, &SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RW_RES_R);
35+
if (pDeviceInit == NULL) {
36+
DebugMessage("WdfControlDeviceInitAllocate failed\n");
37+
return STATUS_INSUFFICIENT_RESOURCES;
38+
}
39+
40+
WdfDeviceInitSetExclusive(pDeviceInit, TRUE); // Ensure we are the only queue for this device
41+
42+
if (!NT_SUCCESS(status = WdfDeviceInitAssignName(pDeviceInit, &ntDeviceName))) {
43+
DebugMessage("Failed to create symbolic link: 0x%x\n", status);
44+
WdfDeviceInitFree(pDeviceInit);
45+
return status;
46+
}
47+
48+
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_EXTENSION);
49+
50+
if (!NT_SUCCESS(status = WdfDeviceCreate(&pDeviceInit, &deviceAttributes, &gDevice)) || gDevice == NULL) { // This will auto handle freeing 'pDeviceInit'
51+
DebugMessage("WdfDeviceCreate failed: 0x%x\n", status);
52+
return status;
53+
}
54+
55+
UCHAR cores = QueryProcessorCount(8); // You can change this default value if you would like
56+
DebugMessage("Starting CoreCount: %lu \n", cores);
57+
58+
PDEVICE_EXTENSION deviceExtension = GetDeviceExtension(gDevice);
59+
deviceExtension->CoreCount = cores;
60+
61+
if (!NT_SUCCESS(status = QueueInitialize(gDevice))) { // Initialize the IOCTL queue
62+
DebugMessage("Queue initialization failed: 0x%x\n", status);
63+
return status;
64+
}
65+
66+
DriverObject->DriverUnload = DriverUnload; // Lastly, register our unload function
67+
return STATUS_SUCCESS;
68+
}
69+
70+
71+
VOID DriverUnload(PDRIVER_OBJECT DriverObject) {
72+
UNREFERENCED_PARAMETER(DriverObject);
73+
PAGED_CODE();
74+
75+
RemoveHook();
76+
}

KernelTrampolineHooking/Driver.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#pragma once
2+
3+
#ifndef Driver_H
4+
#define Driver_H
5+
6+
#include "Common.h"
7+
8+
VOID DriverUnload(PDRIVER_OBJECT DriverObject);
9+
DRIVER_INITIALIZE DriverEntry;
10+
11+
#endif // Driver_H

KernelTrampolineHooking/Hook.c

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
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

Comments
 (0)