Kernel Mode Driver and IOCTL - windows

What I'm trying to do:
User-mode app sends Process ID to driver
Driver gets handle to specified Process ID
Drivers passes opened handle to user-mode app
I'm not sure if this code is even completely working. I'm quite new to drivers, so I haven't set up debugging yet and virtual machine hasn't finished downloading (slow connection problems).
Getting Process ID from user-mode app should be working fine and it should be getting stored in PROCESS_INFO struct as ProcessId. The opened handle is stored in the same struct as ProcessHandle to be sent back to the user-mode application. I'm not sure how to return data from kernel to user-mode tho, maybe someone could explain briefly.
Here's my code:
#include <ntifs.h>
#include <wdf.h>
DRIVER_INITIALIZE DriverEntry;
UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(L"\\Device\\GetSysHandle"), SymbolicLink = RTL_CONSTANT_STRING(L"\\DosDevices\\GetSysHandle");
typedef struct _PROCESS_INFO
{
HANDLE ProcessId;
HANDLE ProcessHandle;
}PROCESS_INFO, *PPROCESS_INFO;
BOOLEAN GetSysHandle(PPROCESS_INFO ProcessInfo)
{
NTSTATUS status = STATUS_ACCESS_DENIED;
PEPROCESS eProcess = NULL;
status = PsLookupProcessByProcessId(ProcessInfo->ProcessId, &eProcess);
if ((!NT_SUCCESS(status)) || (!eProcess))
{
return FALSE;
}
status = ObOpenObjectByPointer(eProcess, 0, NULL, 0, 0, KernelMode, &ProcessInfo->ProcessHandle);
if ((!NT_SUCCESS(status)) || (!ProcessInfo->ProcessHandle))
{
ObDereferenceObject(eProcess);
return FALSE;
}
return TRUE;
}
void Unload(PDRIVER_OBJECT pDriverObject)
{
DbgPrint("# GetSysHandle driver unloaded.");
IoDeleteSymbolicLink(&SymbolicLink);
IoDeleteDevice(pDriverObject->DeviceObject);
}
NTSTATUS DriverDispatch(PDEVICE_OBJECT DeviceObject, PIRP irp)
{
PIO_STACK_LOCATION io;
PPROCESS_INFO ProcessInfo;
NTSTATUS status;
io = IoGetCurrentIrpStackLocation(irp);
irp->IoStatus.Information = 0;
switch (io->MajorFunction)
{
case IRP_MJ_CREATE:
status = STATUS_SUCCESS;
break;
case IRP_MJ_CLOSE:
status = STATUS_SUCCESS;
break;
case IRP_MJ_READ:
status = STATUS_SUCCESS;
break;
case IRP_MJ_WRITE:
ProcessInfo = (PPROCESS_INFO)MmGetSystemAddressForMdlSafe(irp->MdlAddress, NormalPagePriority);
if (!ProcessInfo)
{
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
if (!GetSysHandle(ProcessInfo))
{
DbgPrint("# Failed to get process handle");
status = STATUS_UNSUCCESSFUL;
break;
}
status = STATUS_SUCCESS;
irp->IoStatus.Information = sizeof(PROCESS_INFO);
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
irp->IoStatus.Status = status;
IoCompleteRequest(irp, IO_NO_INCREMENT);
return status;
}
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
{
PDEVICE_OBJECT DeviceObject;
ULONG i;
DbgPrint("# GetSysHandle driver loaded");
IoCreateDevice(DriverObject, 0, &DeviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &DeviceObject);
IoCreateSymbolicLink(&SymbolicLink, &DeviceName);
for (i = 0;i<IRP_MJ_MAXIMUM_FUNCTION;i++)
{
DriverObject->MajorFunction[i] = DriverDispatch;
}
return STATUS_SUCCESS;
}

First and foremost (to avoid confusion): this code is for Windows and Windows Drivers.
The biggest issue I see with your driver code is that you aren't registering the IRP major functions inside of DriverEntry. You will need to set the MajorFunction parameters of the PDRIVER_OBJECT DriverObject to the dispatch functions. Without assigning these MajorFunctions, the driver will have no way of knowing what function to call when receiving a command.
To do this, break up the cases for the switch (io->MajorFunction) into different dispatch functions. For example, IRP_MJ_WRITE will be its own function, and you can declare it like so:
DRIVER_DISPATCH DispatchReadFunction;
In this case, we will want all read commands issued to the driver to run whatever code is inside the DispatchReadFunction function. So the definition will look a little like this:
NTSTATUS DispatchReadFunction(_In_ PDEVICE_OBJECT DriverObject, _In_ PIRP Irp)
{
NTSTATUS status = STATUS_SUCCESS;
//Do read code here
return status;
}
Once we have the DispatchReadFunction function written, you will need to assign it to the DriverObject->MajorFunction in the DriverEntry function like so:
DriverObject->MajorFunction[IRP_MJ_READ] = DispatchReadFunction;
Finally, stub out and write the rest of the IRP_MJ_ functions that you need, making sure that you assign them to the MajorFunction memeber of DriverObject.
My DriverEntry function looks like this (I removed extra functionality):
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
{
NTSTATUS status;
status = STATUS_SUCCESS;
DriverObject->MajorFunction[IRP_MJ_READ] = DispatchReadFunc;
DriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreateFunc;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchCloseFunc;
DriverObject->MajorFunction[IRP_MJ_WRITE] = DispatchWriteFunc;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchDevCtrlFunc;
return status;
}
A good starting point would be here. This link also includes a simple example (driver code and application code) that does what you are attempting to do.
Good Luck!

Related

ZwQuerySystemInformation is Not working Properly

I tried FindProcessidByName with Kernel Mode Driver, sometimes ImageName.Buffer goes to NULL , because of this , when ImageName.Buffer goes to NULL I can not find process ids. Do you have any idea why ImageName.Buffer goes to NULL sometimes, sir ?
typedef struct _SYSTEM_PROCESS_INFO_L
{
ULONG NextEntryOffset;
ULONG NumberOfThreads;
LARGE_INTEGER Reserved[3];
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ImageName;
ULONG BasePriority;
HANDLE ProcessId;
HANDLE InheritedFromProcessId;
}_SYSTEM_PROCESS_INFO_L, *P_SYSTEM_PROCESS_INFO_L;
HANDLE LSFindProcess(LPSTR name)
{
NTSTATUS durum;
ULONG retsize;
HANDLE hProcid = -1;
P_SYSTEM_PROCESS_INFO_L pi;
durum = ZwQuerySystemInformation(SystemProcessInformation, NULL, NULL, &retsize); // request how much memory size we need.
if (!NT_SUCCESS(durum) && durum !=STATUS_INFO_LENGTH_MISMATCH)
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "LS : ZwQuerySystemInformation Failed 1 durum : %p \n",durum);
return -1;
}
PVOID memPtr;
memPtr = ExAllocatePool(PagedPool, retsize);
if (!memPtr)
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "LS : ExAllocatePool Failed \n");
return -1;
}
memset(memPtr, 0, retsize);// zero mem
durum = ZwQuerySystemInformation(SystemProcessInformation, memPtr, retsize, NULL);
pi = (P_SYSTEM_PROCESS_INFO_L)memPtr; // parselliyorz
if (!NT_SUCCESS(durum))
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "LS : ZwQuerySystemInformation Failed 2 durum : %p \n", durum);
return -1;
}
while (pi->NextEntryOffset)
{
if (pi->ImageName.Buffer) //some process null if I dont use this I am getting BSOD.
{
if (!_stricmp(pi->ImageName.Buffer, name))
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "LS : name %ws , pid : %d \n", pi->ImageName.Buffer, pi->ProcessId);
hProcid = pi->ProcessId; // pid
break; // foundedd
}
}
pi = (P_SYSTEM_PROCESS_INFO_L)((unsigned char*)pi + pi->NextEntryOffset);
}
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "LS : LSFindProcess bitti \n");
return hProcid;
}
ZwQuerySystemInformation is Not working Properly
this is of course false. api working properly. if you got wrong results, this is only due errors in code. in concrete code spinet exist several errors.
in kernel mode exist difference between Zw and Nt api - need understand which version need call, if both (like in this concrete case) is exported. if you know that previous mode is kernel mode - need use Nt version. if you don't know previous mode in this point(at compile time) or previous mode user mode - need use Zw version. however this is general note and will be not error always call Zw
the first serious error - this is how ZwQuerySystemInformation called - first time for query requested buffer size retsize (in code) and second time already with this buffer size. but between this calls new threads and processes can be spawn in system. as result returned buffer size can be already not large enough. correct solution - call this api in loop, while it return STATUS_INFO_LENGTH_MISMATCH
the second - memory always must be free. especially in kernel mode. say that code incomplete - no excuse. code can be incomplete and intermediate, but free memory after allocation always must be immediately inserted
else one critical error - while (pi->NextEntryOffset) loop - with this loop we always lost (not process) the last entry (last created process). need change this.
if (pi->ImageName.Buffer) //some process null if I dont use this I am
getting BSOD.
the ImageName is UNICODE_STRING and need work with it respectively. in case ImageName.Buffer the ImageName.Length is also 0. the UNICODE_STRING ImageName; is correct. incorrect only how you use it.
!_stricmp(pi->ImageName.Buffer, name); // ??
the pi->ImageName.Buffer is PWSTR so it can not be used with _stricmp as a matter of principle. think you use c compiler - c++ simply give you error here. but even use _wcsicmp is incorrect here - again pi->ImageName is UNICODE_STRING and need use RtlEqualUnicodeString(&pi->ImageName, name, TRUE) where name must be of course PCUNICODE_STRING but not PCSTR or PSTR or even PCWSTR. if you have PCWSTR name as input - you need wrap it to UNICODE_STRING before call this api. example of code:
NTSTATUS LSFindProcess(PCUNICODE_STRING ImageName, PHANDLE UniqueProcessId)
{
ULONG cb = 0x20000;
PVOID buf;
NTSTATUS status;
do
{
status = STATUS_INSUFFICIENT_RESOURCES;
if (buf = ExAllocatePool(PagedPool, cb))
{
if (0 <= (status = NtQuerySystemInformation(SystemProcessInformation, buf, cb, &cb)))
{
union {
PVOID pv;
PBYTE pb;
PSYSTEM_PROCESS_INFORMATION pspi;
};
pv = buf;
ULONG NextEntryOffset;
goto __0;
do
{
pb += NextEntryOffset;
__0:
if (RtlEqualUnicodeString(&pspi->ImageName, ImageName, TRUE))
{
*UniqueProcessId = pspi->UniqueProcessId;
break;
}
} while (NextEntryOffset = pspi->NextEntryOffset);
}
ExFreePool(buf);
}
} while (status == STATUS_INFO_LENGTH_MISMATCH);
return status;
}
NTSTATUS LSFindProcess(PCWSTR ImageName, PHANDLE UniqueProcessId)
{
UNICODE_STRING us;
RtlInitUnicodeString(&us, ImageName);
return LSFindProcess(&us, UniqueProcessId);
}
thanks for everyone especially #RbMm thanks for informations.
Finished code I hope this post help someone..
/// <summary>
/// Struct SystemProcessInformation
/// </summary>
typedef struct _SYSTEM_PROCESS_INFO_L
{
ULONG NextEntryOffset;
ULONG NumberOfThreads;
LARGE_INTEGER Reserved[3];
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ImageName;
ULONG BasePriority;
HANDLE ProcessId;
HANDLE InheritedFromProcessId;
}_SYSTEM_PROCESS_INFO_L, *P_SYSTEM_PROCESS_INFO_L;
/// <summary>
/// Find Process ID By Name , thanks #RbMm
/// </summary>
/// <param name="imagename">Process name </param>
/// <param name="pid">Output Process id</param>
/// <returns>NTSTATUS.</returns>
NTSTATUS LSFindProcessIdByName(IN PCWSTR imagename, OUT PHANDLE pid)
{
NTSTATUS durum = STATUS_UNSUCCESSFUL;
ULONG qmemsize = 0x1024;
PVOID qmemptr = 0;
P_SYSTEM_PROCESS_INFO_L spi;
UNICODE_STRING uimagename;
RtlInitUnicodeString(&uimagename, imagename); // #RbMm
*pid = -1;
do
{
qmemptr = ExAllocatePool(PagedPool, qmemsize); // alloc memory for spi
if (qmemptr == NULL) // check memory is allocated or not.
{
return STATUS_UNSUCCESSFUL;
}
durum = ZwQuerySystemInformation(SystemProcessInformation,qmemptr, qmemsize, NULL);
if (durum == STATUS_INFO_LENGTH_MISMATCH)
{
qmemsize = qmemsize * 2; // increase qmemsize for next memory alloc
ExFreePool(qmemptr); // free memory
}
} while (durum == STATUS_INFO_LENGTH_MISMATCH); // resize memory
spi = (P_SYSTEM_PROCESS_INFO_L)qmemptr;
while(1)
{
if (RtlEqualUnicodeString(&uimagename, &spi->ImageName, TRUE)) // #RbMm
{
*pid = spi->ProcessId;
break;
}
if (spi->NextEntryOffset == 0)
break;
spi = (P_SYSTEM_PROCESS_INFO_L)((unsigned char*)spi + spi->NextEntryOffset); // next info
}
if (!NT_SUCCESS(durum))
{
ExFreePool(qmemptr); // free memory
return STATUS_UNSUCCESSFUL;
}
ExFreePool(qmemptr); // free memory
return STATUS_SUCCESS;
}
You should not be calling ZwQuerySystemInformation, it's not easily portable across Windows OS's and may not even be available on some of them. The MSDN documentation for this function recommends alternate functions for your particular usage.

How to call an KMDF WDF interface from NDIS Miniport?

I have a Bus Driver that creates a PDO for each physical port of a custom Ethernet card. I also have an NDIS Miniport Driver that installs onto each PDO.
The Bus Driver is based on the static Toaster example.
The NDIS Miniport is based on the Netvmini modified to match the hardware.
ToasterInterface.InterfaceHeader.InterfaceReference =
WdfDeviceInterfaceReferenceNoOp;
ToasterInterface.InterfaceHeader.InterfaceDereference =
WdfDeviceInterfaceDereferenceNoOp;
ToasterInterface.GetCrispinessLevel = Bus_GetCrispinessLevel;
ToasterInterface.SetCrispinessLevel = Bus_SetCrispinessLevel;
ToasterInterface.IsSafetyLockEnabled = Bus_IsSafetyLockEnabled;
WDF_QUERY_INTERFACE_CONFIG_INIT(&qiConfig,
(PINTERFACE) &ToasterInterface,
&GUID_TOASTER_INTERFACE_STANDARD,
NULL);
status = WdfDeviceAddQueryInterface(hChild, &qiConfig);
How do I call a KMDF WDF defined interface from an NDIS Driver?
Thanks
From the NDIS side of your driver, you can call NdisMGetDeviceProperty to obtain the FDO of the individual miniport. From there, call IoCallDriver (or IoSynchronousCallDriver to save a couple lines of code) in the usual fashion to issue IRP_MN_QUERY_INTERFACE.
You need to ensure that you dereference the bus interface before your MiniportHalt completes.
Here is the code that worked for me.
// ***********************************
// Inside Header (Both Drivers, Shared)
// ***********************************
typedef
NTSTATUS
(*PMY_BUS_INTERFACE_TEST) (
_In_ PINTERFACE InterfaceHeader,
_In_ PVOID pDataIn,
_In_ size_t szDataInSize
);
typedef struct _MY_BUS_INTERFACE {
INTERFACE InterfaceHeader;
PMY_BUS_INTERFACE_TEST InterfaceTest;
} MY_BUS_INTERFACE, *PMY_BUS_INTERFACE;
#define MY_BUS_INTERFACE_VERSION 1
DEFINE_GUID(GUID_MY_BUS_INTERFACE, 0x12345678, 0x1234, 0x1234, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88);
// ***********************************
// pdo.c: inside Bus_CreatePdo
// ***********************************
//
// Test Interface
//
MY_BUS_INTERFACE arincInterface;
RtlZeroMemory(&arincInterface, sizeof(MY_BUS_INTERFACE));
arincInterface.InterfaceHeader.Size = sizeof(MY_BUS_INTERFACE);
arincInterface.InterfaceHeader.Version = MY_BUS_INTERFACE_VERSION;
arincInterface.InterfaceHeader.Context = (PVOID)hChild;
arincInterface.InterfaceHeader.InterfaceReference = WdfDeviceInterfaceReferenceNoOp;
arincInterface.InterfaceHeader.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp;
arincInterface.InterfaceTest = Bus_InterfaceTest;
WDF_QUERY_INTERFACE_CONFIG qic;
WDF_QUERY_INTERFACE_CONFIG_INIT(&qic,
(PINTERFACE)&arincInterface,
&GUID_MY_BUS_INTERFACE,
NULL);
// ***********************************
// Inside pdo.c
// ***********************************
NTSTATUS
Bus_InterfaceTest(
_In_ PINTERFACE InterfaceHeader,
_In_ PVOID pDataIn,
_In_ size_t szDataInSize
)
{
NTSTATUS status = STATUS_SUCCESS;
UNREFERENCED_PARAMETER(InterfaceHeader);
UNREFERENCED_PARAMETER(pDataIn);
UNREFERENCED_PARAMETER(szDataInSize);
KdPrint(("Bus_InterfaceTest: pDataIn [%p] [%llu] \n", pDataIn, szDataInSize));
status = -678;
return status;
}
// ***********************************
// Inside Miniport Driver
// ***********************************
//
// Query Interface Setup
//
MY_BUS_INTERFACE myBusInterface;
PMY_BUS_INTERFACE pMyBusInterface = &myBusInterface;
//
// Initialize an event to block on
//
KEVENT event;
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
//
// Build an irp
//
IO_STATUS_BLOCK ioStatus;
PIRP irp = IoBuildSynchronousFsdRequest(
IRP_MJ_PNP,
Adapter->NextDeviceObject,
NULL,
0,
NULL,
&event,
&ioStatus
);
if (!irp) {
pMyBusInterface = NULL;
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
irp->IoStatus.Information = 0;
//
// Get the irp stack location
//
PIO_STACK_LOCATION irpSp = IoGetNextIrpStackLocation(irp);
//
// Use QUERY_INTERFACE to get the address of the direct-call
// ACPI interfaces.
//
irpSp->MajorFunction = IRP_MJ_PNP;
irpSp->MinorFunction = IRP_MN_QUERY_INTERFACE;
irpSp->Parameters.QueryInterface.InterfaceType = (LPGUID)&GUID_MY_BUS_INTERFACE;
irpSp->Parameters.QueryInterface.Version = MY_BUS_INTERFACE_VERSION;
irpSp->Parameters.QueryInterface.Size = sizeof(MY_BUS_INTERFACE);
irpSp->Parameters.QueryInterface.Interface = (PINTERFACE)pMyBusInterface;
irpSp->Parameters.QueryInterface.InterfaceSpecificData = NULL;
//
// send the request down
//
NTSTATUS status = IoCallDriver(Adapter->NextDeviceObject, irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = ioStatus.Status;
}
else if (!NT_SUCCESS(status))
{
KdPrint(("IoCallDriver was not pending or success \n"));
break;
}
//
// test interface
//
char testData[260] = "TEST";
NTSTATUS interfaceTestStatus = (*myBusInterface.InterfaceTest)(
(PINTERFACE)pMyBusInterface,
(PVOID)testData,
strlen(testData)
);
if (interfaceTestStatus == -678)
{
KdPrint(("************************************** \n"));
KdPrint(("*** InterfaceTest SUCCESS *** \n"));
KdPrint(("************************************** \n"));
status = STATUS_SUCCESS;
}
else {
KdPrint(("************************************** \n"));
KdPrint(("*** InterfaceTest FAILURE *** \n"));
KdPrint(("************************************** \n"));
status = STATUS_UNSUCCESSFUL;
(*myBusInterface.InterfaceHeader.InterfaceDereference)(myBusInterface.InterfaceHeader.Context);
break;
}
//
// Dereference Interface
//
(*myBusInterface.InterfaceHeader.InterfaceDereference)(myBusInterface.InterfaceHeader.Context);

Keyboard filter driver unload BSOD

I have developed a keyboard filter driver that changes the keyboard button '1' (above the Q button) to '2'.
This driver works fine.
However, after Unload is executed, pressing the keyboard button causes BSOD.
If the driver is loaded and unloaded without pressing the keyboard button, it will be unloaded normally.
When I check it with Windbg, my driver's ReadCompletion () function is called even after it is unloaded.
I do not know why this is happening even though I've called IoDetachDevice () and IoDeleteDevice ().
In addition, after loading the driver, if you press the keyboard button '1' at the beginning, it does not change to '2'.
And then it changes very well.
I do not know what this is related to.
I hope you will find a solution to this problem.
please answer about my question.
Below is the source code.
#include <wdm.h>
typedef struct
{
PDEVICE_OBJECT NextLayerDeviceObject;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
const WCHAR next_device_name[] = L"\\Device\\KeyboardClass0";
const char dbg_name[] = "[Test]";
NTSTATUS IrpSkip(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
NTSTATUS ret = STATUS_SUCCESS;
PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
DbgPrint("%s IrpSkip() Start\n", dbg_name);
DbgPrint("%s IrpSkip() - MajorFunction %d\n", dbg_name, Stack->MajorFunction);
IoSkipCurrentIrpStackLocation(Irp);
ret = IoCallDriver(((PDEVICE_EXTENSION)(DeviceObject->DeviceExtension))->NextLayerDeviceObject, Irp);
DbgPrint("IoCallDriver return %x\n", ret);
DbgPrint("%s IrpSkip() End\n", dbg_name);
return ret;
}
NTSTATUS ReadCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context)
{
NTSTATUS ret = STATUS_SUCCESS;
PIO_STACK_LOCATION Stack;
unsigned char key[32];
DbgPrint("%s ReadCompletion() Start\n", dbg_name);
if (Irp->IoStatus.Status == STATUS_SUCCESS)
{
DbgPrint("%s ReadCompletion() - Success\n", dbg_name);
RtlCopyMemory(key, Irp->AssociatedIrp.SystemBuffer, 32);
DbgPrint("%s Data : %d %d %d %d %d %d %d %d\n", dbg_name, key[0], key[1], key[2], key[3], key[4], key[5], key[6], key[7]);
if (key[2] == 2)
{
key[2] = 3;
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, key, 32);
DbgPrint("%s Key '1' changed '2'\n", dbg_name);
}
}
//else if (Irp->IoStatus.Status == STATUS_PENDING)
else
{
DbgPrint("%s ReadCompletion() - Fail... %x\n", Irp->IoStatus.Status);
}
if (Irp->PendingReturned)
{
IoMarkIrpPending(Irp);
}
DbgPrint("%s ReadCompletion() End\n", dbg_name);
return Irp->IoStatus.Status;
}
NTSTATUS Read(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
NTSTATUS ret = STATUS_SUCCESS;
PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
DbgPrint("%s Read() Start\n", dbg_name);
PDEVICE_EXTENSION device_extension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
//IoCopyCurrentIrpStackLocationToNext(Irp);
PIO_STACK_LOCATION current_irp = IoGetCurrentIrpStackLocation(Irp);
PIO_STACK_LOCATION next_irp = IoGetNextIrpStackLocation(Irp);
*next_irp = *current_irp;
IoSetCompletionRoutine(Irp, ReadCompletion, DeviceObject, TRUE, TRUE, TRUE);
ret=IoCallDriver(((PDEVICE_EXTENSION)device_extension)->NextLayerDeviceObject, Irp);
DbgPrint("%s Read() End\n", dbg_name);
return ret;
}
NTSTATUS Unload(IN PDRIVER_OBJECT DriverObject)
{
NTSTATUS ret = STATUS_SUCCESS;
IoDetachDevice(((PDEVICE_EXTENSION)(DriverObject->DeviceObject->DeviceExtension))->NextLayerDeviceObject);
IoDeleteDevice(DriverObject->DeviceObject);
DbgPrint("%s Unload()...\n", dbg_name);
return ret;
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
NTSTATUS ret=STATUS_SUCCESS;
UNICODE_STRING _next_device_name;
DbgSetDebugFilterState(DPFLTR_DEFAULT_ID, DPFLTR_INFO_LEVEL, TRUE);
DbgPrint("%s DriverEntry() Start\n", dbg_name);
RtlInitUnicodeString(&_next_device_name, next_device_name);
for (int i = 0; i < IRP_MJ_MAXIMUM_FUNCTION ; i++)
{
DriverObject->MajorFunction[i] = IrpSkip;
}
DriverObject->DriverUnload = Unload;
DriverObject->MajorFunction[IRP_MJ_READ] = Read;
PDEVICE_OBJECT DeviceObject = 0;
PDEVICE_EXTENSION DeviceExtension;
ret = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), 0, FILE_DEVICE_KEYBOARD, 0, TRUE, &DeviceObject);
if (ret == STATUS_SUCCESS)
{
DbgPrint("%s DriverEntry() - IoCreateDevice() Success\n", dbg_name);
}
else
{
DbgPrint("%s DriverEntry() - IoCreateDevice() Fail\n", dbg_name);
return ret;
}
DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
DeviceObject->Flags |= (DO_BUFFERED_IO | DO_POWER_PAGABLE);
ret = IoAttachDevice(DeviceObject, &_next_device_name, &DeviceExtension->NextLayerDeviceObject);
if (ret == STATUS_SUCCESS)
{
DbgPrint("%s DriverEntry() - IoAttachDevice() Success\n", dbg_name);
}
else
{
DbgPrint("%s DriverEntry() - IoAttachDevice() Fail\n", dbg_name);
IoDeleteDevice(DriverObject->DeviceObject);
return ret;
}
DbgPrint("%s DriverEntry() End\n", dbg_name);
return ret;
}
Below is Windbg Call Stack.
0: kd> k
# ChildEBP RetAddr
00 82f33604 82eea083 nt!RtlpBreakWithStatusInstruction
01 82f33654 82eeab81 nt!KiBugCheckDebugBreak+0x1c
02 82f33a1c 82e4c5cb nt!KeBugCheck2+0x68b
03 82f33a1c 975e36e0 nt!KiTrap0E+0x2cf
WARNING: Frame IP not in any known module. Following frames may be wrong.
04 82f33aac 82e83933 <Unloaded_Test.sys>+0x16e0
05 82f33af0 8efed7a2 nt!IopfCompleteRequest+0x128
06 82f33b14 8eea7b74 kbdclass!KeyboardClassServiceCallback+0x2fa
07 82f33b78 82e831b5 i8042prt!I8042KeyboardIsrDpc+0x18c
08 82f33bd4 82e83018 nt!KiExecuteAllDpcs+0xf9
09 82f33c20 82e82e38 nt!KiRetireDpcList+0xd5
0a 82f33c24 00000000 nt!KiIdleLoop+0x38
The CallBack function does not seem to be released properly.
How do I solve this problem?
if you pass pointer to own driver body (ReadCompletion in your case) - driver must not be unloaded until this pointer is used (ReadCompletion called and returned your case)
as notified Harry Johnston need use IoSetCompletionRoutineEx - but documentation for this is bad and not explain all details. absolute mandatory study windows src files (WRK-v1.2 for example) and binary windows code. if you look for implementation of IoSetCompletionRoutineEx - you can view that this routine nothing do for prevent you driver for unloading. it simply allocate small memory block, save here your DeviceObject, Context and CompletionRoutine and set IopUnloadSafeCompletion as completion and pointer to allocated memory block as context.
what is IopUnloadSafeCompletion doing ?
NTSTATUS
IopUnloadSafeCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
PIO_UNLOAD_SAFE_COMPLETION_CONTEXT Usc = Context;
NTSTATUS Status;
ObReferenceObject (Usc->DeviceObject);
Status = Usc->CompletionRoutine (DeviceObject, Irp, Usc->Context);
ObDereferenceObject (Usc->DeviceObject);
ExFreePool (Usc);
return Status;
}
but this assume that Usc->DeviceObject IS VALID at calling IopUnloadSafeCompletion time. you can delete/de-reference DeviceObject inside CompletionRoutine , do some task which cause your driver unload - and will be no crash, because your CompletionRoutine protected by adding reference to your device. but if IopUnloadSafeCompletion will be called when your device already destroyed and driver unloaded - any way will be crash.
partial solution will be call ObfReferenceObject(DeviceObject) in your dispatch routine and ObfDereferenceObject(DeviceObject) in completion routine. this on practice resolve problem. so code must be next
NTSTATUS OnComplete(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID /*Context*/)
{
ObfDereferenceObject(DeviceObject);// !!!
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
if (Irp->PendingReturned)
{
IrpSp->Control |= SL_PENDING_RETURNED;
}
if (IrpSp->MajorFunction == IRP_MJ_READ &&
Irp->IoStatus.Status == STATUS_SUCCESS &&
(Irp->Flags & IRP_BUFFERED_IO))
{
if (ULONG n = (ULONG)Irp->IoStatus.Information / sizeof(KEYBOARD_INPUT_DATA))
{
PKEYBOARD_INPUT_DATA pkid = (PKEYBOARD_INPUT_DATA)Irp->AssociatedIrp.SystemBuffer;
do
{
DbgPrint("Port%x> %x %x\n", pkid->UnitId, pkid->MakeCode, pkid->Flags);
} while (pkid++, --n);
}
}
return ContinueCompletion;
}
NTSTATUS KbdDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
IoCopyCurrentIrpStackLocationToNext(Irp);
if (0 > IoSetCompletionRoutineEx(DeviceObject, Irp, OnComplete, NULL, TRUE, TRUE, TRUE))
{
IoSkipCurrentIrpStackLocation(Irp);
}
else
{
ObfReferenceObject(DeviceObject);// !!!
}
return IofCallDriver(
reinterpret_cast<DEVICE_EXTENSION*>(DeviceObject->DeviceExtension)->_NextDeviceObject, Irp);
}
call ObfReferenceObject(DeviceObject); in KbdDispatch prevent unload your driver until ObfDereferenceObject(DeviceObject); called inside OnComplete.
you can ask for what in this case IoSetCompletionRoutineEx at all, if we yourself call ObfReferenceObject / ObfDereferenceObject ? because if DriverUnload already called - all your code hold only on single reference on DeviceObject - so when you call ObfDereferenceObject(DeviceObject); from OnComplete - your device will be deleted and driver unloaded inside ObfDereferenceObject and finally this routine returned to your unloaded code. so sense of IoSetCompletionRoutineEx is protect your completion routine.
but need understand that this is anyway not 100% correct solution. call IoDetachDevice/IoDeleteDevice for attached device not correct from DriverUnload. ( this must be called from IRP_MN_REMOVE_DEVICE or FAST_IO_DETACH_DEVICE callback)
assume next scenario - somebody call NtReadFile for device A to which your B device is attached. NtReadFile get pointer to your B device via IoGetRelatedDeviceObject. internally this routine call IoGetAttachedDevice. read this:
IoGetAttachedDevice does not increment the reference count on the
device object. (Thus no matching call to ObDereferenceObject is
required.) Callers of IoGetAttachedDevice must ensure that no device
objects are added to or removed from the stack while
IoGetAttachedDevice is executing. Callers that cannot do this must use
IoGetAttachedDeviceReference instead.
assume that while NtReadFile using pointer to your B device, another thread called your DriverUnload which delete B device and unload driver. handle/file object exist on device A - this hold it and prevent from unloading. but your attached B device not hold nothing. as result if NtReadFile or any another I/O subsystem routine which use your device execute in concurrent with DriverUnload where you call detach/delete device - system can crash already inside NtReadFile code. and you nothing can do with this. only one way after call IoDetachDevice some(how many ?!) time wait before call IoDeleteDevice. fortunately possibility of this case very low usual.
so try understand - system can crash in NtReadFile already. even if your Dispatch called - your DeviceObject can be deleted/not valid already or driver unloaded during dispatch routine. only after you call ObfReferenceObject(DeviceObject) all become ok. and all this problem because you try detach attached device in DriverUnload (windows not designed for this).
also can noted many another errors in your code. say completion routine must not return Irp->IoStatus.Status it must return or StopCompletion (i.e STATUS_MORE_PROCESSING_REQUIRED ) or any another value - usual ContinueCompletion (i.e STATUS_CONTINUE_COMPLETION or 0) also need not hardcode "\\Device\\KeyboardClass0" but use IoRegisterPlugPlayNotification with GUID_CLASS_KEYBOARD if you not wdm driver. also for xp need special handler for IRP_MJ_POWER ( Passing Power IRPs ) but may be this is already not actual if xp support not actual.
code example can be look like:
struct DEVICE_EXTENSION
{
PDEVICE_OBJECT _NextDeviceObject;
};
NTSTATUS KbdPower(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
PoStartNextPowerIrp(Irp);
IoSkipCurrentIrpStackLocation(Irp);
return PoCallDriver(
reinterpret_cast<DEVICE_EXTENSION*>(DeviceObject->DeviceExtension)->_NextDeviceObject, Irp);
}
NTSTATUS OnComplete(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID /*Context*/)
{
ObfDereferenceObject(DeviceObject);
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
if (Irp->PendingReturned)
{
IrpSp->Control |= SL_PENDING_RETURNED;
}
if (IrpSp->MajorFunction == IRP_MJ_READ &&
Irp->IoStatus.Status == STATUS_SUCCESS &&
(Irp->Flags & IRP_BUFFERED_IO))
{
if (ULONG n = (ULONG)Irp->IoStatus.Information / sizeof(KEYBOARD_INPUT_DATA))
{
PKEYBOARD_INPUT_DATA pkid = (PKEYBOARD_INPUT_DATA)Irp->AssociatedIrp.SystemBuffer;
do
{
DbgPrint("Port%x> %x %x\n", pkid->UnitId, pkid->MakeCode, pkid->Flags);
} while (pkid++, --n);
}
}
return ContinueCompletion;
}
NTSTATUS KbdDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
IoCopyCurrentIrpStackLocationToNext(Irp);
if (0 > IoSetCompletionRoutineEx(DeviceObject, Irp, OnComplete, NULL, TRUE, TRUE, TRUE))
{
IoSkipCurrentIrpStackLocation(Irp);
}
else
{
ObfReferenceObject(DeviceObject);
}
return IofCallDriver(
reinterpret_cast<DEVICE_EXTENSION*>(DeviceObject->DeviceExtension)->_NextDeviceObject, Irp);
}
NTSTATUS KbdNotifyCallback(PDEVICE_INTERFACE_CHANGE_NOTIFICATION Notification, PDRIVER_OBJECT DriverObject)
{
if (::RtlCompareMemory(&Notification->Event, &GUID_DEVICE_INTERFACE_ARRIVAL, sizeof(GUID)) == sizeof(GUID))
{
DbgPrint("++%wZ\n", Notification->SymbolicLinkName);
HANDLE hFile;
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, Notification->SymbolicLinkName, OBJ_CASE_INSENSITIVE };
IO_STATUS_BLOCK iosb;
if (0 <= IoCreateFile(&hFile, SYNCHRONIZE, &oa, &iosb, 0, 0, FILE_SHARE_VALID_FLAGS, FILE_OPEN, 0, 0, 0, CreateFileTypeNone, 0, IO_ATTACH_DEVICE))
{
PFILE_OBJECT FileObject;
NTSTATUS status = ObReferenceObjectByHandle(hFile, 0, 0, 0, (void**)&FileObject, 0);
NtClose(hFile);
if (0 <= status)
{
PDEVICE_OBJECT DeviceObject, TargetDevice = IoGetAttachedDeviceReference(FileObject->DeviceObject);
ObfDereferenceObject(FileObject);
if (0 <= IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), 0,
TargetDevice->DeviceType,
TargetDevice->Characteristics & (FILE_REMOVABLE_MEDIA|FILE_DEVICE_SECURE_OPEN),
FALSE, &DeviceObject))
{
DeviceObject->Flags |= TargetDevice->Flags &
(DO_BUFFERED_IO|DO_DIRECT_IO|DO_SUPPORTS_TRANSACTIONS|DO_POWER_PAGABLE|DO_POWER_INRUSH);
DEVICE_EXTENSION* pExt = (DEVICE_EXTENSION*)DeviceObject->DeviceExtension;
if (0 > IoAttachDeviceToDeviceStackSafe(DeviceObject, TargetDevice, &pExt->_NextDeviceObject))
{
IoDeleteDevice(DeviceObject);
}
else
{
DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
DbgPrint("++DeviceObject<%p> %x\n", DeviceObject, DeviceObject->Flags);
}
}
ObfDereferenceObject(TargetDevice);
}
}
}
return STATUS_SUCCESS;
}
PVOID NotificationEntry;
void KbdUnload(PDRIVER_OBJECT DriverObject)
{
DbgPrint("KbdUnload(%p)\n", DriverObject);
if (NotificationEntry) IoUnregisterPlugPlayNotification(NotificationEntry);
PDEVICE_OBJECT NextDevice = DriverObject->DeviceObject, DeviceObject;
while (DeviceObject = NextDevice)
{
NextDevice = DeviceObject->NextDevice;
DbgPrint("--DeviceObject<%p>\n", DeviceObject);
IoDetachDevice(reinterpret_cast<DEVICE_EXTENSION*>(DeviceObject->DeviceExtension)->_NextDeviceObject);
IoDeleteDevice(DeviceObject);
}
}
NTSTATUS KbdInit(PDRIVER_OBJECT DriverObject, PUNICODE_STRING /*RegistryPath*/)
{
DbgPrint("KbdInit(%p)\n", DriverObject);
DriverObject->DriverUnload = KbdUnload;
#ifdef _WIN64
__stosq
#else
__stosd
#endif
((PULONG_PTR)DriverObject->MajorFunction, (ULONG_PTR)KbdDispatch, RTL_NUMBER_OF(DriverObject->MajorFunction));
ULONG MajorVersion;
PsGetVersion(&MajorVersion, 0, 0, 0);
if (MajorVersion < 6) DriverObject->MajorFunction[IRP_MJ_POWER] = KbdPower;
IoRegisterPlugPlayNotification(
EventCategoryDeviceInterfaceChange,
PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
(void*)&GUID_CLASS_KEYBOARD, DriverObject,
(PDRIVER_NOTIFICATION_CALLBACK_ROUTINE)KbdNotifyCallback,
DriverObject, &NotificationEntry);
return STATUS_SUCCESS;
}
This sounds like it explains your problem:
Note Only a driver that can guarantee it will not be unloaded before its completion routine finishes can use IoSetCompletionRoutine. Otherwise, the driver must use IoSetCompletionRoutineEx, which prevents the driver from unloading until its completion routine executes.
(From the MSDN documentation for IoSetCompletionRoutine.)
PS: the one-keystroke delay in the functionality taking effect is to be expected, because your driver isn't hooked into the read operation that was already in progress when it was loaded. I'm not sure whether there's any reasonable way to do that.

File System Filter Driver - Deny file creation

I created a file system filter driver.
My driver filters IRP_MJ_CREATE and prints the file name.
NTSTATUS DispatchCreate(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp)
{
PFILE_OBJECT FileObject = IoGetCurrentIrpStackLocation(Irp)->FileObject;
DbgPrint("DispatchCreate() : %wZ\n", &FileObject->FileName;)
return DispatchPassThrough(DeviceObject, Irp);
}
This works fine.
And now I want to get Access Denied on every request to create a new file.
(If possible, 'you do not have permission.')
So I tried a few things.
First, I did the following.
NTSTATUS DispatchCreate(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp)
{
PFILE_OBJECT FileObject = IoGetCurrentIrpStackLocation(Irp)->FileObject;
PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
ULONG Option = Stack->Parameters.Create.Options;
if ((Option >> 24) == FILE_CREATE)
{
DbgPrint("DispatchCreate() : File Create Denied, %wZ, %x \n", &FileObject->FileName, Option);
return STATUS_ACCESS_VIOLATION; // or any error code
}
return DispatchPassThrough(DeviceObject, Irp);
}
This worked fine, but it worked a bit strange.
For example, if you do not have administrator privileges, you have a little bit of a say when you try to create something in "C:\".
At this time, I don't know the FileObject may be deleted normally.
So I made the following changes.
NTSTATUS DispatchCreate(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp)
{
PFILE_OBJECT FileObject = IoGetCurrentIrpStackLocation(Irp)->FileObject;
PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
ULONG Option = Stack->Parameters.Create.Options;
if ((Option >> 24) == FILE_CREATE)
{
DbgPrint("DispatchCreate() : File Create Denied, %wZ, %x \n", &FileObject->FileName, Option);
Irp->IoStatus.Status = STATUS_ACCESS_VIOLATION;
return Irp->IoStatus.Status;
}
return DispatchPassThrough(DeviceObject, Irp);
}
However, a slightly different error message occurs.
I want it to behave exactly the same as when "Access Denied" occurs because I do not have normal privileges.
And there is another question.
Unlike other Dispatch routines, IRP_MJ_CREATE and IRP_MJ_CLOSE do not need IoCompleteRequest().
I confirmed that the handle was returned normally even if I only had the following part. (In user mode.)
return STATUS_SUCCESS;
Thank you for reading.
Please answer about my question.
if you need deny some request in filter - not need pass it to attached device - you need yourself set error status and complete IRP
Irp->IoStatus.Status = STATUS_ACCESS_DENIED;
IofCompleteRequest(Irp);// !!!
return STATUS_ACCESS_DENIED; // ! not Irp->IoStatus.Status - you can not access Irp after call IofCompleteRequest
Unlike other Dispatch routines, IRP_MJ_CREATE and IRP_MJ_CLOSE do not
need IoCompleteRequest().
of course this is absolute false. every Irp must be completed by call IofCompleteRequest

Filter hook driver: dispatch routine isn't called

I'm trying to write legacy filter-hook driver, firewall-like: look for dst port and block it.
But when packets are sent, dispatcher routine isn't called.
Register dispatch:
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DrvDispatch;
Start ipfilter driver:
C:\Users\unnamed>net start ipfilterdriver
After that, launch debug driver via Visual DDK. Driver load successfully, but breakpoint in dispatcher isn't reached. What am I doing wrong?
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
UNICODE_STRING DeviceName,Win32Device;
PDEVICE_OBJECT DeviceObject = NULL;
NTSTATUS status;
unsigned i;
RtlInitUnicodeString(&DeviceName,L"\\Device\\driver10");
RtlInitUnicodeString(&Win32Device,L"\\DosDevices\\driver10");
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
DriverObject->MajorFunction[i] = driver1DefaultHandler;
/*
DriverObject->MajorFunction[IRP_MJ_CREATE] = driver1CreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = driver1CreateClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DrvDispatch;
*/
status = IoCreateDevice(DriverObject, 0, &DeviceName,
FILE_DEVICE_DRVFLTIP, 0, FALSE,
&DeviceObject);
if (NT_SUCCESS(status)) {
status = IoCreateSymbolicLink(&Win32Device, &DeviceName);
if (!NT_SUCCESS(status))
dprintf("DrvFltIp.SYS: IoCreateSymbolicLink failed\n");
DriverObject->MajorFunction[IRP_MJ_CREATE] =
DriverObject->MajorFunction[IRP_MJ_CLOSE] =
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DrvDispatch;
DriverObject->DriverUnload = driver1Unload;
}
if (!NT_SUCCESS(status)) {
dprintf("Error in initialization. Unloading...");
driver1Unload(DriverObject);
}
if (!DeviceObject)
return STATUS_UNEXPECTED_IO_ERROR;
/*
DeviceObject->Flags |= DO_DIRECT_IO;
DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
DeviceObject->AlignmentRequirement = FILE_WORD_ALIGNMENT;
*/
DbgPrint("Driver started\n");
return status;
}
NTSTATUS DrvDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
dprintf("DrvDispatch called\n");
PIO_STACK_LOCATION irpStack;
PVOID ioBuffer;
ULONG inputBufferLength;
ULONG outputBufferLength;
ULONG ioControlCode;
NTSTATUS ntStatus;
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
irpStack = IoGetCurrentIrpStackLocation(Irp);
switch (irpStack->MajorFunction) {
case IRP_MJ_CREATE:
dprintf("DrvFltIp.SYS: IRP_MJ_CREATE\n");
break;
case IRP_MJ_CLOSE:
dprintf("DrvFltIp.SYS: IRP_MJ_CLOSE\n");
break;
case IRP_MJ_DEVICE_CONTROL:
dprintf("DrvFltIp.SYS: IRP_MJ_DEVICE_CONTROL\n");
break;
}
ntStatus = Irp->IoStatus.Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return ntStatus;
}
Just forgot register filter-hook callback function in the DriverEntry:
Registering and Clearing a Filter Hook

Resources