I’m making my windows kernel driver to simulate keyboard input when timer is triggered, and using devcon.exe to install/uninstall the driver for testing.
According to the Microsoft guide, I have relevant codes to my basic KMDF project. Currently I can install the driver and confirm that timer is correctly triggered, but virtual keyboard simulation doesn’t work.
Here is my driver code.
#pragma once
#include <ntddk.h>
#include <wdf.h>
#include <vhf.h>
#include <initguid.h>
DRIVER_INITIALIZE DriverEntry;
EVT_WDF_DRIVER_DEVICE_ADD WakeUp_Mon_KMDFEvtDeviceAdd;
EVT_WDF_OBJECT_CONTEXT_CLEANUP WakeUp_Mon_KMDFEvtDriverContextCleanup;
EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL WakeUp_Mon_KMDFEvtIoDeviceControl;
EVT_WDF_TIMER WakeUp_Mon_KMDFEvtTimerFunc;
typedef struct _DEVICE_CONTEXT {
WDFTIMER Timer;
ULONG TimerPeriod;
VHFHANDLE VhfHandle;
BOOLEAN TimerRunning;
} DEVICE_CONTEXT, * PDEVICE_CONTEXT;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_CONTEXT, DeviceGetContext);
#define IOCTL_SET_TIMER CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_CANCEL_TIMER CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
DEFINE_GUID(GUID_DEVINTERFACE_WAKEUP_MON_KMDF,
0xe66df535, 0x8ae7, 0x4216, 0x9b, 0xc3, 0x92, 0x0b, 0x32, 0xb8, 0x21, 0x3b);
typedef struct _TIMER_DATA {
ULONG DueTimeInSeconds;
} TIMER_DATA, * PTIMER_DATA;
UCHAR g_KeyboardReportDescriptor[] = {
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x06, // Usage (Keyboard)
0xa1, 0x01, // Collection (Application)
0x05, 0x07, // Usage Page (Key Codes)
0x19, 0xe0, // Usage Minimum (224)
0x29, 0xe7, // Usage Maximum (231)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input (Data, Variable, Absolute) ; Modifier byte
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8)
0x81, 0x01, // Input (Constant) ; Reserved byte
0x95, 0x06, // Report Count (6)
0x75, 0x08, // Report Size (8)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x65, // Logical Maximum (101)
0x05, 0x07, // Usage Page (Key Codes)
0x19, 0x00, // Usage Minimum (0)
0x29, 0x65, // Usage Maximum (101)
0x81, 0x00, // Input (Data, Array) ; Key arrays (6 bytes)
0xc0 // End Collection
};
VOID SendHIDInput(PDEVICE_CONTEXT DeviceContext, UCHAR KeyCode, BOOLEAN KeyDown);
NTSTATUS
DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{
WDF_DRIVER_CONFIG config;
NTSTATUS status;
DbgPrint("DriverEntryn");
WDF_DRIVER_CONFIG_INIT(&config, WakeUp_Mon_KMDFEvtDeviceAdd);
config.EvtDriverUnload = WakeUp_Mon_KMDFEvtDriverContextCleanup;
status = WdfDriverCreate(DriverObject,
RegistryPath,
WDF_NO_OBJECT_ATTRIBUTES,
&config,
WDF_NO_HANDLE);
if (!NT_SUCCESS(status)) {
DbgPrint("WdfDriverCreate failed with status 0x%xn", status);
return status;
}
DbgPrint("DriverEntry finished with status 0x%xn", status);
return status;
}
NTSTATUS
WakeUp_Mon_KMDFEvtDeviceAdd(
_In_ WDFDRIVER Driver,
_Inout_ PWDFDEVICE_INIT DeviceInit
)
{
WDF_OBJECT_ATTRIBUTES deviceAttributes;
WDFDEVICE device;
WDF_IO_QUEUE_CONFIG ioQueueConfig;
NTSTATUS status;
PDEVICE_CONTEXT deviceContext;
WDF_TIMER_CONFIG timerConfig;
VHF_CONFIG vhfConfig;
UNREFERENCED_PARAMETER(Driver);
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT);
deviceAttributes.EvtCleanupCallback = WakeUp_Mon_KMDFEvtDriverContextCleanup;
status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
if (!NT_SUCCESS(status)) {
DbgPrint("WdfDeviceCreate failed with status 0x%xn", status);
return status;
}
deviceContext = DeviceGetContext(device);
deviceContext->TimerRunning = FALSE; // Initialize timer state
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, WdfIoQueueDispatchParallel);
ioQueueConfig.EvtIoDeviceControl = WakeUp_Mon_KMDFEvtIoDeviceControl;
status = WdfIoQueueCreate(device, &ioQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, WDF_NO_HANDLE);
if (!NT_SUCCESS(status)) {
DbgPrint("WdfIoQueueCreate failed with status 0x%xn", status);
return status;
}
WDF_TIMER_CONFIG_INIT(&timerConfig, WakeUp_Mon_KMDFEvtTimerFunc);
WDF_OBJECT_ATTRIBUTES_INIT(&deviceAttributes);
deviceAttributes.ParentObject = device;
status = WdfTimerCreate(&timerConfig, &deviceAttributes, &deviceContext->Timer);
if (!NT_SUCCESS(status)) {
DbgPrint("Failed to create Wdf timer with status 0x%xn", status);
return status;
}
VHF_CONFIG_INIT(&vhfConfig,
WdfDeviceWdmGetDeviceObject(device),
sizeof(g_KeyboardReportDescriptor),
g_KeyboardReportDescriptor);
status = VhfCreate(&vhfConfig, &deviceContext->VhfHandle);
if (!NT_SUCCESS(status)) {
DbgPrint("VhfCreate failed with status 0x%xn", status);
return status;
}
status = WdfDeviceCreateDeviceInterface(device, &GUID_DEVINTERFACE_WAKEUP_MON_KMDF, NULL);
if (!NT_SUCCESS(status)) {
DbgPrint("WdfDeviceCreateDeviceInterface failed with status 0x%xn", status);
return status;
}
status = VhfStart(deviceContext->VhfHandle);
if (!NT_SUCCESS(status)) {
DbgPrint("VhfStart failed with status 0x%xn", status);
return status;
}
DbgPrint("DeviceAdd callback finished with status 0x%xn", status);
return STATUS_SUCCESS;
}
VOID
WakeUp_Mon_KMDFEvtDriverContextCleanup(
_In_ WDFOBJECT DriverObject
)
{
PDEVICE_CONTEXT deviceContext = DeviceGetContext(DriverObject);
// Clean up the VHF handle if it was created
if (deviceContext->VhfHandle != WDF_NO_HANDLE)
{
VhfDelete(deviceContext->VhfHandle, TRUE);
deviceContext->VhfHandle = NULL;
}
// Ensure the timer is stopped and deleted
if (deviceContext->TimerRunning) {
WdfTimerStop(deviceContext->Timer, TRUE);
}
WdfObjectDelete(deviceContext->Timer);
}
VOID
WakeUp_Mon_KMDFEvtIoDeviceControl(
_In_ WDFQUEUE Queue,
_In_ WDFREQUEST Request,
_In_ size_t OutputBufferLength,
_In_ size_t InputBufferLength,
_In_ ULONG IoControlCode
)
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_CONTEXT deviceContext;
PTIMER_DATA timerData;
size_t bytesReturned;
deviceContext = DeviceGetContext(WdfIoQueueGetDevice(Queue));
UNREFERENCED_PARAMETER(OutputBufferLength);
switch (IoControlCode) {
case IOCTL_SET_TIMER:
if (InputBufferLength < sizeof(TIMER_DATA)) {
status = STATUS_BUFFER_TOO_SMALL;
DbgPrint("Input buffer too small. Required size: %zu, Provided size: %zun", sizeof(TIMER_DATA), InputBufferLength);
break;
}
status = WdfRequestRetrieveInputBuffer(Request, sizeof(TIMER_DATA), (PVOID*)&timerData, &bytesReturned);
if (!NT_SUCCESS(status)) {
DbgPrint("WdfRequestRetrieveInputBuffer failed with status 0x%xn", status);
break;
}
// Set the timer
deviceContext->TimerPeriod = (ULONGLONG)(timerData->DueTimeInSeconds) * 1000;
WdfTimerStart(deviceContext->Timer, WDF_REL_TIMEOUT_IN_MS(deviceContext->TimerPeriod));
deviceContext->TimerRunning = TRUE;
DbgPrint("Set wake timer with due time %lldn", deviceContext->TimerPeriod);
break;
case IOCTL_CANCEL_TIMER:
if (WdfTimerStop(deviceContext->Timer, TRUE)) {
deviceContext->TimerRunning = FALSE;
DbgPrint("Timer canceledn");
}
else {
status = STATUS_INVALID_DEVICE_STATE;
DbgPrint("No running timer existsn");
}
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
WdfRequestComplete(Request, status);
}
VOID
WakeUp_Mon_KMDFEvtTimerFunc(
WDFTIMER Timer
)
{
PDEVICE_CONTEXT deviceContext = DeviceGetContext(WdfTimerGetParentObject(Timer));
if (!deviceContext->TimerRunning) {
DbgPrint("Timer function called but Timer is not running. Ignoring...n");
return;
}
DbgPrint("Timer triggeredn");
SendHIDInput(deviceContext, 0x04, TRUE); // Simulate pressing 'A'
SendHIDInput(deviceContext, 0x04, FALSE); // Simulate releasing 'A'
deviceContext->TimerRunning = FALSE;
DbgPrint("Timer DPC Routine finishedn");
}
VOID SendHIDInput(PDEVICE_CONTEXT DeviceContext, UCHAR KeyCode, BOOLEAN KeyDown)
{
UCHAR Report[] = {
0x01, // Report ID
0x00, // Modifier keys
0x00, // Reserved
0x00, // Keycode 1 (will be set dynamically)
0x00, // Keycode 2
0x00, // Keycode 3
0x00, // Keycode 4
0x00, // Keycode 5
0x00 // Keycode 6
};
if (KeyDown) {
Report[3] = KeyCode; // Set the key code
}
HID_XFER_PACKET packet;
packet.reportBuffer = Report;
packet.reportBufferLen = sizeof(Report);
packet.reportId = Report[0]; // The Report ID
NTSTATUS status = VhfReadReportSubmit(DeviceContext->VhfHandle, &packet);
if (!NT_SUCCESS(status)) {
DbgPrint("VhfReadReportSubmit failed with status 0x%xn", status);
}
}
```
`
And this is driver inf file;
`;
; WakeUp_Mon_KMDF.inf
;
[Version]
Signature="$WINDOWS NT$"
Class=System ; TODO: specify appropriate Class
ClassGuid={e66df535-8ae7-4216-9bc3-920b32b8213b}
Provider=%ManufacturerName%
CatalogFile=WakeUp_Mon_KMDF.cat
DriverVer = 08/12/2024,12.9.24.578
PnpLockdown=1
[DestinationDirs]
DefaultDestDir = 12
WakeUp_Mon_KMDF_Device_CoInstaller_CopyFiles = 11
[SourceDisksNames]
1 = %DiskName%,,,""
[SourceDisksFiles]
WakeUp_Mon_KMDF.sys = 1,,
;
;*****************************************
; Install Section
;*****************************************
[Manufacturer]
%ManufacturerName%=Standard,NTamd64
[Standard.NTamd64]
%WakeUp_Mon_KMDF.DeviceDesc%=WakeUp_Mon_KMDF_Device, RootWakeUp_Mon_KMDF ; TODO: edit hw-id
[WakeUp_Mon_KMDF_Device.NT]
CopyFiles=Drivers_Dir
[Drivers_Dir]
WakeUp_Mon_KMDF.sys
;-------------- Service installation
[WakeUp_Mon_KMDF_Device.NT.Services]
AddService = WakeUp_Mon_KMDF,%SPSVCINST_ASSOCSERVICE%, WakeUp_Mon_KMDF_Service_Inst
; -------------- WakeUp_Mon_KMDF driver install sections
[WakeUp_Mon_KMDF_Service_Inst]
DisplayName = %WakeUp_Mon_KMDF.SVCDESC%
ServiceType = 1 ; SERVICE_KERNEL_DRIVER
StartType = 3 ; SERVICE_DEMAND_START
ErrorControl = 1 ; SERVICE_ERROR_NORMAL
ServiceBinary = %12%WakeUp_Mon_KMDF.sys
;
;--- WakeUp_Mon_KMDF_Device Coinstaller installation ------
;
[WakeUp_Mon_KMDF_Device.NT.CoInstallers]
AddReg=WakeUp_Mon_KMDF_Device_CoInstaller_AddReg
CopyFiles=WakeUp_Mon_KMDF_Device_CoInstaller_CopyFiles
[WakeUp_Mon_KMDF_Device_CoInstaller_AddReg]
;
[WakeUp_Mon_KMDF_Device_CoInstaller_CopyFiles]
;
[WakeUp_Mon_KMDF_Device.NT.Wdf]
KmdfService = WakeUp_Mon_KMDF, WakeUp_Mon_KMDF_wdfsect
[WakeUp_Mon_KMDF_wdfsect]
KmdfLibraryVersion = 1.15
; --- Add the required VHF LowerFilters registry setting ---
[WakeUp_Mon_KMDF_Device.NT.HW]
AddReg = WakeUp_Mon_KMDF_Device_HW_AddReg
[WakeUp_Mon_KMDF_Device_HW_AddReg]
HKR,,LowerFilters,0x00010000,"vhf"
[Strings]
SPSVCINST_ASSOCSERVICE= 0x00000002
ManufacturerName="<Your manufacturer name>" ;TODO: Replace with your manufacturer name
DiskName = "WakeUp_Mon_KMDF Installation Disk"
WakeUp_Mon_KMDF.DeviceDesc = "WakeUp_Mon_KMDF Device"
WakeUp_Mon_KMDF.SVCDESC = "WakeUp_Mon_KMDF Service"`
There is one more suspicious thing, when I remove the driver, it keeps failing. I can see that there are newly created my driver's device in Device manager, but can't delete it manually even. I have noticed that new virtual HID device is also created in Device manager, when I uninstall it, then "uninstall" option is available to the driver device too, but when I remove it, BSOD occurs.
I have checked if there are any misconfigured options in inf file regarding VHF usage, and searched alternative solutions, but didn't find. My expectation is driver should simulate keyboard input 'A' when timer is triggered, and also be able to be uninstalled and installed flexibly.
I'm still in starting point to learn KMDF programming, please help me.
New contributor
Lucifer is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.